返回文章列表

《Claude Code 源码解析系列》第5章|Context 治理(选修)

从业界视角补充 Context 管理设计,对比不同 Agent 系统的上下文策略。

《Claude Code 源码解析系列》第5章|Context 治理(选修)

很多人第一次听到 Context Manage,会把它理解成两件事:

上下文窗口大一点。
历史快满了就压缩一下。

这个理解不算错,但太窄。

如果只是普通聊天,Context 管理确实主要是在处理聊天历史。但一旦系统变成编程 Agent,尤其是会读文件、调工具、跑命令、写代码、调用外部系统的 Agent,Context 就不再只是一段对话记录,而是模型每一轮工作时能看到的整个现场。

真正的问题变成:

Agent 在工程执行过程中,会不断制造新信息。系统怎么决定哪些信息该进入模型,哪些信息该留在外部,哪些信息要被压缩,哪些信息必须长期保存?

这一篇是选修,所以不只讲 Claude Code。

Claude Code 是一个很好的样本,因为它把编程 Agent 的上下文问题暴露得很彻底:文件内容很长,工具结果很长,测试日志很长,任务也经常持续几十轮。但同样的问题也会出现在 LangGraph、OpenAI Agents SDK、AutoGen、Cursor、Devin、OpenClaw、Hermes 这类不同形态的 Agent 系统里。

只是不同项目的重点不一样:

  • Claude Code 更像长任务 CLI Agent(命令行交互式 Agent),重点是工具结果、项目规则、压缩和恢复。
  • LangGraph 更像工作流状态机,重点是结构化 State、checkpoint(执行快照)和可恢复执行。
  • OpenAI Agents SDK 更像应用开发 SDK,重点是区分本地运行时 context 和 LLM 可见 context。
  • AutoGen 更像多 Agent 对话框架,重点是多角色协作、模型上下文和记忆注入。
  • Cursor / Copilot 更像 IDE 内的实时助手,重点是低延迟、局部代码片段和检索。
  • Hermes / OpenClaw / Harness.io 这类系统则更偏长期运行时、入口控制和企业治理。

所以,Context Manage 不是某个产品里的一个功能点,而是所有 Agent 工程都会遇到的一类底层问题。前一篇已经讲过 Claude Code 自己的压缩链路,这里把视角放宽,看它背后的通用治理模型:

模型是无状态的。
任务是连续的。
信息是爆炸的。
上下文窗口是有限的。
系统必须在每一轮重新整理现场。

为了让文章不飘,我们固定一个例子:

用户说:这个项目登录后跳转失败,帮我找原因并修好。

一个真正能干活的 Agent,接下来不会只回答一句”你可以检查路由守卫”。它会:

看项目结构
-> 搜索登录相关代码
-> 读取路由守卫
-> 读取鉴权状态管理
-> 跑测试
-> 分析错误日志
-> 修改代码
-> 再跑测试
-> 总结改动和风险

每一步都会产生新信息。Context Manage 要解决的,就是让这些信息在长任务里既不断线,也不把模型淹死。

一、为什么 Context Manage 会变成工程问题

先把最底层的事实说清楚:

模型本身每次调用都是无状态的。

它不会天然记得上一轮读过哪个文件,也不会天然知道刚才测试失败在哪里。Agent 看起来像在连续工作,是因为模型外面的运行时每一轮都把”当前工作现场”重新组装好,再发给模型。

普通聊天的一轮调用可以简化成:

用户问题
-> 模型回答

但 Agent 的一轮调用更像:

系统规则
+ 项目规则
+ 用户当前目标
+ 历史消息
+ 工具说明
+ 最近工具结果
+ 当前任务状态
+ 压缩摘要
+ 可用外部资源
-> 模型判断下一步

这时,Context Manage 要回答的就不是”怎么保存聊天记录”,而是:

  • 本轮模型到底该看什么?
  • 哪些信息应该每轮都看?
  • 哪些信息只在需要时再取?
  • 哪些工具结果已经过时?
  • 哪些内容太长,需要裁剪?
  • 哪些历史可以摘要?
  • 压缩后怎么保证任务不断线?
  • 多个 Agent 之间怎么隔离上下文?
  • 哪些内部状态不能暴露给模型?

这已经是系统设计问题,不是 prompt 文案问题。

如果没有 Context Manage,Agent 很快会遇到几类典型失败。

1. Token 爆炸

工具结果越堆越多,模型请求越来越长。

一次 grep 可能返回几十个匹配点,一次测试可能吐出几千行日志,一个源码文件可能几千 token。长任务跑到后半段,真正占满窗口的常常不是用户的话,而是工具带回来的环境噪音。

(这里有个坑:很多开发者只看对话轮数,觉得”才聊了20轮,窗口应该够”。但实际上每轮工具调用都在往上下文里塞内容,20轮后 token 可能已经爆了。)

2. 上下文污染

旧信息还留在上下文里,但现实已经变了。

