Skip to content

Keep the system prompt stable across turns so prompt caching survives - #971

Merged
16francej merged 1 commit into
mainfrom
prompt-cache-stability
Sep 8, 2026
Merged

Keep the system prompt stable across turns so prompt caching survives#971
16francej merged 1 commit into
mainfrom
prompt-cache-stability

Conversation

@16francej

@16francej 16francej commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Why

Prompt caches are prefix-based: any change to the system prompt invalidates every cached message block behind it. The per-turn blocks (local time at minute resolution, memory recall, onboarding) were appended to the end of the system prompt, so no session got a cache read at turn start and deep sessions re-wrote their whole history on every turn. Replayed history also never byte-matched the live prompt, because the user entry stored only the input text, not the environment note that was sent with it.

Separately, the compaction budget for 1M-window models was 436k tokens, so long DM sessions sat at 250k+ tokens per call and never compacted.

What

  • The per-turn blocks ride the <environment> note on the user message. Per-scope state and policy (logins, connected apps, roster, obligations, automation directives) stay in the system prompt, where they change rarely and keep system priority. The note is persisted on the user entry, replayed when history is reconstructed, and counted by the token estimator.
  • The split-at-boundary machinery is removed. The same option (PI_SYSTEM_CACHE_SPLIT) now asks pi-ai for long cache retention (1h ttl on Anthropic system, tools and last message; 24h prompt_cache_retention on OpenAI; pi-ai's per-provider compat flags decide where it is safe), so turns tens of minutes apart still hit.
  • contextTokenBudgetForModel is capped at 150k tokens.

Verified

Live against the API with the same 23k-token conversation, second turn one minute later:

shape cache read cache write
before (tail in system) 8,079 15,501
after (tail in user message) 23,577 55

New tests: the system prompt is byte-identical across turns a minute apart; replay reproduces the environment note; the estimator counts it; the budget cap. Typecheck, lint and the orchestrator, memory, prompt, compaction, tape and replay suites pass.

Trade-offs: user entries grow by the environment note (a few k tokens per turn, itself cached); 1h cache writes cost 2× base input instead of 1.25×, paid only on the new suffix.

@16francej
16francej force-pushed the prompt-cache-stability branch 2 times, most recently from 604c16c to 7ba9be4 Compare September 8, 2026 04:57
Prompt caches are prefix-based: any change to the system prompt invalidates
every cached message block behind it. The per-turn blocks (local time at
minute resolution, memory recall, onboarding) were appended to the end of the
system prompt, so in production no session got a cache read at turn start and
deep sessions re-wrote 150-300k tokens of history on every turn. Those blocks
now ride the per-turn <environment> note on the user message; per-scope state
and policy (logins, connected apps, roster, obligations, automation
directives) stay in the system prompt, where they change rarely and keep
system priority. The note is persisted on the user entry, replayed when
history is reconstructed, and counted by the token estimator, so reconstructed
history matches what was sent and compaction sees the real history size.

With the system prompt stable, the split-at-boundary machinery is gone. The
same option now asks pi-ai for long cache retention (1h ttl on Anthropic
system, tools and last message; 24h retention on OpenAI; pi-ai's per-provider
compat flags decide where it is safe), so turns 10-70 minutes apart still hit.
1h writes cost 2x base input instead of 1.25x, paid only on the new suffix.

Cap the compaction budget at 150k tokens. Half of a 1M window let sessions
grow to 300k+ tokens per call without ever compacting.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@16francej
16francej force-pushed the prompt-cache-stability branch from 7ba9be4 to 5738021 Compare September 8, 2026 05:00
@16francej
16francej merged commit b813bc6 into main Sep 8, 2026
20 checks passed
@16francej
16francej deleted the prompt-cache-stability branch September 8, 2026 05:24
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.

1 participant