拆.
上下文与记忆——AI 怎么'记得' · 第 07

隐性管理 vs 显性提示

为什么不告诉 LLM '你被压缩了'

4500读完约 23 分钟craft:B+发布于 2026-06-21

一、原理:让 AI 感知自己"被系统管理"的代价

到现在为止我们看了几种"告诉 LLM"的机制——system reminder(4.6 章)、Context Epoch(4.4 章)、anchored summary(4.5 章)。每种都涉及"系统层信息怎么传给 LLM"。

但有一类信息——opencode 选择不告诉 LLM。这是个深层的设计哲学:让 LLM 不知道自己"被系统管理"

具体例子:

例子 1:compaction 完成后

opencode 跑完 compaction,把 50 个 turn 的历史压缩成一段 summary,塞到 messages 历史里。但不告诉 LLM"刚才系统做了 compaction"。

LLM 下个 turn 开始时看到的 messages 是已经被处理过的——但它感觉就像"我的工作记忆本来就是这样"。

例子 2:token budget 接近上限

opencode 检测到 session 累计 token 接近 limit。但不告诉 LLM"你只剩 5K tokens 了"。

LLM 不知道还剩多少预算——它就按正常方式工作。如果真到极限了,opencode 自己处理(触发 compaction)。

例子 3:tool 失败重试

opencode 调一个工具失败、网络重试、最终成功。LLM 只看到"工具调用结果"——不知道中间重试了几次。

LLM 不需要知道这些细节——它只关心"工具最终的结果是什么"。

例子 4:背景 hidden agent 在跑

opencode 在 fork title agent 和 summary agent 在后台跑。这些 agent 在用 LLM 调用(消耗 token)。但主 LLM 不知道"系统在背景跑别的 LLM 任务"。

主 LLM 只看到自己的 conversation——它不知道有"姐妹 agent"在并行工作。

这些"不告诉 LLM"的设计有个共同特征——LLM 不需要知道这些"系统内部细节"才能做好工作。告诉它反而让它困惑。

为什么困惑?因为 LLM 会"过度解读"系统层信息:

  • 告诉它"被压缩了"——它可能想"那我应该说什么"或者"我前面那些是不是要重新做"
  • 告诉它"token 不多了"——它可能急着收尾,输出质量崩
  • 告诉它"工具重试过"——它可能不信任工具结果,重新跑一遍
  • 告诉它"有姐妹 agent"——它可能想协调,但其实没法

这些"过度反应"破坏了 LLM 的工作专注度。LLM 应该聚焦"用户的任务"——而不是"系统怎么运作"。

这就是隐性管理的设计原则——系统的运作对 LLM 透明,LLM 不需要感知系统

跟它对立的是显性提示——告诉 LLM "现在发生 X 了,你做出反应"。

什么时候用显性提示(前面几章讲过)?

  • mode 切换——LLM 必须知道(行为要变)
  • 用户输入——LLM 必须知道(这是它的工作输入)

什么时候用隐性管理?

  • 系统内部操作——LLM 不需要知道
  • 资源管理——LLM 不需要知道
  • 后台并行任务——LLM 不需要知道

判断标准:LLM 需要主动调整行为吗?需要 → 显性提示。不需要 → 隐性管理。

opencode 在这件事上的产品哲学是克制——能不告诉 LLM 就不告诉。我们看具体怎么实现。

二、案例:opencode 几乎不注入"meta-prompt"

我们看 opencode 在几个场景的具体处理。

场景 1:compaction 完成

opencode 的 compaction 流程:

  1. 检测到 context 接近上限
  2. fork compaction agent,让它读历史写 summary
  3. compaction agent 写出一段 anchored summary
  4. opencode 把 messages 历史中的"被压缩部分"替换为 summary
  5. 主 LLM 在下个 turn 看到的 messages 是已经替换过的

关键点:opencode 在 messages 里加 "[历史已被压缩]" 这种 meta-message。它直接替换内容——LLM 看到的就是"已经如此"。

如果你看 messages 历史,它的样子是:

[第一个 user message: 原始的]
[第一个 assistant message: 原始的]
[第二个 user message: "What did we do so far?"]  <- 这是 compaction 触发器
[第二个 assistant message: <summary 内容>]    <- 这是 LLM 生成的 summary
[新的 user/assistant 消息: 继续工作]

注意第二个 user message——"What did we do so far?"——这是 opencode 触发 compaction 的方式。它伪装成用户问问题,让 LLM 回答(生成 summary)。

这种伪装让 LLM 感觉"我自然地总结了一下"——而不是"我被系统强制摘要了"。

compaction prompt 里明确说"Do not mention that you are summarizing, compacting, or merging context"——LLM 不会暴露这是个系统流程。

结果是 LLM 后续工作时,把 summary 当成"自己的工作记忆的一部分"——继续基于它工作。完全不感觉"被系统管理"。

场景 2:token budget 不传

opencode 内部追踪每个 session 的累计 token。但system prompt 里不放这个信息——LLM 不知道自己已经用了多少 token、还剩多少。

为什么?因为 LLM 知道"还剩多少 token"会触发风险规避行为——它会缩短输出、跳过细节、急着结束。这些都是质量下降。

opencode 选择"让 LLM 不知道预算"——它就按正常质量工作。如果真到极限,opencode 自己处理(触发 compaction 或者警告用户)。

