上下文分层 · 按字节稳定性分三层

DeepSeek 前缀缓存按请求体逐字节前缀匹配——越靠前越稳定,命中率越高。CodePapr 把上下文切成三层:冻结的稳定前缀、压缩时重写的会话状态、每轮追加的活动上下文。点击任意条目展开注入链路与缓存行为。

稳定 · 命中缓存 慢变化 · epoch 重写 高频 · 追加增长 来源 / 注入点
0%
实测前缀命中率
miss 应只剩"本轮新增"
01

Stable Prefix 稳定前缀

字节级冻结 · 整个会话最大缓存区
命中率最高会话内逐字节不变
ImmutablePrefix 不可变前缀对象 · prefix.systemPrompt / tools / params
SYS
System Prompt系统提示词
buildRuntimeSystemPromptruntimeSystemPrompt
冻结
模型身份与行为总纲:身份 + 核心原则 + 模式 intro + workspace + 核心约束 + 工具约束。构造 ImmutablePrefix 时校验禁止任何动态内容(时间戳 / 模板变量直接抛错),并一次性计算 SHA256。
注入链路:settings + rulesSection + mode → buildRuntimeSystemPromptImmutablePrefix.systemPrompt → 请求 messages[0]。前缀哈希在每次 build 时被 RequestBuilder 校验不可变。
RULE
Runtime Rules项目规则
.CodePapr/AGENTS.mdbuildProjectRulesSection
冻结
全项目规则,所有模式与子代理都继承。工作区打开时载入并缓存于 _projectRulesSection,注入系统提示词。opt-in 文件:只有用户在「配置」面板保存规则时才创建 .CodePapr/AGENTS.md,否则为空、不注入。
注入链路:.CodePapr/AGENTS.mdprojectConfigLoader.ts:loadProjectRulesSectionagentStore._projectRulesSection(缓存)→ buildRuntimeSystemPrompt({ rulesSection })promptSystem.ts:1168,位于 identity/principles 之后、mode prompt 之前。子代理通过 UiTaskToolContext.rulesSection 继承。

