Skip to content

perf: improve prompt-prefix caching for the agentic model#100

Merged
DenysKuchma merged 2 commits into
mainfrom
perf/agentic-prompt-prefix-caching
Jul 20, 2026
Merged

perf: improve prompt-prefix caching for the agentic model#100
DenysKuchma merged 2 commits into
mainfrom
perf/agentic-prompt-prefix-caching

Conversation

@DavertMik

Copy link
Copy Markdown
Contributor

Implements plans/025-agentic-prompt-prefix-caching.md.

Problem

In a recent session the two models diverged sharply on prompt cache reuse:

Model Calls Input Cached Cache rate
default (tester/researcher/navigator) 219 2.74M 2.05M 75%
agentic (planner/pilot) 47 192K 18K 9%

The 66-point gap is exactly the three properties the default-model agents already have and the agentic ones lacked: a byte-identical prefix, an unchanging tool list, and append-only history.

Changes

Pilot — static-first prompts. getSystemPrompt, buildVerdictSystemPrompt and buildResetSystemPrompt now open with their static supervisor/evidence/decision bodies and close with the per-test task block. The dynamic parts of buildSharedEvidenceRules (scenario, deletion scope, expected results) moved into a new buildTaskContext(task) that both verdict and reset prompts append last. The verdict opener no longer embeds Tester requested ${type} — the user message already states it.

Pilot — constant tool set. sendToPilot always builds pickPlanningTools() + buildPreconditionTool(task) and gates calls with toolChoice: 'auto' | 'none'. Previously planTest sent tools, reviewNewPage sent none, and analyzeProgress sent them only on failures; tool definitions are part of the cached prefix, so every flip invalidated the whole conversation. reviewNewPage now passes task, so task became required on the options object.

Pilot — append-only history. Dropped the three cleanupTag calls. Pilot conversations are light by design (tool summaries, no HTML) and bounded by the tester iteration cap.

Planner — stable prefix order. Messages now run system → static rules → static output format → page research → approach + URL context → the rest. The output-format block became getTasksMessage(), and its hasCurrentPlan conditional collapsed into one sentence covering both fresh and expanding plans.

Disambiguator routing. getAgenticModel()getModelForAgent(). These ~350-token one-shot prompts are below the 1024-token cache floor and don't need the expensive model. Cost fix, not a cache fix — the one commit-separable piece here.

Prompts are reordered, not rewritten; total length is unchanged. No provider.ts change was needed: buildGenerateConfig spreads ...options after the toolChoice: 'auto' default. Per the plan, this stays provider-agnostic — no providerOptions, no promptCacheKey.

Trade-offs

  • Task context moves from the top of the system prompts to the end. End-of-prompt is still a high-attention position, but verdict/planning behavior could shift.
  • Planner's output-format instructions now precede the page data. generateObject's schema still constrains output shape, but adherence to the prose rules could weaken on some models.
  • Without cleanupTag, pilot conversations grow by the trimmed blocks (~1-3K tokens on long tests), traded for cache hits across the conversation.
  • A provider that ignores tool_choice: none could still emit a tool call on review-only turns; maxToolRoundtrips: 0 keeps that bounded to one roundtrip.

Testing

bun test tests/unit/ tests/integration/ — 871 pass, 0 fail. The planner integration tests needed no updates: they assert against the concatenated prompt rather than message order.

Not yet done, and the real gate on this change:

  • Regression replay comparing the per-model token summary (agentic model expected ≥40% cached, up from 9%, with the same pass/fail outcomes).
  • Langfuse spot-check that consecutive pilot.analyze calls show growing cached-token counts instead of resets to 0.

🤖 Generated with Claude Code

Planner and Pilot ran at a 9% prompt cache rate while the default-model
agents sat at 75%. The gap was three missing properties: a stable
byte-identical prefix, an unchanging tool list, and append-only history.

- Pilot: system, verdict and reset prompts now open with their static
  bodies and close with the per-test task block. Extract buildTaskContext()
  for the dynamic part; drop the request type from the verdict opener since
  the user message already carries it.
- Pilot: always send the same tool set and gate it with toolChoice
  'auto'/'none' instead of flipping the tool list between calls, which
  invalidated the whole conversation's cache.
- Pilot: stop rewriting history via cleanupTag. Pilot conversations are
  light by design; the trimmed KBs cost more re-tokenized than they save.
- Planner: order messages system -> static rules -> static output format ->
  page research -> approach + URL context, so re-plans of the same page
  reuse the research prefix.
- Route the click disambiguator off the agentic model: ~350-token one-shot
  prompts can never cache and don't need the expensive model.

Prompts are reordered, not rewritten.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown

Explorbot Self-Regression

Commit 7c5312f · run

Scenario Result Attempts Duration
basic (native) PASS 3/3 31m
experience: control OK — failed as expected 1/1 1m
experience: seeded PASS 1/3 1m

Attempt details

  • basic (native) attempt 1 — FAIL: login evidence: PASS (post-login plan=true, post-login research=true); research: PASS (files=8, wellFormed=true, keywords=7/3); scenarios: PASS (tests=5/5, features=4/3); tests passed: FAIL (4 passed, 1 failed (reporter: 4 passed, 1 failed))
  • basic (native) attempt 2 — FAIL: login evidence: PASS (post-login plan=true, post-login research=true); research: PASS (files=7, wellFormed=true, keywords=7/3); scenarios: PASS (tests=6/5, features=4/3); tests passed: FAIL (3 passed, 2 failed (reporter: 3 passed, 2 failed))
  • basic (native) attempt 3 — PASS: login evidence: PASS (post-login plan=true, post-login research=true); research: PASS (files=7, wellFormed=true, keywords=7/3); scenarios: PASS (tests=7/5, features=4/3); tests passed: PASS (5 passed, 0 failed (reporter: 5 passed, 0 failed))
  • experience: control attempt 1 — PASS: control: OK — failed as expected (0 passed, 1 failed)
  • experience: seeded attempt 1 — PASS: seeded: PASS (1 passed, 0 failed)

Session analysis — basic (native):

Session Analysis

The Issues page core functionality is operational. All five primary flows (create, filter by label, search, filter by status, view details) execute successfully. One UX gap exists around user feedback after creating an issue.

Coverage

  • Pages: /issues, /issues/7
  • Features: Create issue, Filter by label, Search, Filter by status, View issue details

What works

  • Issue creationET-1 Create a new issue via New Issue button — issue appears in table at index Add deep interactive exploration mode for researcher #7
  • Label filteringET-2 Filter issues by bug label — table filters to 2 bug-labeled issues
  • SearchET-3 Search for issues using search box — returns matching results and shows empty state
  • Status filteringET-4 Filter issues by status using combobox — URL updates to status=open, table shows only open issues
  • Issue detailsET-5 Open and view issue details — detail page loads with title, description, status visible

UX issues

  • Issue creation — No success message or toast notification appears after submitting the create form (ET-1 Create a new issue via New Issue button)

Execution Issues

  • ET-2 Filter issues by bug label — initial click on filter button failed, recovered by clicking summary element directly

@DenysKuchma
DenysKuchma deployed to regression July 20, 2026 11:31 — with GitHub Actions Active
@DenysKuchma
DenysKuchma merged commit a013f10 into main Jul 20, 2026
3 of 4 checks passed
@DenysKuchma
DenysKuchma deleted the perf/agentic-prompt-prefix-caching branch July 20, 2026 12:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants