Prompt caching
Every turn sends the model the whole conversation again: instructions, tool descriptions, everything said so far, plus your new message. Most of that is identical to the previous request. Prompt caching lets the Syntic platform keep that repeated start of the request ready, so the next turn reads it from the cache instead of processing it from scratch. Cached input is cheaper and faster than fresh input.
You do not have to switch anything on. This page explains what Syntic Code does to keep the cache useful, what breaks it, and the few settings you can change.
How a request is laid out
The cache works on the beginning of a request. A later request can reuse a cached part only if everything up to that point is exactly the same, byte for byte. So Syntic Code orders each request from most stable to least stable, and places cache markers where the stable part ends:
| Part | Changes | Cached |
|---|---|---|
| Core instructions and tool rules | Almost never in a session | Yes, marker at its end |
| Session details: environment, git status at start, memory files, working folder | Once per session | No (kept after the marker so they cannot spoil the part above) |
| Earlier conversation | Grows each turn, but old parts stay the same | Yes, a rolling marker on the last message before your new one |
| Your new message | Every turn | No |
The system prompt is split in two at a fixed boundary. Everything before it is the same for every session on every machine, so it is cached as one block. Session details come after it, so a different folder or branch does not throw away the cached instructions.
A second, rolling marker moves forward as the conversation grows. It always sits on the newest message that will still be identical in the next request, so each turn reuses everything up to the previous turn.
Where the cache lives
The cache is kept by the Syntic platform, not on your computer. It is tied to your account and the exact request content. Nothing extra is stored locally.
What keeps the cache warm
- The system prompt is built once. By default it is assembled when the session starts and reused for every request. Editing
SYNTIC.mdin the middle of a session does not change the cached prefix. - Steady tool list. Tool descriptions are part of the cached start. They stay the same until you connect or remove an MCP server or a plugin.
- Sibling subagents start a moment apart. When the agent starts several subagents of the same type at once, they share the same opening. The first one starts at once and the others wait briefly, so they read the cache the first one wrote instead of each paying to write it. The wait is 1,500 ms by default;
SYNTIC_AGENT_STAGGER_MSchanges it, and0turns it off.
What breaks the cache
These change the start of the request, so the next turn is processed fresh and the cache is rebuilt from there:
| Action | Why |
|---|---|
Switching model (/model, fast mode) | Each model has its own cache. |
Compacting (/compact or automatic) | The old conversation is replaced by a summary. |
| Connecting or removing an MCP server, turning a plugin on or off | The tool descriptions change. |
| Rewinding to an earlier message | The conversation after that point is gone. |
| A pause longer than the cache lifetime | The cached entry has expired (see below). |
Starting with --system-prompt-snapshot off | The system prompt is rebuilt every request, so edits show up at once but the instructions block is re-sent each time. |
Working on files, running commands and asking new questions do not break the cache: they only add to the end.
Cache lifetime
A cached entry expires if it is not used for a while. The default lifetime is five minutes, refreshed every time a request reuses it. If you often step away for longer and come back to the same session, a one-hour lifetime saves the cost of rebuilding.
| Setting or variable | Effect |
|---|---|
"promptCacheTtl": "1h" | Ask for a one-hour lifetime for the main conversation. "5m" asks for five minutes explicitly. |
"subagentPromptCacheTtl": "1h" | The same for every subagent. Subagents do not inherit the main setting, because most of them run once. |
cacheTtl: 1h in an agent file | A lifetime for that one custom agent, used when subagentPromptCacheTtl is not set. |
SYNTIC_ENABLE_PROMPT_CACHING_1H=1 | Use one hour for the main conversation when no setting chose a lifetime. |
SYNTIC_FORCE_PROMPT_CACHING_5M=1 | Force five minutes everywhere, whatever the settings say. |
Order of precedence for the main conversation: SYNTIC_FORCE_PROMPT_CACHING_5M, then promptCacheTtl, then SYNTIC_ENABLE_PROMPT_CACHING_1H, then the platform default.
{
"promptCacheTtl": "1h",
"subagentPromptCacheTtl": "5m"
}Share the cache across machines
In scripted runs on many machines (CI runners, for example), the session details in the system prompt differ from machine to machine. Add --exclude-dynamic-system-prompt-sections to a -p run to leave those details out, so the same task on different runners can reuse one cache. The same switch can be set with SYNTIC_EXCLUDE_DYNAMIC_PROMPT_SECTIONS=1. See Scripting and CI.
Check how well it works
| Where | Shows |
|---|---|
/stats | Per model: input, output, cache read (cache r) and cache write (cache w) tokens, plus cost. |
--output-format json | usage.cache_read_input_tokens and usage.cache_creation_input_tokens for the run. |
syntic agents --json | Uncached input and output tokens per background run. |
A healthy long session shows cache reads far larger than fresh input. If cache writes keep growing while reads stay small, look for one of the actions in What breaks the cache happening every turn.
Cost figures already account for caching: cache reads are billed at a lower rate than fresh input. See Usage and billing.