入口:顶部「配置」按钮 → ProjectConfigModal 的 AGENTS.md 标签页编辑保存;文件不存在时用模板预填。注意与 .CodePapr/agents/*.md(子代理定义)区分——后者变成 task 工具,不是规则。
TOOL
Tool Schema工具定义
ToolRegistry.getAll()按名排序 + canonical
冻结
会话开始时冻结全部工具定义(名称 + 描述 + 规范化参数)。工具集变化(MCP 加载、task 工具出现)会整体失效前缀——应由用户操作显式触发,避免隐性 churn。
RequestBuilder 每次 build 校验 tools 不可变(validateToolsImmutable),并对比运行时工具与冻结前缀一致(validateRuntimeToolsMatchPrefix)。
PARAM
Model Params模型参数
temperature / topP / maxTokens / thinking
冻结
参与前缀哈希。用户主动调参(调 temperature、切换 thinking)= 一次有意的整体失效,属可接受代价;系统不应在会话中隐式改参。
reasoning_content 回传按「历史消息是否存在」决定,与每轮 thinking 开关解耦,避免重建时给历史增删字节(DeepSeekProvider)。
Session Bootstrap 会话引导 = log[0] · role: assistant + metadata.isPrefixSystem(Claude 提取为 system)· resolveSessionBootstrap 按 (session × 稳定签名) 记忆化
SKL
Skills 摘要技能目录
.CodePapr/skills/*buildSkillsSection
签名内
只注入名称 + 一句话描述,稳定且小。全文由模型按需调用 skill_load 工具读取,作为工具结果落在尾部——不进前缀。增删 skill 是用户操作(在签名内),触发一次重建。子代理通过 SubagentSessionDeps.skillsSection 继承。
设计意图:「稳定上下文里只注入名称和描述,由模型自行判断是否需要,再按需调用 skill_load 读取完整说明」。
MAP
Repository Map项目结构概览
project graph 缓存buildProjectGraphBootstrapSummary
签名外冻结
目录 / 文件 / 符号鸟瞰,渲染为「## 项目结构概览」。磁盘上易变,但被排除出 bootstrap 签名 → 会话内冻结,改了也不重建。有 graph 缓存才注入。子代理通过 SubagentSessionDeps.projectGraphSummary 继承。
也用于冷启动:memory.md 为空且有 graph 时,后台用 graph 摘要生成初始 memory(agentStore.ts)。
MEM
Stable Memory项目记忆
.CodePapr/memory.mdmemorySection
签名外冻结
跨会话项目知识,渲染为「## 项目记忆」。自动创建(冷启动 bootstrap)。会话中途 agent 写 memory.md 不触发重建(签名外)→ 前缀缓存不破,更新次会话生效。这是"缓存稳定 vs 实时性"的明确取舍。子代理通过 SubagentSessionDeps.memorySection 继承。
对比:项目里 memory.md 往往存在(自动生成)而 AGENTS.md 可能不存在(需用户保存规则)——两者创建机制不同。
CUS
Custom Guidance长期附加指导
settings.systemPromptcharacter prompt
签名内
用户自定义全局指导 + 角色设定,渲染为「## 长期附加指导」。作为附加约束生效,不覆盖系统级规则。settings.systemPrompt 可在设置 → 高级分页编辑;角色提示词通过 CharacterModal 设置。
与 Runtime Rules 区别:rules 来自项目文件(AGENTS.md),custom 来自用户设置 + 角色;两者都在稳定区。子代理通过 UiTaskToolContext.customPrompt 继承。
▲ 以上为命中区 · 以下为 epoch 边界(压缩时重写)
02

Session State 会话状态

慢变化压缩 epoch 重写
一次性失效仅在压缩时接受
CKPT
Conversation Summary检查点摘要
renderContextCheckpointContent9 个 section
epoch 重写
被压缩的历史,作为单条 assistant 消息注入:用户目标 / 约束 / 已完成工作 / 重要文件·命令·错误 / 假设 / 验证状态 / 任务清单 / 待办 / 待确认问题。其中约束、假设与验证结论(即"已知决策")由压缩模型从原始对话 + 工具结果中提取,不是独立注入点,而是 renderedContent 的内部 section。由轮数上限或 token 上限触发压缩时生成,替换掉更早的原始消息。
触发器是两个用户设置:maxConversationRounds(对话轮数上限)与 maxContextTokens(输入上下文上限),共同决定 shouldCompact。压缩 = 开启新 context epoch,接受一次前缀失效。本地兜底用正则抽取(buildLocalContextCheckpointSections),可选 LLM 增强生成结构化 sections。
▲ append-only 边界 · 以下每轮增长
03

Active Context 活动上下文

高频变化append-only 尾部
唯一不可约 miss本轮新增
TAIL
Recent Conversation最近对话
pickRetainedTailMessages6~12 条 / ~8k tokens
追加 · 历史字节稳定
压缩后保留的最近原始消息。保留窗口由压缩逻辑决定;一旦写下,字节不再改动(append-only),对后续轮次仍是命中区。
注意:触发压缩的"轮数上限"是用户设置(maxConversationRounds),但具体保留多少条尾部消息由压缩算法的窗口决定。
PLAN
Live Plan实时任务清单 · Todo
renderTodoListDigest注入 user prompt 尾部
每轮实时
每轮从 live TodoListContext 渲染的实时摘要(目标 + 各任务 status/title/error),由 buildRuntimeUserPrompt 追加到 user prompt 尾部。始终反映当前计划状态,零额外缓存成本(user prompt 本身就在 miss 区)。压缩后下一条 user message 自带 live digest,无需单独冻结快照。
有损快照:仅含 status / title / retry / errorLog 前 200 字符,不含 description / dependsOn / artifacts。完整细节在 live 期间靠 todo 工具每轮回传。checkpoint.payload.todoDigest 仍存储(数据兼容),但不再单独注入为 user 消息。
TASK
Current Task本轮用户消息 · 当前任务
buildRuntimeUserPrompt目标 + 运行时上下文 + 诊断 + Live PlanstripConsumedImages
每轮新增
用户本轮输入(即当前任务)+ 运行时上下文(日期 / 时区)+ 项目诊断 + 实时任务清单,由 buildRuntimeUserPrompt 包装成一条 user 消息。所有动态内容刻意放在尾部,与前缀隔离——这是缓存安全的关键设计。已消费的图片会被剥离,避免 base64 反复膨胀后续请求。
日期/时间绝不上前缀;buildDefaultRuntimeContext 只在需要精确时间时才加时间行。⚠ stripConsumedImages 按"后面是否有 assistant"条件剥离图片,属中部翻转,多模态场景需改为写时确定性剥离。
CALL
Tool Use工具调用
assistant.toolCallssortedStringify 规范化
追加
模型发起的工具调用(名称 + 参数)。参数经确定性序列化,保证重建字节一致。
带 tool_calls 的 assistant 消息,其 reasoning_content 必须持续回传(DeepSeek 要求),否则 400。
RES
Tool Result工具结果
truncateToolOutput写时三阶梯
追加 · 出生即有界
>50k 字符 → 落盘 + 2k 预览 + 路径(read 可回读);30k–50k → 中间截断(头尾各半);150k 硬上限。写时一次性归一化并冻结,废除事后滑动裁剪 → 前缀不再中部翻转。代码检索(read / grep / graph / lsp)是最常见的 tool result 来源。
这是命中率优化的核心:每条工具结果出生即有界,尾部天然受控,miss 收敛到理论下限。阈值按字符(content.length),中文不被字节数误判。重建时直接读取已冻结的工具输出(sortedStringify 保证与实时路径逐字节一致)。

五个关键机制

让"分层"真正转化为命中率的底层设计。
M·01
签名外冻结

memory.md 与 project-graph 在磁盘上易变,但被排除出 bootstrapSignatureresolveSessionBootstrap 按 (session × 稳定签名) 记忆化 bootstrap——磁盘变化不重建,更新次会话生效。保住整段前缀缓存。

M·02
写时三阶梯截断

工具输出在写入 AppendOnlyLog 前一次性归一化(truncateToolOutput):30k 中间截断 / 50k 落盘 / 150k 硬上限。出生即有界,废除事后滑动裁剪,重建逐字节一致。

M·03
压缩 Epoch

轮数或 token 超限时,把较早消息折叠成 checkpoint 摘要 + 保留尾部,开启新 context epoch。这是有意的全量前缀重写——接受一次失效,换上下文受控。

M·04
动态内容尾部隔离

日期、时区、诊断等易变内容只进尾部 user 消息,绝不上前缀。前缀只装"会话内不变"的东西,这是前缀缓存能成立的前提。

M·05
子代理上下文继承

子代理通过 SubagentSessionDeps 继承主会话的 skills、memory、project-graph 和 customPrompt,注入自己的 Session Bootstrap。Bootstrap 以 role: assistant + metadata.isPrefixSystem: true 注入——Claude 提供商据此将其提取为 system 内容,避免首条消息非 user 的 400 错误;DeepSeek/OpenAI 按原样发送。

官方缓存优先序

摘自设置说明 settingsPromptStackDesc —— 越靠前越稳定,命中率越高。
L1系统核心与工具边界
L2项目规则 AGENTS.md
L3模式约束
L4Skills 摘要与 workspace
L5历史对话
L6本轮诊断 / 任务