| """Conceptual/architecture figures for the paper (no model server needed). |
| |
| Produces two vector figures under paper/figures/: |
| * architecture.pdf/png -- the three memory arms feeding a draft-then-verify |
| speculative decoder (no-memory vs frozen global |
| datastore vs our persistent per-user evicting store). |
| * safety_gate.pdf/png -- the speculative-execution decision flow contrasting |
| naive-exec, confidence-gate, and idempotency-gate. |
| """ |
| from __future__ import annotations |
|
|
| from pathlib import Path |
|
|
| import matplotlib |
| matplotlib.use("Agg") |
| import matplotlib.pyplot as plt |
| from matplotlib.patches import FancyBboxPatch, FancyArrowPatch |
|
|
| ROOT = Path(__file__).resolve().parent.parent |
| FIGS = ROOT / "figures" |
|
|
| BLUE = "#1b6ca8" |
| RED = "#d1495b" |
| GREY = "#888888" |
| GREEN = "#2e8b57" |
| DARK = "#2b2b2b" |
|
|
|
|
| def _box(ax, x, y, w, h, text, fc="white", ec=DARK, fs=8.5, lw=1.3, tc=DARK, |
| style="round,pad=0.02,rounding_size=0.06", bold=False): |
| p = FancyBboxPatch((x, y), w, h, boxstyle=style, fc=fc, ec=ec, lw=lw, |
| mutation_aspect=1.0, zorder=2) |
| ax.add_patch(p) |
| ax.text(x + w / 2, y + h / 2, text, ha="center", va="center", fontsize=fs, |
| color=tc, zorder=3, fontweight="bold" if bold else "normal", |
| wrap=True) |
| return p |
|
|
|
|
| def _arrow(ax, xy1, xy2, color=DARK, lw=1.4, style="-|>", ls="-", rad=0.0): |
| a = FancyArrowPatch(xy1, xy2, arrowstyle=style, mutation_scale=12, |
| color=color, lw=lw, linestyle=ls, zorder=1, |
| connectionstyle=f"arc3,rad={rad}") |
| ax.add_patch(a) |
|
|
|
|
| def architecture(): |
| fig, ax = plt.subplots(figsize=(10.5, 4.6)) |
| ax.set_xlim(0, 10.5) |
| ax.set_ylim(0, 4.6) |
| ax.axis("off") |
|
|
| |
| _box(ax, 0.15, 2.0, 1.5, 0.8, |
| "User $u$ query\n(session $s$)", fc="#eef3f7", fs=8.5) |
| ax.text(0.9, 1.75, "recurring, evolving\nper-user tool use", |
| ha="center", va="top", fontsize=6.8, color=GREY, style="italic") |
|
|
| |
| arm_x = 2.35 |
| arms = [ |
| (3.55, GREY, "No memory", |
| "schema-only draft\n(zero history)"), |
| (2.30, RED, "Static datastore (ToolSpec-style)", |
| "one global store,\nbuilt once then FROZEN"), |
| (1.05, BLUE, "Ours: persistent per-user memory", |
| "grows across sessions,\nLRU/LFU eviction,\npersonalized retrieval"), |
| ] |
| for y, color, title, sub in arms: |
| _box(ax, arm_x, y, 3.0, 1.05, "", ec=color, lw=1.6, |
| fc=color + "14" if False else "white") |
| ax.text(arm_x + 0.12, y + 0.86, title, ha="left", va="center", |
| fontsize=8.2, color=color, fontweight="bold") |
| ax.text(arm_x + 0.12, y + 0.36, sub, ha="left", va="center", |
| fontsize=7.2, color=DARK) |
| _arrow(ax, (1.65, 2.4), (arm_x, y + 0.55), color=color, lw=1.2, |
| rad=0.05) |
|
|
| |
| dec_x = 6.15 |
| _box(ax, dec_x, 1.75, 1.95, 1.3, |
| "Draft-then-verify\nspeculative\ndecoder", fc="#fff7e6", |
| ec="#c8860a", fs=8.5, bold=True) |
| for y, color, *_ in arms: |
| _arrow(ax, (arm_x + 3.0, y + 0.55), (dec_x, 2.4), color=color, lw=1.1, |
| ls="--", rad=-0.05) |
| ax.text(dec_x + 0.98, 3.12, "draft tool call", ha="center", va="bottom", |
| fontsize=7, color="#c8860a") |
|
|
| |
| _box(ax, dec_x - 0.1, 0.35, 2.15, 0.85, |
| "Served target model\n(gpt-oss-120b, greedy)", fc="#f2f2f2", fs=7.6) |
| _arrow(ax, (dec_x + 0.95, 1.75), (dec_x + 0.95, 1.2), color=DARK, lw=1.2) |
| _arrow(ax, (dec_x + 1.15, 1.2), (dec_x + 1.15, 1.75), color=DARK, lw=1.2) |
| ax.text(dec_x + 1.35, 1.47, "verify\n(token LCP)", ha="left", va="center", |
| fontsize=6.6, color=DARK) |
|
|
| |
| _box(ax, 8.55, 1.75, 1.8, 1.3, |
| "Accepted tokens\n= mean accepted\ntokens (MAT)", fc="#eaf5ee", |
| ec=GREEN, fs=8.2, tc=DARK) |
| _arrow(ax, (dec_x + 1.95, 2.4), (8.55, 2.4), color=GREEN, lw=1.6) |
|
|
| |
| _arrow(ax, (dec_x + 0.05, 1.9), (arm_x + 3.0, 1.5), color=BLUE, lw=1.1, |
| ls=":", rad=0.28) |
| ax.text(5.1, 1.05, "observe target $\\rightarrow$ write back\n" |
| "(personal store only; static store frozen after warmup)", |
| ha="center", va="center", fontsize=6.8, color=BLUE, style="italic") |
|
|
| ax.set_title("Persistent, personalized tool-call memory feeding a " |
| "speculative decoder", fontsize=10.5, fontweight="bold") |
| fig.tight_layout() |
| FIGS.mkdir(parents=True, exist_ok=True) |
| fig.savefig(FIGS / "architecture.pdf") |
| fig.savefig(FIGS / "architecture.png", dpi=150) |
| plt.close(fig) |
| print("wrote architecture.{pdf,png}") |
|
|
|
|
| def safety_gate(): |
| fig, ax = plt.subplots(figsize=(9.5, 4.2)) |
| ax.set_xlim(0, 9.5) |
| ax.set_ylim(0, 4.2) |
| ax.axis("off") |
|
|
| _box(ax, 0.2, 1.75, 1.75, 0.9, |
| "Memory proposes\na drafted\ntool call", fc="#eef3f7", fs=8.2) |
|
|
| |
| _box(ax, 2.35, 1.75, 1.6, 0.9, "Tool\nidempotent?", fc="#fff7e6", |
| ec="#c8860a", fs=8.4, bold=True) |
| _arrow(ax, (1.95, 2.2), (2.35, 2.2)) |
|
|
| |
| _box(ax, 4.35, 3.0, 2.4, 0.85, |
| "Speculatively EXECUTE\n(wrong $\\Rightarrow$ just re-run)", |
| fc="#eaf5ee", ec=GREEN, fs=8.0) |
| _arrow(ax, (3.95, 2.5), (4.35, 3.35), color=GREEN, rad=0.15) |
| ax.text(4.15, 3.05, "yes", fontsize=7.5, color=GREEN, ha="left") |
|
|
| |
| ax.text(4.15, 1.55, "no", fontsize=7.5, color=RED, ha="left") |
| _box(ax, 4.35, 0.35, 2.4, 2.2, "", ec=RED, lw=1.4, fc="#fdeef1") |
| ax.text(5.55, 2.35, "Non-idempotent tool\n(policy decides)", |
| ha="center", va="center", fontsize=8.0, color=RED, |
| fontweight="bold") |
| _arrow(ax, (3.95, 1.95), (4.35, 1.6), color=RED, rad=-0.12) |
|
|
| ax.text(4.5, 1.75, "naive:", fontsize=7.6, color=DARK, ha="left", |
| fontweight="bold") |
| ax.text(4.95, 1.75, "execute anyway", fontsize=7.6, color=RED, ha="left") |
| ax.text(4.5, 1.32, "conf-gate:", fontsize=7.6, color=DARK, ha="left", |
| fontweight="bold") |
| ax.text(5.15, 1.32, "execute if conf$\\geq\\tau$", fontsize=7.6, |
| color="#c8860a", ha="left") |
| ax.text(4.5, 0.89, "ours:", fontsize=7.6, color=DARK, ha="left", |
| fontweight="bold") |
| ax.text(4.9, 0.89, "NEVER execute;", fontsize=7.6, color=GREEN, ha="left") |
| ax.text(4.9, 0.58, "draft only, wait for verify", fontsize=7.0, |
| color=GREEN, ha="left") |
|
|
| |
| _box(ax, 7.15, 2.55, 2.15, 0.95, |
| "Irreversible harm\nif draft wrong\n(severity-weighted cost)", |
| fc="#fdeef1", ec=RED, fs=7.6, tc=RED) |
| _box(ax, 7.15, 0.65, 2.15, 0.95, |
| "Zero cost, keeps\nlatency win on\nsafe tools", fc="#eaf5ee", |
| ec=GREEN, fs=7.8, tc=DARK) |
| _arrow(ax, (6.75, 1.65), (7.15, 2.9), color=RED, rad=0.1, lw=1.2) |
| _arrow(ax, (6.75, 0.95), (7.15, 1.1), color=GREEN, rad=-0.05, lw=1.4) |
|
|
| ax.set_title("Idempotency-gated speculative execution: naive and " |
| "confidence gates still leak irreversible cost", |
| fontsize=10.0, fontweight="bold") |
| fig.tight_layout() |
| FIGS.mkdir(parents=True, exist_ok=True) |
| fig.savefig(FIGS / "safety_gate.pdf") |
| fig.savefig(FIGS / "safety_gate.png", dpi=150) |
| plt.close(fig) |
| print("wrote safety_gate.{pdf,png}") |
|
|
|
|
| if __name__ == "__main__": |
| architecture() |
| safety_gate() |
|
|