比如 Agent 先读过 auth.ts,后来已经改过这个文件,但历史里还保留着旧版本内容。模型下一轮如果基于旧内容继续推理,就会出现一种很隐蔽的问题:

它看起来在认真分析,
但分析对象已经不是当前代码。

3. 约束丢失

用户一开始说”不要改 public API”,第十轮以后模型可能忘了。

项目规则说”迁移文件不能手改”,但上下文被压缩后这条规则可能没有进入摘要。于是 Agent 继续执行,看起来每一步都合理,但已经越过了边界。

4. 压缩失忆

压缩不是免费午餐。

粗糙的摘要可能只记录”读了哪些文件、改了哪些代码”,却没有保留:

  • 用户真正目标
  • 当前卡在哪里
  • 哪些方案已经试过
  • 哪些约束不能违反
  • 下一步应该做什么

这样的压缩会让模型像一个只看过会议纪要的人,知道大概发生过什么,但没有现场手感。

5. 多 Agent 污染

当系统引入子 Agent 或多 Agent 协作后,问题会更明显。

搜索 Agent 读了大量资料,执行 Agent 只需要最终结论。如果把搜索过程的全部草稿都塞给执行 Agent,下游不仅不会更聪明,反而会被噪音带偏。

多 Agent 系统最怕的不是信息不够,而是每个 Agent 都背着别人的中间态往前跑。

二、Context 不是 Prompt,也不是 Memory

讲 Context Manage 之前,先把几个容易混的词拆开。

概念通俗解释它回答的问题
Prompt任务说法我该怎么要求模型?
Context当前工作台模型这轮到底看到了什么?
Memory可复用记忆哪些事实要跨任务保存?
Transcript原始档案完整过程怎样审计和恢复?
State结构化状态当前任务机器状态是什么?
Artifact外部产物文件、日志、diff、报告放在哪里?

可以用一个生活类比:

Prompt 像题目要求。
Context 像桌面上摊开的资料。
Memory 像资料柜。
Transcript 像录音录像。
State 像项目看板。
Artifact 像真正生成出来的文档和代码。

很多 Agent 失控,都是因为这些东西混在了一起。

把 Transcript 当 Context,每轮都会 token 爆炸。

把 Context 当 Memory,临时噪音会污染长期记忆。

把 Memory 当 Prompt,模型会把”经验”误读成不可违反的强规则。

把 State 只放在自然语言历史里,长任务一压缩就丢。

所以 Context Manage 的第一条原则是:

不要把所有信息都塞进一条聊天历史。

工程上更稳的做法,是把不同信息放在不同层里,然后在每一轮模型调用前,动态组装本轮需要的那一小部分。

三、先分清动作层和架构层

很多资料讲 Context Management,会先列出一组动作:

Offload:把大对象搬出 prompt
Reduce:裁剪、抽取、摘要
Retrieve:需要时再召回
Isolate:把任务拆到独立上下文
Cache:复用稳定上下文或计算结果

这五个动作很有用,但它们回答的是:

上下文太多了,我能做哪些处理?

而工程实现还要回答更前面的问题:

这条信息能不能给模型看?
谁比谁更权威?
它现在是热信息还是冷信息?
它应该是原文、摘要、引用,还是结构化状态?
从哪里召回?
怎么压缩才不丢真相?
在哪个边界内使用?

这就是本地 wiki 里那篇 Agent 上下文管理:七维模型与上下文操作系统 更有价值的地方:它把 Context Management 从”处理动作清单”升级成了七维架构模型。

动作层像工具箱,七维模型像设计图。

工具箱告诉你有锤子、钳子、螺丝刀;设计图告诉你哪里能敲、哪里不能敲、先装哪一层、出问题时怎么追责。

四、七维模型:把上下文做成可治理工作集

如果把 Context Manager 当成一个真正的子系统,它至少要从七个维度管理信息。

Visibility:哪些信息对模型可见
Authority:冲突时谁更权威
Temperature:信息处在热、温、冷、冻结还是长期记忆层
Shape:信息以什么形态存在
Retrieval:缺信息时从哪里召回
Compression:太长时怎么压缩且不丢真相
Boundary:不同任务、Agent、租户、权限之间怎么隔离

这七维不是平行名词,而是一条非常实际的工程链路。

1. Visibility:先决定模型是否应该看见

第一道闸门不是压缩,而是可见性。

上下文大致可以分成三类:

类型例子处理方式
llm_visible用户目标、项目规则、关键代码片段、筛过的检索结果可以进入模型上下文
runtime_onlyAPI key、权限对象、session、trace、内部依赖、数据库连接只能给工具和运行时使用
artifact_ref大日志、大文件、网页快照、完整 diff原文外置,给模型引用和预览

很多 Agent 系统一开始会犯的错,是把”工具拿得到”误认为”模型也该看得到”。

比如 OpenAI Agents SDK 特别区分 local context 和 LLM context,说白了就是在做 Visibility:工具函数可能需要当前用户对象、logger、依赖容器和权限信息,但模型不一定需要看见这些对象,更不应该看到 secret(密钥等敏感配置)。

