免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

上下文工程实战:Agent多轮对话信息管理与压缩策略

上下文工程实战:Agent多轮对话信息管理与压缩策略 1. 上下文工程到底在解决什么问题1.1 从一个真实翻车现场说起去年我帮一个团队调他们的客服 Agent模型用的是当时口碑很不错的一个开源大模型Prompt 写了整整两页角色设定、语气要求、业务边界、输出格式全都写清楚了。单轮测试的时候效果很好回答准确、语气得体。但一上生产环境就出问题用户聊到第三轮Agent 开始忘记自己刚才承诺过什么聊到第五轮它把上一个用户的问题和当前用户的问题混在一起回答聊到第八轮它干脆把系统提示里的核心规则给忘了开始自由发挥。团队一开始以为是模型能力不够换了更大的模型问题依旧。后来怀疑是框架的锅换了一套 Agent 框架重写还是老样子。最后我们坐下来把每一轮实际发给模型的完整请求打印出来看才发现真正的问题每一轮对话我们都在往上下文里无脑追加历史消息到第八轮的时候上下文里塞了将近六千字的内容其中大量是重复的寒暄、已经解决了的旧问题、以及和当前问题无关的中间过程。模型不是变笨了是被我们喂了太多垃圾信息注意力被稀释了。这就是上下文工程要解决的核心问题。它不是写一个更好的 Prompt而是管理模型在每一次推理时能看到的全部信息——包括系统指令、历史对话、工具调用结果、检索到的文档、当前用户输入以及这些内容以什么顺序、什么格式、占多少空间出现在上下文窗口里。1.2 上下文工程和 Prompt 工程的区别在哪很多人把这两个概念混着用我觉得有必要掰扯清楚。Prompt 工程关注的是怎么把一句话说清楚。比如你让模型做情感分类是写“请判断以下评论是正面还是负面”还是写“你是一个情感分析专家请对用户评论进行三分类”这属于 Prompt 工程的范畴。它的优化对象是单次输入的文本表述。上下文工程关注的是在有限的上下文窗口里放什么、不放什么、怎么放。它要处理的问题包括历史对话保留几轮、超出窗口后怎么压缩、工具返回的长结果怎么截断、多个信息源之间怎么排优先级、系统指令放在开头还是结尾效果更好、检索到的文档片段怎么去重和排序。它的优化对象是整个上下文的结构和内容策略。打个比方Prompt 工程像是你给下属写一张便签告诉他做什么上下文工程像是你管理一个下属的整个工作台决定他桌上放哪些文件、哪些文件放最上面、哪些文件该归档、哪些该扔掉。便签写得再好桌上堆满了无关文件他也干不好活。1.3 为什么现在必须重视这件事三个现实原因。第一上下文窗口再大也不够用。现在主流模型动辄 128K 甚至 200K 的上下文窗口看起来很大但实际用起来很快就不够了。一个 Agent 如果接了搜索工具、数据库查询工具、代码执行工具每次工具调用返回的结果可能就有几千字。几轮下来窗口就满了。而且窗口越大推理成本越高延迟越大这不是免费的。第二长上下文不等于好效果。业界已经有很多实测表明模型在超长上下文中的信息检索能力并不是均匀分布的。关键信息放在中间位置模型很容易忽略这就是所谓的“迷失在中间”现象。你把所有东西都塞进去模型反而抓不住重点。第三Agent 场景天然需要多轮、多工具、多信息源。单轮问答不需要复杂的上下文工程但 Agent 要规划、要调工具、要观察结果、要反思、要继续行动每一步都在往上下文里加东西。没有一套管理策略上下文会迅速膨胀成一团乱麻。2. 上下文的核心组成与优先级排序2.1 一个 Agent 的上下文里到底有什么我把一个典型 Agent 单次推理时的上下文拆成六层从优先级高到低排列层级内容类型典型长度是否可压缩优先级1系统指令与角色设定200-1000字不可压缩最高2当前用户输入10-500字不可压缩最高3工具定义与调用格式说明500-3000字可精简高4最近几轮对话历史500-3000字可摘要中高5工具调用返回结果100-5000字可截断中6检索到的外部知识500-8000字可筛选中低这个排序不是拍脑袋来的。系统指令和当前用户输入是模型必须完整看到的丢了就出大问题。工具定义决定了模型能不能正确调用工具但可以通过精简描述来压缩。对话历史保留最近几轮通常够用更早的可以摘要。工具返回结果往往很长但只有一部分有用可以截断或提取关键字段。检索知识最灵活可以按相关性筛选后只放最相关的几条。2.2 为什么优先级排序这么重要因为上下文窗口满了之后你必须决定丢什么。如果没有明确的优先级常见的错误做法是“先进先出”——把最早的消息丢掉。但最早的消息里往往包含系统指令和初始任务描述丢掉它们等于让模型失去了行动纲领。我见过一个团队的做法是系统指令永远保留当前用户输入永远保留工具定义永远保留然后剩下的空间按“最近优先”填充对话历史和工具结果。这个策略简单但有效因为大多数 Agent 任务中最近的交互和当前问题最相关。更精细的做法是给每一层分配一个预算。比如总共 8000 token 的上下文预算系统指令和用户输入占 1000工具定义占 1500对话历史占 2000工具结果占 2000检索知识占 1500。超出预算的部分按优先级裁剪。这个预算可以根据任务类型动态调整——如果是知识问答类任务检索知识多分一点如果是多轮对话类任务历史多分一点。2.3 系统指令的位置有讲究系统指令放在上下文的什么位置效果差别很大。我实测下来放在最开头是最稳的因为模型对开头和结尾的信息注意力最集中。但有些场景下把最关键的约束条件在上下文末尾再重复一遍能显著降低模型违反规则的概率。比如你要求 Agent“绝对不能透露内部价格”只在开头写一遍聊到第五轮它可能就忘了。但如果在每一轮上下文的末尾都追加一句“提醒不得透露内部价格”遵守率会明显提升。这个技巧叫“指令重锚定”代价是每轮多花几十个 token但换来的是行为稳定性很划算。3. 上下文压缩的四种实战策略3.1 滑动窗口最简单但最容易踩坑滑动窗口就是只保留最近 N 轮对话更早的直接丢掉。实现简单一行代码的事。但它有个致命问题如果用户在第三轮说了一个关键信息比如“我的订单号是 12345”到第十轮才用到滑动窗口早就把它滑没了。我的做法是滑动窗口加一个“关键信息提取”步骤。每轮对话结束后用一个小模型或者规则引擎从这轮对话里提取出实体和关键事实存到一个独立的“记忆区”。滑动窗口只负责保留最近的对话流记忆区负责保留跨轮次的关键信息。每次构造上下文时把记忆区的内容以简短列表的形式附在系统指令后面。3.2 摘要压缩用模型来省模型的空间当对话历史太长时用一个便宜的小模型把前面的对话压缩成一段摘要。比如把十轮对话压缩成三百字的摘要保留关键决策、用户偏好、已解决的问题丢掉寒暄和重复确认。这里有个坑摘要本身也会丢信息。我试过让模型摘要结果它把用户明确说过的“不要推荐红色款”给漏掉了导致后面推荐了红色款。解决办法是在摘要 Prompt 里明确要求“必须保留用户的所有否定性约束和明确偏好”。另外摘要不要一次性做而是滚动进行——每新增几轮就更新一次摘要而不是等窗口满了才从头摘要。3.3 工具结果截断只取需要的字段工具调用返回的结果经常是一大坨 JSON里面大部分字段 Agent 根本用不到。比如查天气的 API 返回了温度、湿度、风速、气压、紫外线指数、日出日落时间但 Agent 只需要温度和天气状况。这时候在工具层就做截断只把需要的字段传给模型能省大量空间。更激进的做法是让模型自己决定要什么字段。在工具定义里说明“返回结果包含以下字段”然后模型在调用时指定需要哪些字段工具层按需返回。这个方案实现成本高一些但对上下文空间的节省非常明显。3.4 检索结果重排与去重别把整个文档库塞进去RAG 场景下检索回来的文档片段经常有大量重复内容。比如你检索“退款政策”返回的五个片段里三个都在说同一件事。这时候需要做去重和重排只保留信息量最大的两三条。我常用的做法是先按向量相似度召回 Top 20然后用一个交叉编码器重排取 Top 5再做一次去重计算片段之间的相似度超过阈值的只保留一条最后把剩下的片段按相关性从高到低排列放入上下文。这样能把检索部分的 token 消耗降低一半以上同时不损失关键信息。4. 工具调用场景下的上下文管理4.1 工具定义怎么写才不浪费空间工具定义是上下文里很容易被忽视的 token 消耗大户。一个 Agent 如果接了十个工具每个工具的描述写两百字光工具定义就两千字。而且这些定义每一轮都要重复发送累积起来非常可观。精简工具定义的原则只说模型不知道的不说模型能猜到的。比如一个查询天气的工具你不需要写“这个工具用于查询天气输入城市名称返回温度”模型看到工具名get_weather和参数city就知道怎么用了。你需要写的是模型猜不到的信息比如“城市名称必须用英文”“返回温度单位是摄氏度”“只支持中国城市”。另外工具描述里的示例要克制。给一个典型示例就够了给三个示例纯属浪费。如果工具参数复杂用 JSON Schema 的 description 字段做字段级说明比在工具描述里写一大段自然语言更省空间也更准确。4.2 工具调用结果的格式化技巧工具返回结果给模型看的时候格式很重要。同样的信息用紧凑的格式比用松散的格式能省不少 token。比如返回一个用户列表松散的 JSON 是这样的{ users: [ {name: 张三, age: 28, city: 北京}, {name: 李四, age: 32, city: 上海} ] }紧凑的格式可以写成用户列表: 张三(28,北京), 李四(32,上海)信息完全一样token 数差了一倍多。模型对紧凑格式的理解能力完全没问题没必要为了“好看”而浪费空间。4.3 多工具并行调用时的上下文组织当 Agent 需要同时调用多个工具时返回结果的排列顺序会影响模型的推理。我试过把搜索结果、数据库查询结果、计算器结果混在一起返回模型经常搞混哪个结果对应哪个工具。后来改成按工具调用顺序分组每组前面加一个明确的标记比如[搜索结果] ...内容... [数据库查询结果] ...内容... [计算结果] ...内容...这样模型能清楚知道每段内容的来源推理准确率明显提升。如果工具之间有依赖关系比如先查数据库拿到 ID 再调另一个接口那就在上下文里保留这个调用链的痕迹让模型知道“这个结果是在那个结果的基础上得到的”。5. 上下文工程的常见翻车场景与排查5.1 模型突然“失忆”了最常见的症状前面几轮明明说过的信息后面模型完全不记得。排查步骤第一步打印完整上下文。不要只看你传进去的消息列表要看实际发给模型的完整请求体。很多框架会在你不知情的情况下做截断或重排。第二步检查截断策略。是不是滑动窗口把关键信息滑掉了是不是摘要的时候漏掉了是不是工具结果太长把历史挤掉了第三步检查信息位置。关键信息是不是被放在了上下文的中间位置试试把它移到开头或结尾。第四步检查格式。关键信息是不是被埋在一大段文字里试试用列表或加粗标记把它突出出来。5.2 模型开始“胡言乱语”症状模型开始编造不存在的信息或者把不同来源的信息混在一起。这通常是上下文里存在矛盾信息导致的。比如检索结果里有一条说“退款需要 7 天”另一条说“退款需要 3 天”模型不知道该信哪个就可能自己编一个“退款需要 5 天”。解决办法是在上下文里明确标注信息的时效性和来源可信度或者干脆在放入上下文之前就做冲突检测只保留最新或最可信的那条。5.3 工具调用格式错误症状模型输出的工具调用格式不对解析失败。这通常是工具定义和上下文里的示例不一致导致的。排查方法检查工具定义的 JSON Schema 和你在上下文里给的调用示例是否完全一致。我踩过的坑是工具定义里参数名是user_id但示例里写的是userId模型就跟着示例走了。另外如果上下文里历史工具调用记录很多模型可能会模仿历史记录里的格式所以历史记录里的格式也必须是对的。5.4 上下文长度超限报错这个最直接但排查起来也有讲究。不要只看总 token 数要分项统计系统指令多少、工具定义多少、历史多少、工具结果多少、检索结果多少。找出占比最大的那一项针对性优化。我常用的一个快速诊断方法是把上下文按来源分成几块每块单独算 token然后看哪块最大。十有八九是工具结果或者检索结果超了因为这两块最不可控。症状可能原因排查动作解决方向模型失忆关键信息被截断或位置不佳打印完整上下文检查截断策略调整优先级关键信息前置或后置模型胡言乱语上下文存在矛盾信息检查多来源信息是否冲突冲突检测标注来源可信度工具调用格式错定义与示例不一致对比 Schema 和示例统一格式清理错误历史记录长度超限某类内容占比过大分项统计 token 占比压缩占比最大的部分6. 我总结的一套上下文工程实操框架6.1 三层记忆结构我把 Agent 的记忆分成三层工作记忆当前这一轮推理直接需要的上下文包括系统指令、当前用户输入、最近两轮对话、当前工具定义。这层是必须完整保留的。短期记忆最近十轮对话的摘要、最近五次工具调用的关键结果、当前任务相关的检索片段。这层按相关性筛选后放入。长期记忆跨会话的用户偏好、历史任务记录、领域知识库。这层不直接放入上下文而是通过检索按需调入。每轮推理时工作记忆全量放入短期记忆按预算放入长期记忆按相关性检索后放入。这样既保证了核心信息的完整性又控制了上下文长度。6.2 动态预算分配不要给所有任务用同一套预算。我根据任务类型做了几套预设对话类任务历史占 40%工具结果占 20%检索占 20%其他占 20%。知识问答类任务检索占 50%历史占 20%工具结果占 15%其他占 15%。工具操作类任务工具定义和结果占 50%历史占 25%检索占 10%其他占 15%。这些比例不是固定的可以根据实际效果微调。关键是有一个明确的预算框架而不是让上下文随意膨胀。6.3 上下文健康度检查我在生产环境里加了一个上下文健康度检查环节每次构造完上下文后自动检查几项指标总 token 数是否超过预算的 80%系统指令是否完整保留当前用户输入是否完整保留是否存在重复内容相似度超过 0.9 的片段关键实体是否在上下文中出现任何一项不通过就触发告警人工介入调整。这个检查帮我提前发现了很多潜在问题避免了很多线上翻车。7. 一些零散但实用的经验关于上下文工程还有一些不成体系的体会但都挺实用一并写出来。不要迷信大窗口。我试过把 200K 窗口全部填满效果反而不如只填 20K 精挑细选的内容。窗口大不代表效果好信息密度比信息总量重要得多。给模型留“思考空间”。上下文里不要塞得太满留出 10%-20% 的余量。模型在生成回复时也需要空间来做推理上下文太满会影响生成质量。格式一致性比格式美观重要。上下文里所有内容的格式要统一不要一会儿用 JSON 一会儿用 YAML 一会儿用自然语言。格式切换会让模型消耗额外的注意力来解析结构。测试要覆盖边界情况。不要只测正常流程要测上下文刚好满的时候、关键信息刚好被截断的时候、工具返回超长结果的时候。这些边界情况才是最容易出问题的地方。记录每一次翻车。我维护了一个“上下文翻车日志”每次线上出问题就记下来什么症状、什么原因、怎么解决的。积累了几十条之后发现大部分问题都是重复的有了日志之后排查效率高了很多。上下文工程这个方向说到底就是一句话在正确的时间把正确的信息以正确的格式放在正确的位置。听起来简单做起来全是细节。但正是这些细节决定了一个 Agent 是能用还是好用。
返回列表