ARTICLE DETAIL

资讯详情

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

AI Agent上下文工程实战:四层结构解决多轮对话失忆与幻觉

AI Agent上下文工程实战:四层结构解决多轮对话失忆与幻觉 1. 上下文工程到底在解决什么问题1.1 从一次线上事故说起去年冬天我接手了一个客服场景的 AI Agent 项目功能测试全部通过上线第三天开始间歇性胡言乱语。用户问“我的订单为什么还没发货”Agent 回复了一段关于退货政策的说明用户追问“那你帮我查一下物流”它开始背诵公司简介。排查了两天才定位到根因多轮对话累积到第 11 轮时历史消息把系统提示词挤出了有效注意力范围模型看到的“人设”和“规则”已经残缺不全。这不是模型能力问题是上下文管理问题。后来我把这个案例反复讲给团队新人听因为它精准地说明了上下文工程要解决的核心矛盾大语言模型LLM的上下文窗口是有限资源而真实业务对话的信息量是无限增长的两者之间的剪刀差就是所有诡异行为的温床。很多人把上下文工程等同于“写提示词”这是最大的误解。提示词工程关注的是“怎么把一句话说清楚”上下文工程关注的是“在有限的 token 预算内如何动态组织模型每一步能看到的全部信息”。前者是静态的文本技巧后者是动态的系统设计。一个 AI Agent 能不能扛住真实业务八成取决于上下文工程做得好不好而不是模型选得多贵。1.2 上下文窗口不是越大越好现在主流模型的上下文窗口动辄 128K、200K甚至标称 1M token。新手容易产生一个错觉窗口这么大把所有东西塞进去不就行了实测下来完全不是这么回事。第一个问题是成本。API 按 token 计费每次请求都把 10 万 token 的历史全量传过去单次调用成本可能是精简后的 20 倍。一个日活几千的 Agent一个月烧掉的钱够买台服务器。第二个问题是注意力稀释。业界有个被反复验证的现象叫“迷失在中间”Lost in the Middle当上下文很长时模型对开头和结尾的信息记得牢中间部分容易被忽略。你把 50 条历史消息一股脑塞进去关键的那条用户偏好可能正好落在被忽略的区间。第三个问题是延迟。输入 token 越多首 token 响应时间越长。用户等 8 秒才看到第一个字体验直接崩盘。所以上下文工程的第一性原则是不是能塞多少而是该塞多少。把每一次模型调用都当成一次精打细算的预算分配这才是工程化的思路。1.3 上下文工程的四层结构我把一个成熟 AI Agent 的上下文拆成四个层次这个分层是我踩了无数坑之后总结的后面所有实操都围绕它展开层级内容变化频率典型 token 占比系统层人设、规则、工具定义、输出格式约束极低10%-20%记忆层用户画像、长期偏好、历史摘要低10%-15%状态层当前任务进度、已确认信息、待办事项中15%-25%对话层近期原始消息、检索到的知识片段高40%-60%这个比例不是拍脑袋定的是我在多个项目里通过日志分析统计出来的经验值。系统层和记忆层相对稳定可以配合缓存机制降低成本状态层和对话层是动态的需要每轮重新组装。理解了这四层你就知道该在哪里做优化、哪里做取舍。2. 核心机制拆解上下文是怎么被组装的2.1 一次请求的完整生命周期要理解上下文工程得先搞清楚从用户发消息到模型返回结果中间到底发生了什么。我以最常见的 Agent 架构为例把流程拆成六步接收输入拿到用户这一轮的消息做基础清洗去多余空格、统一标点。意图识别用一次轻量调用或规则判断用户想干什么决定后续走哪条处理路径。记忆检索从向量库或结构化存储里捞出与当前意图相关的历史信息。上下文组装按四层结构拼接成最终的 prompt这一步是核心。模型调用把组装好的上下文发给 LLM拿到回复或工具调用指令。状态更新把本轮的关键信息写回记忆层更新任务状态。新手最容易忽略的是第 4 步和第 6 步。第 4 步决定了模型看到什么第 6 步决定了下一轮模型能看到什么。这两步做不好Agent 就会表现得“记性差”或者“前后矛盾”。2.2 系统提示词的稳定性设计系统层是整个上下文的锚点它的设计原则是稳定优先、精简至上。我见过太多项目把系统提示词写成两千字的小作文结果每轮都占掉大量预算还容易被后续内容稀释。我的做法是把系统提示词拆成“不变部分”和“可变部分”。不变部分是 Agent 的身份、核心规则、输出格式这部分内容固定可以配合 prompt caching 机制大幅降低成本。可变部分是当前场景的临时约束比如“本轮用户情绪激动回复要更温和”这部分按需注入。一个精简的系统提示词模板长这样你是[角色]服务于[业务场景]。 核心规则 1. [规则一] 2. [规则二] 输出要求[格式约束] 可用工具[工具列表及简要说明]注意工具定义这块很多人把每个工具的完整 JSON Schema 都塞进系统提示词几十个工具下来光定义就上万 token。更聪明的做法是按需加载先给模型一个工具名称列表等它决定要用某个工具时再动态注入该工具的详细参数说明。这个技巧在工具数量超过 10 个时效果立竿见影。2.3 记忆层的检索与压缩记忆层是上下文工程里技术含量最高的部分。它要解决两个问题存什么和取什么。存什么方面我的经验是不要什么都往向量库里塞。原始对话直接入库会导致检索质量极差因为口语化的表达和用户真正关心的信息点往往对不上。正确的做法是在每轮对话结束后做一次信息抽取把结构化的事实存进数据库把需要语义检索的内容做摘要后存进向量库。取什么方面纯向量相似度检索在 Agent 场景下经常翻车。用户说“还是用上次那个方案吧”向量检索根本不知道“上次那个方案”是什么。这时候需要结合时间衰减和实体关联近期记忆权重高与当前对话实体相关的记忆优先召回。我常用的记忆检索打分公式是这样的score 0.5 * 语义相似度 0.3 * 时间衰减因子 0.2 * 实体匹配度时间衰减因子用指数衰减半衰期设成 7 天左右具体数值要根据业务调整。实体匹配度就是看记忆里提到的实体和当前对话的实体有没有重叠。这个公式不复杂但比纯向量检索稳得多。2.4 状态层的任务追踪状态层是让 Agent 显得“有脑子”的关键。没有状态层的 Agent每轮对话都是失忆的用户得反复重复已经说过的信息。状态层我一般用一个结构化的 JSON 对象来维护包含三个字段confirmed已确认的信息、pending待确认的信息、progress任务进度。每轮对话后更新这个对象下一轮组装上下文时把它序列化成自然语言注入。举个例子一个订机票的 Agent状态层可能是这样{ confirmed: {出发地: 北京, 目的地: 上海, 日期: 下周三}, pending: {舱位等级: null, 是否需要接机: null}, progress: 已收集基础信息待确认舱位 }注入到上下文时转成一句话“当前任务进度用户已确认从北京飞上海日期下周三还需确认舱位等级和接机需求。”模型看到这句话就知道该问什么、不该重复问什么。2.5 对话层的裁剪策略对话层是最占预算的部分也是最需要动态管理的部分。我的裁剪策略分三档最近 N 轮保留原文N 一般取 3 到 5保证短期连贯性。较早对话做滚动摘要每积累 5 轮做一次摘要摘要本身也会随对话推进不断合并更新。超长对话做分段归档把很久以前的内容压缩成“话题标签 关键结论”需要时再展开。这里有个细节很多人做错摘要不是简单地把对话缩短而是要保留决策依据和未解决问题。用户说“不要红色的”摘要里必须体现这个否定约束否则后面模型可能又推荐红色。我一般要求摘要必须包含三类信息已确认的事实、已排除的选项、待解决的疑问。3. 实操落地从零搭一套上下文管理系统3.1 技术选型与整体架构假设你要用 Python 搭一个中等规模的 AI Agent我推荐的技术栈是这样的编排框架LangChain 或 LangGraph前者适合线性流程后者适合有分支和循环的复杂 Agent。向量库本地开发用 Chroma生产环境用 Milvus 或 Qdrant。结构化存储PostgreSQL存用户画像和任务状态。缓存Redis缓存系统提示词和热点记忆。模型接入通过统一的 API 网关层调用方便切换不同厂商的模型。选 LangGraph 而不是纯 LangChain 的原因是上下文组装本身就是一个有状态、有分支的流程用图结构表达更清晰。比如“如果用户情绪负面走安抚路径否则走正常路径”用条件边实现比 if-else 堆在代码里优雅得多。3.2 上下文组装器的实现核心的组装器我一般写成一个独立的类职责单一接收各层的数据输出最终的 prompt。这样做的好处是方便测试和调优你可以单独替换某一层的策略而不影响其他部分。class ContextAssembler: def __init__(self, token_budget8000): self.token_budget token_budget def assemble(self, system, memory, state, dialogue): # 按优先级分配预算 budget self.token_budget parts [] sys_text self._render_system(system) parts.append(sys_text) budget - self._count_tokens(sys_text) mem_text self._render_memory(memory, max_tokensint(budget * 0.3)) parts.append(mem_text) budget - self._count_tokens(mem_text) state_text self._render_state(state) parts.append(state_text) budget - self._count_tokens(state_text) dia_text self._render_dialogue(dialogue, max_tokensbudget) parts.append(dia_text) return \n\n.join(parts)这段代码的关键在于预算分配的顺序。系统层优先级最高先分配记忆层和状态层次之对话层拿剩下的。如果对话层预算不够就触发裁剪逻辑。这个顺序不能反否则可能出现系统提示词被挤掉的灾难。3.3 Token 预算的精确计算预算分配不能靠猜得精确计算。不同模型的 tokenizer 不一样中文和英文的 token 比例也差很多。我的经验值是中文大约 1 个字对应 1.5 到 2 个 token英文大约 1 个单词对应 1.3 个 token。实际项目中我会用对应模型的 tokenizer 库来精确计数比如 tiktoken。但每次组装都精确计数有性能开销所以我会做一个近似计数 精确校验的两级机制组装时用快速估算组装完做一次精确校验超了就触发裁剪。这里有个坑要提醒不要把 token 预算用满。我一般留 15% 的余量因为模型输出也要占 token而且不同版本模型的 tokenizer 可能有细微差异。把预算卡到 100%很容易在边界情况翻车。3.4 记忆写入的时机与格式记忆写入的时机很讲究。太频繁会拖慢响应太稀疏会丢信息。我的做法是异步写入主流程返回回复后把记忆抽取任务丢进消息队列由后台 worker 处理。这样用户感知不到延迟记忆也能及时更新。写入格式上我坚持结构化优先。能用数据库字段存的绝不塞进向量库。比如用户的姓名、订单号、偏好设置这些是精确查询能搞定的放向量库纯属浪费。只有那些需要语义理解的模糊信息比如“用户对物流速度比较敏感”才值得做向量化。3.5 多轮对话的摘要触发机制摘要什么时候触发我的经验是不要按固定轮数触发而是按token 增长量触发。当对话层的 token 数超过预算的 80% 时触发一次摘要把最老的一半对话压缩掉。摘要的 prompt 也要精心设计不能简单说“总结一下”。我用的模板是这样的请从以下对话中提取 1. 已确认的事实用户明确表达的信息 2. 已排除的选项用户明确拒绝的内容 3. 待解决的疑问尚未有答案的问题 4. 用户情绪倾向正面/中性/负面 用简洁的条目输出不要遗漏任何否定性约束。这个模板的重点是第 2 条和第 4 条。否定性约束最容易在摘要中丢失而情绪倾向直接影响后续回复策略。4. 常见问题与排查技巧实录4.1 Agent 前后矛盾怎么排查前后矛盾是上下文工程最典型的故障。排查思路是逐层核对先看系统提示词有没有被挤掉再看记忆层有没有正确召回最后看状态层有没有更新。我遇到过一个案例Agent 在第三轮说“已为您记录”第五轮又问“请问您要记录什么”。查下来是状态层更新了但没持久化下一轮读的是旧状态。这类问题的通用排查方法是打印每轮实际组装的完整 prompt对比模型看到的内容和你的预期差在哪里。4.2 响应越来越慢的优化路径响应变慢通常有三个原因上下文越来越长、检索越来越慢、模型调用次数越来越多。优化顺序建议是先看上下文长度如果每轮都在增长说明裁剪策略没生效。再看检索耗时向量库的索引没建好会导致检索随数据量线性变慢。最后看调用次数有些 Agent 一轮对话调了五六次模型能合并的尽量合并。我一般会在日志里记录每轮的 token 数、检索耗时、模型调用次数做成监控面板。数据一可视化瓶颈在哪一目了然。4.3 常见问题速查表现象可能原因排查方向忘记早期约定对话层裁剪过度检查摘要是否保留否定约束重复询问已答信息状态层未更新核对状态写入逻辑回复风格突变系统提示词被稀释检查系统层 token 占比检索结果不相关记忆写入质量差检查信息抽取环节首 token 延迟高上下文过长统计输入 token 数工具调用参数错误工具定义不完整检查动态注入逻辑4.4 几个反直觉的实操心得第一个心得少即是多。我做过 A/B 测试把上下文从 6000 token 砍到 3000 token回复质量反而提升了。原因是关键信息密度提高了模型注意力更集中。不要迷信“信息越多越好”。第二个心得格式比内容重要。同样的信息用清晰的分段和标题组织比一大段文字效果好得多。模型对结构化输入的解析能力明显更强。我习惯用【系统】、【记忆】、【状态】这样的标记分隔各层模型能准确区分信息来源。第三个心得给模型留退路。当上下文里信息不足时明确告诉模型“如果信息不足请直接询问用户不要猜测”。这句话能大幅降低幻觉率。很多项目出问题就是因为模型在信息不全时硬编答案。第四个心得定期做上下文审计。我会每隔一段时间把线上真实的 prompt 抽样出来人工看一遍经常能发现自动化测试覆盖不到的问题比如某个字段一直是空的、某段记忆从来没被召回。这种人工审计的价值自动化监控替代不了。上下文工程这件事说到底是在有限的资源里做无限的权衡。模型能力会越来越强窗口会越来越大但“在正确的时间给模型正确的信息”这个核心命题不会变。把四层结构理清楚把预算分配做精细把记忆管理做扎实你的 Agent 就能从“玩具”变成“工具”。我在实际项目里最大的体会是与其花时间研究最新的模型参数不如把上下文组装的每一行代码都打磨到位后者带来的体验提升往往更直接、更稳定。
返回列表