symfonic.agent.prompts.stratigraphic¶
stratigraphic ¶
Stratigraphic prompt assembly for prompt_layer_mode='stratigraphic'.
Phase B Commit 4 / 7.0.13 cache-regression fix. Wraps the existing
legacy prompt builder output with the L0 Brain Kernel template so the
Anthropic provider sees an explicit cache_control breakpoint on a
contiguous, session-stable prefix.
Three-layer model:
- L0 Brain Kernel --
brain_v1.txt/brain_v1_jit.txt. 100% static, loaded once per process via importlib.resources. On its own L0 is far below Anthropic's ~1024-token minimum cacheable prefix, so it is NOT given its own breakpoint -- it is the head of the cached region, not a region of its own. - L1 OS Services -- the session-stable body: domain directives, SOUL schema, required labels, onboarding checklist, tool manifest, plugin contributions. NO timestamp, NO memory context, NO hydrated context, NO active tools, NO prospective tasks.
The onboarding checklist arrives here as part of the extraction
directive (extraction_part is spliced into the L1 body), where it
lists categories to RECORD when the user volunteers them. It is not a
behavioural instruction: nothing tells the agent to ask for those
fields, and core has no completion signal that would tell it to stop. The single
cache_control breakpoint is placed at the END of L1, so the
cached prefix is exactly L0 + L1 -- one contiguous region that
clears the 1024-token minimum and contains zero per-turn content.
* L2 Task -- per-turn volatile content (timestamp, memory context,
hydrated/RAG context, active tools, prospective tasks, extraction
section). Appended AFTER the breakpoint and left uncached, so it
never forces a cache_creation write priced at 1.25x.
Why one breakpoint and not two: Anthropic caches a prefix. The breakpoint marks the end of the cacheable region; everything before it (across every block) is the cached prefix. Marking L0 separately would either be silently dropped (L0 is sub-minimum) or be redundant with the L1 breakpoint. A single breakpoint at the end of L1 is the correct and cleaner expression of "cache L0+L1, leave L2 hot".
Returned by :func:build_stratigraphic_prompt:
prompt-- a JSON-encoded list of Anthropic content blocks ready to hand toSystemMessage(content=<list>)after ajson.loads. The consolidator inreact.pydetects this shape and forwards it as a list rather than a flattened string.brain_prompt_version-- theBRAIN_PROMPT_VERSIONconstant string, written intostate['brain_prompt_version']for telemetry.system_prompt_hash-- the first 16 hex chars of the SHA-256 of the L1 body. L1 is now session-stable, so warm turns with the same domain + manifest produce a stable hash and Anthropic cache hits can be correlated against it.
StratigraphicPrompt
dataclass
¶
Result of a stratigraphic build.
Attributes:
| Name | Type | Description |
|---|---|---|
prompt |
str
|
JSON-encoded list of Anthropic content blocks. Each block
is |
brain_prompt_version |
str
|
Kernel version tag, e.g. |
system_prompt_hash |
str
|
16-hex-char SHA-256 prefix of the L1 body. |
build_stratigraphic_prompt
async
¶
build_stratigraphic_prompt(*, l1_body: str, jit: bool, l2_body: str | None = None, force_last_cache: bool = False, system_prefix_cache_ttl: Literal['5m', '1h'] = '5m') -> StratigraphicPrompt
Assemble L0 (Kernel) + L1 (stable) + optional L2 (per-turn).
The cached prefix is exactly L0 + L1: a single cache_control
breakpoint is placed at the end of L1. L2, when supplied, is
appended after the breakpoint and left uncached so its per-turn
volatility never triggers a 1.25x cache_creation write.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
l1_body
|
str
|
The session-stable system body -- domain directives,
SOUL schema, required labels, onboarding checklist, tool
manifest, plugin contributions. MUST NOT contain per-turn
content (timestamp, memory context, hydrated context, active
tools, prospective tasks); those belong in |
required |
jit
|
bool
|
Whether to load the JIT-mode kernel
( |
required |
l2_body
|
str | None
|
The per-turn volatile body. When |
None
|
force_last_cache
|
bool
|
Forwarded to |
False
|
Returns:
| Type | Description |
|---|---|
StratigraphicPrompt
|
class: |
StratigraphicPrompt
|
blocks, the kernel version tag, and the L1 body hash. |
Source code in symfonic/agent/prompts/stratigraphic.py
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 | |
strip_cache_control ¶
Return prompt's block list with every cache_control removed.
The mechanism half of T3.1.4 finding 1; the engine owns the policy half
(which families may be annotated -- see
SymfonicAgent._stratigraphic_annotates_cache). Split that way because
"what a block looks like" belongs to whoever builds one, and "whose wire
can be described" belongs to whoever knows the provider.
The envelope is preserved: the caller still receives a JSON list of
{"type": "text", "text": ...} blocks in the same order, minus the
annotation. Flattening instead would make attachments the odd region out,
since _to_block already encodes those Anthropic-shaped for unknown.
Returns None when prompt is not a decodable block list, so a caller
can degrade deliberately rather than ship markers it meant to remove.