别让你的 Agent 死在内耗上:Context Engineering 是时候认真对待了

同样的模型框架,别人跑起来很稳,你的一跑就开始胡言乱语、反复调用工具、输出一堆看似结论实则跑不通的东西。问题大概率不在模型,而在上下文。这篇文章从实战角度聊聊 Context Engineering——一个被严重低估的 Agent 工程课题。

别让你的 Agent 死在内耗上:Context Engineering 是时候认真对待了

同样的 Agent 框架,同样的模型,为什么有人跑得很稳,你的跑三分钟就开始迷路?

它开始重复调用同一批工具,然后查了一堆没用的日志,最后输出一段看起来很像结论、但根本跑不通的东西。

这种场景做 Agent 的人应该都不陌生。

问题多半不在模型。模型还是那个模型。问题出在你喂给它的上下文——Agent 每次调用 LLM 之前,窗口里到底塞了什么、塞得干不干净、顺序对不对、工具描述够不够清楚,这些因素加起来,才真正决定了它的行为。

我一直觉得 Context Engineering 是被严重低估的一环。很多人花大量时间选模型、调 prompt、折腾框架,却忽略了一个更根本的问题:Agent 拿到的是不是一个够格的上下文环境。

场景对比:同样的 LLM,两种效果

先看一个电商售后的例子。

用户说:"我上周买的耳机右耳没声音了,怎么处理?"

如果 Agent 拿到的上下文很少,它大概率这样回:

"请问您购买的是哪款耳机?订单号是多少?能否描述一下故障表现?"

这段回复不能说错。但它像一个刚上岗的客服新人,只会按流程追问。

代码逻辑没问题,LLM 调用也没问题。但模型手里没有背景信息,它只能一步步"要"。

换一个上下文充足的版本——在调用 LLM 之前,系统先把该查的信息都查了:

  • 查订单系统,定位到上周的购买记录:索尼 WH-1000XM5,3 月 25 日下单
  • 查保修状态,在 7 天无理由退换期内
  • 查历史工单,老客户,之前没售后纠纷
  • 挂载 create_return_ordercheck_inventory 工具

这时候 Agent 可以这样回:

"查到您 3 月 25 日买的索尼 WH-1000XM5,还在退换期内。仓库显示同款有库存,直接帮您换货,2-3 天寄出新品。需要我操作吗?"

差距就这么出来了。前一个 Agent 在"要信息",后一个 Agent 在"解决问题"。

Agent 的失败,根子往往都在上下文。 上下文不够,模型再强也只能猜;上下文给对了,中等水平的模型也能把任务做下去。

Context Engineering 到底管什么

Tobi Lutke 有一句话说得很好:Context Engineering 是给 LLM 提供足够上下文,让任务在模型能力范围内"有可能被解决"。

关键在这个"有可能"。它不是说上下文给够了模型就一定能解决,而是强调——如果没有这些上下文,任务压根就不具备可解条件。

很多人把 Context Engineering 和 Prompt Engineering 混在一起讲。但它们的关注点不一样。

Prompt Engineering 关心的是指令怎么写——措辞、顺序、格式、语气。

Context Engineering 关心的是另一件事:这轮调用前,模型窗口里该放哪些信息、以什么结构放、什么时候放、什么时候撤掉。

类比一下的话,Prompt Engineering 是教厨师怎么做一道菜,Context Engineering 是给厨师准备厨房——食材在哪、刀具在哪、调料怎么分类、火候参考在哪。

我更喜欢另一个类比:Context Engineering 是 LLM 的内存管理。

LLM 的上下文窗口就是一块有限内存。Context Engineering 管的就是这块内存里装什么、换出什么、什么时候读、什么时候写。

窗口满了,就要淘汰内容。这和操作系统里的页面置换有点像——LRU、优先级策略,一个道理。

拆开来看,Context Engineering 至少管六块:

  1. System Prompt——规则、角色、约束、执行流、输出格式
  2. User Prompt——用户输入的业务数据和指令
  3. Memory——短期的滑动窗口历史和长期的向量库记忆
  4. RAG & Tools——检索内容怎么放进来、工具描述怎么挂载
  5. Structured Output——JSON Schema、function call 的返回结构
  6. Token 优化——摘要压缩、历史剔除、Context Caching

每一块出问题了,Agent 的表现都会被拖累。

窗口不是越长越好

直觉上会觉得:窗口越大,塞的信息越多,模型应该表现越好。

实际不是这样。上下文存在边际收益递减,塞过头之后效果还可能变差。

原因和 Attention 机制有关。n 个 Token 会产生 n² 量级的注意力计算。当上下文从 1K 扩展到 100K Token,问题不只是"信息被稀释"这么简单——模型要在更多 Token 之间判断哪些相关、哪些不相关。上下文越长,噪声越多,信号越难被挑出来。

学术上有一个更精确的说法叫 Context Rot(上下文腐化)和 Lost in the Middle。研究发现模型对上下文中间位置的信息记忆更弱,对开头和结尾更敏感,整体呈 U 型分布。

这两个现象都说明一件事:上下文不是越长越好。

