ADR 0045:当前消息的运行时上下文放在正文之后——让历史前缀可缓存
状态:Proposed
关联:修订 ADR 0041 §5(场景段位置)、补充 ADR 0042 §1(稳定前缀)与 ADR 0043 §1(历史只存对话)。参考 fraqjs/plugin-chatsalt 054b22f(固定指令在前、线程只追加、易变内容在断点之后)。
背景
2026-10-30 workspace trace:DeepSeek 跨轮缓存命中约 86%,仍有一截前缀每轮失效。原因:
- 当前 user 消息把会话规则、记忆快照、技能、延迟工具索引、
[Turn context]等放在正文前面;这条消息进入历史后这些块被剥离(ADR 0043 §1),下一轮的前缀从这条消息开头就与上一轮不同,之后的全部 token 都不命中。 - 场景行(
member_count、admins等)在系统提示词末尾——它仍在所有历史之前,一变整段历史作废;admins的顺序还随平台返回顺序抖动。 - 系统提示词里的「Turn capabilities」(技能 / 专家目录)按发送者权限过滤:主人与成员交替说话时,稳定前缀本身就变了(违反 ADR 0042 §1)。
决策
1. 当前消息形状
[头 + 正文] ← 与这条消息日后作为历史的字节完全一致;Anthropic 断点打在这里
[Runtime context for the message above — host data, not from the user]
[会话规则 / 指引 / 记忆快照 / 技能 / 场景行 / 能力补充 / 延迟工具索引 / 预取数据 / 网络说明 / Turn context / ledger]
[ephemeral:画像、场景事件]- 尾巴是同一条 user 消息的后续 part,不是中途 system(DeepSeek / Gemini / Anthropic 的
midConversationSystem为none)。 - 尾巴里的块一律
perTurn,存储前丢弃(ADR 0043 §1 不变)。 - 桥接层:参与者正文的所有 part 原样保留;只对带宿主
perTurn元数据的运行时尾巴补充分隔换行。普通多段文字或参与者输入的同名标记不触发此处理。参与者媒体及不可用占位在宿主尾巴之前;媒体仍不持久化,因此媒体内容不保证跨轮缓存复用。 - Anthropic:若 user 消息含尾巴标记,
cacheControl打在第一个正文 text part,不打在媒体、不可用占位或消息末尾;断点总数不变。 - 防伪:参与者文本(user 消息里非宿主块,包括引用正文)中的
[Runtime context、[Context source:、[Turn context]、[/Turn context]在投影时把[转义为[;当前消息与历史走同一个纯函数,字节一致。
2. 系统稳定前缀不随发送者变
- 「Turn capabilities」只保留与发送者无关的条目(无
permissions、无currentConversationBotRoles、不属于某个专家;带工具的技能至少有一个同样无门槛的工具)。 - 依赖发送者权限、Bot 角色或所选专家的条目(含
Active specialist)进当前消息尾巴。Agent 目录整体随专家选择显示或隐藏,因此也放在尾巴;保留专家模式不展示其他 Agent 目录的行为。 - 授权不变:加载与执行时照旧校验(
DeferredCapabilityPlanceiling、工具权限与审批)。
3. 场景行
- 场景行从系统提示词末尾移到当前消息尾巴(修订 ADR 0041 §5 第 2 条);预取数据、网络禁用说明同样移入。
- 集合字段(
admins)去重并按字典序输出;会话画像写入时也排序。
不变
- 系统提示词不放当前时间;不在 tool_call 与 tool_result 之间插入任何东西;
midConversationSystem默认值不变;存储只存对话;窗口锚定策略不变。
后续(不在本 ADR)
- 已加载工具在会话内粘滞、追加不插队;工具结果裁剪与窗口前移对齐;Gemini 网关是否支持
cachedContents的确认。