Analysis · NousResearch/hermes-agent · commit b2360086

Where the cache
stops paying.

KV caching is not a knob you tune. It is a prefix match — and its efficiency is decided by a single number: the position of the first token that changes. This is a forensic map of those boundaries, drawn from how Hermes Agent actually assembles its prompt.

3
Prompt tiers
4
Max breakpoints
0.10×
Cache read cost
10
Hard boundaries
System prompt · token view—
Cache-hit prefix
—
read at 0.10×
Recomputed tail
—
prefilled at 1.00×
Relative cost
—
vs. no cache
Volatility mover72

Drag the divider. Move a volatile block one position later and the cache-hit prefix grows — the recomputed tail (and its cost) collapses. That is the entire discipline.

01 · First principles

The one law: it is a prefix cache.

A decoder transformer attends every new token to all previous tokens. The Key/Value projections of those past tokens are computed once and stored — the KV cache. Provider "prompt caching" is the same idea at provider granularity: the provider stores the KV state of a token prefix and bills a cheap cache read instead of a full prefill.

The consequence is absolute. A cache hit requires an exact match from token 0 to token N. The first divergence invalidates every token after it. So efficiency is never "how much did I cache" — it is where does the first changed token sit. Everything downstream of that position is recomputed at full price.

"Tiers are ordered stable → context → volatile so implicit longest-prefix caches keep the unchanged scaffold." agent/system_prompt.py · build_system_prompt(), L796-799
Cache horizon simulator · 100,000-token contextthe gold line = first divergence
First change at60%
Cached tokens
—
—
Recomputed
—
—
Cost / turn
—
Sonnet-class $3/M base
Savings vs cold
—
vs. 100% recompute
02 · Prompt construction

The architecture is a campaign to push the horizon right.

Hermes builds the cached system prompt as three ordered tiers, joined by blank lines. The ordering is the optimisation: the most durable content is written first, so the byte sequence survives longest against the prefix rule. Nothing is re-rendered mid-session.

Assembled system prompt · tier orderoptimal
byte-identical across sessions
Stable
Identity (SOUL.md), tool & model guidance, execution guidance, coding brief, platform hint, pinned skills.
per project / worktree
Context
Caller system_message, project files (.hermes.md → AGENTS.md → CLAUDE.md → .cursorrules), git workspace snapshot.
changes most on rebuild
Volatile
Skills index, MEMORY.md, USER.md, plugin sections, profile line, timestamp line, runtime environment hints (host / home / cwd).
Cacheable prefix100%

Stable content leads. A timestamp that changes every turn sits last — after it, nothing needs to survive.

RULE 01
Shared files precede worktree specifics
Inside the context tier, the shared project files come before anything naming the current worktree — so sessions of one project in different git worktrees share a prefix covering the whole context block, instead of stopping at the first cwd-dependent line.
RULE 02
The runtime block always ends the prompt
The environment block carries the current cwd/backend — it is the most volatile content, so it is pinned to the absolute end. A marker distinguishes this layout from legacy prose quoting the heading.
RULE 03
Surfaces switch without rewriting bytes
A desktop↔TUI switch keeps the stored prompt and delivers the new surface's guidance as a one-shot note on the per-turn user-message channel — so the cached prefix survives the switch.
03 · The boundaries

Ten limits the campaign runs into.

Some are physics. Some are provider policy. Some are economics. None can be tuned away — they can only be designed around. Tap a card for the evidence in the source.

04 · The breakpoint budget

Four markers. That is the ceiling.

Anthropic permits a maximum of four cache_control breakpoints per request. You cannot mark every meaningful boundary — so you spend the budget where reuse is highest. Hermes' default spends two on the system prompt and rolls the last two across recent messages.

◆ BREAKPOINT 1
Static system prefix
The stable tier — byte-identical, cached across every turn of the conversation.
◆ BREAKPOINT 2
End of system prompt
Closes the full prompt so context-file changes re-prefill only from here.
◆ BREAKPOINTS 3 & 4
Rolling last-two window
The two most recent non-system messages. Re-established within 1–2 turns after any rewrite.
BOUNDARY
The cap is structural
With no static prefix detected, the layout degrades to one system marker plus the last three messages. The budget reallocates; it never grows.
BOUNDARY
The carrier changes the boundary
Native Anthropic honours a marker on every message. On an OpenRouter/Nous envelope the marker relocates onto the tool_result block — and a LiteLLM-style proxy copies parts verbatim, landing it at tool_result.content[0] → a non-retryable HTTP 400. There, tool parts carry no marker at all.
SAFETY
Idempotent by construction
Prior decoration is stripped from a per-message copy before re-planning, so a second call can never accumulate past four markers.
"Skill/webhook/cron builders concatenate a large static scaffold with a small volatile tail into one user-message string. Only the builder knows where the tail begins, so it registers the stable prefix here and the cache planner places a breakpoint at that boundary." agent/prompt_cache_boundary.py · module docstring
05 · The time boundary

A cache expires. Efficiency does too.

Entries live for 5 minutes or 1 hour. A write costs a premium (1.25× for 5m, 2× for 1h); a read costs ~0.10×. If the next turn arrives after expiry, you pay a cold re-write at full cost. So the optimal TTL is a function of how far apart your turns arrive — nothing else.

