Files
deepseek-harness/.agents/notes/implemented/simplification/2026-07-20-unwrap-injected-content-envelopes.zh.md
T
Turtle f593ef5ab5 fix(session): drop the <context> envelope and project context verbatim
context/message previously defaulted to a <context source="…">…</context>
wrapper. No model is trained on a <context> tag either, and message
framing does not belong on the session surface: the surface projects the
durable log, and a caller that wants a frame formats its own content —
which the one heavy producer (workspace-context) already does with its own
<system-reminder> frame, opting out via 'raw'. The tag only added
machinery — ContextEnvelope plus an envelope field threaded through
InjectOptions, HookContext, the context/message event, and the agent-loop
inject/additionalContexts plumbing.

context/message now projects its content verbatim as a user-role message,
sharing one deriveEventMessage case with user/message and steering/message.
ContextEnvelope and every envelope field are removed; context/message.meta
still carries durable, model-hidden JSON state. Regenerated catalogs and
website API; refreshed the three affected keyless snapshots (envelope field
only; timestamps unchanged).

Broadens and renames the steering Agent Note to cover both envelope
removals as one decision.

Agent Note: .agents/notes/implemented/simplification/2026-07-20-unwrap-injected-content-envelopes.md
2026-07-20 16:22:20 +08:00

4.8 KiB
Raw Blame History

Agent Note: 注入内容逐字投影,去除 XML 封套

Status: implemented

English | 中文

问题

两类注入的会话内容在渲染进模型 transcript(文本记录)时被包在 XML 封套里:steering/message 包成 <steering source="…">…</steering>,context/message 包成 <context source="…">…</context>(后者有一个 'raw' 退出选项可跳过封套)。这些封套意在告诉模型「这是注入内容,不是用户在说话」。

两个问题:

  • 没有模型在这些标签上训练过。 <steering> 和 <context> 是任何模型都未被教会去读的任意标记,因此这层框架只是徒增 token 而没有可靠效果,还可能起反作用——已录制的 transcript 显示,模型会把 <steering> 指令当成第三方元数据而拒绝服从,只回答原始提示。
  • session 表层是承载框架的错误层次。 表层的职责是把持久日志投影为模型 transcript;决定内容如何措辞并不是它的事。想要特定框架的调用方可以在注入前自行格式化内容——唯一的重度生产方(workspace-context)本就这样做,它自带完整的 <system-reminder> 框架,并用 envelope: 'raw' 退出 <context> 封套。剩下的标签机制(ContextEnvelope 类型,以及贯穿 InjectOptions、HookContext、context/message 事件和 agent loop 的 envelope 字段)所服务的区分,本应归属调用方。

决策

注入的会话内容逐字投影,框架由调用方自行负责。deriveEventMessage 通过一个共享分支渲染 user/message、context/message 和 steering/message,都返回 { role: 'user', content: event.data.content };它们的内容块原样到达模型。context/message 的 source/meta 和 steering/message 的 turn 保留在持久事件日志中,但不渲染。

ContextEnvelope 类型和所有 envelope 字段都被移除——包括 SessionEventMap 中的 context/message、InjectOptions、HookContext,以及 dsh-agent-loop 中 inject()/additionalContexts 的相关管线。workspace-context 不再请求 'raw';它自带框架的内容渲染方式不变。renderTagged/renderContextEnvelope 辅助函数被删除。context/message.meta 仍携带持久的、对模型隐藏的 JSON 状态。

封套曾携带的 source 归属并未丢失——它仍保留在持久事件上;只是不再渲染进 transcript。

权衡的替代方案

  • 保留 <context> 封套,只对 steering 去封套 —— 会为一个没有模型会读的框架位保留 ContextEnvelope/envelope 机制,并保留主要生产方本就退出的那种不一致。
  • 仅对插件来源的内容保留 envelope 字段 —— 会按 source.kind 把一条投影拆成两条,却没有观察到任何收益;插件引导 agent(智能体)时(钩子桥接器的轮次续行原因)同样希望指令被遵从,而不是被贴标签。
  • 把去封套的逻辑移入适配器 —— 规范投影就是模型可见契约(「模型可见 ⟺ 已记录」);让各适配器在框架上各行其是,会使派生的 transcript 依赖于适配器。调用方确实想要的框架应放进调用方自己的内容里,而不是适配器。

结果

  • 中途引导与注入的 context 以与普通用户提示相同的权重到达模型。
  • transcript 不再区分注入内容与用户消息;需要这一区分的消费方读取持久事件日志,其中事件类型、source 和 meta 完整保留。
  • hook-{cc,codex}-stop-continue ACP 快照已重新录制:旧录制捕获的是模型把 steering 当作第三方元数据而拒绝服从,正是本次修复针对的失败模式。
  • 内容块词汇表 Agent Note 中关于带标签封套的条款已修订为指向本文。

推迟事项

workspace-context 已经自行为内容加框架:它把一个完整的 <system-reminder>…</system-reminder> 块作为消息内容发出,而不依赖表层封套。这种调用方自有的模式才是应保留的——表层逐字透传内容,任何框架都住在生产方自己的内容里。

曾经存在两条框架路径——调用方自行加框架(workspace-context 的 <system-reminder>),以及表层封套(deriveEventMessage 加上的 <context>/<steering>)。本次变更移除了后者,只留下调用方自有的框架。如果未来又需要带标签的框架,应由事件的 meta map(生产方附加、对模型隐藏的元数据字段)来统一它,交给专门的渲染器或适配器消费,而不是在 deriveEventMessage 中重新硬编码标签。生产方在 meta 中声明所需的框架,由一个渲染器统一施加;session 表层的投影始终保持逐字透传。