ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

上下文工程实战:从上下文污染到三层记忆模型的Agent治理指南

上下文工程实战:从上下文污染到三层记忆模型的Agent治理指南 先聊一件让我头疼了两个月的事。上个月我们上线了一个贷款咨询的Agent功能不复杂用户进来问利率、算月供、查审批进度偶尔问问材料清单。第一天测试群里全是好评大家都在夸回答够快、语气也自然。第三天开始有人发现Agent回答同一个问题时语气和准确性开始漂移。上午还在坚持某套产品口径下午就自己改了说法。我拉日志一看上下文窗口里的历史对话已经滚了快一百轮早期的业务规则早被挤出去了最要命的是某次工具调用返回了一份两千多字的PDF解析结果直接把后面用户的真实意图挤到了注意力边缘。这就是上下文工程Context Engineering要解决的问题。过去我们聊提示词工程是在跟模型对话的开头把话说清楚而上下文工程处理的是Agent在长时间运行过程中怎么在每个时刻都准确地让模型看到“此刻该看到的东西”。说直白点它是一门关于注意力资源分配的手艺。这篇文章我想结合自己从0到1搭建几个Agent踩过的坑把上下文工程的核心思路、技术方案和避坑经验一次讲透给准备入坑或者已经在被上下文折磨的朋友一点参考。1. 上下文工程到底是什么一次线上事故让我重新认识它1.1 一次上下文挤爆的真实案例上面说的贷款咨询Agent只是引子真正让我把上下文工程当回事的是另一个更典型的案例。当时做一个数据洞察Agent它的任务是从用户上传的Excel里读取数据然后回答各种统计问题。流程很简单解析文件抽几行样本数据塞进提示词让模型分析。初期一切正常直到有用户传了一个带72列、三千多行的销售明细表。为了“让模型充分理解数据”我们当时的设计是把前200行完整数据全部放进去。光这200行转成Markdown表格就差不多25k token。然后灾难来了。第一轮问答还正常第二轮用户追问“按地区拆一下华东大区的月度趋势”Agent为了回答这个问题又调了一次工具去重新读取数据。新工具结果又被完整追加进上下文。这一轮下来上下文总token数飙到接近60k。接着模型开始“胡言乱语”把不同月份的数字算错甚至开始引用早期样本里根本不存在的数据。当时我第一反应是模型能力不行后来把完整请求日志打出来逐个字段排查才发现问题出在上下文本身上模型收到的内容里有效信息占比不到三成其余全是重复的原始表格、历史对话和无意义的中间步骤。这就是典型的上下文管理失能——窗口还够大但注意力早被稀释了。1.2 提示词工程与上下文工程的本质区别很多人会问提示词工程和上下文工程有什么区别我拿自己的理解做个划分。提示词工程解决的是“开局怎么把话说清楚”。你在系统提示词里定义角色、目标、约束、输出格式这是静态的写一次基本不动。它解决的问题是模型首轮输出的质量。上下文工程解决的是“每一轮开局之后模型眼前到底应该摆什么”。一个Agent每轮推理时模型看到的其实是一个拼接起来的完整文本系统提示词、历史对话、工具返回结果、用户当前输入、可能的中间推理状态。这个拼接的过程每一轮都在动态变化。怎么决定哪些保留、哪些丢弃、哪些压缩、哪些检索回来这就是上下文工程的核心。我见过不少团队系统提示词写了一版又一版各种few-shot示例层层叠加但Agent跑几轮之后依然变蠢。原因很简单他们只优化了“开局”没有管理“过程”。一个真实的Agent通常跑个三五轮就会开始处理工具结果十轮以上历史对话就开始膨胀二十轮以上如果还没有任何压缩和淘汰策略LLM的能力衰减会以肉眼可见的速度发生。这就是模型注意力机制的一个特性Transformer的self-attention对位置有天然偏好上下文中间位置的信息最容易丢失这个现象在学术界叫“lost in the middle”。你做上下文工程的时候必须时刻记住这个底层规律——关键指令放开头最新信息放末尾中间位置留给冗余容忍度高的内容。2. 上下文窗口的结构化设计给Agent搭一张工作台2.1 五个区块的划分与职责我后来把Agent每轮推理时看到的上下文强制划分成了五个逻辑区块像给Agent搭了一张固定工作台。这五个区块各有职责不能混系统指令区角色定义、业务目标、安全边界、回复风格。这部分是“宪法”几乎固定不变。工具说明区每个工具的用途、参数、什么情况下调用。这部分是“工具箱使用手册”。动态状态区当前任务的目标、已有结论、执行进度。这部分是“工作台上的便签”。观察记录区最近几轮的工具调用结果、关键数据快照。这部分是“刚做完的实验记录”。对话历史区用户和Agent最近的问答对。这部分是“和用户聊天的录音”。五层分完你会发现很多问题暴露出来了。比如我们之前的Agent把所有东西混在一起工具说明散落在对话历史中间模型经常在需要调用工具时找不到正确的工具描述转而自己瞎编一个工具名或者干脆不调工具。这里要特别说明一点不同的模型对这五个区块的敏感度不一样。我在GPT-4级别的模型上测试过系统指令区对行为的控制力最强但换成某些开源模型工具说明区的权重反而更高因为它们的指令跟随能力相对弱需要靠完整的工具描述来引导。所以区块划分是通用思路具体权重分配要根据你的模型来调没有一劳永逸的方案。2.2 Token预算怎么分一张可落地的配置表上下文窗口再大也不能真的把它当无限容量用。这里有个关键认知窗口大小和有效注意力不是一回事。即使你用的是128k窗口的模型当上下文里填充了太多无关内容模型对关键信息的关注度会急剧下降而且每次请求的延迟和成本都会随着token数线性上涨。我从实际项目中总结了一套token预算分配方案适合大部分业务型Agent给你做个参考区块预算占比参考值以32k窗口为例管理策略系统指令区8%2-3k固定不变不随对话增长工具说明区3%1k按需注入动态裁剪动态状态区15%4-5k每轮覆写只留最新结论观察记录区25%8k保留最近2轮工具结果旧结果做摘要对话历史区35%10-12k滑动窗口摘要压缩预留/用户输入14%4-5k保证当前输入一定有空间这个比例不是拍脑袋定的核心逻辑是对话历史是吃token的大头但不能让它无限膨胀所以要给它设一个硬上限同时用摘要兜底观察记录区只留最近两轮是为了保证工具调用的链式推理不断裂再老的记录就提炼成结论放进动态状态区。你可能会问那32k窗口的模型是不是必须把窗口用满才划算恰恰相反我实际生产中都建议把目标占用控制在窗口的60%-70%。原因有两个第一留出余量给模型生成输出避免达到硬上限被截断第二集中注意力比扩大视野更重要你把一个新用户的问题加一个精简的上下文压到10k以内效果往往比塞满25k的背景资料好得多。2.3 工具调用结果最容易被忽略的“太肥”源头聊到Token消耗很多人惯性思维是“对话历史太长”但实际上在Agent场景里真正把上下文撑爆的往往不是对话而是工具返回结果。一个接口返回几千字JSON太常见了一段网页正文动不动5k-10k token一次PDF解析可能直接塞进20k。我见过一个最极端的情况有个Agent需要查询企业工商信息工具返回的数据包里有股东、变更记录、行政处罚等十几个字段的完整JSON。开发人员图省事直接把整个JSON塞进上下文。结果这个Agent单轮对话耗掉40k token每次请求光模型费用就够喝一壶的。这里有几个处理原则都是我实测后沉淀下来的工具侧做字段裁剪在调用工具前就定义好返回结构只让Agent拿到当前任务真正需要的字段。例如查工商信息时按需指定只要“统一社会信用代码、注册资本、成立日期”不要整包返回全部登记信息从源头控制体积。结果侧做截断和摘要如果工具返回确实没办法裁剪那就对返回内容做策略截断保留首尾、去重或者用一个小模型把结果先归纳成要点。代码执行类工具的返回治理比如Agent写代码去跑数据返回的可能是一大段stderr日志。这类返回经常充斥着无意义警告直接丢进去就是纯污染。可以在代码解释器的输出环节做处理只保留最后一行exit code和有限长度的核心输出。我自己后来封了一个工具结果处理器每个工具返回前都必须过一遍先按规则截断到2k token以内再标记是否包含需要“长期记住”的结论如果需要就同步生成一条摘要存进状态区。这套改造完成后同样一个销售分析Agent单轮上下文token数从60k降到了12k响应速度提升了将近一倍而且准确率反而更高了因为模型终于能看清数据了。3. 上下文管理的三项核心技术截断、压缩与检索3.1 滑动窗口截断先保住最近N轮截断是上下文管理最基础的手段也是最容易做错的。很多人简单粗暴地把对话历史从最前面砍掉结果模型失去了关键背景信息。正确做法是滑动窗口加楼层保护。我的配置方式是对话历史保留最近6轮完整内容再往前的内容全部收进摘要。这里有个细节——6轮这个数字不是随便定的它是跟Agent的典型任务链长度强相关的。如果Agent经常需要多步工具调用比如问完城市再问天气再定行程这个过程可能要4-5轮才能完成窗口小了就顶不住。而如果只是普通FAQ式问答3轮就够了。你需要根据自己Agent最常跑的链式长度来定然后留50%余量。另外截断不是只在接近上限时才做而是每一轮都应该对历史队列做一次“过期淘汰”。我见过一些设计等上下文快爆了才触发清理结果一次性把前面的重要信息全砍了Agent当场“失忆”。好的策略是把清理动作做成常态永远保持上下文处在有序的紧凑状态。3.2 摘要压缩让Agent记住结论而不是原文截断解决的是“太多了放不下”的问题但单纯截断会丢信息。所以需要一个配套动作对即将被挤出窗口的内容做信息蒸馏并把蒸馏结果留在上下文里。这其实就是摘要压缩。常见的做法有两种一种是基于规则的——只保留关键字段比如用户原始诉求、最终结论、重要数字另一种是基于模型的——定期调用LLM把历史对话浓缩成一段结构化摘要。我实操下来的建议是能用规则不要用模型能用小模型不要用大模型。因为基于模型的摘要不仅花钱还会引入幻觉。比如之前销售分析Agent历史对话里有一轮用户提到“华东区三月的退货率大约2.3%”模型做摘要时把这个数字写成了“2.3”看起来差不多但后面所有计算全部跟着错。为了防这种坑我的摘要模板会强制要求数字必须原样引用不允许改写依然由模型压缩的话压缩完我再跑一道规则校验把关键数字和原对话做比对不一致就回滚改由截断方案兜底。纯规则能解决大部分场景。把每轮对话抽取出三个字段——用户核心诉求、Agent给出的结论、未决的遗留事项——拼成一个状态块这个状态块就是Agent后续推理的“记忆锚点”。这比把十几轮冗长对话全塞进去效果要好得多。3.3 向量检索召回让长期记忆变成外挂如果Agent要服务的场景跨越很长时间比如用户每天来问一次持续一个月摘要也会越来越长最终还是会突破窗口极限。这时候就需要第三件武器——向量检索召回。你可以把向量检索理解为给Agent配了一个外置硬盘平时不占用工作内存需要时再把相关的内容加载回来。具体流程是每一轮对话结束后把该轮内容切片、向量化、写入向量数据库下一轮新问题进来时先用用户的问题去向量库里做相似度检索把最相关的历史对话记录捞回来拼进上下文。这套方案在需要长期记忆的场景非常有用比如私人财务助理、健康管理Agent、陪伴型Agent。但要注意向量检索不是银弹它有召回不准确的风险检索回来的内容可能跟当前任务毫无关系反而造成新的上下文污染。所以设计时要有阈值过滤相似度低于某个值就不召回并显式告诉Agent“没有找到相关历史按新问题处理”。3.4 混合策略怎么搭一个值得参考的配比方案这三种技术不是互斥的生产级Agent通常是把它们组合在一起形成一个分层记忆体系层级技术生命周期典型容量工作记忆完整上下文窗口当前轮次5-20k token短期记忆滑动窗口 摘要压缩最近N轮对话10-30k token长期记忆向量检索召回天/周/月级数百万token我们内部把这套结构叫“三层记忆模型”实现了之后Agent的稳定性确实有了质的提升。工作记忆承担当前推理的直接输入短期记忆保存最近几轮的原始细节同时用摘要保住更早的关键信息长期记忆则负责把真正有价值的历史信息按需捞回来。三者配合既保证了窗口不爆也尽量减少了信息丢失。这里要提醒一句三层记忆模型不是一上来就该做的。如果你的Agent只是单轮问答、无状态调用那连短期记忆都不需要加了反而累赘。我见过不少团队刚开始就上复杂的记忆系统结果排错排到怀疑人生。我的建议是先跑通核心流程在真实日志里观察上下文膨胀速率再决定在哪个层级补强。4. 多Agent协作中的上下文隔离与传递4.1 会话级与任务级上下文要分层如果你的系统是单Agent跑完整条链路上文那些基本够用了。但如果你在做多Agent协作——比如一个主管Agent调度几个专家Agent就需要考虑上下文的隔离问题。刚开始搭多Agent时我踩过一个很蠢的坑为了让每个专家Agent都能理解全局我把主Agent的完整上下文复制了一份传给每个子Agent。结果就是每个子Agent回复时都“非常全面”全面到把用户真正要问的事情淹没在了背景信息里。而且多个专家都拿着相同的历史对话回答的口径反而各种冲突。后来想明白了一个道理上下文必须按职责分层而不是按职位复制。会话级上下文是全链路共享的只保存用户核心目标、关键约束、已完成步骤的结论任务级上下文是每个子Agent私有的只装完成当前子任务所需的信息。举个实际例子一个研究型Agent被用户要求对比三款云服务商的定价。主管Agent会拿到会话级上下文包括用户预算、地区、需求规模然后分配三个调研子任务给三个专家Agent。每个专家只拿到一个服务商的官方定价文档地址和调研要求这就是任务级上下文。专家们各自干活把结论汇总回来主管再做最后综合。这样一来每个Agent看到的上下文都很干净注意力自然集中。4.2 父子Agent之间的信息损耗怎么控制多Agent协作有个绕不开的问题子Agent的执行结果要怎样传回父Agent才能尽量减少信息损耗我的经验是让子Agent的输出遵循固定结构并且强制做三层拆分结论层直接回答分配给它的子任务给出结论性描述比如“A服务商的按量付费比B贵12%”。证据层给出支撑结论的关键数据点比如具体价格、生效条件控制在有限字数以内。原始材料层如果是需要长期存档的完整资料写入临时存储只返回引用地址。父Agent在汇总时只需要读取前两层原始材料层按需再取。这个设计实际上是把上下文工程里的“压缩”原则应用在Agent与Agent之间的通信协议上效果非常明显——父Agent的上下文不会再被几十页原始报告撑爆子Agent的产出也真正地“被看到”了。另外父Agent给子Agent下发任务时也要尽量少带无关历史。只传任务本身和目标约束不要传用户跟父Agent闲聊的记录。子Agent需要保持专注用户昨天抱怨过一句“最近带宽不太稳定”跟今天的定价对比没有任何关系传到子Agent那里只会造成注意力干扰。4.3 上下文漂移一个容易忽视的故障源多Agent系统里还有一个很隐蔽的故障源——上下文漂移。我说的不是模型幻觉而是指Agent系统在长时间运行后各Agent对同一件事的理解出现偏差。举一个真实场景一个销售辅助Agent系统主管Agent在会话级上下文中记录了一条规则“给客户报价不能低于8折”。这条规则在会话开始时被正确写入了每个子Agent的任务指令里。但随着对话推进某个子Agent在工具调用结果中看到一条“历史成交价7.5折”的信息它就把7.5折当成新规则了。后续所有报价都按新规则走而主管Agent并不知情因为那个子Agent的私有上下文并没有被主管同步到。解决漂移问题我的做法是让子Agent在每次任务的结尾回传一个“理解确认”字段——确认它最终执行时使用的规则是什么。父Agent拿到这个字段后跟自己的规则基线做比对发现不一致就触发纠正。这个设计并不复杂但能把漂移问题从“随机出现”变成“可监测、可干预”。如果你不想做得这么重至少要在系统里埋观察点每个Agent在关键决策后把它的决策依据写进日志方便事后复盘“它是基于什么做出这个决定的”。没有这个埋点上下文漂移即使发生了你也很难定位。5. 避坑实录上下文污染是最贵的隐形开销5.1 四个常见的上下文污染来源做了这么多项目我总结出上下文污染的四个主要来源每一个都是花真金白银买来的教训无关历史残留对话轮次多了模型把早期用户随口说的一句话当成当前指令。比如用户在开场说“等下我可能要问很多问题”这句话一直被保留在上下文里结果每轮问答模型都以为用户要连续追问回答风格变得越来越发散。工具结果的冗余前面说过这是大头。JSON字段多、日志噪声大、PDF全文解析后没有精细化提取全都是污染源。错误信息的持续放大某轮工具调用返回了一个过期报价模型当成事实记录进上下文之后每一轮都基于这个错误报价计算越跑越偏。这种污染是“传染性”的危害极大。隐含状态的错乱多个异步任务交错进行时上下文里的状态信息互相覆盖。比如用户同时问了两件事Agent在处理第二件事时把第一件事的中间状态当成了当前状态。前两种可以通过规则处理后两种则需要靠状态管理机制来兜底。状态信息一定要显式声明它的适用范围例如用“当前活跃目标”来标注当下正在处理的任务用“已完成目标”来区分历史状态模型才不会把旧的当成新的。5.2 一套可以照着做的上下文治理清单我给你列一份上下文质量的检查清单做Agent开发时逐条过一遍能过滤掉大部分低级问题检查项标准不合格的后果上下文总量占窗口的60%-70%以下成本飙升、注意力分散系统提示词无重复、无矛盾、不超过3k行为漂移、指令冲突工具描述每个工具描述不超过200 token工具误调用、漏调用工具返回已裁剪、已结构化、不超过2k上下文膨胀、关键信息被淹没历史对话保留最近N轮超出部分已摘要早期噪声一直干扰后续推理关键数字摘要与原对话比对一致基于错误数字推理结果全错子Agent输出三层拆分结论证据材料引用父Agent上下文被撑爆汇总困难状态标记活跃目标与已完成目标显式区分任务交错时状态错乱5.3 技术栈选型与工程落地的几个参考很多人在做上下文工程时容易一上来就想用复杂的框架。以我个人的经验工具选型还是“够用就好”。服务端用FastAPI作为Agent网关把用户请求接入、工具调用编排、上下文组装放在一层流程控制用LangGraph这类带状态管理的框架它能帮你管理好各节点之间的状态流转状态写入和读取都有清晰的钩子会话存储用Redis缓存最近对话和短期摘要向量库我常用pgvector直接复用Postgres省掉多维护一套基础设施的麻烦。前端界面上如果你在调试阶段强烈建议搞一个请求日志查看器把每一轮的上下文结构可视化哪一块占了多少token、被模型实际关注的重点有哪些全部展示出来。我见过太多团队在“黑盒”状态下调试Agent完全靠猜。有了可视化的上下文面板很多问题一眼就能看出来。另外我特别想聊一下并发场景。很多朋友问Agent怎么扛并发其实上下文工程就是扛并发的前置条件。上下文体积直接决定了每次请求的token消耗和推理延迟。我们把单个请求的上下文从60k压到12k之后同样的GPU资源吞吐量直接翻了接近三倍。如果你的上下文没有治理好就上高并发只会放大成本问题而不是解决并发问题。最后再分享一个在实战中验证过的细节上下文工程不会一蹴而就它需要持续观测和调优。我会在每次版本迭代后跑一轮回归用固定的测试问题集对比上线前后的输出稳定性重点盯上下文占用曲线和关键决策依据的保持率。Agent的能力上限由模型决定但它的稳定下限基本由上下文工程的质量决定。把这块打磨到位了你会发现Agent的“智商”其实没有那么飘。
返回列表