场景 3:tool 重试细节不传

packages/opencode/src/session/retry.ts 处理工具调用失败的重试。重试逻辑大致是:

  • 工具失败 → 检查错误类型
  • 如果是网络错误、5xx、rate limit → 自动重试(指数退避)
  • 如果是逻辑错误(参数错、文件不存在)→ 不重试,直接返回错误
  • 重试成功 → 把成功结果塞回 messages

LLM 看到的只是最终结果——成功就是成功,失败就是失败。它不知道中间是否重试、重试了几次、每次的错误是什么。

这种简化让 LLM 看到的世界更"理想"——它不需要在每次 tool 调用后想"是不是真的成功了,会不会其实重试过"。

如果 LLM 看得到重试细节——会过度怀疑结果。隐藏细节让 LLM 信任结果。

场景 4:hidden agent 完全独立

opencode 的 hidden agent(title、summary、compaction)跑在独立 fiber。它们有自己的 LLM 调用、自己的 token 消耗、自己的执行流。

主 LLM 完全不知道有这些 hidden agent 存在。它的视野只有自己的 conversation——hidden agent 跑得到结果会进入 session 状态(标题更新了、summary 写好了),但 LLM 看不到 hidden agent 的过程。

这种隔离让主 LLM 专注于自己的工作——不需要跟 hidden agent 协调。

但要注意——hidden agent 的结果还是会影响 LLM。比如 compaction 跑完后,主 LLM 下个 turn 看到的 messages 已经被替换。但 LLM 不知道"是 compaction agent 替换的"——它感觉就是"自然如此"。

这是个微妙的平衡——隐性管理不是完全隔离,是"让 LLM 看到结果不看到过程"。

三、设计启示:不戳穿"你是被框架的 AI"的设计哲学

这一章的核心论点:让 LLM 不知道自己"被系统管理"——是 AI 产品工程的高级设计哲学

如果你做 AI 产品,下面几条原则有用:

1. 默认"不告诉 LLM"

每个系统层事件,先问自己——LLM 必须知道才能正确工作吗?

如果"知道了 LLM 表现更好"——告诉它。

如果"知道了反而困惑或过度反应"——不告诉。

默认偏向"不告诉"——给信息的成本(LLM 反应)通常高于不给的成本。

2. 让"系统操作"伪装成"LLM 自然行为"

opencode 的 compaction 触发器是个 user message "What did we do so far?"——伪装成用户问问题。这是个精妙的设计——让 LLM 感觉是它"自然总结了一下",不是"系统强制执行"。

这种伪装让 LLM 行为更自然——不会因为感觉"被强制"而扭曲输出。

3. 资源管理不暴露

token budget、cost、quota、rate limit——这些是产品/用户层面的概念。LLM 不需要知道。

如果 LLM 知道"还剩 5K tokens"——它会扭曲输出(缩短、省略、急着结束)。

让产品层自己管理资源、给 LLM 一个"无限"的视野。

4. 重试细节不暴露

工具调用失败、自动重试、最终结果——LLM 只看最终结果。

如果 LLM 知道"重试了 3 次"——它会怀疑结果。

简化它的视野,让它信任结果。

5. 并行任务相互隔离

如果你的产品有多个 agent / fiber 并行工作——它们各自独立,不互相感知。

不要让 agent A 知道"agent B 正在跑"——它会想协调,但其实没法。

各自独立运行,结果通过共享状态汇合。

6. 元层信息只在必要时显式

少数场景 LLM 必须知道元层信息:

  • mode 切换(行为要变)—— 显式提示
  • max-steps(必须立刻停)—— 显式提示
  • agent 切换(人设要变)—— 显式提示

大多数场景隐性管理够了。

7. 监控 LLM 的"困惑"信号

观察 LLM 输出有没有"困惑"迹象——重复问问题、过度澄清、行为突然变化。

如果出现——可能是某个系统操作泄漏给 LLM 了。回头检查"是不是不该告诉它"。

8. 别把 LLM 当"知情同事"

直觉上把 LLM 当"知情同事"——告诉它你在做什么、为什么。这是新手错误。

LLM 不是同事——它是个任务执行器。给它任务上下文,不要给它系统上下文。

把 LLM 当"无知但能干"——它聚焦执行、不卷入系统设计——产品体验最好。

最后一个观察。"不告诉 LLM"这个设计哲学反新手直觉。新手做 AI 产品时本能想"我应该告诉 LLM 越多越好"——以为信息多 = 决策好。

实际上反过来——LLM 的优秀决策需要"聚焦的信息",不是"全部信息"。系统层信息会污染它的思考——让它去想"为什么"而不是"做什么"。

opencode 团队在这件事上的克制——让 hidden agent 隐身、让 compaction 透明、让重试不暴露——是 AI 产品工程的高级形态。它不是"信息透明度低"——是"信息分层"。LLM 看它该看的,系统层处理它该处理的,两者各司其职。

理解这个哲学,你才能做出真正"丝滑"的 AI 产品。否则你的 LLM 会被系统层信息"绑架"——一直在思考"我被监控、我被压缩、我快没预算"——而不是"用户要我做什么"。

下一章 4.8 是第 4 篇的收尾——我们看一个看似缺失但实际是有意设计的特性——为什么 opencode 没有"跨 session memory"