news 2026/10/22 4:20:00

AI Agent上下文工程实战:从多轮对话失忆到稳定决策的完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AI Agent上下文工程实战:从多轮对话失忆到稳定决策的完整指南

如果你做过几个正经的 AI Agent 项目,迟早会撞上一堵墙:模型明明够聪明,工具也接上了,提示词改了十几个版本,可 Agent 一跑多轮就“失忆”,该调的工具不调,不该编的答案瞎编。我在连续踩了两个月坑之后才慢慢反应过来,问题大多不是提示词写得不够花,而是上下文压根就没做对。

先说清楚这句话什么意思。提示词当然重要,但提示词只是“表层指令”。Agent 真正干活靠的是模型窗口里的那堆信息——用户说了什么、之前做过什么、工具返回了什么、系统规定有哪些约束、当前任务处于什么阶段。这些东西组成了上下文,它才是 Agent 行为的真正底座。提示词写得再好,只要上下文缺失、混乱或超时,模型就是一个没有记忆的临时工,你布置任务再清晰它也干不利索。

这篇文章我想把这段时间摸出来的经验完整记录下来,包括我踩坑的过程、上下文设计的具体拆解、一次完整任务的实操笔记、以及一套可复用的排查方法。适合正在做 Agent 开发、接 API 搞工作流、或者被多轮对话效果不稳折磨得想骂人的同学参考。

1. 先说说我怎么从“疯狂调提示词”走到“上下文工程”的

1.1 调提示词调到怀疑人生的那段经历

我的第一个 Agent 项目是做一个内部知识库问答助手,需求不复杂:用户问问题,Agent 从文档库里检索片段,再用大模型组织回答。刚开始效果还挺好,因为单轮问答、检索内容直接塞进 Prompt 就能用。后来需求升级成“多轮对话 + 主动调用工具 + 跨会话记忆”,噩梦就开始了。

当时我的第一反应是“提示词没写清楚”。于是开始一轮又一轮地优化 system prompt:加上角色设定,加输出格式约束,加所谓的“思维链示范”,加禁止幻觉的警告,甚至给模型写了一篇小作文解释它应该怎么一步步思考。每个版本单独测试效果都不错,一放进真实的多轮流程里,依然频繁出错。

印象最深的一个场景:用户在第二轮问“刚才那份报告里的数据帮我做个表”,Agent 完全忘了第一轮说的“报告”是指哪份文档,重新检索了一个不相关的内容,然后一本正经地给出了错误表格。我后来检查日志才发现,模型收到的上下文里,第一轮的对话摘要被截断了,因为我把整段历史对话原样拼进 Prompt,超过了窗口长度。当时我还没来得及骂模型笨,先被自己蠢到了。

1.2 转变的关键:我不再只改“文字”,而是重新设计“信息输入结构”

后来我拿同一个出错的例子去做对照实验:一组只是继续打磨提示词,一组重新整理上下文。结果让我非常意外——只是把第一轮的对话摘要从“丢掉”变成“结构化保留”,并把当前任务目标放到上下文最前面,模型的准确率直接从 61% 提升到 83%。同样的模型、同样的提示词、同样的工具,唯一的变量就是上下文的结构和组织方式。

从那天起我开始把 Agent 的开发重心从“写提示词”挪到“设计上下文”。我不再问“这段 prompt 写得是不是不够优雅”,而是问“模型此刻看到了什么、缺了什么、什么信息是多余的”。这几乎是一种思维范式转变。提示词只是上下文的一部分,它负责“怎么回答”,而上下文里更大量的信息——用户原始输入、中间推理、工具返回值、历史记忆——负责“回答什么”和“凭什么这么回答”。

简单打个比方:提示词是开会时主持人说的一段开场白,上下文是会议桌上摆着的所有资料和此前几轮发言记录。开场白再精彩,桌上没有资料、前面的话都丢了,会议照样开不下去。这一点,后来被我自己在多个项目里反复验证了。