工程上别迷信窗口大小。不同模型的衰减曲线不一样,有些退化平缓,有些退化很陡,具体阈值要靠实测。但有一点是确定的:上下文必须当作有限资源来管,你要找的不是"塞满窗口",而是高信噪比的平衡点。

动态挂载:别当垃圾桶

上下文窗口不是垃圾桶。很多 Agent 失败不是信息不够,而是塞了太多无关信息。

一个常见错误是:把所有工具描述一次性塞进去。Agent 面对几十个工具时,它得在茫茫多的 JSON Schema 里挑对的。既浪费 Token,也增加误调用概率。

更合理的做法是工具懒加载——先通过向量检索找出当前任务最相关的 Top-5 工具定义,再挂载进去。这和人查手册差不多,先把整本手册背下来不是正常人的做法。

但这里有一个限制:Anthropic 强调在设计阶段就精简工具集,别把工具集合做得过度膨胀。工具太多,后面再做检索也只是补救。

另一个常见问题是 Observation 处理。工具调用返回的结果——API 报错日志、查询结果——最好先让 LLM 做一次摘要,只把关键信息写回上下文。原始日志洪流直接塞进去,很容易把模型淹没。

Token 不够时的三级降级策略

长任务跑到后面,窗口一定会紧张。这时候不能靠感觉删内容,得有优先级。

我在实际项目中用的策略是三级淘汰:

  • 低优先级(可折叠):早期对话历史。不保留原文,交给 LLM 压缩成摘要。保留架构决策、关键事实,丢掉冗余的工具调用结果。
  • 中优先级(可精简):RAG 检索出来的背景资料。二次裁剪,只留和当前任务直接相关的段落,别整段保留。
  • 高优先级(绝对保护):System Constraints、当前核心工具描述、关键任务目标。这些一旦丢了,Agent 很容易开始乱跑。

这套策略看着简单,但绝大多数 Agent 系统根本没有做优先级区分——所有内容一视同仁,先来后到,窗口满了就从最老的消息开始扔。这通常是最差的淘汰策略,因为最早的消息往往是 System Prompt 和核心指令。

Just-in-Time 按需加载

预检索的模式在简单问答场景里挺好用。但到了复杂 Agent 任务里就不太够了——预检索拿到的是"调用前看起来相关"的信息,而 Agent 执行过程中会不断发现新线索,这些线索在预检索时根本还不存在。

Just-in-Time 的思路是:不要一开始就装载所有可能相关的信息。Agent 运行时先维护轻量级引用——文件路径、数据库查询、Web 链接。真正需要时再通过工具动态拉取。

Claude Code 是很好的例子。它分析大型代码库时不会把所有文件都塞进上下文。它先通过目录结构、文件名、搜索命令定位目标,再用 headtailgrep 逐步读取。靠文件名和目录结构理解信息位置,靠文件大小和时间戳判断优先级,而不是上来就把全部内容吞进去。

但 Just-in-Time 不是"不预处理"。它对工具集和导航策略的要求比预检索更高。如果导航工具不好用,或者启发式规则写得差,Agent 很容易追进死胡同,浪费上下文和调用次数。

更现实的方案是混合策略:确定性高的静态知识预检索,运行中有动态发现的信息按需拉取。

长任务的上下文持久化

Agent 如果跑好几个小时,只靠单窗口管理是不够的,需要跨窗口持久化。

目前主流的有三种方法:

Compaction(压缩):当上下文快满时,把历史内容交给 LLM 总结,用摘要开启新窗口。难点在取舍——保留太多压缩没意义,保留太少关键上下文丢了。比较实际的做法是拿真实 Agent 轨迹反复调压缩 prompt,先保证重要信息别漏,再删冗余。

Structured Note-taking(结构化笔记):让 Agent 把关键进展写到外部文件里,比如在项目根目录维护 NOTES.md。上下文重置后读取笔记继续工作。人类工程师写 to-do list 也是一样的道理。

Sub-agent(子 Agent):别让一个 Agent 扛所有状态。把专门任务拆给专业化的子 Agent,每个子 Agent 可以自己探索大量上下文(几万 Token),但返回给主 Agent 的只是一段 1000-2000 Token 的高密度摘要。这样主 Agent 的上下文会干净很多,搜索过程被隔离在子 Agent 里,主 Agent 只处理分析和决策。

三种方法各有适用场景。Compaction 适合需要持续对话的长流程;Note-taking 适合有清晰里程碑的迭代式开发;Sub-agents 适合需要并行探索的复杂研究任务。

最后说两句

Agent 的工程水平,很大程度上体现在上下文管理上。

很多人花大量时间在选模型、调 prompt、折腾框架,却忽视了最直接影响 Agent 行为的一环:上下文。一个上下文管理系统做得好的 Agent,用中等模型也能稳定输出。反过来,上下文乱成一锅粥,换什么 SOTA 模型都救不了。

如果你现在做 Agent 也遇到"时而聪明时而智障"的问题,先别急着换模型。看看你的上下文是怎么构造的——很有可能是那个 "garbage in, garbage out" 的 "garbage in" 出了问题。

评论

此博客中的热门博文

我写了半年 prompt,最后发现最好的技巧就三个