一句话记:

能不让模型看的,就不要让模型看;能用引用表达的,就不要贴原文。

2. Authority:冲突必须有裁决链

Context 里经常会出现冲突。

用户当前说”直接改 generated 文件”,项目规则说”不要改生成文件”;长期记忆说”用户喜欢 Redis”,当前任务说”不要引入 Redis”;旧摘要说测试已经通过,最新工具结果说测试失败。

如果系统没有显式裁决链,就等于把冲突丢给模型凭语感判断。

一个稳妥的默认优先级可以是:

System / Safety Policy
> Tenant / Organization Policy
> Project Rules
> Current User Instruction
> Current Task State
> Verified Retrieval Result
> Long-term Memory
> Historical Summary
> Raw Old Conversation

这条链路的意义不是”所有系统都必须这样排”,而是提醒你:

Authority 必须被设计出来,不能靠 prompt 里多写几句”请遵守”。

Claude Code 的系统规则、项目规则、权限模式、工具安全检查,说白了就是在不同层上共同做 Authority。企业 Agent 里的 RBAC(基于角色的访问控制)、approval(审批流)、audit(审计),则是把 Authority 从 prompt 拉到运行时和组织治理里。

3. Temperature:信息要分冷热层

上下文不是只有”短期”和”长期”两层。

更好用的分法是:

层级含义例子
Hot当前必须使用,默认进 prompt当前用户目标、最近失败日志、正在编辑的文件
Warm当前可能相关,通常摘要或状态化保存已排除原因、读过文件摘要、当前假设
Cold需要时再召回代码索引、文档索引、历史 session
Frozen完整原始记录,只用于审计和恢复transcript、完整日志、网页快照
Long-term Memory跨会话稳定事实用户稳定偏好、项目惯例、长期规则

这会让 Context Manager 更像一个内存管理器:

Hot 用完后降成 Warm。
Warm 变稳定后可能进入 Long-term Memory。
Cold 被检索命中后升温成 Hot。
Frozen 不进 prompt,但能恢复真相。

这也是压缩摘要容易出问题的地方。很多系统把 Hot 现场压成一段 Warm 摘要,却没有保留最近尾巴,于是模型下一轮就丢了现场手感。

4. Shape:同一信息可以有不同形态

不要把所有信息都写成自然语言。

同一段失败日志,可以有很多形态:

形态适合什么时候
Raw需要逐行分析原始内容
Extract只要命令、退出码、错误类型、关键栈
Summary旧历史回顾
Structured State当前任务状态、失败尝试、下一步
Reference原文太长,只保留 artifact ID 或路径
Diff代码变更比完整文件更重要
Graph模块关系、任务 DAG、表关系

比如测试失败日志不一定要整段进入模型,可以先变成:

command: pnpm test auth
status: failed
error: TypeError user.id should be string
file: src/auth/session.ts
test: redirects after login
artifact: logs/test-auth-2026-05-03.txt
next_step: inspect mock user construction

这就是 Shape 的价值:

同一条信息,换一种形态,token 成本、可检索性和可靠性都会变。

LangGraph 的 State、Claude Code 的 compact summary、OpenAI Agents SDK 的 tool context、企业 pipeline 里的 execution context,都可以从 Shape 这个维度理解。

5. Retrieval:召回不是只有向量检索

很多人一说召回就想到向量库,但 Agent 的召回路径远不止这一种。

成熟系统至少会有多路召回:

召回路径适合什么
Recent Tail最近对话、当前工具结果、当前状态
Rule LoadingAGENTS.mdCLAUDE.md、项目规则
Keyword Search函数名、报错码、字段名、配置项
Vector / Hybrid Search文档语义、相似经验、复杂知识
Tool Search工具、技能、插件的渐进式加载
Artifact Lookup大日志、大文件、网页结果
Memory Search用户偏好、长期事实、项目惯例
Graph Traversal模块依赖、任务 DAG、数据库关系

代码场景里,关键词、符号、文件路径往往比纯向量更重要。

企业知识库里,混合检索、权限过滤和来源可信度更重要。

多 Agent 场景里,artifact lookup 和结构化 handoff 更重要。

所以 Retrieval 的关键不是”有没有 RAG”,而是:

这类任务缺信息时,最可靠的信息入口是什么?

6. Compression:缩小工作集,不是删除真相

压缩也不只有 LLM 总结。

可以分成几类:

压缩方式含义风险
Truncate直接截断最容易丢关键约束
Extract抽取关键字段抽取规则不完整会漏信息
Summarize用模型摘要容易摘要漂移
Distill归纳成结构化状态需要设计 schema
Archive + Ref原文外存,只保留引用后续要能 rehydrate
Rehydrate需要时重新展开原文需要保留可追踪来源

更稳的顺序通常是:

先 Offload 大对象
-> Extract 关键字段
-> Distill 成结构化状态
-> Summarize 旧历史
-> 必要时 Truncate