2. 上下文到底在“上下文”什么:四个维度的拆解

2.1 别把上下文理解成“把历史聊天记录拼进Prompt”

很多人做 Agent 第一直觉就是把所有东西拼到一个大 Prompt 里,你以为模型全能记住,但实际效果非常差。原因在于大模型对长上下文的注意力是有限的,尤其在多轮、多工具调用的场景里,真正影响决策的往往是最开头和最结尾的内容——中间一大段看得多、用得上。直接拼接历史消息,是一种“物理上塞进去了,逻辑上没被用上”的假上下文。

我现在的做法是把上下文的构建拆成四个独立的层次,每一层解决一类问题,组合起来才是完整的决策上下文。

2.2 四个维度缺一不可,组合不当就会“只见树木不见林”

第一个维度是系统级上下文。这是最稳定的一层,包含 Agent 的角色定义、边界约束、行为准则、输出规范。它不随对话轮次变化,相当于给模型发了一本“员工手册”。系统级上下文决定模型“以什么身份、按什么规矩做事”,这层如果写得不够严谨,后面几层再完整也容易被带偏。注意,它不是传统的“提示词模板”,而是要为动态任务留出结构化接口,比如把当前任务状态、可用工具列表做成变量在运行时注入。

第二个维度是会话级上下文。就是当前这轮对话里用户说了什么、上一轮 Agent 回答了什么、用户对上一轮回答做了什么纠正。这层信息决定模型能不能“接着话头往下说”。这里的难点在于“全量保留”和“摘要压缩”的平衡。我的经验是:最近两轮对话保留完整原文,更早的对话做结构化摘要,而不是一字不差地塞进去。因为时间一长,完整原文里大量寒暄、重复表达、无关细节会稀释真正关键的意图信息。

第三个维度是任务级上下文。这是我在初学阶段最容易忽略的。Agent 在执行一个多步骤任务时,模型需要知道“当前处于整个任务流程的哪个阶段”“已经完成了哪些步骤”“下一步打算做什么”。没有这层信息,你看到的就是模型每次都开一个全新的脑回路。任务级上下文建议用一个独立的状态结构体来维护,比如一个 JSON 对象,明确记录当前步骤数、已完成动作、待办列表、关键中间结果。每次调用模型前,把这个状态体序列化进上下文里。

第四个维度是事实级上下文。这是模型回答具体问题的依据——从知识库检索到的片段、工具返回的数据、数据库查询结果、上游系统传过来的业务字段。这层信息直接决定回答“靠不靠谱”。事实级上下文的关键是新鲜度和来源标注:每条事实应该能溯源,至少说明是来自哪个文档、哪个接口、哪次查询,这样模型就有依据判断“该信什么、拿什么当证据”。

2.3 上下文“失效”的三个深层原因:漂移、稀释、错位

我后来反思那些翻车案例,本质上就是这三个问题在作怪。

上下文漂移指模型在长流程中逐渐偏离初始设定和目标。原因通常是初始目标只出现在第一轮提示里,后续轮次的上下文没继续携带它。你让 Agent“写一篇关于X的文章”,第二轮你补了“刚才的结论再展开一下”,它可能就奔着 Y 去了。防止漂移的办法是按轮次加固核心目标——每一轮拼接上下文时都把当前目标重新提炼一次,放在模型注意力最集中的位置。

上下文稀释是指关键信息被大量低价值内容淹没。比如把整个对话历史、十页文档、全套工具 schema 全塞进去,模型注意力有限,关键指令被挤到看不见的位置。这点我用过一个非常形象的实验来理解:在上下文末尾加一个极其显眼的无关指令,模型往往会执行那个无关指令,而中间真正该执行的反而被忽略。这就是典型的稀释效应。稀释没有完全的解决方案,只能靠“剪枝 + 排序”把高价值信息前置、管理好占用预算。

