资讯详情

AI Agent上下文工程实战:ReAct循环中的上下文压缩与记忆分层

📅 2026/10/8 4:50:26 | 华诺云谱 👁 阅读
AI Agent上下文工程实战:ReAct循环中的上下文压缩与记忆分层
1. 为什么上下文工程成了 AI Agent 的胜负手做 AI Agent 的人十有八九都经历过这样的场景模型本身能力不差工具也接好了ReAct 循环也跑通了但一到真实任务上就开始犯迷糊——要么把之前确认过的信息忘得一干二净要么在无关的细节里反复打转要么把工具返回的结果理解得面目全非。很多人第一反应是模型不行换个更大的模型结果发现问题依旧。真正的原因往往不在模型权重里而在你喂给它的那堆上下文里。上下文工程Context Engineering这个词这两年才真正被拎出来单独讨论但它要解决的问题一点都不新。大语言模型本质上是无状态的它没有记忆没有持续的意识每一次推理调用都是一次失忆症患者重新读一遍病历。你给它的上下文就是它此刻全部的世界观。上下文里有什么它就认为世界是什么样上下文里缺什么它就会用训练时的先验去脑补而脑补出来的东西大概率不是你想要的。我见过太多团队把 80% 的精力花在选模型、调 prompt 模板、接工具上却只留 20% 甚至更少的精力去设计上下文怎么组织、怎么裁剪、怎么在 ReAct 循环里动态更新。结果就是 Agent 在 demo 里表现惊艳一上生产就原形毕露。这不是模型的问题是上下文工程没做到位。这篇文章想聊的就是 AI Agent 场景下上下文工程到底在工程什么。我会从 ReAct 循环的上下文结构讲起拆解上下文窗口的预算分配逻辑讲清楚记忆分层、工具结果压缩、状态机式上下文管理这些实操层面的东西最后给出一套可以照着搭的上下文工程骨架。适合已经跑通过基础 Agent、正在被Agent 不稳定折磨的开发者也适合刚开始学 AI Agent、想少走弯路的同学。提示本文讨论的上下文工程聚焦在 Agent 运行时runtime的上下文组织不涉及模型训练阶段的上下文长度扩展技术那是另一个话题。2. ReAct 循环里上下文到底长什么样2.1 一次 ReAct 调用的完整上下文解剖很多人对 ReAct 的理解停留在Thought → Action → Observation这个三元组上觉得只要把这三样拼起来喂给模型就行了。但真实跑起来你会发现一次完整的 ReAct 调用上下文里塞的东西远不止这三样。我把它拆成几个层次来看系统层System角色定义、能力边界、输出格式约束、工具清单及调用规范。这一层是静态的但它的措辞直接决定了模型的行为模式。任务层Task当前要解决的目标、约束条件、成功标准。这一层往往被写得太随意导致模型不知道做到什么程度算完。历史层History之前所有轮次的 Thought、Action、Observation 记录。这是上下文膨胀的重灾区。记忆层Memory从历史中提炼出来的关键事实、用户偏好、已确认的结论。这一层是压缩后的历史。工具层Tools当前可用的工具定义、参数 schema、调用示例。工具一多这一层能吃掉几千 token。即时层Scratchpad模型当前这一轮的推理草稿也就是它正在想的 Thought。这六层叠在一起才是模型真正看到的上下文。问题在于历史层会随着轮次线性增长工具层在工具多的时候非常占地方而系统层和任务层如果写得太啰嗦也会白白吃掉预算。一个跑了 20 轮的 Agent历史层轻松突破一万 token这时候模型对早期信息的注意力已经严重衰减了。2.2 为什么把历史全塞进去是最常见的错误我刚开始做 Agent 的时候也犯过这个错觉得信息越多越好把每一轮的完整对话都原样保留生怕漏掉什么。结果就是两个典型症状。第一个症状是注意力稀释。模型在长上下文里对关键信息的召回率会下降尤其是当历史里充斥着大量工具返回的冗余 JSON、重复的确认语句、无关的中间结果时真正重要的那几条信息就被淹没了。这不是模型笨是信噪比太低。第二个症状是上下文漂移。当历史里包含了早期错误的假设、被推翻的结论、已经废弃的方案时模型会把这些也当成有效信息导致它在后续推理里反复回到已经被否定的路径上。我遇到过 Agent 在第 3 轮确认了用户要的是 A 方案到第 15 轮又突然开始讨论 B 方案就是因为第 2 轮里有一句被否定的 B 方案讨论还留在历史里。正确的做法不是全塞而是有选择地保留 主动压缩。这就引出了上下文预算的概念。2.3 上下文预算的分配逻辑假设你用的模型上下文窗口是 128K听起来很宽裕但实际可用预算远没有这么多。我的经验分配是这样的层级建议占比说明系统层5%-10%静态但要精炼工具定义能省则省任务层5%目标、约束、成功标准必须清晰记忆层10%-15%压缩后的关键事实动态更新历史层30%-40%保留最近 N 轮 关键轮次的摘要工具层10%-20%工具多时用按需加载即时层剩余留给模型当前推理这个分配不是死的但核心原则是历史层不能无限膨胀必须有淘汰和压缩机制。我一般会保留最近 5-8 轮的完整记录更早的轮次压缩成摘要只保留结论性的信息。工具层如果工具超过 10 个就要考虑分组加载或者按任务阶段动态注入。注意不同模型的注意力分布不一样有的模型对开头和结尾敏感有的对中间也还行。上线前一定要用真实任务测一下你的上下文布局别照搬别人的比例。3. 上下文压缩不是删字是提炼结构3.1 摘要压缩的三种粒度上下文压缩最直接的手段是摘要但摘要的粒度选择很关键。我实践下来分三种粒度轮次级摘要把一轮完整的 Thought-Action-Observation 压缩成一两句话。比如第 3 轮查询了用户订单表确认订单 12345 状态为已发货。这种粒度适合保留过程脉络但会丢失细节。阶段级摘要把连续几轮归为一个阶段压缩成一段话。比如阶段一第 1-5 轮完成了用户身份确认和订单查询确认目标订单为 12345状态已发货用户诉求是查询物流。这种粒度适合长任务能大幅压缩 token。事实级提取不保留过程只提取出确认的事实存进记忆层。比如订单 12345 已发货用户偏好邮件通知。这种粒度最省 token但丢失了推理链路。我的做法是三者结合最近几轮用轮次级稍早的用阶段级再早的只留事实级。这样既保留了近期推理的连贯性又不会让历史无限膨胀。3.2 工具返回结果的压缩策略工具返回结果是上下文膨胀的最大来源。一个数据库查询可能返回几十行 JSON一个网页抓取可能返回几千字正文这些原样塞进上下文几轮下来就爆了。我的压缩策略分三步结构化裁剪只保留任务相关的字段。比如查询订单只留订单号、状态、时间其他字段全砍掉。这一步在工具封装层做不要让模型去处理原始数据。结果摘要如果返回的是长文本先用一个小模型或者规则方法摘要成几句话再喂给主模型。这一步会增加一次调用但省下的 token 和提升的稳定性完全值得。错误归一化工具报错时不要把原始堆栈直接塞进去而是归一化成工具 X 调用失败原因参数 Y 格式错误。原始堆栈对模型理解问题没帮助反而干扰。我踩过的一个坑是早期直接把工具返回的完整 JSON 塞进上下文结果模型经常把 JSON 里的某个无关字段当成任务目标开始围绕它瞎推理。后来在工具层做了字段白名单问题就消失了。3.3 压缩会不会丢信息一个实测对比很多人担心压缩会丢关键信息。我做过一组对比测试同一个多轮任务一组用全量历史一组用压缩历史跑 50 次看成功率。方案平均 token 消耗任务成功率平均轮次全量历史18K62%14.2压缩历史6K81%9.7结果很反直觉压缩后的成功率反而更高。原因就是前面说的信噪比问题——全量历史里噪音太多模型反而容易跑偏。压缩不是丢信息是把信息重新组织成模型更容易消费的结构。当然压缩也有风险。如果摘要做得太激进把关键约束条件漏掉了模型就会做出错误决策。我的经验是约束条件、已确认结论、用户明确要求这三类信息绝对不能压缩掉必须原样保留。可以压缩的是推理过程、工具原始返回、重复确认。4. 记忆分层让 Agent 记住该记的4.1 短期记忆、工作记忆、长期记忆的分工Agent 的记忆不能只有一种。我一般分三层短期记忆Short-term当前任务内的上下文随任务结束而清空。就是前面说的历史层和即时层。工作记忆Working当前会话内的关键事实和状态跨轮次保留但会话结束就清。比如用户当前在查订单 12345已经确认用户身份。长期记忆Long-term跨会话保留的用户偏好、历史结论、领域知识。比如这个用户偏好简洁回复上次处理过类似问题方案是 X。这三层的更新频率和存储方式完全不同。短期记忆在内存里工作记忆可以用一个结构化的状态对象长期记忆需要持久化存储加检索。4.2 工作记忆的状态机设计工作记忆最容易做成一锅粥。我的做法是用一个显式的状态对象来管理而不是靠模型自己从历史里回忆。这个状态对象大概长这样working_memory { task_goal: 查询订单 12345 的物流状态, confirmed_facts: [ 用户身份已确认, 订单 12345 状态为已发货 ], pending_items: [ 需要获取物流单号, 需要查询物流轨迹 ], constraints: [ 用户要求只返回最终状态不要中间过程 ], last_action: query_order_status, last_result: 已发货 }每一轮推理前把这个状态对象序列化后注入上下文。这样模型不需要从冗长的历史里自己提取状态直接读结构化的状态就行。实测下来这一招对多轮任务的稳定性提升非常明显尤其是任务超过 10 轮之后。状态对象的更新由谁来做两种方案一是让模型在每轮输出里附带状态更新指令二是用一个独立的轻量模型或规则引擎来更新。我倾向后者因为让主模型同时干推理和状态管理容易顾此失彼。4.3 长期记忆的检索与注入时机长期记忆的关键不是存是什么时候取、取多少、怎么注入。存的时候好办把会话里的关键结论、用户偏好、成功方案提取出来打上标签存进向量库或结构化存储。取的时候要注意不是每轮都检索而是在任务开始时检索一次以及任务中途遇到似曾相识的情况时再检索。每轮都检索会引入大量无关记忆反而干扰。注入的时候要克制。我一般只注入 top 3 相关的记忆并且明确标注这是历史参考让模型知道这不是当前任务的直接信息。如果不标注模型容易把历史记忆当成当前事实做出错误判断。提示长期记忆的检索质量高度依赖 embedding 模型和标签设计。我建议在标签里加上任务类型、时间、结果状态检索时可以做过滤比纯向量相似度靠谱得多。5. 工具上下文别让工具定义吃掉半个窗口5.1 工具定义膨胀的典型场景工具一多工具定义就成了上下文大户。一个工具的定义包括名称、描述、参数 schema、参数说明、调用示例稍微写详细点就是 200-500 token。10 个工具就是 2000-5000 token20 个工具直接上万。我见过一个 Agent 接了 30 多个工具光工具定义就占了 15K token模型还没开始干活上下文已经用掉一大半。更糟的是工具太多会让模型选择困难经常调错工具或者该调不调。5.2 按需加载与工具分组解决工具膨胀的核心思路是按需加载。具体做法工具分组按功能域把工具分成几组比如订单组用户组物流组。每轮只注入当前任务阶段相关的组。两阶段选择先用一个轻量调用让模型从工具清单里选出可能需要的几个再把这几个的完整定义注入。这一步多花一次调用但省下的 token 很可观。描述精炼工具描述不要写小作文一句话说清楚做什么和什么时候用就够了。参数说明用 schema 里的 description 字段别在工具描述里重复。我实测过一个案例30 个工具全量注入 vs 按需加载 5 个token 从 15K 降到 3K工具选择准确率从 71% 提升到 89%。这个投入产出比非常高。5.3 工具调用结果的格式化约定工具返回结果的格式直接决定了模型能不能正确理解。我的约定是成功时返回结构化数据字段名用业务语义不要用缩写。失败时返回统一的错误结构包含错误类型、错误信息、建议的下一步。永远不要返回原始堆栈、HTML、超长文本这些在工具层就处理掉。一个反面案例某次工具返回了一个嵌套五层的 JSON模型在后续推理里把第三层的某个字段当成了任务目标整个跑偏。后来改成扁平结构加明确字段名问题解决。6. 上下文工程的实操骨架与避坑清单6.1 一套可复用的上下文组装流程把前面的东西串起来我常用的上下文组装流程是这样的任务开始注入系统层 任务层 检索到的长期记忆top 3 当前阶段工具组。每轮推理前注入工作记忆状态对象 最近 5 轮完整历史 更早历史的阶段摘要 事实级记忆。工具调用后压缩工具返回结果更新工作记忆状态判断是否需要触发历史压缩。轮次超过阈值比如 10 轮触发一次阶段摘要把前 5 轮压缩成阶段级摘要释放 token。任务结束提取关键结论存入长期记忆清空工作记忆和短期记忆。这个流程不是死的但核心思想是上下文是动态组装的不是静态拼接的。每一轮注入什么取决于当前任务阶段和已有信息而不是无脑全塞。6.2 我踩过的五个上下文坑坑一系统提示写太长。早期我把系统提示写成了一篇小作文结果模型经常忽略后面的任务指令。后来精简到 500 token 以内只留角色、边界、输出格式效果反而更好。坑二历史不压缩。前面说过了全量历史导致注意力稀释和上下文漂移压缩后成功率反而上升。坑三工具定义不分组。工具一多就全量注入导致 token 爆炸和选择困难。按需加载后明显改善。坑四工作记忆靠模型自己维护。让模型从历史里自己提取状态结果经常漏掉关键事实。改成显式状态对象后稳定多了。坑五长期记忆每轮都检索。引入大量无关记忆干扰当前推理。改成任务开始时检索一次中途按需检索。6.3 上下文质量的检查清单上线前我会过一遍这个清单系统层是否精简到 500 token 以内任务层的成功标准是否明确可判定历史层是否有压缩机制阈值是多少工具层是否按需加载单轮注入工具数是否控制在 8 个以内工作记忆是否有显式状态对象更新逻辑是否独立于主模型长期记忆检索是否克制是否标注了历史参考工具返回结果是否在工具层做了裁剪和归一化是否用真实任务测过上下文布局的注意力分布这个清单看起来简单但每一条背后都是踩过的坑。尤其是最后一条很多人上线前不做真实任务测试结果上线后才发现模型对上下文中间部分的信息召回率极低。6.4 上下文工程和 prompt 工程的关系最后聊一个容易混淆的点上下文工程和 prompt 工程是什么关系我的理解是prompt 工程关注的是怎么把一句话说清楚上下文工程关注的是在什么信息环境下说这句话。prompt 工程是局部优化上下文工程是全局设计。一个 Agent 的 prompt 写得再好如果上下文里塞满了噪音、缺了关键事实、工具定义膨胀照样跑不好。反过来说上下文工程做得好prompt 可以写得很朴素因为模型需要的信息都在上下文里不需要靠 prompt 去提醒它。这也是为什么我越来越觉得AI Agent 的竞争最终会落到上下文工程的竞争上。模型能力会趋同工具生态会标准化但上下文怎么组织是每个团队自己的工程功力。我在实际项目里的体会是把上下文工程当成一个独立的模块来设计给它单独的代码结构、单独的测试用例、单独的监控指标比如每轮 token 消耗、压缩触发频率、记忆命中率而不是把它散落在 Agent 主循环的各个角落。这样你才能持续优化它而不是每次出问题都去改 prompt 碰运气。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