压缩最大的风险叫摘要漂移:摘要悄悄改写了用户约束、失败原因或未决问题。

所以压缩结果必须保留:

  • 来源范围
  • 关键约束
  • 已失败尝试
  • 未决问题
  • artifact 引用
  • 下一步

这和 Claude Code 式 compact 的核心是一致的:摘要不是写读后感,而是写交接单。

7. Boundary:隔离是主线程自保机制

Boundary 是最容易被低估的一维。

子 Agent 的价值不只是”并行做事”,而是上下文隔离。

这些任务尤其适合隔离:

  • 大规模搜索和资料调研
  • 长日志分析和测试排障
  • 代码库扫描和网页抓取
  • 数据清洗和独立实现子任务
  • 高权限工具调用
  • 多租户数据访问

边界可以有很多层:

边界作用
Thread不同会话上下文隔离
Task不同任务状态隔离
Subagent子任务局部上下文隔离
Tool工具权限和输入输出边界
Artifact大对象外置,不污染消息流
Permission高风险动作审批
Tenant不同组织、用户、数据域隔离
Sandbox执行环境隔离

好的子 Agent 设计应该是:

输入窄:任务 + 约束 + artifact 引用
输出窄:结论 + 证据 + 建议下一步 + 置信度

主线程不应该接收子线程的完整过程回放。

没有 Boundary 的多 Agent,很容易从”协作”变成”互相污染”。

五、一个 Agent 执行任务时,上下文是怎么涨起来的

还是看登录跳转 bug 的例子。

最开始用户只给了一句话:

这个项目登录后跳转失败,帮我找原因并修好。

如果 Agent 真要解决问题,它可能会制造下面这些上下文:

步骤新产生的信息
看目录项目结构、框架类型、入口文件
读 package.json测试命令、依赖、脚本
搜索 login匹配文件、相关函数、路由路径
读路由守卫鉴权逻辑、redirect 处理
读状态管理token、user、session 存储方式
跑测试失败日志、stack trace、测试名
修改代码diff、变更文件、实现假设
再跑测试新结果、新错误或通过证明

这些信息有些很热,有些很快变冷。

当前失败日志很热,因为下一步要根据它定位。

旧搜索结果是温的,因为可能还能参考,但不一定要原样保留。

第一次读取的旧文件内容在文件被修改后就变冷,甚至可能变成污染源。

完整 transcript 很重要,但它是冷档案,不应该每轮完整塞进模型。

所以 Context Manager 要做的不是”全部保存”,而是不断判断:

现在这一步,模型最该看到哪几块信息?

这就是上下文治理的核心。

六、工程上会遇到的问题,以及怎么解决

下面把 Context Manage 的工程问题按”症状 -> 根因 -> 方案”拆开。

问题一:模型不知道现场

症状是:Agent 开始猜。

它没读路由代码,却开始改跳转逻辑;没看测试,却说”应该是 token 没存”;没看项目规则,就按自己习惯改目录结构。

根因不是模型不会推理,而是当前 context 里没有足够现场。

解决方式是动态上下文注入:

  • 启动时加载项目规则和工作目录信息。
  • 当前任务相关文件按需读取。
  • 搜索结果先作为候选,不要一次性全塞。
  • 工具结果写回消息历史或结构化状态。
  • 需要外部知识时通过检索、Web、MCP 或数据库工具召回。

这里的重点是”按需”。

上下文不是越多越好。稳定 Agent 不是看过最多材料的 Agent,而是每一轮拿到最相关材料的 Agent。

问题二:工具结果太长

症状是:token 用量迅速上升,模型越来越慢,最后触发上下文限制。

根因通常不是用户聊太多,而是工具输出太肥。

解决方式是先治理工具结果,再治理聊天历史:

  • 给每类工具设置结果预算。
  • 长日志只保留错误摘要、关键栈、退出码和相关文件。
  • 大文件优先返回片段、行号、符号索引或引用。
  • 搜索结果先返回匹配概览,模型需要时再读具体文件。
  • 对已过时的工具结果做 snip 或 micro compact。

这点 Claude Code 很典型。编程 Agent 里最容易炸窗口的往往不是推理,而是 Bash、Read、Grep 这些工具返回了太多真实世界的信息。

(实际生产中,我见过一次 find . -name "*.log" 返回了 12MB 内容直接进上下文的惨案。后来加了硬限制:单条工具结果超过 4KB 就自动转 artifact。)

问题三:旧信息污染新判断

症状是:Agent 基于旧代码继续分析,或者重复调查已经排除过的方向。

根因是上下文里缺少”信息时效”。

解决方式是给上下文加生命周期:

  • 文件读取结果要记录版本、mtime、hash 或读取时间。
  • 文件修改后,旧读取结果应该降权或标记过期。
  • 测试日志要关联对应 commit、命令和时间。
  • 搜索结果只作为线索,不作为当前事实。
  • 关键事实尽量引用来源,而不是只写自然语言总结。