上下文错位指信息之间逻辑矛盾或者时间错位。比如系统提示里说“不要编造数据”,但事实层检索回来的片段恰好过时了;再比如上一步工具返回成功,但任务状态没更新,模型误以为工具还没调用。错位问题一般出在你拼接上下文的代码里,而不是模型本身。所以排查这类问题最有效的方式是:把你拼出来的完整上下文打印出来自己读一遍。

这里给一个我自己认为很关键的结论:控制上下文效果的核心手段不是“写得更长”,而是“排得更对”。信息的位置、彼此的关系、谁在前谁在后,直接影响模型的注意力分配。上下文工程的一半工作,是在做信息架构设计,而不是写作。

3. 实操笔记:一次完整 Agent 任务的上下文设计与流转

3.1 定义具体场景:一个跨多轮、带工具调用的客服场景

为了不空谈理论,我拿一个我做过的实际场景来完整走一遍流程。场景是:某个产品的售后服务助手,用户第一轮反馈“我的设备连不上WiFi”,第二轮说“刚才的方法试了没效果”,之后 Agent 需要调用一个“设备状态查询”工具,根据返回结果推荐进一步操作。

这个场景涵盖了上下文设计里几乎所有关键要素:多轮对话记忆、用户意图跟踪、工具调用编排、工具结果整合、最终答复生成。而且它能清晰展示如果上下文做得不对,模型会在哪一步开始犯傻。

3.2 关键步骤一:开局前的系统级上下文设置

开工前我会先定义系统级上下文。它不是一段写死的文字,而是一个模板,按需动态注入。

system_context = f""" 你是某设备品牌的售后技术支持助手。 你的任务是根据用户描述、设备信息和工具查询结果,给出清晰、可执行的故障排查建议。 行为准则: - 只能依据用户提供的信息和工具返回结果作答,禁止编造诊断结论。 - 每给出一个排查步骤,都要说明“为什么要这么做”,让用户理解逻辑。 - 如果用户反馈步骤无效,不要重复相同建议,给出升级路径。 当前支持的工具: - check_device_status(device_id): 查询设备在线状态、信号强度、固件版本。 当前会话目标: {goal} # 每一轮运行时动态注入 设备信息: 设备ID: {device_id} 历史处理记录摘要(如有): {session_summary} """

这里我想重点说一个经常被忽略的细节:系统级上下文的最后几行,一定要是“与当前任务直接相关的变量信息”。模型对结尾内容的注意力通常很强,把目标、设备信息放在末尾,其实是在引导模型把注意力落在眼前具体要处理的事上。注意把“可用工具”列出来也非常重要,模型只有知道了有哪些工具、工具是干什么的,才可能在推理过程中合理规划调用。如果你的 Agent 经常“明明有工具却不用”,先检查上下文里工具清单是否存在,而不是怪模型笨。

3.3 关键步骤二:每一轮请求的动态上下文组装

处理每一轮用户输入时,我建议做一次上下文重组,而不是直接把上一轮的完整消息列表丢进去。这一步是上下文工程的“临门一脚”,做不好就前功尽弃。

def build_request_context(state, user_input, tools_result=None, recent_history=None): goal = extract_current_goal(state, user_input) # 任务级上下文:当前阶段、已做步骤、下一步计划 task_state = { "当前阶段": state.current_phase, "已完成步骤": state.completed_actions, "下一步计划": state.next_actions } # 会话级上下文:最近两轮完整保留 + 更早压缩摘要 session_context = { "最近对话原文": recent_history[-2:], "更早对话摘要": state.session_summary } # 事实级上下文:工具返回结果 + 来源标注 facts = [] if tools_result: facts.append({ "来源": "check_device_status接口", "查询时间": tools_result.query_time, "返回内容": tools_result.data }) # 最终上下文按照“任务状态 -> 对话上下文 -> 事实依据 -> 系统约束”的顺序拼接 final_context = f""" ### 当前任务状态 {json.dumps(task_state, ensure_ascii=False)} ### 对话上下文 {json.dumps(session_context, ensure_ascii=False)} ### 工具返回的事实依据 {json.dumps(facts, ensure_ascii=False) if facts else "本轮未调用工具"} ### 系统规则与输出要求 {system_context} """ return final_context