Turn-arrival simulator—
Gap between turns20s
Cache reads
—
turns served warm
Cache writes
—
1.25× / 2.0×
Cold re-writes
—
expired before next turn
Total cost
—
10 turns, 100K prefix
MEASURED
Why "auto" exists
On one install, across two days of per-call logs: 63% of interactive cache-write tokens were cold re-writes after a 5–60 minute idle gap — the 1h tier saves ~42% of write cost there. But on subagents and cron, where turns arrive every few seconds, the 1h tier would have cost ~49% more.
POLICY
auto = who paces the session
1h for human-typed sources (cli, tui, desktop, messaging). 5m for machine-paced sources: subagent, cron, oneshot, webhook, kanban, api, tool, batch. Auxiliary calls treat auto as 5m.
ROUTE CLAMP
Not every route has a 1h tier
Qwen / Alibaba-family routes drop 1h→5m. The 1h allow-list is a measured set, not a guess — and one model is known to ignore the 1h tier on its route entirely.
06 · The economics boundary

Caching only pays after a second use.

A write's premium is only recovered by a later read. Model a fixed 100K-token prefix reused across N turns: the no-cache line is linear; the cached lines carry an upfront premium then run at a tenth of the cost. Where they cross is the break-even — ~1.3 turns for the 5m tier, ~2.1 for the 1h tier. A single-shot request never profits.

— no cache (1.00×) — 5m tier (1.25× write / 0.10× read) — 1h tier (2.00× write / 0.10× read)
BOUNDARY
Model identity is in the key
Provider caches are scoped to the model (and account / API key) serving the request. Any mid-conversation model change — a /model switch, a fallback, a credential-pool rotation onto another account — means the next request gets zero cache hits and re-reads the whole conversation at undiscounted price. Inherent to the mechanism.
BOUNDARY
Slot-keyed routes thrash on forks
Content-addressed caches (Anthropic, DeepSeek, Gemini) hit on any matching prefix; OpenAI routes over a prefix match. xAI/Grok pins a conversation to one server slot. Two divergent streams under one key evict each other — so fork-heavy conversations pay repeatedly.
BOUNDARY
Rotating the scope moves the bucket
The cache is keyed by a conversation id. Legacy compression rotation minted a fresh physical id → a fresh bucket → a miss. Hermes maps the physical id to its compression-lineage root, so the scope is rotation-stable. /new starts a fresh scope; forks stay isolated.
07 · Design lineage

Nothing here was invented in a vacuum.

The prompt architecture is an accumulation — each block carries a provenance comment naming where it came from. Tracing them is tracing the intellectual ancestry of the modern agent harness.

Parallel tool calls
Cline
cline/cline#11514 · cost rationale = resent context × round-trips
Execution discipline
GPT-5 guide + OpenClaw
OpenAI GPT-5.x prompting guide & OpenClaw #38953
Compaction
Claude Code
/compact flow + focus-topic preservation
Context files
Claude Code / Cursor
CLAUDE.md & .cursorrules discovery
08 · Inside Hermes

The whole machine, inside out.

Cache efficiency is one room in a large house. Everything above depends on the engine underneath it — a single conversation loop that drives every turn, and the subsystems hung off it. Here is the rest of the machine.

The agent loop · run_conversation()step 1 / 6
Iterations used0 / max90

The loop runs the same shape for every turn: build, call, act, append, repeat — and compress when the window fills.

The twelve subsystems

Tap any card for what it is, how it works, and where it lives in the source.

EXTENSIBILITY
A tool is three files
Drop tools/your_tool.py with a top-level registry.register() call, add the name to toolsets.py, and the auto-discovery imports it. Every handler returns a JSON string. A check_fn hides the tool until its requirements are met.
EXTENSIBILITY
A skill is a folder
A SKILL.md with YAML frontmatter (name, description, triggers) plus optional references/, templates/, scripts/. The agent loads them on demand — the index is in the prompt, the body is fetched only when used. Skills are the agent's procedural memory.
EXTENSIBILITY
Everything else is a plugin
Plugins can contribute tools, context engines, memory providers, platform adapters, image/video/transcription providers, prompt sections, and pre/post-LLM hooks. MCP servers register through the built-in client; IDEs attach via the ACP adapter.
"Build system prompt → loop while iterations < max: call LLM with OpenAI-format messages + tool schemas → if tool_calls, dispatch each and append results, continue → if text, return. Context compression triggers automatically near the token limit." agent/conversation_loop.py · high-level loop shape
09 · Evidence

Every claim has a line number.

Drawn from a shallow clone of NousResearch/hermes-agent at commit b2360086. Primary evidence:

agent/system_prompt.py — three-tier assembly agent/prompt_builder.py — identity, guidance, env hints agent/prompt_caching.py — breakpoint planner, TTL policy agent/prompt_cache_boundary.py — builder-declared prefix agent/prompt_cache_scope.py — rotation-stable scope agent/context_compressor.py — compression algorithm agent/surface_switch.py — cache-safe surface switch website/docs/developer-guide/prompt-assembly.md website/docs/developer-guide/context-compression-and-caching.md SOUL.md — default identity

Cache-cost multipliers (0.10× read · 1.25× / 2.00× write) are vendor-tier reference values used to model the economics. The break-even conclusions follow directly from them and are independent of any single provider's exact rate card.