这也是为什么 Context Manager 不只是字符串拼接器。它最好能把信息当成带元数据的对象,而不是一坨文本。

问题四:规则之间冲突

症状是:系统规则、项目规则、用户当前指令、长期记忆互相打架。

比如:

系统要求不能泄漏 secret。
项目规则要求不要改生成文件。
用户当前要求直接修改 generated 文件。
长期记忆说用户偏好快速完成。

如果这些都只是自然语言堆在上下文里,模型可能会凭语感选择一条。

解决方式是建立权威层级:

System / 安全策略
-> 组织级规则
-> 项目级规则
-> 当前用户指令
-> 长期偏好
-> 检索和工具结果

工程实现上,可以把规则分为:

  • 强约束:系统必须拦截或审批。
  • 软约束:进入上下文让模型参考。
  • 情境约束:只在匹配路径、工具或任务时注入。

这也是为什么大项目里一份超长的 AGENTS.mdCLAUDE.md 不一定更好。规则太长、太泛、太冲突,最后会变成上下文噪音。

(实战经验:规则文件超过 500 行时,模型反而开始忽略其中的约束。不如拆成多份,按需加载。)

问题五:压缩后任务断线

症状是:compact 之后,模型还知道大概发生了什么,但不知道下一步该干什么。

根因是摘要只记录”历史”,没有记录”状态”。

好的压缩摘要不是文章总结,而是任务交接单。

它至少应该保留:

用户目标
不可违反的约束
当前阶段
已经读过的重要文件
已经修改的文件
关键判断和证据
失败过的尝试
最近测试结果
下一步建议

同时,压缩后最好保留最近几轮原始消息和关键工具结果。

可以理解成:

摘要负责保留主线。
最近尾巴负责保留现场手感。

这比”把所有旧历史压成一段话”稳定得多。

问题六:多 Agent 互相污染

症状是:一个子 Agent 的草稿、假设、错误路径影响了另一个 Agent。

根因是所有 Agent 共享一条线性聊天历史。

解决方式是上下文隔离:

  • 子 Agent 拿局部任务,不拿全局历史。
  • 子 Agent 返回结构化结果,不返回完整思考过程。
  • 上游只传递可验证产物、引用和结论。
  • 共享状态用 schema 管理,不靠自然语言互相转述。
  • 每个 Agent 有自己的工具权限和上下文预算。

复杂任务里,隔离往往比协作更重要。

没有隔离的多 Agent,很容易从”多人协作”变成”多人同时污染同一张桌子”。

问题七:成本和延迟失控

症状是:Agent 能做事,但每一步都慢、贵、啰嗦。

根因是每轮都带了太多固定内容,或者每次都重新检索、重新读、重新解释。

解决方式包括:

  • prompt cache:稳定系统提示和工具说明尽量缓存。
  • lazy loading:工具详细说明、规则文件、长文档按需加载。
  • progressive disclosure:先给摘要和索引,需要时再展开全文。
  • local context:运行时依赖和内部状态不送给模型。
  • structured state:机器可处理的状态不要变成自然语言 token。

这里最重要的判断是:

大上下文窗口解决容量问题,不解决信息纪律问题。

窗口再大,如果你每轮都塞一堆不相关信息,模型仍然会慢、贵、容易跑偏。

七、不同项目是怎么处理 Context 的

接下来把几个代表项目放在同一张图里看。

这不是为了比较谁更先进,而是为了看清楚:不同宿主环境下,Context Manage 的重点会完全不一样。

1. Claude Code:长任务 CLI Agent 的上下文防线

Claude Code 的典型场景是:

在真实代码仓库里,连续读文件、改代码、跑命令、修 bug。

它最突出的上下文问题是工具结果和长任务历史。

所以它的 Context Manage 重点是:

  • 项目规则:通过 CLAUDE.md、规则文件、路径范围等方式注入项目上下文。
  • 工具结果治理:大文件、大日志、搜索结果不能无限进入消息历史。
  • 自动压缩:接近上下文限制时,把历史折成摘要继续执行。
  • 会话恢复:transcript、resume、最近尾巴帮助任务不断线。
  • 子 Agent 隔离:把搜索、分析、实现等任务分开,降低主上下文压力。

Claude Code 给人的启发是:

编程 Agent 的上下文管理,第一优先级不是长期记忆,而是让工具循环持续跑下去。

因为它真正面对的是:

文件太长。
日志太长。
错误太多。
任务轮次太多。
用户约束不能丢。

所以 Claude Code 更像一套”主循环里的上下文治理防线”。

2. LangGraph:把上下文从聊天历史拆成结构化 State

LangGraph 的视角不太一样。

它不把 Agent 主要看成一条聊天历史,而是看成一个图:

节点执行
-> 更新 State
-> checkpoint
-> 下一节点继续

它的 Context Manage 重点是:

  • State schema:哪些信息是任务状态,要结构化保存。
  • Checkpoint:每个 super-step 之后保存图状态。
  • Thread:同一个会话或任务的状态序列。
  • Time travel:可以回到历史 checkpoint 调试或分叉。
  • Fault tolerance:节点失败后从成功 checkpoint 恢复。