这段代码看着简单,但背后有几个经过验证的原则。第一,不要省掉任务状态。模型不是在“聊天”,它是在完成一个流程,任务状态让它知道自己走到哪了。第二,最近对话保留原文、旧对话只留摘要,是为了既保留“连续性”又不被“细节噪音”淹没。第三,工具结果单独成段而不是混在对话里,是为了让模型能明确区分“用户说的”和“系统查询到的”,减少幻觉依赖。

我当时做完这个结构调整后,最直观的感受是:Agent“乱来”的次数少了一大半。以前隔三差五出现的“东拉西扯”现象,多半是因为模型根本没有“当前进行到哪一步”这个信息,只能靠猜。

3.4 关键步骤三:工具调用返回后的上下文整合

工具调用在 Agent 机制里有它的特殊地位。我见过很多新手设计方案时,把工具返回结果简单拼在对话末尾就完事。效果很飘,因为模型分不清哪段是“用户说的话”和哪段是“系统的客观返回”。

我的处理方式是用代码块或结构化的 JSON 把工具返回结果包起来,同时还要加上一句类似“以下内容为工具返回的客观数据,请基于事实回答,不要编造”的边界说明。这不是玄学,它是在引导模型区分“信息来源类型”,这种区分对减少幻觉很有效。

tool_result_block = f""" <tool_output> 工具: {tool_name} 调用状态: success 返回数据: {json.dumps(tool_response, ensure_ascii=False)} </tool_output> 注意:以上数据是对外部系统的客观查询结果。如果数据缺失或与用户描述不一致,请如实指出,继续排查,而不要强行解释或编造一个合理理由。 """ # 然后把这段拼到事实级上下文的区域 request_context = merge_context(task_state, session_context, tool_result_block, system_context)

为了让大家感受到这个“边界提示”的威力,我分享一个对比数据:同一套模型和工具,不加上面的边界说明时,当工具返回“设备信号强度正常”但用户说“连不上WiFi”时,模型有接近 40% 的概率会替用户找借口,比如回复“可能是运营商问题”这种没有任何依据的话。加上边界说明后,模型直接指出“信号强度显示正常,但用户仍连接失败,说明问题可能出在设备认证层面”,并给出了合理的升级路径。上下文怎么组织,直接影响模型的“因果归因”。

3.5 token 预算估算与窗口分配经验

做上下文工程不能回避 token 预算。我给一个我自己经常用的分配经验,以 8K 上下文窗口为例:

内容区域预算占比说明
系统级上下文10%-15%角色、规范、工具说明,不宜过长
任务级状态5%-10%结构化 JSON,保持精简
会话级上下文20%-30%最近对话原文(2轮)+ 早期摘要
事实级上下文30%-40%工具返回、检索内容、数据库结果
模型输出预留15%-20%留给回复生成空间

分配表只是起点,关键是养成“先算后写”的习惯。我写任何一段要进上下文的文字前,都会先估一下 token 量级。比如“设备信息历史处理记录摘要”这种,如果摘要超过 500 token,我就得考虑是不是摘要方法出了问题。上下文不是无限黏土,你的每一段塞进去的内容都在抢占其他内容的预算。

工具 schema 的写法也很占 token。现在很多模型 API 里工具参数都要求 JSON Schema 格式,写得完整但冗长。我的优化策略是:不把全部工具一次性传给模型,而是做“工具分组预选”。根据当前任务阶段从工具库里选 2-3 个最可能用到的,把它们的 schema 放进上下文。这样既省钱又减少模型选择困难症。这个技巧在我项目里把 token 平均消耗降低了约 25%,效果非常明显。

4. 上下文出问题的典型症状与排查手册

