本文整理自 CNB 上的开源课程 《Vibe Agent:从零搭建 Coding Agent》。课程讲义、示例代码与练习仓库均在 CNB 上公开,文中示例出自该仓库对应章节。

第 2 章的最小 Agent Loop 每轮把 messages 全量发一遍。三五轮对话里看不出问题,任务一长,这段历史就成了主要的成本和风险来源。

二十轮,259 倍

课程 demo_13_context_explosion.py 让一个旅行助手反复执行同一句追问:总结之前讨论过的要点,再补充 3 条新建议。每轮回复全量塞回 messages,连跑 20 轮,记录 usage 里的 prompt_tokens。

轮次 prompt tokens total tokens
1 32 432
5 1771 2171
10 3946 4346
15 6121 6521
20 8296 8696

20 轮,prompt 从 32 涨到 8296,259 倍。增长几乎是匀速的:从第 2 轮起,每轮比上一轮多 435 个 token 左右,因为每轮追加的追问与回复长度基本固定。单轮是线性增长,但每一轮都要把前面全部历史重新发一次,累计开销随轮次平方级上升。20 轮的 prompt 合计约 8.3 万 token,total 合计约 9.1 万。

三类消息,三种膨胀速度

信息类型 来源 单条规模 累计速度
system 角色提示 几百 token 不增长
user、assistant 对话历史 100 到 500 token 线性增长
tool 工具返回 几百到几万 token 可能爆炸

最贵的是 tool 消息。read 把整个文件原样塞回上下文,bash 跑一次 npm test 返回几千行 STDOUT,grep 命中几百行也一条不落。一次 read 带回 5000 token 并不稀奇,30 轮就顶到 128K 的天花板。没有上下文管理的 Coding Agent,跑过几轮就可能崩。

tool 消息的写法可以改。同样是查文件,只返回命中的几行和行号,与把整个文件丢回去,token 差一个数量级。工具返回值是上下文的一部分,返回格式在设计工具时就要考虑,不能等到窗口快满了再裁。

三个后果同时出现

撞窗口只是第一层。第二层是成本:每轮重发整段历史,同一段内容被反复计费,到第 10 轮,第 1 条消息已经按 10 次计过价。第三层是模型变笨。Lost in the Middle 的测试把同一段信息放在开头、中间、结尾三种位置,放在中间时模型用上它的比例明显更低;上下文越长,早期约束越容易落进这段被忽略的区间。窗口还有余量,答案质量已经开始下滑。

成本量级可以直接看课程给出的价格表:

模型 上下文窗口 输入价格 30 轮预估成本
GPT-4o 128K 约 18 元 / 百万 token 约 0.36 元
Claude 3.5 Sonnet 200K 约 22 元 / 百万 token 约 0.55 元
DeepSeek-V3 128K 约 1 元 / 百万 token 约 0.025 元

成本还有一层容易被忽略:输入按 token 计费,每一轮都要为新旧全部内容付一次钱。30 轮下来,实际输入量接近把历史逐轮累加,而 messages 的最终长度只反映末尾一轮。

这些是课程讲义给出的量级。换成容量看,128K 大约是一本中篇小说或几万行代码,200K 能装半部《三体》。数字看着宽裕,但前面的实验里 20 轮对话已经吃掉 8.3k token,真实任务往往不止 20 轮。窗口大和可以不管,是两件事:Lost in the Middle 在便宜的模型上同样成立。按 128K 算,二十轮的 8296 token 连上限的十分之一都不到,问题不在装不下,在于每一轮都要重新装一遍。

对应的工程解法各管一头:设 token 阈值,触发后主动压缩消息,管撞窗口;让重复信息只算一次,缓存命中或者用摘要替代原文,管成本;重要事实前置、无关历史后移,管注意力。

上下文工程管什么

2024 年 Andrej Karpathy 说 Context engineering is the new prompt engineering。放到 Agent 上,意思是在正确的时机把正确的信息放进 messages。课程把它拆成四个维度:ContextManager 管本轮 messages 怎么压,Memory 管跨会话的事实怎么存和取,工具输出格式管 tool 消息怎么短而有用,Skills 管领域规范怎么按需加载。第 4 章覆盖前两个。这四个维度对应四类不同的失效:本轮 messages 太长、跨会话记忆丢失、工具输出污染上下文、领域知识塞不进 system prompt。分开处理比在一个地方加压缩逻辑更可控。这一章排在工具与范式之后,因为此前的循环都默认历史可以无限增长。

写 prompt 是基本功,上下文工程决定这套循环能跑多长。两者的区别在于,前者关心一句话怎么说,后者关心每一轮该看到什么。

这对搭 Agent 意味着什么

上下文管理属于前提条件。判断一个 Agent 能不能跑完长任务,先看它怎么组织历史:有没有阈值,tool 消息要不要裁,早期约束放在哪一层。窗口从 128K 涨到 200K,改变的是上限,不改变增长方式。只要每轮全量重发,任何窗口都会被填满。

下一节给出四种具体的压缩策略,从十行代码的截断到工程级的 GSSC 流水线。