这给人的启发是:

不要让聊天历史承担所有状态责任。

如果一个任务有明确步骤、明确节点、明确中间状态,放进 LangGraph 这类状态图里,会比塞在自然语言对话里稳很多。

Claude Code 更像”长任务交互式执行”,LangGraph 更像”可恢复的工作流状态机”。

两者都在做上下文管理,但切入点不同:

Claude Code:先治理消息和工具结果。
LangGraph:先治理状态和执行边界。

3. OpenAI Agents SDK:把本地 Context 和 LLM Context 分开

OpenAI Agents SDK 的文档里有一个很重要的区分:

Local context:你的代码和工具运行时能看到的上下文。
LLM context:模型生成时真正能看到的上下文。

这个区分非常工程化。

很多开发者容易把 context 理解成”发给模型的东西”。但在真实应用里,有些信息工具需要用,模型不需要看;有些信息甚至不应该给模型看。

比如:

  • 数据库连接
  • logger
  • 当前用户对象
  • 权限状态
  • 内部依赖
  • tool call metadata
  • usage 统计

这些更适合放在本地 RunContextWrapper 一类对象里,由工具函数和生命周期 hook 使用,而不是直接注入模型上下文。

模型需要知道的信息,则可以通过几种方式进入 LLM context:

  • 放进 instructions。
  • 放进本轮 input。
  • 暴露成工具,让模型需要时调用。
  • 用 retrieval 或 web search 按需补充。

OpenAI Agents SDK 给人的启发是:

Context Manage 的第一步,是区分”运行时需要”还是”模型需要”。

这能避免两种坏结果:

把不该给模型看的内部状态泄漏进 prompt。
把模型不需要看的依赖对象浪费成 token。

4. AutoGen:多 Agent 场景里的模型上下文和记忆注入

AutoGen 的典型场景是多 Agent 对话和协作。

这类系统的问题不是一个 Agent 忘不忘,而是多个 Agent 如何共享信息、隔离角色、控制消息历史。

它的 Context Manage 重点是:

  • model context:不同 Agent 调模型时看到哪些消息。
  • memory:从记忆存储里查相关内容,再更新模型上下文。
  • role separation:不同 Agent 的职责和可见信息不同。
  • team orchestration:多个 Agent 的消息传递和终止条件。
  • context variants:有些场景保留完整历史,有些场景只保留窗口或头尾。

AutoGen 给人的启发是:

多 Agent 系统里,上下文管理首先是边界管理。

一个 reviewer Agent 不应该拿到 executor 的所有工具权限。

一个 researcher Agent 不应该把所有搜索草稿都塞给 writer。

一个 planner Agent 的中间假设也不应该自动变成全局事实。

多 Agent 不是把更多模型放进同一个聊天框,而是给不同角色分配不同上下文、不同状态和不同输出契约。

5. Cursor / Copilot:IDE 场景优先低延迟和局部相关性

IDE 助手的场景又不一样。

它们经常要在你敲代码时快速补全、解释、改写。这里的核心矛盾不是”长任务恢复”,而是:

在很短时间内,找到当前光标附近最有用的代码上下文。

所以 IDE 场景的 Context Manage 更偏:

  • 光标前后代码片段。
  • 当前文件符号。
  • import 和类型信息。
  • 相似代码块。
  • 最近编辑文件。
  • 语义索引或增量索引。

它不一定每次都需要完整项目理解。

很多时候,低延迟的局部启发式比重型全局检索更实用。

这给人的启发是:

Context Manage 要服务场景,不要默认追求最完整。

补全任务需要快、近、准。

修 bug 任务需要长链路、工具反馈和恢复。

企业流程任务需要权限、审计和流程上下文。

不同场景的”好上下文”不是同一个东西。

6. Hermes / OpenClaw / Harness.io:长期运行时和治理上下文

再往上一层,Context Manage 会从单次任务扩展成运行时治理。

OpenClaw 更像个人 Agent 的入口和控制平面。它关心的是多渠道消息、自动化任务、设备节点、浏览器和本地能力怎样接入同一套会话系统。

Hermes 更像自我改进的 Agent runtime。它关心的是长期记忆、用户画像、技能沉淀、跨 session 搜索和可复用经验。

Harness.io 这类企业工作流系统更关心 pipeline context、secrets、connectors、RBAC、approval 和审计。

它们的共同点是:

Context 不再只是模型输入,而是整个运行环境的一部分。

到了这一层,Context Manage 要管的东西会更多:

  • 谁触发任务。
  • 任务来自哪个渠道。
  • 当前执行环境是谁的机器或沙箱。
  • 哪些 secret 可用。
  • 哪些审批已经通过。
  • 哪些历史经验可以复用。
  • 哪些操作必须被审计。

这说明 Context Manage 的终点不是”更聪明的 prompt”,而是 Agent Harness 的一部分。

八、放在一起对比

