Mid-conversation 注入
在对话半途塞进新指令
一、原理:什么时候需要在对话中段加 system reminder
我们已经看过几种"系统层信息"——system prompt(每次重组)、AGENTS.md(自动加载)、Context Epoch(状态变化通知)、anchored summary(压缩后的工作记忆)。
这一章我们看一个更细粒度的机制——mid-conversation 注入:在对话过程中,agent 主动在某个 turn 的 user message 里塞一段"系统提醒"。
听起来跟 Context Epoch 类似——但有区别。
Context Epoch 是重大状态变化(mode 切换、模型切换)——产生一个时间分界线。
Mid-conversation 注入是任意时机的辅助提醒——不一定是状态变化,可能是任何"agent 觉得 LLM 应该被提醒"的时刻。比如:
- 用户改了 AGENTS.md,提醒 LLM 看新规则
- 文件被外部工具修改,提醒 LLM 重新读
- 距离 max-steps 还有 3 轮,提醒 LLM 收尾
- session 的 token 用量接近限额,提醒 LLM 精简
这些都不是"模式变化"——是细粒度的提醒。
为什么需要这种细粒度机制?因为有些"系统状态"无法通过 system prompt 的重组传达。具体说:
问题 1:system prompt 是"当前状态",不是"变化"
system prompt 写的是"现在 cwd 是 X"——但它不说"cwd 从 Y 变成了 X"。LLM 看 system prompt 不知道变化发生了——它以为 cwd 一直是 X。
问题 2:messages 历史里 LLM 自己的话不能改
LLM 在前一个 turn 说"我会按 AGENTS.md 的规则做"。后来用户改了 AGENTS.md——但 LLM 历史里的话不能改。LLM 不会自己注意到"我引用的规则版本旧了"。
问题 3:每次重组 system prompt 不够细粒度
每次重组只能传达"当前状态全貌"——不能传达"某件具体事情发生了,注意一下"。
要解决这些问题,需要在user message 里塞一段 <system-reminder>。让 LLM 看到具体的"提醒事件"——而不是只看到状态全貌。
我们看 opencode 怎么实现。
二、案例:opencode 的 system-reminder 机制
opencode 多个场景使用 mid-conversation 注入。我们看几个具体例子。
例 1:mode 切换 reminder
packages/opencode/src/session/prompt/build-switch.txt 是 5 行文字:
<system-reminder>
Your operational mode has changed from plan to build.
You are no longer in read-only mode.
You are permitted to make file changes, run shell commands, and utilize your arsenal of tools as needed.
</system-reminder>
当用户从 plan 切到 build mode,opencode 把这段塞到下个 turn 的 user message 末尾。
注意几个细节:
<system-reminder>是个 XML 标签——明确语义- 内容是关于变化的具体描述——不是当前状态全貌
- 用 positive language("You are permitted")——告诉 LLM "现在能做什么",不是"你之前不能做什么"
- 极简——只有 3 个 statement——LLM 看一眼就懂
例 2:max-steps reminder
第 1.9 章我们讲过 max-steps.txt:
CRITICAL - MAXIMUM STEPS REACHED
Tools are disabled until next user input. Respond with text only.
STRICT REQUIREMENTS:
1. Do NOT make any tool calls
2. MUST provide a text response summarizing work done so far
3. This constraint overrides ALL other instructions
...
这是 LLM 自己"想看 assistant 消息"的伪造——但它的本质也是 mid-conversation 注入。区别只是注入位置(assistant role 而非 user message)。
例 3:context update reminder
当 opencode 检测到环境发生变化(cwd 改了、git branch 切了、关键文件修改了),它在下个 turn 的 user message 里加一段:
<system-reminder>
The working directory has changed from /old/path to /new/path.
Any file paths in previous messages may no longer be valid.
</system-reminder>
明确告诉 LLM——"前面的路径可能失效了"。
例 4:plan file reference
build mode 启动时,除了 mode 切换 reminder,opencode 还会加 plan file 引用:
<system-reminder>
[mode 切换通知]
A plan file exists at /Users/lee/project/.opencode/plans/auth-refactor.md.
You should execute on the plan defined within it.
</system-reminder>
这是 plan → build 的"交接"——plan agent 写了方案在文件里、build agent 看到这条 reminder 知道方案在哪。
例 5:compaction 完成 reminder
compaction 跑完后,opencode 把生成的 summary 塞进 messages,但不直接告诉 LLM"刚做了 compaction"。
为什么?因为告诉 LLM 它被压缩了反而困惑——它会想"那我应该重新读 summary 还是接着 summary 工作?"
opencode 选择静默处理——把 summary 放在历史的某个位置,LLM 看上去就像那是它工作记忆的一部分。不是"系统通知"。
这跟前 4 个例子是反例——并不是所有"系统层信息"都需要 reminder。compaction 反而不要 reminder。
这种"什么时候用 reminder、什么时候不用"是个产品判断。我们看 opencode 的判断框架。
判断框架 1:LLM 需要"主动调整行为"的时候用
mode 切换——LLM 必须主动开始用之前禁用的 tool——用 reminder。
cwd 变化——LLM 后续操作必须用新路径——用 reminder。
compaction——LLM 不需要主动调整什么(继续工作就好)——不用 reminder。
判断框架 2:变化是"用户感知的"还是"系统内部的"用 reminder vs 不用
用户主动切 mode——这是用户感知的——用 reminder(让 LLM 跟用户对齐)。
系统自动 compaction——用户没感知到——不用 reminder(保持透明)。
判断框架 3:信息密度足够低才用
reminder 应该简短——1-5 句话。如果你需要塞很长的"状态描述"——考虑换个机制(比如更新 system prompt)。
reminder 是"小信息量、高优先级"——长 reminder 跟 system prompt 重叠,没意义。
判断框架 4:reminder 频率要克制
如果每个 turn 都加 reminder,LLM 会"麻木"——把 reminder 当背景。
只在真正重要的变化点用。普通环境信息(每 turn 更新的 cwd、time)放 system prompt 末尾就够。
opencode 大部分 turn 没有 reminder——只在 mode 切换、abort、特殊事件时才有。
三、设计启示:注入频率和侵入性的 trade-off
这一章的核心论点:mid-conversation 注入是细粒度状态变化的传达机制——但要克制使用、避免 LLM 麻木。
如果你做 AI 产品考虑用 reminder 机制,下面几条原则有用:
1. 明确 reminder 触发条件
写一份明确的"什么情况下加 reminder"清单。比如:
- mode 切换 → 加
- agent 切换 → 加
- 关键文件被外部修改 → 加
- 配置变更 → 加
- 接近 max-steps → 加
- cwd 改变 → 加
- 普通文件被 LLM 自己改了 → 不加
- 时间走过几分钟 → 不加
清晰的清单防止 reminder 失控。
2. 用 XML 标签明确语义
<system-reminder> 或类似标签。让 LLM 区分"这是系统通知"vs"用户的话"。
3. 内容简短具体
每个 reminder 1-5 句话。说清"变化是什么 + LLM 应该怎么响应"。
不要长篇大论解释原因——LLM 看不完整。
4. 用 positive language
"You can now ..." 比 "You are no longer restricted from ..." 强。
正面表达让 LLM 直接知道"能做什么"——不需要反向推理。
5. 一次只通知一个变化
如果两件事同时变了——两段 reminder 分别表达,比合并成一段长 reminder 好。
LLM 处理多变化时容易混淆——分开传递降低难度。
6. 频率要克制
不要每个 turn 都加。让大多数 turn "干净"——只在真正重要时加 reminder。
观察 LLM 的反应——如果它开始"忽略"reminder(不调整行为),可能是 reminder 太频繁了。
7. 区分用户感知 vs 系统内部
用户感知的变化(mode、agent、明确操作的结果)——用 reminder。
系统内部的事(compaction、内部 cache 刷新、heartbeat)——不用 reminder。
LLM 应该跟用户对同一个"上下文模型"——别让它看到用户看不到的东西。
8. 测 reminder 的效果
观察 LLM 在 reminder 前后的行为是否符合预期。
如果加了 mode 切换 reminder 后 LLM 仍然守旧 mode 规则——可能 reminder 写得不够明确、或者 mode 切换没触发 reminder 注入。
debug 这些场景。
最后一个观察。Mid-conversation 注入是个侵入性机制——你在 LLM 看到的对话流里塞东西。用好它能让 LLM 行为精准;用得过头会让 LLM 困惑("这些都是用户说的还是系统说的?")。
opencode 的做法是克制 + 明确——只在重要时刻加、用 XML 标签明确语义、内容简短具体。这种克制让 reminder 真正起作用。
如果你的产品需要 reminder 机制——从克制开始,逐步加。不要一开始就到处塞——会废掉这个机制。
下一章 4.7 我们看一个更深层的话题——为什么有些事情不要告诉 LLM。隐性管理 vs 显性提示的设计哲学。