我在实际调试Agent应用的时候发现一个很有意思的现象很多团队把精力全扑在模型选型、Prompt设计、工具调用这些环节上结果跑起来效果还是不稳定——有时候回答得头头是道有时候直接牛头不对马嘴。问题大多出在那些看不见的环节上也就是上下文工程。这个名词听起来高大上但说穿了就是你怎么管理和组织喂给模型的那一堆信息。它直接决定Agent能不能在复杂多轮任务里保持稳定、不跑偏、不犯低级错误。这篇文章不聊虚的直接拆解上下文工程的核心机制、常见坑和可落地的实操方案。无论你是在搭一个简单的问答机器人还是在搞一个多工具协作的复杂Agent系统这篇文章的内容都适用。我会把我在项目中踩过的坑、试过的方案、最后沉淀下来的经验一并写出来方便你少走弯路。1. 上下文工程到底是什么——先搞清楚我们解决的问题1.1 从一次翻车现场说起先说一个我印象很深的场景。某次给一个客服Agent做优化任务很简单用户问订单状态Agent需要查询订单系统再结合售后政策回答。单看每个环节都挺正常Prompt写得也没问题。但跑起来后发现一个怪毛病——只要用户多问几轮比如先问订单状态再问退款政策接着问物流信息Agent就开始拿上一轮的数据往下一轮的答案里硬塞。比如用户问“退款什么时候到账”Agent回答里竟然带着“您的订单预计X月X日送达”这种话。原因不复杂每一轮对话产生的中间结果都被堆进上下文里模型分不清哪些信息是当前任务需要的哪些是历史残留。这就是上下文工程没做到位的典型症状——信息全堆进去了但模型不知道怎么取舍。所以我把上下文工程定义为对进入模型上下文窗口的所有信息进行筛选、组织、压缩和结构化管理的过程。它解决的不是“模型能不能理解”而是“模型能不能在正确的时间用正确的信息做正确的推理”。1.2 上下文窗口不是越大越好很多人有一个直觉上下文窗口越大Agent就越聪明。这个直觉只对了一半。拿某主流模型举例窗口有128K听起来很宽裕但你真的把128K全部塞满推理速度会明显下降而且模型对长文本中间部分的信息感知力会减弱——这个现象在业内被称为“lost in the middle”。我自己做过一个对比测试同样是让Agent总结一份项目文档文档总长度约60K token分两种方式喂——一种是完整塞入另一种是先把文档做分段摘要再拼接。结果是第二种方案的回答准确性高了接近20%推理耗时也缩短了将近一半。原因很简单摘要后的信息密度更高关键内容不会埋在大量冗余文字里。所以上下文工程的第一条原则就是能少喂就少喂能精喂就精喂。窗口再大也不能当垃圾桶用。1.3 上下文工程适用的典型场景不是所有应用都需要重度做上下文工程。如果你只是做一个单轮问答输入输出都是一句话级别那简单拼Prompt就够了。真正需要上下文工程的场景一般有这几个特征多轮对话状态需要跨轮维护比如客服、助手、教育类应用。多工具协作Agent需要调用多个外部API每个工具的返回结果都要参与后续决策。长文档处理输入源是几十页甚至上百页的材料不可能无脑塞进上下文。多角色任务一个Agent需要先后扮演不同角色完成不同子任务。如果你做的东西带上面任意两个特征上下文工程就是绕不开的环节。2. 上下文失控的三大根源——理解问题才能解决问题2.1 指令冲突系统提示词和用户输入打架我在调Agent的时候发现很多莫名其妙的错误根源是指令之间的隐性冲突。系统提示词里写着“你是购物助手只回答商品相关问题”但用户输入里带着“帮我写一首诗”模型就会在“拒绝回答”和“满足用户”之间摇摆。更隐蔽的冲突出现在动态注入的上下文里。比如你从数据库里拉了一条用户画像里面写着“用户偏好简洁回答”但系统提示词里写的是“请提供详细的解释说明”两条指令同时出现在上下文窗口里模型就会表现得极不稳定——有时候详细有时候简洁完全看运气。解决思路是给指令排优先级。系统级指令System Prompt应该是最顶层约束动态注入的业务上下文比如用户画像、历史摘要属于次级指令而用户当前的输入属于最优先指令。这层逻辑需要在拼接上下文的时候用分隔符和明确的层级提示词表达出来不能指望模型自己判断。2.2 信息稀释关键信息被噪声淹没第二个常见问题是上下文里的噪声太多把关键信息冲淡了。我见过一个团队为了“保险起见”把每次工具调用返回的完整JSON都塞进上下文一个简单的查天气操作返回结果里带着几十个字段包括风速、湿度、气压、空气质量指数。Agent只是想知道“明天会不会下雨”结果被一坨无关字段干扰。这里有一个经验法则工具返回结果必须做字段级过滤。在调用工具之后、拼接上下文之前写一层转换逻辑只保留当前决策真正需要的字段。比如查天气只需要保留天气现象、最高温、最低温、降水概率这四个字段就够了。信息少了模型反而更准。2.3 记忆污染历史轮次的错误被循环放大多轮对话里最头疼的问题就是记忆污染。Agent在某一轮犯了个小错误比如把用户的订单号记错了这个错误会写进历史摘要里然后在下一次摘要更新时被当作“事实”保留下来继续参与后续推理。错误就这样一轮一轮被放大。我处理过最极端的一个案例Agent在第3轮把用户名“张三”误写成“张四”到第12轮的时候整个对话摘要里用户名都是错的最终导致查不到订单记录。定位这个问题的过程非常痛苦因为从表面上看每一轮的回答都还算流畅但关键字段就是不对。后来我养成了一个习惯所有写进长期记忆的信息都必须是结构化字段并且要标注来源轮次和时间戳。一旦发现关键字段有更新就直接覆盖旧值而不是在摘要里追加一句“用户名字是张四”。这样做能尽量切断错误信息的传播链。3. 上下文管理实操——从简单到进阶的三套方案3.1 方案一滑动窗口最简单但够用如果你的应用没有太复杂的长期记忆需求滑动窗口是最省事的方案。思路很直白只保留最近N轮对话作为上下文更早的信息直接丢弃。N的取值要看业务复杂度。我自己的经验是简单任务单工具调用、单轮知识问答窗口取5~8轮就够复杂任务多工具协作、分步推理可以放宽到10~15轮。超过这个范围收益会急剧递减因为早期轮次的信息大都已经反映在后续轮次里了。滑动窗口的缺点是显而易见的——它会丢信息。用户在第3轮提到的个人信息到第20轮还在用但窗口已经把它挤掉了。所以如果你发现Agent经常“忘记”用户说过的话说明你需要升级方案。3.2 方案二摘要记忆对抗遗忘的入门解法摘要记忆是目前最通用的中间态方案。核心思路是每经过一定轮次就把当前对话历史交给模型做一次摘要压缩把摘要作为长期记忆保存原始对话则从上下文窗口中移除。这里有几个实操细节需要特别注意。一个是触发时机我建议按轮次或按token阈值触发摘要比如每5轮做一次或者上下文超过20000字就触发。另一个是摘要的分层策略——全局摘要只保留用户侧的核心信息偏好、身份、目标而放弃过程性的对话内容。我实际用下来的体感是摘要记忆能把Agent的“长期记忆力”拉高一个量级但它也有明显短板摘要本身就是有损压缩细节必然丢失。比如用户在某轮对话里报了一个收货地址摘要里可能只留下“用户提供了收货地址”而没有地址详情。所以关键信息不能只靠摘要记忆还得配合结构化存储比如单独的字段表这个我在后面单独展开。3.3 方案三检索增强把上下文窗口变成可查询的数据库当你的应用涉及大量外部知识比如产品文档、政策法规、私有知识库就不能靠简单的滑动或摘要来解决问题了得引入检索增强生成RAG。RAG的思路不是把全部知识塞进上下文而是用户提问时先从知识库检索出最相关的片段再把这些片段和问题一起拼给模型。这里的核心难点在选择“最相关的片段”——你不能只靠关键词匹配因为用户的问题往往是模糊的、口语化的。我常用的做法是双路召回第一路用向量相似度检索把问题和知识片段都做向量化取相似度最高的Top K第二路用关键词匹配把用户问题里的关键实体订单号、产品名、日期和知识库里的实体做匹配找到精确命中两个结果做并集再按相关性打分排序取前K个进入上下文。K的取值一般取3~8个片段比较合适。太少了可能漏信息太多了又会出现前面说的信息稀释问题。3.4 进阶技巧结构化信息与短期记忆分离我发现很多团队的上下文管理做得不好核心原因是把两类信息混在一起一类是“事实型信息”订单号、用户名、产品规格一类是“过程型信息”用户第几轮说了什么、工具返回了什么中间结果。这两类的生命周期完全不同——事实型信息可能整个会话都需要用过程型信息往往当前轮结束就没用了。我的做法是建立一个“会话状态表”用结构化的方式维护关键事实字段。每轮对话结束后Agent会从当前轮次里抽取出关键实体和状态变化更新到这张表里。拼接上下文时把状态表渲染成一段结构清晰的文本放在历史对话摘要之前。举个例子用户说“我上周买的那双鞋有点问题”状态表里会更新字段“product_type: shoes, purchase_time: last_week, complaint: quality_issue”。后续Agent回答时只需要看这张表就知道用户侧的核心信息不需要回翻历史记录。这种方案的威力在复杂任务里非常明显。我做一个跨部门协作的Agent时需要同时维护审批状态、申请人信息、审批意见、时间节点等几十个字段。如果全靠对话历史模型早就晕了。但用状态表之后模型每次只读一张紧凑的表推理准确率直接上了一个台阶。4. 工具选型与参数调优——细节决定体验上限4.1 模型选择的隐性差异上下文工程的实际效果与你选的模型高度相关。不同模型对上下文的处理方式差异很大——有的模型对长上下文中Late Position的信息更敏感有的则更均衡。这不是玄学跟模型训练时的注意力机制设置和指令微调数据分布有关。所以你在落地上下文工程之前最好先在目标模型上做一组基准测试。我的做法是准备一组刻意构造的“钓鱼测试”在上下文的开头、中间、结尾分别埋入几个关键事实然后问一个需要同时引用三处信息的问题看模型是否都能正确捞出来。实测下来不同模型的得分差异可以到30%以上选错了模型你再怎么调上下文方案都补不回来。4.2 系统提示词的结构化设计系统提示词是上下文工程里性价比最高的杠杆但大多数人都没把它的潜力用出来。我见过太多的System Prompt就是一段话“你是一个智能助手请根据上下文回答问题。”这种写法基本等于把决策权完全交给了模型结果就是输出风格飘忽不定。我推荐的System Prompt结构是这样的第一段角色定义用一句话说清楚模型是什么、主要职责是什么第二段行为准则列出3~5条硬性规则比如“用户的问题如果与订单相关必须先查询订单系统不能凭记忆回答”第三段输出格式说明指定结构化输出的模板比如“回答必须包含Reason和Conclusion两个字段”第四段边界说明明确标注哪些问题必须拒绝回答比如“涉及财务审批的操作必须转人工”。这个结构的好处是它把模型的决策空间收敛到一个可控范围内同时保留了足够的灵活性。我再加一条建议System Prompt里的每一条规则都要确保“可验证”——也就是说Agent输出完之后你可以写一段校验逻辑检查它有没有违反规则。如果规则写得模糊比如“回答要友好”你根本没法自动校验。4.3 参数调优温度和采样的工程视角参数调优这块很多人就看过一句“temperature越低越稳定”然后直接套用但实际效果往往会打折扣。我的经验是上下文工程做到位之后temperature的影响会变小但它仍然是最后一道可控的护栏。对于Agent类应用我建议默认temperature设在 0.1~0.3 之间top_p 设在 0.8~0.9。这个区间既能保证输出相对稳定又不至于完全机械。如果你发现Agent经常“一本正经地胡说八道”先把temperature降到0再排查上下文里是否存在冲突信息或缺失信息否则就算你把temperature设为负数也救不回来。还有个容易被忽略的参数是max_tokens。很多人为了方便直接把输出上限设成和上下文窗口一样大结果模型有时会“凑字数”生成一堆毫无信息量的废话白白消耗上下文空间。更合理的做法是根据业务场景估算单次回答的最大长度设一个合理的上限比如客服场景给500~800 token就足够了。4.4 上下文拼接顺序位置决定感知度同样的信息放在上下文的不同位置模型对其感知的程度完全不同。根据我的测试和业界公开的观察模型对上下文开头和结尾的信息最敏感中间位置的信息容易被忽略。操作层面我的拼接顺序是最前面System Prompt全局指令接着会话状态表关键事实精炼然后检索到的知识片段按相关度排序最相关的放在最前面然后短期对话历史最近几轮最后用户当前这轮的问题配上明确的“现在请回答”的指令标记。这个顺序的目的是把最重要的两种信息——全局指令和当前任务——放在上下文的两端把辅助信息放在中间。实测下来同样的信息量仅靠调整顺序就能改善10%左右的准确率。5. 常见问题速查——直接对着症状找药方5.1 症状一Agent忘了用户前面说的话这种情况基本都是上下文窗口把早期信息挤掉了。排查思路是先看历史对话是否被完整保存在上下文中如果是滑动窗口方案检查窗口轮数是否太小如果是摘要记忆方案检查摘要里是否保留了用户侧核心字段。我遇到过一个特殊情况摘要做得很完整但Agent还是“忘事”后来发现是摘要更新的频次太低了——上下文已经超过3万字符了摘要还停留在5轮之前的状态。解决办法是把摘要触发阈值调小同时增加一个“关键字段变更立即更新摘要”的规则。5.2 症状二Agent把工具返回的原始数据直接当答案这个问题的根源是上下文拼接时没有对工具返回结果做“再加工”。工具返回的JSON是给程序读的不是给模型读的。直接塞给模型它可能会照着JSON字段念一遍甚至把字段名也带进回复里。我的处理方式是在工具调用后面加一层“结果包装器”把原始JSON转换成自然语言描述比如“订单状态查询结果订单号SD20241201当前状态为运输中预计送达时间为12月28日”。让模型直接引用加工后的文本而不是原始JSON。5.3 症状三回答变得啰嗦每句话都像在写报告这个常见于System Prompt里面塞了过多的“行为准则”或者上下文里存在大量重复的指令。我见过一个案例开发者为了“保险”在System Prompt里同时写了“回答要简洁”和“要详细解释推理过程”两条规则直接冲突模型选择了两者叠加——每句话都很短但整体逻辑非常冗余还要带个“推理过程”的小尾巴。解决思路是定期审查System Prompt。我会给每一条规则打一个标签约束型限制做什么还是引导型建议怎么做。约束型规则最多保留5条引导型规则控制在3条以内。如果超过这个量模型的行为就会开始漂移。5.4 症状四多轮之后输出格式开始乱这个问题很典型前5轮输出的是合法JSON到第10轮突然开始夹带Markdown代码块标记或者字段名变了。原因大概率是上下文里的历史输出把“格式漂移”传染给了当前输出——模型在模仿最近的历史输出风格。解决方案有两个方向。第一个是在System Prompt里给一个极简的格式示例few-shot让模型有明确的参照物第二个是每次拼接历史对话时对旧轮次的输出做规范化处理——比如把JSON字符串统一转成格式化文本去掉那些偏离模板的部分防止“坏例子”进入上下文。6. 避坑清单——这些经验是用真金白银换来的6.1 不要盲目追求“把全部信息放进上下文”信息越多模型越容易迷失。我倾向于把上下文理解成一根吸管而不是一个水桶——你只能通过这根吸管向模型传递少量精准的信息。如果你发现自己试图往上下文里塞越来越多的内容往往是上游的设计出了问题要么是任务拆得不够细要么是知识库检索不准。6.2 给关键信息加时间戳和来源标注这是从一次严重事故里总结出来的教训。有一次Agent误用了过期的政策条款回答用户导致用户投诉。问题就出在知识库更新后旧版本文档仍然被检索到了而且没有标注版本时间模型根本不知道新旧之分。现在我要求所有进入上下文的知识片段必须带上“更新时间”“来源文档ID”必要时加一句“本文档更新于X月X日如果与后续版本冲突请以最新版本为准”。这个小小的标注有时候就能避免一次重大事故。6.3 建立上下文质量的自动巡检机制上下文工程跟模型调优一样不是“配一次就完事”的它会随着应用逻辑的迭代而逐渐劣化。我给自己定了一个规矩每周跑一次上下文巡检脚本自动采集真实会话中的上下文内容检查以下几项指标上下文中是否出现了重复的指令或矛盾的说法工具返回结果是否出现了未被加工的原始JSON长期摘要里是否存在过时或互相矛盾的事实字段上下文平均长度是否在持续增长增长说明筛选逻辑可能失效了。巡检脚本本身用普通字符串匹配加规则就能实现不需要多复杂的算法。关键是定时、定指标、有反馈闭环。6.4 成本和性能的平衡意识上下文工程做得好不好不仅影响效果还直接影响账单和延迟。每多喂进去1000个token推理成本和延迟就往上走一截。很多团队在初期设计时完全没考虑这一点等用户量上来之后发现成本爆了再回头优化上下文。我的建议是从第一天起就给Agent立一个“上下文预算”的概念——比如单次请求上下文上限8000 token多1个token都不让过。预算制会逼着你在信息密度上做文章反而会让应用效果变得更好。这些就是我目前在上下文工程这个方向上最核心的实践经验了。整套方法论的核心其实就一句话把模型当成一个需要你精心准备资料的决策者只给它看“刚刚好”的信息它才能做出“刚刚好”的决策。方向对了剩下的交给时间和迭代就好。