从Prompt到上下文工程:Agent性能与稳定性的真正分水岭 最近Agent圈子有个特别有意思的转向——大家终于开始意识到把模型变聪明这件事光靠调Prompt已经玩不出花了。真正让Agent从“demo能用”到“生产环境扛得住”的分水岭其实是上下文工程。我用这个词有点拗口换个说法你就懂了Prompt决定模型怎么回答而上下文决定模型看见什么。后者才是Agent性能的天花板。这篇东西写给两类人一类是被复杂Agent搞得焦头烂额的开发者另一类是刚入门、以为“写个好的system prompt就能搞定一切”的AI应用爱好者。我会从概念拆到实操再把我踩过的坑、排查过的病例全倒出来尽量让你读完就能用到自己的项目里。1. 为什么说 Prompt 不是 Agent 的全部1.1 一个让我彻底改观的实验先说个我自己的经历。之前做一个文档问答类Agent模型效果一直不稳同一个问题换个说法就答偏。我当时第一反应肯定是调Prompt——把系统提示词写得越来越长加了各种约束、few-shot示例甚至给模型“立人设”。结果呢效果有提升但天花板很明显而且Prompt越长模型越容易在无关约束上“用力过猛”有时候会为了遵循某条规则而忽略用户真正的问题。后来我换了个思路不碰Prompt只改上下文的组装方式。原来我一股脑把检索到的文档片段拼进上下文为了让“相关”内容多甚至塞进去五六段。改动之后我只保留两段最相关的片段再把检索得分、文档来源、时间戳这些元信息用结构化方式附加上去。结果非常直接——同样的问题回答准确率肉眼可见地上升了。这个实验让我意识到模型的推理能力是固定的但上下文是你唯一可以完全控制的东西。你给它看什么、用什么样的顺序看、哪些信息被强调、哪些信息被省略这决定了它能力的上限。Prompt重要吗重要但它只是上下文里的一小层。1.2 分清 Prompt 和上下文工程的边界很多人把两者混为一谈其实分工完全不同。拿人来做类比Prompt相当于你给一个新同事发了一封邮件写清楚“请你做某件事注意12345条要求”上下文工程则相当于你把工作台收拾好了——相关资料放在左手边、历史记录打开、常用工具摆上手边、杂乱的无关文件全部收走。邮件写得再好工位乱成一团效率照样上不去。在Agent场景里上下文通常由这么几块组成组成部分典型内容传统Prompt工程会管吗系统提示词角色定位、输出格式、行为约束管用户请求当前轮次的输入一般不管检索结果RAG召回的相关文档片段不管历史对话摘要之前聊了什么的压缩记忆不管工具调用结果函数返回的JSON、状态码、API响应不管运行时元信息时间、用户身份、环境状态不管看到了吗传统Prompt工程只覆盖其中一小块而其余几块恰恰决定了Agent能不能在真实场景里稳定工作。这就像你给下属安排工作——要求写得再细如果他手里拿到的资料是错的、工具是缺的照样干不成。2. 上下文工程的四块核心拼图2.1 检索与召回决定哪些信息能进上下文上下文工程的第一步是决定“哪些信息值得被放进上下文”。这一步在RAG场景里尤其关键。很多人以为检索就是“把相关片段找出来”但实际做的时候会发现难点在于“相关”的定义。我常用的方案是混合检索先用向量相似度召回Top 20再用BM25做关键词召回Top 10两路结果做一个重排序rerank最后只取Top 2-4个片段放进上下文。为什么不用纯向量因为纯向量在某些专有名词、代码片段、编号规则上经常翻车——语义相似不等于字面匹配而很多业务场景恰恰吃字面匹配。切片策略也要单独提。固定的256 token或512 token切片看起来省事但会把一句话从中间截断。我做项目的时候习惯用“章节感知切片”——先按文档结构标题、段落、列表切再把太短的段落合并到临近片段里。这样检索到的片段语义更完整模型理解起来也更省力。另外召回片段一定要带元信息。比如来源文件名、章节标题、页码、时间戳。这不仅是给模型提供引用依据更关键的是让模型判断信息的优先级。我见过很多项目把“最近更新的一版”和“三个月前的老文档”同时塞进上下文模型完全分不清哪个优先最后答出来一个混合版本看起来都对细究全是坑。2.2 记忆分层全局记忆、会话记忆与工作记忆Agent和单次问答最大的区别就在于它要处理“多轮连续性”。这里最容易犯的错是拿一个大列表把所有历史消息全存下每次请求全部拼进上下文。方案简单但token消耗会爆炸而且越往后的对话模型越容易被早期的细节带偏。我做项目时一般把记忆拆成三层全局记忆存用户的长期偏好和基础信息比如“用户是产品经理关心指标是转化率”会话记忆存当前这轮对话的要点用自然语言摘要的方式维护而不是存原始消息列表工作记忆则是当前正在处理的任务状态比如“正在生成第三季度报告数据源已选定”。这三层会以不同的方式进入上下文。全局记忆固定放在系统提示词后面会话摘要每次动态生成并放在用户消息前工作记忆通常以结构化的JSON片段嵌入。做这个分层之后多轮对话的稳定性会有质的提升——因为模型每次看到的上下文都是“当前任务最重要的信息”而不是一坨糊涂账。2.3 工具结果回填让Agent真正“动手”Agent区别于ChatBot的核心在于工具调用。很多做Agent的人会发现工具返回的结果模型经常“视而不见”或者胡乱解读。问题大概率出在回填方式上。最典型的错误是把工具返回的原始JSON直接拼接给模型。模型能读JSON但读起来累关键信息全淹没在一堆字段里。我的做法是加一个“格式化层”——把我预期的字段抽取出来转成自然语言再放回上下文。比如一个查询天气的工具返回了JSON你直接塞进去模型也能解读但如果你转成“北京当前温度32度体感36度建议午后减少户外活动”模型就不需要再从json里自己找重点——这不但减少理解偏差还省token。还有个细节容易被忽视工具调用的失败信息也要回填。不止是成功返回的比如“你调用的接口超时了错误码是408”模型才能决定下一步——是重试还是换个方案。如果失败信息不暴露给模型它就会卡在“假装成功”的幻觉里继续往下走。2.4 压缩与裁剪上下文不是越大越好上下文窗口这几年越做越大动辄128K甚至200K token但“窗口大”不等于“应该全塞满”。我实测下来的结论是上下文越长模型的注意力和指令遵循能力都会下降——这实际上是注意力机制的一个已知弱点也叫“lost in the middle”。哪怕上下文窗口容得下也不要无脑堆。有效的操作有两个方向。一是裁剪按时间衰减和按相关性衰减把不重要的历史对话、检索片段、工具记录逐层淘汰。二是压缩用摘要模型把长对话压成一段简洁的summarized memory并把原文的关键数据点数字、日期、ID单独抽出来存成结构化字段防止摘要丢信息。这一块我最想强调的是上下文工程里的“工程”二字意味着你要把上下文当成一个有生命周期的资源——它不只是一次请求里的输入而是一个持续维护、更新、淘汰的动态结构。谁把这件事做好了谁就能让Agent在长周期任务里一直保持稳定状态。3. 实战一个多工具 Agent 的上下文搭建全过程3.1 场景设定与选型纸上谈兵说得再多不如直接拆一个实际项目。我这边最近做了一个“项目数据分析助理”——用户可以用自然语言问项目状态、查历史报告、生成周报摘要。这个Agent涉及三个工具文档检索查项目报告、数据库查询查指标数据、周报生成调内部报表接口。这个场景非常适合讲上下文工程因为它的核心难点在于用户一句话进来Agent需要自己判断调用哪些工具、以什么顺序调用然后把多路结果整合进一个回答里。模型的推理能力在这里不是瓶颈真正决定成败的是——每轮请求里Agent能看到哪些信息。3.2 上下文组装五步法我整理了一个可复用的组装流程五个步骤每一步都对应一个上下文区块第一步系统提示词保持精简。只写角色定位、工具清单、输出约束控制在200-400 token以内。不需要把所有的业务背景和示例问题都塞进去那些应该放检索而不是固定占住上下文。第二步插入全局记忆。比如“你正在服务产品团队关注的指标是项目进度、风险、人天消耗”。这段一般是动态取的来自用户画像不随对话变化。第三步注入当前请求。这里有个小技巧不光写用户的原话还要加上“请求意图解析”的备注。比如用户问“三季度的上线时间能保住吗”我会先让一个小模型识别出意图标签然后组装成“用户想确认三季度上线计划是否延期敏感指标上线时间期望输出判断依据”。第四步塞检索结果和工具结果。按信息源类型分组排列同一个数据源的多个片段要紧挨着并且按相关性降序排。关键片段前面可以加一行摘要提示比如“以下内容来自项目周报V3版是最新的计划数据”。第五步追加会话摘要。前面的对话压缩成分条摘要放在最后作为“记忆区”。这里要控制长度——我一般限制在3-5条每条一句话以内。一个组装好的上下文大概是这个样子的片段顺序从上到下是System、全局记忆、用户请求、检索结果、工具结果、会话记忆。为什么这么排因为模型对上下文开头和结尾的信息注意力更强位置偏差所以最重要的请求放中间偏前记忆类信息放最靠后让模型做参考而不是抢关注。3.3 实操中两个让我印象极深的调整第一个调整是关于工具结果回填的。最早版本我直接把SQL数据库查询的返回字段原样拼进上下文比如status: 1, delay_days: 3, owner: 张三。模型经常能读到但回答里的表达很生硬有时候还分不清字段含义。我加了一个轻量级的中转工具返回后先用一个格式化函数把结果转成自然语言句子比如“项目A当前状态为进行中已延期3天负责人为张三”。就这么一个改动回答的自然度和可靠性同时提升属于性价比最高的一次调整。第二个调整是每次对话结束后的记忆更新。最初的方案是固定保留最近10轮对话原文但跑到第五轮的时候模型就开始“遗忘”第一轮说过的重要前提。后来改成“每轮对话结束后单独调一次摘要模型生成新的会话记忆”。这样虽然多了一次模型调用但长对话的稳定性提升非常明显。实际项目里如果对延迟敏感可以用更轻量的抽取式摘要甚至直接把关键字段维护在一个结构化状态对象里不一定要每次都跑生成式模型。4. 常见翻车现场与排查思路4.1 上下文太长模型响应变慢还容易答偏这个现象几乎是必然的——上下文越长首token延迟越高模型注意力被稀释的概率也越高。我见过有人盲目信任128K窗口把整个项目文档全塞进去结果单次请求要等十几秒回答质量还忽上忽下。排查思路很简单先看单轮请求的token分布用计费信息就能看到System占了多少、历史占了多少、检索占了多少。一般会把历史对话换成摘要检索限制到Top 3以内工具结果做字段裁剪。更重要的是打电话“越大越好”的念头——在性能测试里同样的问题用2K上下文和8K上下文分别测一次体验差距会大到让你吃惊。4.2 检索结果明明相关但模型不参考反而自己编这个问题我也被坑过好多次。检索Top片段其实是对的但模型答出来的内容和片段完全对不上——它在“自由发挥”。最可能的原因是检索内容在上下文里被系统提示词盖住了或者被别的信息冲淡了再一种可能就是你检索到的片段缺少“对话里类似的表述”——模型没意识到这片内容就是对当前问题的直接回答。对策有两个方向。一个是给检索结果加一个明确的“权威标签”比如“以下内容来自项目知识库回答时优先依据此内容推理”。另一个是把用户问题直接嵌到每个检索片段前面让每个片段看起来像是“针对用户问题的潜在答案”这种方法经常被用在一些RAG项目里实测有效。4.3 多轮对话越聊越笨早期信息全被冲掉这是最典型的上下文管理问题。早期信息不是“消失”而是被淹没在后来的一大堆工具结果和检索记录里。模型从概率上就会更关注新信息、更关注周围有详细展开的内容于是重要的早期约束就丢了。排查方法把完整上下文拉出来看早期的关键约束在第多少token的位置。如果超过了总长度的70%那么模型几乎肯定已经“失忆”。此时要么做硬性的截断和摘要优先级高的关键约束单独抽出来放System或最顶层要么对早期信息做“强化”每次组装上下文的时候把关键点重复一遍。4.4 问题排查速查表表现症状可能原因排查手法答案和检索内容对不上检索片段权重太低或未标记为权威依据检查检索片段在上下文中的位置加优先级标签多轮后遗忘关键约束早期信息位置太靠后被新信息淹没测量关键信息token位置提前或强制重复工具结果被模型无视返回JSON格式难理解或失败信息未暴露改用自然语言回填暴露错误状态上下文爆炸、响应变慢历史消息无压缩检索塞入过多片段改成摘要式历史检索裁剪到Top 3模型“两头看、中间忘”关键信息被放在上下文中间位置把核心数据提到上下文前30%或后20%位置输出不一致同一问题答案不同上下文顺序不稳定或检索结果波动固定组装顺序检索结果按相关性重排去噪这张表基本覆盖了我日常排查Agent问题时的80%场景。遇到奇怪的效果波动先别急着怀疑模型能力——拿一张纸把上下文里的内容写出来从上到下看一遍很多时候问题直接就暴露了。5. 上下文工程的后续演进方向做了一段时间上下文工程后我个人对Agent的发展有了一个明确判断模型的通用能力会继续提升但Agent应用的上限比拼的是谁更会管理信息环境。有几个方向我认为接下来一定会更热。一是上下文路由。不是每个请求都要走完整的检索-召回-重排链路而是先判断这个请求需要哪些上下文源——需要查库的直接查库需要检索的直接检索能靠缓存回答的直接用缓存。这背后是一个独立的“上下文调度器”它会根据当前任务动态决定组装策略。这其实很像操作系统里的内存管理有缓存、有交换、有优先级。二是结构化的上下文协议。目前上下文基本都是自然语言堆叠未来很可能会出现一种“结构化上下文”的形式——标准的元信息层、数据层、指令层让模型和工具都能更高效地解析。这个方向的价值在于减少模型的理解成本提升信息的确定性。三是上下文压缩的智能化。目前的摘要式压缩还会丢信息尤其丢数字和关系。更好的方案是“语义压缩”——不只压缩为几句话而是提取关键实体和关系转成结构化的知识表示再搭配自然语言摘要一起进入上下文。这个思路还比较新但我在一些实验里已经看到明显收益。我个人实际摸下来的体会是上下文工程不是一个“要不要做”的问题而是“早做还是晚做”的问题。只要你的Agent要从demo走向真实场景迟早会遇到和上下文管理相关的瓶颈。早一点把上下文组装流程结构化、可观测、可调试后面遇到问题你会少掉很多头发。而且很多工具和方法论都是通用的不管底下的模型换成什么这套思路都能复用。