4.1 五个我常遇到的“症状”和它们背后的上下文原因

很多 Agent 的“行为诡异”其实有共性。我把这几年遇到的归类成五个:

症状一:Agent 答非所问,回答内容跟用户当前问题完全不在一个频道上。原因大概率是会话级上下文被压缩过度,最近一轮的关键信息被删掉了。解决办法是检查摘要逻辑,确保最近轮次完整保留。

症状二:Agent 反复给出同一个建议,无论用户怎么反馈。原因很可能是任务级状态没有更新,“已完成步骤”还是空列表,模型以为自己是第一次回答。查一下 state 里的 completed_actions 有没有在工具返回后被正确追加。

症状三:调了工具但结果没用上,回答里看不到工具数据的痕迹。原因基本是工具返回结果在拼接时被放到了上下文末尾,模型注意力分配不到,或者它根本分辨不出那是工具返回的。解法是把工具返回整合到事实级上下文区域,并加上边界提示。

症状四:早期用户需求被遗忘,对话到后半程“跑题”。这就是上下文漂移,需要把核心目标在每一轮的上下文里重新提炼一次。

症状五:模型生成的每一步操作都没有依据、逻辑混乱。原因往往是事实级上下文里缺少来源标注,模型不知道哪些话有据、哪些话是猜。给事实加来源标注能明显改善。

4.2 链路追踪排查法:把上下文打印出来,自己当“人肉模型”读一遍

排查上下文问题最高效的方式不是猜测,而是人肉模拟模型。具体步骤我之前实践下来很可靠:

第一步,把 Agent 某一次出错的完整调用日志导出来,翻译成“你在执行前看到了什么”。包括系统上下文、任务状态 JSON、对话历史摘要、工具返回结果、用户当前输入。这一步能定位 80% 的问题:信息缺了什么、顺序不对、哪条是假的。

第二步,把自己当模型读一遍这份上下文,看能不能得出和 Agent 相同的错误结论。如果能,说明上下文设计有问题;如果不能,才考虑是不是模型能力限制。这个判断标准非常重要,它能把大量“模型智障”的模糊抱怨,转变成具体可修的上下文缺陷。

第三步,针对发现的问题,用最小复现场景去验证。不要改完所有东西再测试,一次只改一个变量,比如把对话摘要从“全文”改成“结构化摘要”,然后单独对比效果。这样你才能真正通过实验归因,而不是靠感觉。

4.3 上下文日志记录:为 Agent 建立“黑匣子”

最后提醒一个很容易被忽略的点:上下文工程离不开可观测性。你必须给 Agent 的每次调用加上日志记录,把拼接之后的完整上下文输出到日志文件里。否则出了问题根本没法查。

我自己的项目里,有个简单的 LogContext 工具代码:

import json import logging def log_context(tag, context: dict): logging.info(f"--- context snapshot: {tag} ---") for key, value in context.items(): logging.info(f"### {key}: {json.dumps(value, ensure_ascii=False)[:2000]}")

这段代码会在每次调用大模型前,把本轮上下文的每个组成部分分别记录。日志里能直接看到:哪一轮任务状态是什么、工具结果有没有拼接进去、系统提示有没有被截断。排查问题从“猜”变成“看”,效率提高不止一个档次。

另外关于长会话管理,我想分享一个因为我偷懒吸取的教训:早期做长会话时,我图省事直接把前几轮摘要放在上下文里,结果摘要本身会失真、丢失关键细节。后来我改成“分层摘要机制”:每两轮生成一个小结,每十轮把小结再汇总成上一级摘要,这样不同层级的摘要服务于不同粒度的任务需求。这个做法在上下文压缩任务里实战表现非常好。好的上下文工程往往是由这些“笨功夫”堆出来的,它不花哨,但每一项都能直接提升效果下限。

5. 一点过来人的心得