可以用一张表把这些项目放回同一个坐标系。

项目 / 系统主要场景Context Manage 的核心解决的主要问题容易忽略的边界
Claude CodeCLI 编程 Agent项目规则、工具结果、压缩、恢复长任务不断线,工具输出不炸窗压缩摘要仍可能丢现场细节
LangGraph图式工作流 AgentState、checkpoint、thread、time travel状态可恢复,节点可调试模型输入仍需单独治理
OpenAI Agents SDK应用型 Agent SDKlocal context 与 LLM context 分离工具依赖、运行时状态、模型可见信息分层开发者仍要设计哪些信息注入模型
AutoGen多 Agent 协作model context、memory、角色边界多角色消息传递和记忆增强共享历史过多会互相污染
Cursor / CopilotIDE 实时助手光标附近、相似代码、索引检索低延迟下拿到局部相关上下文不适合默认承担长任务状态
Hermes / OpenClaw个人长期运行时gateway、memory、skills、session search多入口、长期记忆、经验复用长期记忆需要防止陈旧和污染
Harness.io 类系统企业流程 Agentpipeline context、secrets、RBAC、audit把 Agent 放进可治理流程灵活性会受流程边界约束

这张表最想说明的是:

不同项目不是在同一道题上给不同答案,而是在不同宿主环境里处理不同上下文压力。

Claude Code 的压力来自长任务和工具输出。

LangGraph 的压力来自状态可恢复。

OpenAI Agents SDK 的压力来自应用运行时和模型输入的边界。

AutoGen 的压力来自多 Agent 协作。

Cursor 的压力来自低延迟代码场景。

Hermes / OpenClaw 的压力来自长期运行和多入口。

企业 harness 的压力来自权限、审计和流程嵌入。

九、如果自己实现一个最小 Context Manager

如果要自己写一个 Mini Agent,不建议一上来就做复杂向量库或多层记忆。

更稳的做法,是把 Context Manager 明确拆成几个小组件:

组件责任
Visibility Filter判断哪些信息能进模型,哪些只能留在运行时
Authority Resolver处理冲突和优先级
Temperature Manager管理 Hot / Warm / Cold / Frozen / Long-term Memory
Retrieval Router决定从规则、关键词、向量、工具、artifact、memory 还是图里召回
Compression Engine负责外置、抽取、摘要、结构化和 rehydrate
Boundary Controller管理 thread、task、subagent、tenant、permission、sandbox 边界
Context Budgeter负责 token 预算、选择解释和 ContextPlan

这样 Context Manager 就不再是”每轮拼 prompt 的一段代码”,而是一个可调试的工作集规划器。

先做最小闭环就够:

1. 所有消息和工具结果进入 transcript,完整保存。
2. 每轮模型请求前,从 transcript / state / memory / tools 里收集候选 context。
3. 给候选 context 打标签:来源、类型、热度、权威性、token 估算、是否可见。
4. 按当前任务选择最相关的一批。
5. 对大工具结果做裁剪或摘要。
6. 保留最近 N 轮原始消息。
7. 旧历史压成任务交接摘要。
8. 压缩摘要里强制保留目标、约束、已做、失败、下一步。
9. 所有压缩前原始内容仍保存在 transcript,方便恢复和审计。

一个很朴素的数据结构可以长这样:

type ContextItem = {
  id: string
  kind: "instruction" | "user_goal" | "tool_result" | "file" | "summary" | "memory" | "state"
  source: string
  visibility: "llm_visible" | "runtime_only" | "artifact_ref"
  authority: "system" | "org" | "project" | "user" | "task_state" | "retrieval" | "memory" | "summary"
  temperature: "hot" | "warm" | "cold" | "frozen" | "long_term"
  shape: "raw" | "extract" | "summary" | "reference" | "diff" | "structured" | "graph"
  boundary: "thread" | "task" | "subagent" | "tool" | "tenant" | "sandbox"
  tokenEstimate: number
  freshnessTs?: string
  conflictKey?: string
  confidence?: number
  ttl?: string
  content?: string
  ref?: string
}

然后每轮生成一个 ContextPlan

type ContextPlan = {
  selected: ContextItem[]
  compressed: Array<{ from: string; to: string; method: "extract" | "summarize" | "distill" | "archive_ref" }>
  dropped: Array<{ id: string; reason: string }>
  conflicts: Array<{ key: string; winner: string; losers: string[]; reason: string }>
  budget: {
    total: number
    used: number
    buckets: Record<string, number>
  }
}

ContextPlan 的价值是解释性。

当 Agent 失误时,你不只看到”模型答错了”,而是可以追问:

这一轮到底选了哪些上下文?
哪些规则被丢掉了?
哪个工具结果被压缩了?
长期记忆为什么被注入?
某条用户约束为什么没进 prompt?

没有 ContextPlan,上下文行为就是黑箱拼接。有了它,Context Manager 才像一个可调试系统。

每轮调用前,Context Manager 做一次选择:

