
1. 上下文工程为什么它突然成了 AI Agent 的命门这几年搞 AI Agent很多人上来就聊模型选型、框架对比、并发扛不扛得住但实际把 Agent 推到真实业务里跑一圈就会发现真正卡脖子的往往不是模型智商而是上下文这一层。上下文工程这个提法最近在圈子里越来越频繁地出现它和 Prompt Engineering 不是一回事也比单纯调 prompt 要狠得多。我这里直接说结论上下文工程解决的是 Agent 在复杂任务里“记忆什么、遗忘什么、引用什么、拼接什么”的系统性问题它决定了 Agent 是聪明的助手还是只会复读机式回答的花架子。我自己经手过几个 Agent 项目最典型的一个是基于 FastAPI LangChain LangGraph 搭的任务型 Agent刚上线时模型用的是当时的主流闭源模型单轮对话效果惊艳但一旦涉及多步骤工具调用、跨会话记忆、长文档阅读理解就开始各种翻车工具参数张冠李戴、关键约束被遗忘、上下文一长就开始胡说八道。排查到最后绝大多数问题的根源都在上下文管理上而不是模型能力。也就是从那个项目开始我把上下文工程从“辅助技能”提到了“核心架构”的位置。这篇文章适合谁适合正在开发 AI Agent 的工程人员也适合那些想从单轮 Prompt 走向多轮 Agent 化应用的算法工程师。我会把上下文工程拆成几层来讲包括上下文的结构化设计、记忆机制、窗口管理与压缩策略、多代理协作中的上下文传递以及流式场景下上下文如何和并发控制结合。这里面有很多是我在真实项目里踩过坑之后摸索出来的方法不是教科书上的理论推演可以直接拿去用。2. 上下文工程的底层逻辑从 Token 拼接开始看本质2.1 模型的“记忆”本质上是一次性阅读要理解上下文工程先得把大模型的记忆机制搞清楚。大模型本身没有持久记忆它每次推理时能看到的全部信息就是输入窗口里的那一大段 Token。你可以把它想象成一个只有短期记忆、但阅读速度极快的人你把一份材料递给他他当场读完当场答题材料一收走他就什么都不记得了。所谓的多轮对话其实是你每次把历史对话记录重新抄一遍给他看。这个机制决定了 Agent 系统里所有的上下文管理本质上都是在做一件事决定“哪部分信息值得被再次抄写进这一轮的输入”。那些看似高深的上下文工程方法拆到底就是在优化这个抄写策略——抄什么、不抄什么、按什么顺序抄、抄多详细。明白了这一点你再看 Agent 崩溃的很多场景就通了上下文太短关键信息被挤出窗口上下文太长模型注意力被无关信息稀释上下文顺序不对模型对指令的遵循度骤降。2.2 上下文丢失的三种典型表现我在项目里总结过上下文丢失的三种典型表现基本覆盖了日常开发中 90% 的“模型怎么突然变蠢了”问题。第一种是硬截断丢失最常见也是最粗暴的。上下文窗口塞满了开发人员直接对历史消息做切片只保留最近 N 条。结果就是模型把任务最初的目标给忘了你在第一轮让它“先查询库存再生成采购单”聊到第五轮它只记得生成采购单完全不记得要查库存。第二种是相关性丢失当上下文里有大量无关内容时模型注意力被稀释对关键指令的遵循度下降。好比让学生在一本五百页的教材里找一道题的答案他能找到但明显更吃力。第三种是格式混乱导致的关键信息淹没工具返回的 JSON 和系统指令混在一起模型分不清哪个是“用户要求”哪个是“工具结果”结果就是它开始一本正经地编造工具输出。针对这三种丢失单纯调 Prompt 是治标不治本必须从上下文的结构化设计入手。2.3 上下文工程的核心矛盾信息完整性与注意力有限性所有上下文工程方法本质上都在调和一对矛盾信息完整性和注意力有限性之间的矛盾。你希望模型掌握足够多的背景信息来做出准确判断但信息一多模型的注意力就会分散反而影响判断质量。这里有个很直观的例子金融领域的 Agent 要分析一份上市公司年报完整年报可能有十万字以上而模型的上下文窗口可能只能容纳其中的一半。你是硬塞进去导致截断还是做摘要导致细节丢失上下文工程的做法是分层处理——把硬性约束比如分析框架、输出格式要求放到系统提示词里把高度相关的数据片段比如财务三表的关键指标放到当前轮次的上下文中把完整的年报放到检索索引里按需调用。这就好比你让一位分析师做事不会把整柜子资料全堆到他桌上而是先把要用的关键报表和行业基准递给他其余资料放在手边随时查阅。这个“分层递送”的思路就是上下文工程的核心方法论不是把信息全部塞进窗口而是让信息以合理的结构、在正确的时机、以恰当的形态出现在模型面前。3. 上下文结构化的三个关键设计系统级、任务级、数据级3.1 系统级上下文身份、约束、技能边界系统级上下文是 Agent 的“人格和职业操守”对应到工程实现上就是 System Prompt 部分。这一块的设计质量直接决定了 Agent 的稳定性但也是很多人最忽视的部分往往就是一句话“你是一个智能助手”了事。系统级上下文至少应该包含四个要素身份定义、行为准则、技能边界、输出协议。身份定义不用太花哨但要确切——比如“你是采购流程助理负责处理采购申请单的审批与催办”行为准则要写清楚“什么能做、什么不能做”——比如“只能查询采购系统数据不得自行修改订单状态”技能边界要明确“你会什么、不会什么”——比如“不具备库存预测能力当用户询问预测建议时明确说明无法提供并建议人工介入”输出协议则约定回答的格式比如“涉及金额必须四舍五入到小数点后两位并标注币种”。我自己常用的一个做法是把系统级上下文写成结构化的 Key-Value 形式而不是自然语言长段落。实测下来结构化描述比散文式描述在指令遵循度上要高不少尤其是在多约束条件下。后来我读到一些关于 Prompt 结构化的研究也印证了这一点模型对列表和键值对的解析一致性远好于对长段落的理解一致性。3.2 任务级上下文当前目标、执行状态、约束清单任务级上下文描述的是“当前这一轮、这个任务模型的执行目标是什么、已经做到哪一步、有哪些限制条件”。这一层是上下文工程里最要花心思设计的地方因为它动态变化每一轮执行后都要更新。以 LangGraph 为例它的 State 设计天然适合承载任务级上下文。我通常会在 State 里定义这几个字段current_goal当前目标由首轮用户输入解析而来并在后续执行中保持不变、task_progress执行进度记录已完成哪些步骤、待执行哪些步骤、constraints约束清单比如时间限制、金额上限、必须经过审批的环节、artifacts中间产物比如工具返回的查询结果、生成的临时文件路径。这个设计解决了一个很实际的问题当一次任务需要多次调用工具时模型需要清楚地知道自己正处在整个流程的哪个阶段。没有任务级上下文的 Agent经常出现“重复执行已经完成的步骤”或者“跳过尚未执行的步骤”这两种迷之操作。有了 task_progress 的持续更新Agent 的执行路径变得可控像是一条有路标的高速公路而不是一个凭感觉走的十字路口。3.3 数据级上下文按需引用而非全量灌入数据级上下文指的就是那些业务数据、文档片段、工具返回结果。这一层最常见的错误就是“全量灌入”——工具返回了 200 条数据全部塞进上下文让模型处理。这不仅是 Token 浪费更重要的是降低了模型对数据的利用率。更合理的做法是“先检索、后引用、再总结”。工具返回大量数据时先让模型或规则模块做一次粗筛只把最相关的若干条数据拼进上下文。比如订单查询工具返回了 50 条订单记录不需要全部呈现给模型而是先按照用户问题里的条件比如“最近三天未发货的订单”做一次过滤只把符合条件的 8 条记录拼入上下文同时附上统计信息“共 50 条订单其中 8 条未发货其余 42 条已发货”。这样做有两个好处一是模型处理的数据量大幅降低推理速度和准确性都有提升二是最关键信息相对突出模型更容易捕捉到重点。做数据级上下文时我的经验法则是一个单轮任务中拼入上下文的数据片段最好控制在 2000 Token 以内超过这个量就开始考虑分步处理或摘要压缩。4. 记忆机制的设计短期记忆、长期记忆与工作记忆的协同4.1 短期记忆会话内的上下文滚动窗口短期记忆对应的是当前会话内多轮对话的历史信息。最朴素的实现方式就是把所有历史消息拼起来但这很快就会撞上上下文窗口的天花板。所以短期记忆的关键在于设计滚动窗口策略。我通常采用按 Token 数和按轮数双阈值控制的方式。举个例子当历史消息总 Token 数超过 8000 时触发压缩策略同时无论 Token 数多少最近 20 轮对话必须完整保留。压缩不是简单丢弃而是对早期对话做摘要生成一段“对话摘要”放在窗口最前面再接最近 20 轮的完整内容。这样做的好处是模型既能通过摘要保持对早期关键信息的感知又能拿到最近几轮的完整上下文来精准执行当前指令。这里有一个细节值得注意摘要生成本身也要消耗 Token而且摘要的质量直接决定了后续对话的连贯性。所以摘要的生成时机要选好我建议在每轮对话结束时检查是否需要压缩而不是等到下一轮用户消息进来时才处理否则用户会明显感受到首 Token 延迟变高。4.2 长期记忆跨会话的知识沉淀长期记忆解决的是跨会话的信息保留问题。比如用户上周让你分析一份市场报告今天问你对某个结论的看法Agent 应该记得那份报告的内容。长期记忆的实现通常依赖外部存储比如向量数据库或传统的 KV 存储。在实践中我把长期记忆分为显性和隐性两种。显性长期记忆是用户明确要求记住的信息比如“以后每周一上午提醒我检查广告预算”这类信息适合用结构化存储记录在关系型数据库或 KV 存储里读取时直接查询即可。隐性长期记忆则是从历史会话中挖掘出来的用户偏好或行为模式比如用户经常关注华东区域的销售数据这类信息适合向量化存储通过语义检索来命中和引用。两类记忆的读取时机也不同。显性记忆在每次会话开始时就应该加载形成系统级上下文的补充隐性记忆则是在用户问题进来后先做一次向量检索命中相关内容再拼入任务级上下文。很多 Agent 项目把两类记忆混在一起存导致读取时要么漏掉关键内容要么检索出大量无关的噪音这就是设计初期没做好区分。4.3 工作记忆当前正在处理的数据集工作记忆这个概念是我在做复杂任务型 Agent 时引入的它指的是“当前这一轮执行中模型必须紧握不放的信息集”。打个比方你要算一笔账心算时你得把几个关键数字临时记在脑子里这些数字就是你的工作记忆。在 Agent 系统里工作记忆包含的内容有当前工具调用的输入参数、最近一次工具返回的原始结果、用户本轮问题中的关键实体和约束条件。工作记忆的特点是容量小但精度要求高需要完整保留不适合做摘要压缩。我曾见过一个失败的案例某 Agent 在调用数据库查询工具之前先把用户提供的表名压缩成了简称结果工具执行时传入的参数和实际表名对不上查了半天查不出数据最后才发现是摘要环节把关键参数给吞了。所以我的建议是工作记忆区域在上下文中做一个独立分区用明确的标记符与其他区域分隔开并且这个区域不做任何压缩或截断处理宁可让上下文窗口少放一些背景数据也要保证工作记忆的完整性。5. 上下文压缩策略详解摘要、Selective Context、结构化丢弃5.1 摘要压缩提取关键信息而不是流水账摘要压缩是上下文工程里最常用的手段但很多项目做出来的摘要质量一言难尽原因在于摘要的生成方式太随意。我在实践中摸索出一套“分角色摘要法”效果比整体摘要稳定不少。所谓分角色摘要就是把对话内容按角色拆分后再分别摘要。用户说了什么、助手说了什么、工具返回了什么分开来提炼而不是把整段对话丢给模型让它一口气总结。这样做的好处是不同角色的信息密度不同用户消息里的核心诉求、助手消息里的关键结论、工具返回里的重要数据提炼时使用的侧重点截然不同。用户消息侧重提炼意图和约束助手消息侧重提炼回复中的结论与承诺工具消息侧重提炼返回数据里的关键指标和异常项。分角色摘要之后还需要把摘要拼接回对话流保持时间顺序和逻辑关系。拼接时建议在摘要开头加一行生成时间戳方便后续出现信息冲突时判断哪个更新。这个方法看起来多花了一步但实际提升的是整个 Agent 在长对话中的连贯性值得投入。5.2 Selective Context让模型自己挑值得保留的信息Selective Context 是一种让模型参与上下文筛选的动态方法思路是在对话进行到一定阶段时让模型自己判断哪些历史信息对完成当前任务仍然重要然后只保留这些被标记为重要的信息。具体实现上我通常会在对话的第 N 轮比如 10 轮触发一次选取流程把当前全部历史消息整理成带编号的列表然后给模型一个指令“请从以上对话历史中选择对于完成用户当前目标至关重要的消息返回消息编号列表不要包含无关内容。”模型返回编号列表后程序再根据编号从历史消息中提取对应的完整消息内容拼接成压缩后的上下文。这个方法的效果挺好但也有一个问题模型在“信息选择”任务上偶尔会漏掉关键内容。所以我会加一道保险——在模型选择结果之外始终保留最近几轮对话的完整内容不管模型认为它们是否重要。这样做既利用了模型的筛选能力又避免了模型失误导致的上下文丢失。5.3 结构化丢弃明确哪些信息可以安全遗忘结构化丢弃和前两种方法思路相反它是从根上定义“哪些信息可以安全地被遗忘”不需要模型参与判断而是由开发者预先设定规则。这个思路在长流程 Agent 中特别有用。我在金融数据分析 Agent 里定义过一套丢弃规则原始工具返回的完整 JSON 数据在完成结果提取后即为可丢弃状态只保留提取后的关键指标中间过程的调试日志信息在写入日志文件后即丢弃不进入上下文已过期的时间敏感数据比如三个月前的行情快照在后续的分析中不再引入重复性信息比如用户连续三次询问相同问题时只保留最晚一次的问题和回答。结构化丢弃的优点是规则明确、执行成本低、不占用 Token。缺点是需要开发者对业务逻辑有深度理解才能准确判断什么信息可以安全丢弃。很多 Agent 项目不敢做结构化丢弃怕丢掉关键信息但实际上如果不主动丢弃上下文窗口迟早会被耗尽到时候丢的是更关键的信息。这个取舍要趁早想清楚。6. 多轮工具调用中的上下文流转LangGraph 实践复盘6.1 工具调用链中的上下文接力在真实的 Agent 应用中单次任务往往需要串行调用多个工具。比如做一个“对比上周与本周销售数据”的分析任务Agent 需要先调用查询接口拉取数据再调用计算接口做聚合最后生成分析结论。这里面每一环的输入输出都构成上下文交接的节点。实战中我习惯把工具调用链设计成“参数槽位”模式在任务级上下文中预定义好当前调用链中每个工具所需的参数槽位每个工具执行完就把返回结果的关键字段填入下一个工具的参数槽位。这个过程中上一个工具的完整返回不需要拼入下一个工具的上下文只需要把提取出的关键字段传递下去。这样的上下文接力设计有一个明显的好处模型在每一步看到的都是精简后的中间结果不会被上一次工具返回的几百行 JSON 干扰。同时因为槽位参数是明确写入到任务级上下文的模型不需要自己“回忆”上一个工具返回了哪些字段极大减少了幻觉的产生。在我自己的 LangGraph 项目里这个设计让工具链的执行准确率从原来的 70% 出头提升到了 90% 以上是上下文工程里投入产出比最高的一项改造。6.2 LangGraph State 设计把上下文工程落到代码里LangGraph 的 State 机制是承载上下文工程的最佳载体因为它天然把“流程状态”和“数据流向”绑定在一起。我常用的一个 State 定义长这样from typing import TypedDict, Annotated, Any from operator import add class AgentState(TypedDict): # 任务级上下文 current_goal: str task_progress: Annotated[list[str], add] constraints: list[str] artifacts: dict[str, Any] # 工作记忆 current_tool_inputs: dict[str, Any] last_tool_outputs: dict[str, Any] # 记忆分区 conversation_summary: str recent_messages: Annotated[list[dict], add] # 元信息 step_count: int token_used: int这里的 Annotated[list[str], add] 是 LangGraph 的归约器用法意思是每当有新元素写入该字段时不是覆盖而是追加。很适合用来累计 task_progress 和 recent_messages保证每一步执行后的状态都在积累而不是被后一步覆盖。这个 State 配一个压缩节点的用法是每执行 3-5 个节点后检查 State 中的 token_used 是否超过阈值如果超过触发一场压缩流程把 conversation_summary 替换成包含最新进展的分角色摘要并清理 recent_messages 中超出滚动窗口的部分同时把 last_tool_outputs 中已消费完的数据清空。这样整体上下文大小始终可控Agent 的执行速度和稳定性都保持在一个可接受的水平。6.3 工具返回数据的预处理与上下文注入时机工具返回数据什么时候注入上下文、以什么格式注入、注入哪些字段这些细节决定了模型对工具结果的利用效率。我的经验是工具返回数据尽量在调用模型之前完成提取和格式化而不是把原始返回原样注入。举个例子查询航班信息的工具返回了一个包含航班号、起降时间、价格、余票数量等字段的 JSON 数组用户的问题是“帮我找明天早上从北京到上海最便宜的航班”。如果把全部返回注入上下文模型需要自己从 20 个航班数据里找最便宜的这个过程容易出错。更稳妥的做法是在工具调用后加一个预处理节点用简单的代码逻辑先筛选出符合“明天早上、北京到上海”条件的航班按价格排序然后把排序后的前 3 个航班记录以自然语言描述注入上下文。def extract_cheapest_flights(tool_output: str, date: str, route: str) - str: # 工具返回是 JSON 字符串先解析再筛选 flights json.loads(tool_output) candidates [ f for f in flights if f[date] date and f[route] route ] candidates.sort(keylambda x: x[price]) top3 candidates[:3] # 转成自然语言描述注入上下文 lines [f符合条件的最便宜航班如下] for i, flight in enumerate(top3, start1): lines.append( f{i}. 航班号{flight[flight_no]} f{flight[dep_time]}起飞{flight[arr_time]}到达 f票价{flight[price]}元余票{flight[seats]}张 ) return \n.join(lines)这样做虽然多了一步代码逻辑但换来的是模型可以“直接阅读、直接回答”的上下文而不是再去做一层隐含的数据分析和筛选。上下文工程的一个关键原则就是能在代码里确定的事情不要在提示词里指望模型去确定。7. 上下文窗口的动态管理从固定窗口到自适应分配7.1 多分区上下文布局系统、任务、数据、记忆互不挤占当 Agent 逻辑变复杂上下文窗口里的内容会来自多个来源系统指令、用户输入、历史对话、工具返回、检索文档。如果这些内容不加分区地混合在一起模型每次都要自己理清“哪段是规则哪段是数据”效率和准确性都难以保证。我把上下文设计成四个物理分区每个分区有清晰的边界标记。分区一为系统指令区包含身份、准则、输出协议分区二为任务状态区包含当前目标、执行进度、约束清单分区三为工作记忆区包含本轮输入的关键实体、上一轮的输出摘要、当前需要处理的数据片段分区四为历史对话区包含压缩后的早期对话摘要和滚动窗口内的近期对话记录。在代码层面我会在拼接上下文时给每个分区加上不同的一级标记让模型一眼就能分辨信息的归属。比如[SYSTEM] 你是采购流程助理... [TASK STATE] 当前目标核对采购单 PO-2301 的审批状态 执行进度已完成供应商信息查询待执行审批记录查询 约束审批金额超过 50000 元需要额外部门负责人确认 [WORKING MEMORY] 采购单 PO-2301 当前状态待审批 供应商名称杭州云帆科技有限公司 审批金额68000 元 [HISTORY] [摘要] 用户先后查询了供应商资质和采购单基本信息... [近期消息] 用户请帮我确认这张采购单现在卡在哪个环节。实测下来带分区的上下文比混合式上下文在任务型 Agent 上的指令遵循度提升明显尤其在需要同时遵守多个约束的场景下。分区标记还能帮助模型在回答时引用正确的信息来源减少张冠李戴的情况。7.2 动态阈值切换Token 不足时的降级策略上下文窗口是有限的但任务复杂度和数据量是动态的。我开发了一套动态阈值切换策略当 Token 接近窗口上限时自动降级上下文内容保证核心任务不中断。降级策略分四档。第一档是舒适区Token 使用率低于 60%所有分区完整展示不压缩。第二档是收缩区Token 使用率在 60% 到 80% 之间历史对话区触发摘要压缩工作记忆区保持完整数据区只保留关键字段。第三档是紧张区Token 使用率在 80% 到 95%除了历史对话区压缩外数据区的数据由完整记录降级为统计摘要系统指令区的技能边界描述精简为要点列表。第四档是保命区Token 使用率超过 95%数据区只保留最近一次工具调用的输出任务状态区的执行进度只保留最后三步其余全部压缩系统指令区只保留身份和目标。这套策略我用一个简单的计数器实现每轮执行前统计已用 Token 数再根据阈值档位决定上下文的组装方式。实践证明动态降级比一刀切的“到 90% 就报错”要实用得多尤其在长流程工具调用中合理降级可以避免上下文溢出导致的整个任务失败。7.3 上下文预取与预热让关键信息提前就位另一个提高上下文利用效率的手段是预取与预热。预取是在用户问题尚未明确指向某个数据源时根据会话背景和用户画像提前把最可能要用的数据片段加载到缓存中。预热则是在新会话开始时主动把该用户相关的显性长期记忆以及最近一次会话的摘要加载进上下文让 Agent 在用户说出第一句话时就已经具备足够的背景。我在客服类 Agent 里的实践是用户会话建立后先查询用户的最近订单记录和售后工单状态把这两类数据预取到数据缓存区但先不注入上下文。等到模型判断用户的问题涉及订单或售后时再快速把缓存数据注入。这样做的好处是响应速度更快因为大部分数据检索时间被提前消耗掉首 Token 延迟显著降低。缺点是增加了无效请求的概率有些预取的数据最终没被用到浪费了部分检索资源。这个取舍要看具体业务场景如果是高价值用户或长流程任务预取的收益远大于浪费。在实际项目中我会用会话上下文里的一些信号来做预取决策比如用户历史会话中频繁提到的商品品类、一段时间内反复查询的报表类型等把这些信息存入用户画像表新会话建立时直接触发预取。长时间运行下来预取命中率能到 60% 左右整体体验的提升是很明显的。8. 长文本与 RAG 场景下的上下文工程检索增强不是万能的8.1 RAG 中的上下文陷阱检索结果不是越多越好很多团队引入 RAG 的初衷是解决长文档理解问题但实践中 RAG 经常遇到一个尴尬的局面检索出来的片段越多模型回答越差。原因不难理解——检索出来的片段里包含大量不相关或弱相关的内容模型被迫在噪音中找信号注意力被分散。我在 RAG 场景下的上下文工程核心思路是“检索后重排、重排后精选”。第一步用向量检索召回 Top-20 候选片段第二步用一个轻量的重排模型或规则方法对这些片段按与用户问题的相关度打分第三步只取重排后得分最高的 Top-5 片段并且每个片段做截断处理只保留与问题相关的部分超过长度限制的直接切掉不保留完整段落。这样做的依据是对于大多数问答任务模型真正需要的核心信息往往集中在几个关键片段里而不是满篇的泛泛叙述。上下文工程的精髓本来就是为了让模型把注意力聚焦在最重要的信息上RAG 的引入不应该改变这个原则反而需要更克制。8.2 长文档理解的分层上下文结构处理十万字级别以上的长文档需要一套分层的上下文结构而不是一次性把整个文档塞进去。我把长文档处理分为三层摘要层、索引层、原文段层。摘要层包含整个文档的分章节摘要用于回答“这篇文章大概讲了什么”之类的问题索引层包含各章节的小标题、关键术语及其出现的段落编号用于定位“某个话题在文中的哪个位置被讨论”原文段层则是具体段落或小节的完整内容只有在需要精确引用或深入分析时才注入上下文。这套结构的执行流程是用户问题进来先用摘要层做整体判断确定问题涉及的章节范围再通过索引层定位到具体段落位置最后从原文中截取对应段落经过程序的查重和裁剪后注入上下文。整个过程用户无感体感就是 Agent 想找什么就能找到什么。实测中这套结构处理招股书级别的长文档上下文使用量比全量灌入降低了 80%回答准确率反而有所提升。8.3 检索结果冲突时的上下文裁决机制长文本场景下很容易出现内容冲突比如不同章节对同一事件的描述不一致或者历史文档和最新文档的数据对不上。模型在这种冲突面前往往会自作主张地选择一边更麻烦的是它通常不会主动告诉你“这里存在冲突”。我在上下文工程设计里加入了冲突裁决机制在注入引用片段时程序会检查片段之间是否存在关键字段的冲突。比如一份文档说“项目预算为 500 万”另一份文档说“项目预算为 800 万”程序检测到数字冲突后不直接把两个片段同时注入而是让模型先进行冲突标注输出“发现数据冲突两份资料对项目预算的表述不一致分别为 500 万和 800 万”然后引导用户确认哪份资料是当前有效版本。这个机制的实现不复杂关键在于从系统指令层面就约束模型遇到冲突时必须显式指出来而不是悄悄选一个。我在系统级上下文中写了这样一条规则“当你从不同资料中提取到的信息存在明显不一致时必须首先指出差异不得自行选择其中一个作为唯一答案。”这条规则加上冲突检测代码能让 Agent 在数据矛盾场景下的表现专业很多至少不会给用户一种“它在糊弄我”的感觉。9. 多代理协作中的上下文传递从单脑到群体智能的跨越9.1 多代理架构下的上下文隔离与共享多代理架构是 Agent 应用发展的一个重要方向比如一个团队里同时有数据分析代理、文案生成代理、质量控制代理各司其职。多代理协作时上下文工程的问题从“一个模型怎么管理上下文”变成了“多个模型之间怎么传递上下文”复杂度上了不止一个量级。我实践后总结出两条基本原则上下文职责隔离、上下文共享显式化。职责隔离是指每个代理只保存自己的任务上下文不要把所有代理的历史对话都复制到每个代理里否则每个代理都要消化一份臃肿的全局上下文。共享显式化则指代理之间需要传递的信息必须经过明确的打包和解包过程而不是直接把整段上下文引用传递过去。举个例子数据分析代理完成分析后不会把 50 轮对话历史作为文本传给文案代理而是把分析结论整理成一份结构化的“任务交接包”包含分析结果摘要、关键数据指标、注意事项比如“数据口径为含税价格”文案代理只需要读交接包就能开始工作完全不需要了解数据分析代理中间经历了多少轮工具调用。9.2 主代理-子代理模式的上下文汇总机制在多代理系统里主代理负责统筹全局子代理负责具体执行。这种模式下主代理需要持续收集所有子代理的执行状态但如果每个子代理每执行一步就把新状态推送一次主代理的上下文很快就会被淹没。我采用的机制是“子代理汇报摘要、主代理按需深挖”。子代理在每次任务完成后不会直接向主代理发送完整过程而是发送一份结构化摘要内容包括任务完成状态、关键发现、请求的下一步指令。主代理维护一份子代理状态表记录每个子代理的最新摘要。只有当某个子代理出现了需要深入调查的异常时主代理才会主动向该子代理请求更详细的执行日志再决定是否将这部分细节注入上下文。这样做的好处是主代理的上下文压力大幅降低始终能看到“全局地图”而不是陷入某个子代理的细节泥潭。决策时的视野反而更清晰。多代理协作中最怕的就是主代理被局部细节牵着走丢了大局观而这个机制正好避免了这一点。9.3 多代理上下文一致性问题如何避免“左手不知道右手在做什么”多代理协作场景里一个高频翻车现场是“信息不同步”。采购代理在催供应商发货而财务代理还在等发票信息两个代理各自为战导致流程卡住。这个问题的本质是代理之间的状态共享没有做好。我在 LangGraph 里用公共状态池来解决这个问题。公共状态池存的是会话级别的全局共享信息比如采购单当前状态、正在等待的审批人、发票开具节点等。每个子代理在完成关键状态变更时必须先更新公共状态池再继续下一步。主代理和所有子代理在每轮执行开始时都会读取公共状态池的最新内容作为系统级上下文的补充。实现上我用一个 Redis 实例来存公共状态池键是会话 ID值是 JSON 格式的状态对象。子代理更新状态时使用事务操作防止并发写入导致状态覆盖。每次状态变更都带上时间戳读取时直接拉取最新版本。这套方案跑下来多代理之间的信息一致性明显改善“左手不知道右手在做什么”的情况大幅减少。10. 并发场景下的上下文工程流式请求与上下文隔离的实操经验10.1 Agent 并发架构中的上下文存储当一个 Agent 服务要同时服务多位用户时上下文就不能再放在进程内存里的单一变量里了必须有独立的上下文存储层。我在实践中的做法是引入三类存储来承载上下文的不同部分短期会话状态用 Redis适合快速读写和设置过期时间长期用户记忆用 PostgreSQL 或向量数据库存结构化画像和向量化的历史记忆工作流执行状态的持久化副本用对象存储或者数据库表用于任务中断后的恢复和审计。这个分层存储的好处是并发一旦上来内存不会因为承载上下文而爆掉。我之前见过一个项目上下文全部放在进程内存的字典里单机服务 20 个并发就频繁触发内存告警后来改造为 Redis 集中存储后同样的配置支撑了 200 个并发还游刃有余。Agent 的并发瓶颈往往不在模型推理而在上下文存储和管理的架构上。10.2 上下文隔离防止用户数据串线的底线并发场景下最可怕的事故就是用户数据串线。用户 A 的上下文被拼到了用户 B 的会话里轻则回答错误重则泄露隐私这在任何生产环境都是不可接受的。上下文隔离的第一道防线是所有上下文的读写必须带上 session_id 维度。Redis 的键设计成 agent:{session_id}:context系统提示词的组装也严格按照 session_id 从存储层拉取绝对不允许使用全局共享的上下文变量。第二道防线是在上下文组装完成后做一个完整性校验检查系统指令区、任务状态区、工作记忆区里的 session_id 是否一致不一致则直接拒绝执行。第三道防线是日志层面的脱敏与审计。每次模型调用前和调用后记录 session_id 和 Token 使用量但日志中不落具体用户敏感数据。这套三道防线设计看上去繁琐但对于生产级 Agent 应用来说是底线工程不能省略。10.3 流式请求下的上下文动态注入流式场景给上下文工程提出了新挑战模型在生成回答的同时新的上下文可能在不断产生。比如一个 Agent 在向用户流式转述分析结果时用户中途表达了新的需求新的需求必须被纳入到后续生成中但已经生成的流式文本不能回滚。我的处理方法是在流式生成过程中把用户的追加需求放入任务级上下文的“待处理队列”字段。当前这一轮的生成如果接近尾声模型可以自然接收新需求但如果新需求与当前生成内容存在逻辑冲突我倾向于先让当前生成完成在回答末尾明确提示“已收到您的新要求将在下一轮处理”然后在下一轮将新需求作为当前目标的一部分重新组装上下文。这种做法的好处是避免流式输出被中途打断造成内容割裂也让 Agent 的每一步输出都保持完整性。实测下来这种处理方式在用户体验上比“立即打断并重写”要好很多。用户看到的是 Agent 先完成了当前回答然后紧接着开始处理新需求过程流畅自然不会出现答非所问或者文章半途换主题的混乱感。11. 生产环境落地时的七个避坑建议与调试技巧11.1 上下文压缩的时机选择压缩时机是个容易被忽视的细节。我见过不少项目在每次模型调用前都做一次完整压缩流程导致响应延迟高得离谱。压缩本身也要消耗 Token 和计算时间频繁执行反而得不偿失。我建议的压缩触发条件是上下文 Token 数超过设定阈值的 70%且距离上一次压缩至少执行了 5 轮以上对话或者上下文 Token 数超过阈值的 90%必须立即执行压缩无论距离上一次压缩过了多少轮。这样既保证了上下文不过期膨胀又避免了无谓的压缩开销。11.2 摘要质量的隐式验证摘要压缩最容易出的问题就是“摘要失真”模型在总结时把用户原本的意图变了样。一条规则用户在后续对话中如果表示“我说的是另一个意思”且该轮对话前的摘要部分涉及相关话题要立刻用“撤回”的方式修正摘要。具体操作是将摘要中出错的条目标记为“已更正”并追加一条用户的最新澄清作为更高优先级的信息。这个隐式修正机制我实践后觉得非常管用。因为 Agent 的上下文工程永远不可能做到完美预测真正决定系统可靠性的往往是它能不能在错误发生之后快速修正。与其追求一次性完美的摘要不如在摘要机制里内置一条纠错路径。11.3 调试上下文工程的必备技能可视化追踪上下文工程依赖的调试方式和传统代码调试完全不同因为问题往往出在模型读到的那段拼接文本里而不是代码逻辑里。我强烈建议项目初期就搭好上下文追踪的可视化面板。我通常用 LangSmith 或 Langfuse 这类工具来记录每次模型调用的完整输入输出。排查问题时先找到异常回答对应的那次调用直接查看拼接完成后的完整上下文逐段检查“系统指令区是否被覆盖任务状态区是否过期工作记忆区是否有语义冲突历史摘要是否与最新对话矛盾”绝大多数上下文相关问题这样看一遍原始输入就能定位到原因。另一个实用技巧是在上下文的各分区之间加上不同的分隔标记追踪面板里一目了然。如果不加标记整段拼接文本长且杂人工排查时的阅读效率会低很多。11.4 上下文工程与 Token 成本的平衡最后一条是关于钱的。上下文工程做得越精细单次调用消耗的 Token 可能越少但处理逻辑变复杂后辅助流程如摘要生成、重排过滤本身的 Token 消耗会上升。我在项目里有一套成本记账机制为每一场会话建立一个 Token 账单记录模型调用的输入输出 Token、摘要压缩消耗、重排模型消耗等。一周结束后复盘看看各个模块的 Token 消耗占比。从我的经验来看合理的分布是模型主调用占 70% 左右摘要压缩占 15%重排和预取占 10%其他辅助流程占 5%。如果摘要压缩占比超过 25%说明压缩触发过于频繁需要下调压缩阈值如果数据预取占比过高说明预取命中率偏低需要优化预取决策逻辑。这个调优过程是持续进行的上下文工程不是一个一次性的搭建任务而是和业务一起演进的一项长期工程。我在实际运行中的体会是上下文工程没有一个“放之四海而皆准”的银弹方案每个业务场景的最优解都不同。但底层的方法论是通用的结构化分区、主动压缩、按需注入、显式追踪。把这几件事做扎实Agent 的整体表现会有质的提升。如果你的 Agent 项目也遇到了“单轮对话惊艳、多轮任务翻车”的情况不妨先把模型换掉的事放一放回到上下文这一层仔细排查一遍大概率能找到真正的病根。