1. Agent 与 Context:先搞清楚这两个词到底在说什么
最近后台收到不少留言,问的都是同一类问题:Agent 开发时 Context 到底怎么管理?报错 "Context is too large and auto-compaction could not recover this" 到底该怎么办?还有人把 Agent、Harness、Skill 这几个词混在一起,越看越乱。今天我就把这块内容彻底拆开讲一遍,尽量用大白话把我实际踩过的坑和验证过的方案都交代清楚。
先给个最简单的定义:Agent 是一个能自主完成任务的程序,它不是一个单纯的函数调用,也不是一条写死的 if-else 流程,而是一个能够感知环境、做出决策、调用工具、观察结果并持续迭代的循环。Context 则是这个 Agent 在运行过程中所能看到的全部信息的总和,包括系统提示词、用户输入、工具返回结果、历史对话记录、中间推理过程等等。你可以把 Agent 想象成一个新入职的员工,Context 就是他能看到的桌面文件、聊天记录、任务清单和参考资料,桌面越乱、文件越多,他干活越容易懵,也越容易做错决定。
这一篇主要面向三类人:正在做 Agent 开发但老是被 Context 报错折磨的工程师,准备系统学习 Agent 架构的初学者,以及想搞清楚 LangChain、Dify、CrewAI、Claude Code 这类框架底层到底怎么管理上下文的进阶玩家。看完你至少能回答三个问题:Context 为什么会爆?Context 爆了之后自动压缩为什么有时候救不回来?在架构层面有哪些手段可以从根上缓解这个问题。
提示:本文所有内容基于我在实际项目中的经验总结,涉及的具体参数和报错信息来自公开讨论与常见实践,不同框架版本可能略有差异,但底层原理是一致的。
2. Context 原理拆解:Token、窗口与注意力机制
2.1 最大上下文长度到底是什么意思
报错信息里那句 "this model's maximum context length is 1048576 tokens" 很多人看了就头皮发麻。1048576 个 token 是 2 的 20 次方,也就是 1M token,这是某些大模型 API 的上下文窗口上限。注意,这个数字是输入和输出共享的,不是说你就能塞 100 万个 token 进去然后模型还能给你吐 100 万个 token 出来。实际使用时,输入占了多少,输出就只有剩下的额度。
打个比方,上下文窗口就像一个只能装 100 件货的仓库,你的系统提示词占了 10 件,用户的提问占了 20 件,工具返回的文档占了 50 件,模型回复占了 20 件,那这个仓库就已经满了。再往里塞任何东西,都会报 "maximum context length" 错误。很多人第一次遇到这个报错就慌了,以为是代码写错了,其实只是仓库满了而已。
从原理上说,Transformer 架构的模型在推理时需要对上下文里所有的 token 两两计算注意力权重,这个计算量是 O(n²) 级别的。也就是说上下文长度翻一倍,计算量要翻四倍。这既是物理限制,也是成本限制。所以各大模型厂商都在上下文窗口上做文章,你看到 128K、200K、1M 这些数字,本质上都是在工程层面优化注意力计算的结果。
2.2 Token 是怎么被消耗的,为什么说没干什么就爆了
我在实际开发里发现,很多人对 token 消耗的认知是有偏差的。总觉得"我就发了几句指令,怎么会消耗那么多 token"。实际上,一个 Agent 每执行一步工具调用,都要把当前所有消息重新发给模型一次。假如你有 10 条历史消息,每条平均 1500 token,那每次请求光历史消息就要消耗 15000 token。Agent 执行了 20 步,就是 30 万 token 的消耗。如果中间某一步的工具返回了一个特别大的 JSON 文档,那这一下就可能吃掉几万 token。
这就是为什么很多 Agent 项目跑着跑着就报 Context 超限,不是因为你一次输入特别大,而是因为循环累积导致的增长。更麻烦的是,工具返回结果往往不在你的控制范围内。比如你让 Agent 去抓取一个网页,网页正文可能有 5 万 token,那一次抓取就把整个窗口占掉了一小半。再比如你让 Agent 读取一个代码仓库里的多个文件,每个文件几千行,随便读几个文件,上下文就到顶了。
我自己的经验是,给 Agent 做设计时,一定要提前估算最坏情况下的 token 消耗,而不是按最好情况来。最好情况谁都会设计,最坏情况才是工程能力的体现。
2.3 从 Context 到记忆:窗口、工作记忆与长期存储
很多人在聊 Agent 记忆时会把概念搅在一起。这里我做一个清晰的区分:Context 窗口是 Agent 一次请求能看到的全部 token 集合,相当于工作记忆;向量数据库、文件存储、KV 存储这些是长期记忆;而提示词里写的那几条规则是固定的系统设定,类似肌肉记忆。
现在主流的 Agent 框架里,记忆管理通常分三层。最底层是原始历史记录,无论是对话轮次还是工具调用结果,原样保存;中间层是摘要,也就是把历史记录压缩成摘要,只保留关键信息;最上层是关键事实抽取,也就是从历史中提取出用户的偏好、项目的重要决策、任务的当前状态等结构化信息。Context 窗口里随时装的,就是这三层内容的混合体,不同框架对这三层的配比策略完全不同。
这个三层结构非常重要,因为你处理 Context 超限问题的时候,本质上就是在决定:哪些信息必须留在窗口里,哪些信息可以被压缩,哪些信息可以直接丢掉。想清楚这个优先级,你才能在框架层面做出合理的选择。
3. Agent 架构剖析:从单循环到多 Agent 协作
3.1 经典 ReAct 循环:Agent 的最小骨架
现在市面上几乎所有的 Agent 框架,底层都有一个 ReAct 循环(Reason + Act),也就是推理 + 行动 + 观察的循环。循环的每一步是这样的:模型根据当前 Context 里所有信息,决定下一步动作;如果动作是调用工具,那就执行工具,拿到结果;把工具结果重新写回 Context;模型再根据更新后的 Context 决定下一步。这个循环一直持续到模型认为任务已经完成为止,或者达到预设的最大步数限制。
理解这个循环就能明白一个关键问题:为什么 Context 管理这么重要。因为在 ReAct 循环里,每一步都是基于完整 Context 重新推理的。Context 里的信息越多、越杂乱,模型做出错误决策的概率就越大。反过来说,如果你能在每一步都把 Context 里塞入的信息控制在最小必要集合,那模型的表现会显著提升。这不是玄学,是注意力机制的特性决定的。
在 LangChain 里,这个循环体现在 AgentExecutor 或 Agent 类的实现上;在 Dify 里,你看到的 Chatflow 节点编排本质上也是在搭建 ReAct 循环;在 CrewAI 里,Agent + Task + Process 的组合也是循环的一种变体。换个壳,底层逻辑大同小异。
3.2 Harness 和 Skill:Agent 的能力边界与工具箱
热搜词里出现了 "harness 和 agent 区别",还有一个 "claude agent skills: a first principles deep dive" 的内容,可见很多人对 Harness、Skill、Tool 这几个概念还是模糊的。
从我的理解来说,Agent 本体是大脑,Harness 是这个大脑的外骨骼,Skill 是大脑能调用的技能包。Harness 负责循环控制、工具调度、错误恢复、上下文管理等基础设施能力;Skill 则是预先写好的专项能力模块,比如"将网页保存成 Markdown"、"从 PDF 中提取表格"、"调用某个内部 API 完成数据清洗"。Skill 更接近一个封装好了的功能单元,通常包含触发条件、执行步骤、所需的工具依赖和输出规范。
为什么 Harness 和 Agent 经常被拿来对比?因为有些框架把 Harness 的概念做得很重,Agent 只负责决策,所有的实际执行都交给了 Harness 里的 Skill;而有些框架 Agent 自己就包含了工具调用逻辑,Harness 的概念很轻或者干脆没有。Claude Code 这类工具里 Agent 和 Harness 的边界就非常清晰。你写的一个个 Skill 文件被 Harness 加载进 Context,Agent 根据任务描述决定调用哪个 Skill,Skill 的执行结果再返回给 Agent 做下一步决策。
这个"技能与执行分离"的设计非常值得借鉴。我自己的一个多 Agent 项目里,最开始把所有的工具调用逻辑都写死在 Agent 的 prompt 里,结果每个人物角色的 prompt 越来越长,维护成本急剧上升。后来改成 Skill 化方案,每个技能独立成模块,Agent 只需要在 prompt 里声明能用的技能列表,就清爽多了。
3.3 多 Agent 架构:为什么能用多 Agent 解决 Context 压力
多 Agent 架构最近火热,很大程度上是因为它能缓解单 Agent 的 Context 压力。你把一个复杂的任务拆成多个子任务,分配给不同的 Agent,每个 Agent 只需要处理自己的子任务和对应的上下文,这样一来单个 Agent 的 Context 窗口压力就小很多。
以 CrewAI 为例,一个典型的招聘 Agent 项目可以拆成三个角色:简历筛选 Agent、面试评估 Agent、最终决策 Agent。简历筛选 Agent 只需要读取简历文件并提取关键信息,输出一个结构化摘要;面试评估 Agent 接收摘要和 JD,输出匹配度评估;最终决策 Agent 只看前两个 Agent 的输出,做出最终判断。每个 Agent 的 Context 里都不需要装全部原始简历,只需要装上游的输出摘要。
这种设计有几个明显的好处。一是上下文体积大大缩小,原始简历可能每个 3000 token,你筛选 100 份简历就是 30 万 token,但如果 Agent 只接收摘要,每份简历只需要 300 token。二是单个 Agent 的 prompt 可以更聚焦,职责单一,回答质量更高。三是方便维护和测试,你可以单独测试每一个 Agent 而不必跑整个链路。
但是多 Agent 架构也不是没有代价。Agent 之间的信息传递会产生损耗,如果上游摘要做得不好,下游 Agent 就容易做出错误判断。另外多 Agent 的编排逻辑更复杂,调试难度更高。所以我的建议是:不要为了多 Agent 而多 Agent,只有在单 Agent 确实遇到 Context 瓶颈或职责严重混杂时才值得拆。
3.4 Agent 框架选型:LangChain、Dify、CrewAI 哪个适合你
这个高频问题我给一个相对主观但不偏颇的判断。LangChain 是最底层的框架,灵活度最高,但也意味着你需要自己动手处理的东西最多。Context 管理、Agent 循环、Tool 封装、记忆存储,每个环节都有很多方案可以选择,组合起来自由度惊人,但对新手非常不友好。你如果是做研究、做 PoC,或者需要深度定制,LangChain 是好选择。
Dify 是偏应用层的平台,强调可视化编排。它的 Context 管理有很多内置策略,比如对话历史摘要、上下文变量、知识库检索等,很多场景不需要写代码就能解决。但它的代价是灵活性不如 LangChain,复杂的 Agent 逻辑可能会被平台的抽象限制住。我和很多同行交流下来的共识是:Dify 适合快速搭应用,不适合做复杂 Agent 系统。
CrewAI 是专注于多 Agent 协作的框架,它的抽象层级介于 LangChain 和 Dify 之间。它的 Process 概念让你可以定义顺序执行还是层级管理,Agent 之间的任务分配和结果汇总都有现成方案。如果你明确要做多 Agent 系统,CrewAI 值得优先考虑。
还有一个不可忽视的选手,就是 Claude Code 这类 AI 编程 Agent。它本身不是一个让你开发 Agent 的框架,而是一个已经成型的 Agent 产品,但它暴露出来的 Skill 机制、Harness 设计、Context 管理策略都很有参考价值。把它的设计吃透,对你自研 Agent 非常有帮助。
4. 实操篇:Context 超限问题的完整排查与解决方案
4.1 报错分类:哪些是 Context 太满,哪些是其他问题
先说一个实际开发中最常见的迷惑场景:报错信息一眼看上去都是 Context 相关,但实际上原因千差万别。我做了一个分类,帮助大家快速定位。
第一类是 "Context is too large and auto-compaction could not recover this"。这个报错在 Claude Code 这类工具里很常见,意思是上下文已经超出了模型窗口,自动压缩也救不回来。为什么会救不回来?因为自动压缩通常是把历史对话做摘要,但摘要本身也要消耗 Context 空间,如果历史记录里包含大量工具返回结果,摘要之后仍然超限,压缩就彻底失效了。
第二类是 "this model's maximum context length is 1048576 tokens. however"。这个报错通常出现在直接调用 API 时,提示你输入和输出的总长度超过了模型支持的最大窗口。这里有个容易被忽略的坑:有时候你的输入并没有达到最大值,但因为你设置了较高的 max_tokens,模型无法生成足够长的回复,也会报类似错误。
第三类是 "failed to load model. failed to initialize the context: failed to allocate buffer"。这个报错多见于本地运行大模型的场景,比如通过 llama.cpp 加载模型时。它本质上不是 Context 逻辑问题,而是显存或内存不足,无法为上下文分配足够的缓冲区。解决方向是降低 context_length、换更小的模型或者增加硬件资源。
第四类是 "has been blocked by CORS policy: the request client is not a secure context"。这个报错跟 Context 完全不相关,它是浏览器跨域限制,常见于前端直连大模型 API 的场景。解决办法是在服务端做代理转发,或者配置允许的跨域来源。
注意:报错信息里带 "Context" 字样并不代表一定是上下文超限,先确认错误类型再动手修改,能省掉很多无效调试时间。
4.2 自动压缩为什么有时候失效,以及如何手动干预
很多 Agent 产品自带 auto-compaction 功能,也就是在 Context 即将超限时,自动把前面的历史记录压缩成摘要。这个功能听起来很美好,但实际使用中经常会遇到救不回来的情况。我总结下来有三个原因。
第一个原因是工具结果体积过大。系统的自动压缩通常只处理对话历史,而不会处理工具返回的大块数据。如果你的 Agent 经常读取大型文件,这些内容直到被新的消息挤掉之前都会一直占着 Context,压缩处理不了它们。
第二个原因是压缩阈值设置太低。很多产品的自动压缩策略是等到 Context 使用率达到某个比例(比如 70% 或 80%)才触发,而压缩本身需要一定的时间和处理空间,如果在触发时 Context 已经接近极限,压缩过程可能还没完成就报错了。
第三个原因是摘要本身的质量问题。自动压缩生成摘要需要额外调用模型,而摘要在压缩的过程中也要占据一定的 Context 空间,如果摘要写得太长,压缩后体积仍然超限。
应对方案很简单也很硬核:不要完全依赖自动压缩。你要在代码层面主动管理 Context。比如在每次工具调用后检查当前 Context 使用量,如果超过阈值就提前把历史记录中的旧消息替换为摘要,或者把大的工具结果移出 Context,存到外部存储中,只保留一个引用。手动干预虽然麻烦,但可控性好得多。
4.3 基于 Token 计算的实践:监控、估算、预留
在项目里,我会给 Agent 加一个 Token 使用监控模块,在每次请求前后统计 Token 消耗,打印当前会话的累计消耗和剩余窗口。这样既方便调试,也能在设计阶段就发现问题。
计算逻辑可以参考这个公式:每次请求消耗 Token = 系统提示词 Token + 历史消息 Token + 工具返回 Token + 本轮用户输入 Token + 最大输出 Token。其中历史消息 Token 是最不可控的变量,因为它随着对话轮次增长。我建议在每次请求前用 Tokenizer 做一次精确计算,然后根据计算值决定是否需要截断历史或压缩摘要。
预留输出空间也很重要。如果你设置的 max_tokens 是 4096,那每次请求前必须保证上下文中的输入 Token 加上 4096 不超过最大上下文长度。否则即使输入没超限,也会因为输出空间不够而报错。很多人在排查时只顾着输入大小,忽略了输出预留,排查了半天才发现问题出在 max_tokens 设置上。
我这里有一个实用策略:把上下文窗口划分为三块——固定开销区、动态缓冲区和输出预留区。固定开销区是系统提示词,尽量精简;动态缓冲区是消息历史、工具结果,这一块要动态控制;输出预留区是你设置的 max_tokens,通常建议留出总窗口的 15% 到 25%。这个比例不是最优解,因为不同模型和任务差异很大,但它是一个合理的起点。
4.4 主动式 Context 管理策略:摘要、滑动窗口、外部存储
与其等 Context 爆了再救火,不如在架构设计时就做好主动管理。我这里整理几种经过实测的策略。
摘要压缩是目前最通用的方案。实现思路是:当历史消息超过一定条数或 Token 阈值时,将最早的多条消息交给模型生成摘要,用一个"系统摘要"消息替换原始消息。这个方案有一个实现细节必须注意:摘要消息的位置尽量放在系统提示词之后、其他消息之前,这样模型在推理时既能保持摘要的全局视角,又不会干扰最近的对话内容。
滑动窗口是比较激进但也最简单的方案。你可以只保留最近 N 轮消息,更早的消息直接丢弃。优点是上下文体积可控,缺点是模型会丢失早期的关键信息。这个方案适用于对话上下文对当前任务影响较小的场景,比如工具调用类任务和批量处理类任务。
外部存储加引用的方案最复杂但最可靠。具体做法是:把大块数据,比如文件内容、工具返回的 JSON、长文档,写入外部存储,如向量数据库或对象存储,然后在 Context 里只放一个引用标识。当模型需要读取详细信息时,通过检索工具去外部存储中获取相关片段。这个方案实际就是 RAG 架构,也是当前企业级 Agent 的主流方案。
实践下来,真正好用的 Agent 系统通常会把这三种方案组合使用:滑动窗口控制历史轮次,摘要压缩保留早期关键信息,外部存储承载大块不可变数据。三者的配比要根据任务类型调整,没有一个放之四海而皆准的公式。
4.5 Skill 化改造:从"什么都在 prompt 里"到"按需加载"
我前面提到过 Skill 化改造,这里展开讲一下具体怎么做。假设你有一个 Agent 需要处理网页抓取、文本摘要和数据分析三个任务。最原始的做法是把这三个任务的详细说明和步骤都写进系统提示词,结果提示词可能到 3000 token。
Skill 化的思路是:为每个任务创建一个独立的 Skill 模块,系统提示词里只写"你有以下技能可用:网页抓取、文本摘要、数据分析。使用技能时,技能描述会自动加载"。当 Agent 决定调用网页抓取技能时,框架才把对应的步骤和工具说明注入到 Context 中。这样一来,基础上下文只有 500 token,调用技能时额外增加 800 token,总计 1300 token,比原来的 3000 token 节省了将近一半。
这个方案的另一个好处是:Skill 可以复用。你做好的网页抓取 Skill 可以用于任何一个 Agent,不必重复编写逻辑。Claude Code 的 Skill 机制就是这么做的,而且它支持将网页保存成 Markdown。网上提到的 "agent 将网页保存成 markdown 的 skill",本质上就是一个预置好的 Skill 模块。
Skill 化改造的几个步骤可以总结为:梳理 Agent 的常用任务,为每个任务写清楚触发条件、执行步骤、所需工具和输出规范,设计一个 Skill 注册表,然后在框架层实现按需加载。听起来简单,但实际过程中最耗时的是确定技能边界和触发条件,这需要你对 Agent 的日常使用场景有足够深入的了解。
4.6 Context 注入的时机与顺序:细节决定成败
除了控制 Context 的体积,Context 内容的排列顺序对模型决策质量的影响也很大。我在调优过程中发现几个值得注意的顺序问题。
大多数模型的注意力机制对前后位置的 token 权重不同,系统提示词通常放在最前面,这是设定全局规则的最佳位置。紧随系统提示词之后放的是结构化信息,比如当前任务的描述、用户的约束条件、关键的上下文摘要。中间部分放动态信息,比如工具调用结果、历史对话。最近的消息往往放在最后,因为模型通常更关注靠近末尾的内容。
有一个实际操作中的坑:如果你把大段的工具返回结果直接插在中间,它可能会把早期的重要信息挤到注意力边缘,导致模型忽略关键上下文。解决方法是给工具返回结果写一个结构化摘要,把摘要放在对话中,完整结果通过外部存储按需加载。
还有一个常见的顺序问题是系统提示词越写越长。有人喜欢把各种规则、示例、约束全部堆进系统提示词,结果系统提示词占了大半窗口。我的建议是只保留最核心的规则,把示例放到少样本里在合适的时机展示,把不常用但必要的信息放到 Skill 或工具描述中。这样可以让系统提示词精简到 500 token 以内,给动态信息留出更多空间。
5. 实战案例:从报错到稳定运行的一次完整优化
5.1 原始场景与问题现场
我负责的一个文档自动化处理 Agent 在测试阶段频繁报 Context 超限,报错信息里 "auto-compaction could not recover this" 出现了十几次。这个 Agent 的任务是:读取用户上传的多份 PDF,提取关键信息,生成一份汇总报告。
最初的架构很简单:PDF 内容被完整读取后作为文本连续追加到消息历史中。测试时用户上传了 8 份 PDF,每份约 50 页,平均每份文本约 15000 token。8 份就是 12 万 token。再加上系统提示词、中间对话和工具调用结果,总 Context 迅速逼近模型 200K 窗口上限。由于自动压缩是逐轮触发的,压缩一次就要消耗几万 token 来做摘要,而这几万 token 又增加了 Context 压力,最终陷入了"压缩-报错-再压缩-再报错"的循环。
5.2 优化方案:分治加摘要加外部存储
这个项目的优化方案分了三个层次。
第一层是 PDF 解析策略调整。不再一次性解析整份 PDF,而是先抽取每个 PDF 的目录结构和首尾摘要,生成一个 2000 token 以内的文件概要。这个概要会进入 Agent 的 Context,让 Agent 对文件有一个全局认知。Agent 可以根据任务需要,再决定是否深入读取某个 PDF 的具体章节。
第二层是历史消息的摘要压缩。引入了一个消息压缩模块,当消息历史超过 4 万 token 时,系统会把早期消息压缩成一个摘要消息,保留关键信息,包括用户需求、已提取的要点、已有的中间结论,然后将其放回 Context。
第三层是输出报告的增量维护。之前系统要把所有中间结果都保留在 Context 里,最后统一生成报告。优化后,我让 Agent 每处理完一份 PDF 就把该 PDF 的结论写入外部存储,Context 中只保留结论的短摘要。当所有 PDF 处理完毕,Agent 从外部存储拉取全部结论,统一生成最终报告。
5.3 实测数据与效果对比
优化前后最直观的对比是成功率。在相同数据集上,优化前 8 份 PDF 的任务运行成功率只有 20%,大部分时间都在处理 Context 超限报错;优化后同样的任务成功率提升到 95%,剩余 5% 的失败发生在某个 PDF 特别长,比如超过 200 页时,不过这种情况可以通过调整文件分块策略继续优化。
Token 消耗方面,优化前平均每个任务消耗约 90 万 token,优化后平均消耗约 45 万 token,基本砍半。同时 Context 峰值使用率从满负荷降到峰值约 65%,自动压缩的触发频率几乎降为零。模型输出的稳定性也有明显提升,因为上下文干扰项减少了,生成的报告结构和内容一致性显著提高。
还有一个重要的数据是处理速度。优化前因为频繁触发压缩和重试,单任务耗时约 18 分钟;优化后单任务耗时约 9 分钟,提升了一倍。这个提升主要归功于减少了压缩调用和重试次数,而不是模型本身的推理速度提升。
5.4 从这个案例提炼出的通用原则
这个案例可以提炼出几条通用原则,适用于大多数 Agent 项目。
第一条是控制大块数据的进入时机。任何可能超过 2000 token 的数据都不应该直接进入 Context,除非当前任务确实需要完整读取。应该先让 Agent 基于摘要做决策,再按需读取细节。
第二条是压缩要主动做,不要等自动压缩触发。建议在每次工具调用后显式检查 Context 使用量,超过阈值就主动执行摘要压缩。主动压缩比自动压缩更可控,因为你可以在压缩前保存必要的状态,避免压缩后丢失关键信息。
第三条是区分持久化信息和临时信息。需要长时间保留的信息,比如任务目标、用户偏好、关键结论,放在独立存储中;只需要在单次请求中使用的信息,比如某个工具的即时返回结果,用完就从 Context 中移除。这个"临时性"思维很多开发者容易忽略,导致 Context 里堆了很多过期数据。
6. 常见问题速查表与新手避坑指南
6.1 高频报错与解决方案速查表
我把开发中常见的报错和解决方案整理成了一张表,方便大家直接查。
| 报错信息 | 实际原因 | 解决方案 |
|---|---|---|
| Context is too large and auto-compaction could not recover this | Context 总量超限,压缩空间不足 | 主动管理 Context;工具结果移出窗口;降低单轮数据量 |
| maximum context length is 1048576 tokens. however... | 输入加输出超过模型窗口上限 | 控制输入 Token;降低 max_tokens;减少历史消息 |
| failed to load model. failed to initialize the context | 本地模型加载时显存/内存不足 | 降低 context_length;换更小模型;增加硬件配置 |
| has been blocked by CORS policy | 前端直接调用 API 跨域被拦截 | 配置服务器代理转发或设置正确的跨域白名单 |
| agent execution terminated due to error | Agent 循环执行过程中抛出未捕获异常 | 检查工具调用异常处理逻辑;增加重试和回退策略 |
这个表不可能覆盖所有情况,但覆盖了我工作中最常见的 90%。如果你遇到不在这张表里的报错,优先尝试在框架层面打开调试日志功能,观察是哪个环节出了问题。
6.2 新手最容易踩的五个坑
第一个坑是把 Context 窗口当成无限大。很多人看到模型支持 1M token 就放松了警惕,觉得怎么用都不会超。但实际成本和速度都会随着上下文长度急剧上升,即使不报错,质量也会下降。我建议设计阶段就给 Context 使用量设一个软上限,比如总窗口的 70%。
第二个坑是忽略系统提示词的长度优化。系统提示词每增加 1000 token,每次请求都会多烧 1000 token,如果 Agent 执行了 100 步,就是额外 10 万 token。很多项目里系统提示词可以优化掉一半以上的冗余内容。
第三个坑是多 Agent 里所有 Agent 共享同一个超级大的全局 Context。这个做法会让多 Agent 架构失去了压缩上下文的优势。正确做法是每个 Agent 只接收自己需要的上游摘要。
第四个坑是不做 Token 监控就开始调优。没有监控数据,你根本不知道问题出在系统提示词、历史消息还是工具返回结果。先加监控,再谈优化。
第五个坑在本地模型场景比较常见:上下文设了 8K 或 16K,但显存只能支撑 4K 的 KV cache,结果模型加载正常,一运行就 Context 报错或 OOM。这种问题要靠实测来确认,不要只盯着配置数字。
6.3 我对 Context 调优优先级的一个排序
根据我的经验,Context 优化手段的效果排序大概是这样的:减少不必要的大块数据进入 Context,效果最明显,也最容易实施;对已有历史进行摘要压缩,效果好但需要维护摘要质量;滑动窗口强制截断历史,实施简单但信息丢失风险高;增加模型窗口上限,效果有限且成本高;升级模型能力,让模型在更少上下文下也能做出正确决策,效果最好但代价最高。
这个排序告诉我们:不要一上来就想换成更大的窗口或更强的模型。先把手头的 Context 管理做好,往往能获得 2 到 3 倍的性能提升。等这些手段都用尽了,再去考虑升级模型也不迟。
7. 进阶思考:Agent 开发中 Context 的未来趋势
这个领域的最前沿方向值得关注。一个明显的趋势是上下文工程正在成为一门独立的技术方向。过去我们只关注 prompt 工程,现在 Context Engineering 的概念开始流行,它强调不只是写好提示词,而是设计整个上下文的使用流程:什么信息进入、什么信息退出、什么时候压缩、什么时候检索外部信息、信息如何跨 Agent 传递。
另一个趋势是 Agent Skill 生态的标准化。越来越多的框架开始支持可插拔的 Skill 模块,Skill 像插件一样可以安装卸载。这会让 Agent 的 Context 管理走向模块化,未来你不再需要为每个 Agent 手工编排提示词,而是直接组装各种 Skill。
还有一个方向是个性化记忆层。当前 Agent 的记忆主要存在于会话内部,会话结束就清了。未来的 Agent 会把用户的长期偏好、历史任务记录、交互习惯保存起来,每次新会话开始就把这些个人记忆加载进初始 Context。这也对 Context 管理提出了新挑战,因为长期记忆的体积会长得很快,如何在不超限的前提下只加载最相关的那部分记忆,会是重点课题。
做 Agent 开发这几年,我最大的体会是:Context 问题从来不是单纯的工程问题,它更像是 Agent 系统设计的一面镜子。你 Context 管理得好不好,直接反映了你对任务的理解深不深、对数据流的规划合不合理。想通这一点之后,遇到报错就不慌了,因为你知道每一个 Context 报错背后,都对应着一次设计上的优化机会。
如果你正在做 Agent 开发,我的建议很简单:先把你当前某个任务的 Context 使用情况完整地打出来看看,一定会有意外发现。我看到过太多人埋头调 prompt、换模型,最后发现根因只不过是某个工具把几万 token 的数据塞进了上下文。先做到 Context 可视化,再谈优化,你的 Agent 系统会稳定很多。