collect candidates
-> remove runtime-only items
-> resolve authority conflicts
-> drop stale tool results
-> prefer hot context
-> compress large items
-> preserve recent tail
-> inject final context

伪代码可以这样理解:

function buildContext(task, state, transcript, memory, budget) {
  const candidates = collect(task, state, transcript, memory)
  const visible = applyVisibilityFilter(candidates)
  const resolved = resolveAuthorityConflicts(visible)
  const fresh = updateTemperatureAndFreshness(resolved, state)
  const retrieved = routeRetrievalIfNeeded(fresh, task)
  const shaped = transformShape(retrieved)
  const compressed = compressToBudget(shaped, budget)
  const selected = enforceBoundaries(compressed, task)

  return [
    stableInstructions(selected),
    projectRules(selected),
    taskSummary(selected),
    recentTail(transcript),
    toolResults(selected),
    currentUserInput(task),
  ]
}

这里最重要的不是代码,而是思想:

Context 应该是被构建出来的,不是自然增长出来的。

自然增长的聊天历史,跑久了一定会变脏。

预算也不要只看总 token。更稳的做法是按桶分配:

预算桶建议比例
System / Policy / Project Rules10%-20%
Current User Input + Task State10%-20%
Recent Tail15%-25%
Retrieved Context20%-35%
Tool Results / Artifact Preview10%-20%
Long-term Memory5%-10%

超预算时,不要第一反应就砍最近对话。

更合理的顺序通常是:

先删低置信度 retrieval
-> 再删过期 memory
-> 再把大工具结果转成 artifact 引用
-> 再压缩旧历史
-> 最后才缩 recent tail

因为最近尾巴往往承载当前现场感,砍得太早,模型会突然变”远”。

MVP 可以按这个路线走:

版本目标
v0.1最近 N 轮、任务状态 JSON、项目规则、artifact 外置、手动 compact、ContextPlan 日志
v1.1加 keyword / vector / hybrid retrieval 和 freshness filter
v1.2加 memory namespace、TTL、冲突解决
v1.3加 subagent context、structured report、parent merge
v1.4加多租户策略、权限感知检索、审计和 explain UI

十、压缩摘要应该怎么写

很多系统的 compact 不稳,是因为摘要目标写错了。

它把摘要当成”前情提要”,而不是”接班材料”。

对 Agent 来说,更好的 compact 模板是:

用户目标:
[用户最初要完成什么]

硬约束:
[不能违反的规则、用户明确要求、权限边界]

当前状态:
[任务现在停在哪一步,不是泛泛总结]

关键事实:
[从文件、日志、工具结果里确认过的事实,带来源]

已读文件:
[文件路径 + 读到的重点 + 是否可能过期]

已改文件:
[文件路径 + 改了什么 + 为什么改]

已尝试但失败的方案:
[避免下一轮重复踩坑]

最近验证结果:
[命令、结果、失败信息或通过证据]

下一步:
[压缩后第一步应该继续做什么]

这个模板的重点是”可继续执行”。

只知道历史没有用,Agent 必须知道下一步从哪里接上。

十一、选型时应该问什么

如果你在设计自己的 Agent 系统,不要先问”用哪个框架最强”。

先问这些问题:

我的 Agent 是低延迟补全,还是长任务执行?
任务状态能不能结构化?
工具结果会不会很长?
是否需要跨 session 记忆?
是否有多 Agent 协作?
是否需要企业权限和审计?
是否允许模型看到内部运行时状态?
失败后要不要恢复?
压缩后最怕丢什么?

不同答案会导向不同设计:

  • 如果是 IDE 补全,优先做局部代码上下文和低延迟索引。
  • 如果是工作流任务,优先做 State、checkpoint 和可恢复执行。
  • 如果是应用 SDK,优先区分 local context 和 LLM context。
  • 如果是编程 CLI Agent,优先治理工具结果、压缩和最近尾巴。
  • 如果是多 Agent,优先设计边界、角色、handoff 和结构化产物。
  • 如果是长期个人助理,优先设计记忆分层、技能沉淀和过期策略。
  • 如果是企业流程,优先把权限、审批、secret 和审计放进 context 体系。

这比直接比较”谁的模型更强”更接近工程现实。

十二、一句话总结

如果把这篇压成一句话:

Context Manage 不是把更多内容塞进模型,而是在有限窗口里持续决定:什么信息该被模型看到、以什么形态看到、什么时候看到、过期后怎么处理、空间不够时怎么压缩、任务中断后怎么恢复。

再压成六个词:

选择
注入
召回
压缩
隔离
恢复

Claude Code、LangGraph、OpenAI Agents SDK、AutoGen、Cursor、Hermes、OpenClaw 这些项目看起来差异很大,但它们都在回答同一个底层问题:

当模型没有真正的记忆,
而任务又必须连续推进时,
外层系统怎样管理模型这一轮该看到的世界?

这就是 Context Manage 的价值。

它不是 Agent 的附属功能,而是 Agent Harness 的核心能力之一。