做 Agent 这段时间,最大的体会是:提示词工程和上下文工程不是对立关系,而是主次关系。提示词定的是“怎么回答”,上下文定的是“回答什么、凭什么回答”。当你的 Agent 出现各种不明原因的“笨拙表现”时,先别急着怀疑模型智商,把你的上下文打印出来,通读一遍,你会发现大部分问题就明晃晃地写在里面。

我个人的一条操作建议,也是我现在做每个 Agent 任务默认的动作:任何一段要进入模型窗口的信息,先问自己三个问题——它是什么类型(系统任务对话工具)?它应该放在哪个层级?它在这个层级里占多少 token?想清楚这三个问题,Agent 的行为稳定性会肉眼可见地提升。

如果你正在被 Agent 的多轮对话效果折磨,不妨先停下来做一件事:把你最近一次出错调用所看到的完整上下文导出、通读、像审稿一样去审视它。大多数情况下,那个让你崩溃的“弱智行为”,病因就在那堆上下文里。把上下文做对,你的提示词才能发挥它该有的价值。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/22 4:19:57

macOS虚拟机安装最佳方案:从工具选型到性能调优实战汇总

macOS虚拟机安装最佳方案汇总经常有朋友问我&#xff0c;想在macOS里跑一个干净的测试环境&#xff0c;或者临时开一台macOS虚拟机做点编译、自动化验证&#xff0c;到底怎么搞最省事。这个事我前前后后折腾了小半年&#xff0c;踩了不少坑&#xff0c;也试过好几套不同的方案&…

作者头像 李华
网站建设 2026/10/22 4:19:32

多模型AI助手桌面端聚合:Cherry Studio配置与知识库实战

用过一阵子各种网页版AI之后&#xff0c;我的桌面终于变成了这个样子&#xff1a;写代码的时候开着一个对话窗口&#xff0c;查资料的时候切到另一个&#xff0c;翻译一段文字又得换一个页面&#xff0c;电脑稍微开久了&#xff0c;光是浏览器标签就密密麻麻排了一整排。我一直…

作者头像 李华
网站建设 2026/10/22 4:18:41

CUDA独立运行期模块:边界、架构与故障排查实践

1. 模块定位&#xff1a;先搞清楚“独立运行期模块”指什么1.1 这一讲的背景&#xff1a;为什么反复强调“独立”如果你让一个刚接触 GPU 编程的同学说说 CUDA 是什么&#xff0c;大概率会得到“NVIDIA 的并行计算框架”“用来写核函数的语言”这类回答。但很少有人在意一个更基…

作者头像 李华
网站建设 2026/10/22 4:18:40

大模型API厂商能力对比:技术选型与避坑实战指南

做AI应用开发的人&#xff0c;迟早会撞上一个绕不开的问题&#xff1a;项目要接大模型API&#xff0c;到底选哪家。市面上能提供的厂商少说也有十几家&#xff0c;每一家都把自己的模型能力说得天花乱坠&#xff0c;跑分一个比一个高&#xff0c;价格一个比一个低。可真到了要把…

作者头像 李华
网站建设 2026/10/22 4:18:14

LLM上下文工程:模型注意力如何决定Agent成败

做Agent有一个特别容易被低估的环节&#xff1a;上下文工程。这个词听起来像是把提示词调出花样的进阶版&#xff0c;但实际做下来你会发现&#xff0c;它决定了一个Agent是“看起来聪明”还是“真的能干”。我最早丢掉幻想&#xff0c;是在一个很简单的场景里&#xff1a;让模…

作者头像 李华
网站建设 2026/10/22 4:17:43

一次硬盘数据丢失后的完整救援与归档复盘

2024年12月5日&#xff0c;我给这场突发事故起了个名字——“遗失的数字”。那天下午我本来只想从移动硬盘里找一份旧项目素材&#xff0c;结果发现一个以年份命名的文件夹还在&#xff0c;但里面的子目录只剩下一堆乱码空壳&#xff0c;实际文件少了一大半。数字资产的丢失不像…

作者头像 李华