🦾 AI Agent 🟡 进阶 ⏱ 10 分钟

🧠 上下文管理怎么设计?多轮对话 & Agent

模型没有记忆,所谓多轮就是每次都得把历史重新打包。算预算 → 挑内容 → 排顺序,把打包做得聪明。

1

一句话定义 + 上下文由什么组成

🎯 一句话定义

上下文管理 = 在有限的 context window 里,每轮调用前对可用信息进行筛选、压缩、排序,组装出最终送给模型的 messages。

模型本身无状态,所谓「多轮」就是你每次都得把历史重新打包。

上下文管理的全部工作,就是把这个打包过程做得聪明。

🧩 一次调用含哪几块

① System Prompt:角色设定、行为约束、工具定义
② 历史对话轮次:用户说了什么、模型回了什么
③ 工具调用 & 结果:Agent 场景的函数记录和返回
④ 检索/外部注入:RAG 召回片段
⑤ 当前用户输入:这一轮最新消息

上下文管理的核心操作:对 ②③④ 做裁剪和压缩,确保总量不超窗口,且最重要的信息排在最好的位置。

2

裁剪策略:留什么、丢什么

💰 Token 预算制

每次调用前先算账:

可用预算 = 窗口上限 − System Prompt − 当前输入 − 预留输出

剩下的才是历史 + 工具结果 + 检索内容的共享额度。超了就必须砍。

🪟 滑动窗口(最基础)

保留最近 N 轮原文,更早的丢弃或降级为摘要。

N 取决于场景:客服对话 5-10 轮,代码 Agent 3-5 轮(因为代码片段长)。

🏆 重要性评分

给每条历史打分,综合:
• 新鲜度:越近权重越高
• 类型:工具结果 > 用户关键指令 > 闲聊
• 是否被引用:后续提到的权重更高
• 用户标注:说「记住这个」的优先保留

按分数从高到低填进预算,填满为止。

策略核心思想适用
Token 预算制先算账再裁剪所有场景的基础
滑动窗口留最近 N 轮客服、通用对话
重要性评分按分填预算信息价值差异大时
3

压缩策略:怎么缩小体积

📝 摘要压缩

早期对话即将被挤出窗口时,用 LLM 压成紧凑摘要。

• 阶段性摘要:每 N 轮或任务节点生成一次
• 递归摘要:摘要太长再对摘要做摘要

摘要建议结构化:[用户目标] → [已尝试方案] → [当前卡点] → [待确认事项],比自由文本省 token 且信息密度高。

🧰 工具结果精简

Agent 场景最易爆窗口的环节。一个 API 返回几百行 JSON,不能原样塞进历史。

• 只提取关键字段(状态码、核心数据、错误信息)
• 超长结果截断 + 提示「完整结果已存储,需要时可再调」
• 或让模型生成一个「工具结果摘要」替换原始返回

🗂️ 结构化状态对象

任务型 Agent 维护一份独立的 JSON 状态(current_step, slots, decisions),每轮更新。

放在 System Prompt 之后作为固定区块,让模型始终知道「现在在哪、还差什么」。

比从对话历史里推断进度更可靠、更省 token。

4

排序策略:放在哪

模型对上下文不同位置的注意力不均匀——开头和结尾利用率最高,中间容易被忽略(lost-in-the-middle 现象)。

位置放什么原因
最开头(System Prompt 后)结构化状态、核心约束模型一定会仔细看
靠前高度相关的检索结果、关键背景primacy 效应
中间远期摘要、次要历史接受低利用率
靠后近期 2-3 轮原文、当前输入recency 效应,对末尾最敏感
5

Agent 场景的额外考量

🔌 工具调用的上下文隔离

工具调用和结果不该跟对话历史混在同一个 messages 里平铺。

• 工具调用请求作为独立的 tool_calls 结构传递
• 工具结果走单独通道,由上下文管理器决定是否压缩后再展示给模型
• 避免一个长工具返回把后面的对话挤出去

🌿 分支 / 回溯支持

用户可能说「回到刚才那个方案」。

若只维护一条线性历史就没法回溯。

方案:
• 维护一棵对话树,每个节点是一步
• 用户回溯时从那个节点 fork 出新分支
• 上下文编译器按当前分支路径组装 messages

🔁 失败重试的上下文差异

重试同一轮时,不要完全复用上一次的上下文。

至少把上次模型输出的错误部分去掉或标记为「此路不通」,

避免模型重复犯同样的错。

6

工程落地的关键点

🔍
上下文可观测

每一轮到底塞了什么、占多少 token,必须有日志。出问题才能定位是「塞少了」还是「塞多了」。

📊
预算告警

接近窗口上限时主动通知,而不是等到 API 报错。

🛟
降级策略

极端情况(工具返回异常大),宁可丢信息也要保证调用成功——返回降级提示而非直接崩。

🧪
测试用长对话

用自动化脚本跑 50+ 轮对话,验证压缩和裁剪在极限情况下的表现。

🎬 一句话总结:上下文管理就三件事
① 算预算
窗口上限 − System Prompt − 当前输入 − 预留输出 = 可用额度
② 挑内容
裁剪(留什么丢什么)+ 压缩(怎么缩小体积)
③ 排顺序
重要信息放开头和结尾,利用 primacy/recency 效应

记忆系统是另一个话题,它的产出会作为输入之一进入这个流程;但上下文管理本身的职责就是管好「这一轮模型能看到什么」。

一句话总结

上下文管理 = 在有限 context window 里,每轮调用前对可用信息做筛选、压缩、排序,组装出最终 messages。三件事:① 算预算(窗口−System−当前输入−预留输出)② 挑内容(裁剪:滑动窗口/重要性评分;压缩:摘要/工具结果精简/结构化状态)③ 排顺序(重要放头尾,利用 primacy/recency)。Agent 场景要隔离工具上下文、支持分支回溯、失败重试别复用错误上下文。

🎤 面试问答 · 口语化回答
上下文管理是 Agent 工程面试的高频深挖题,面试官想看你有没有真的做过长对话产品:
面试官多轮对话的上下文管理一般怎么设计?
你

核心思想是每轮调用前先算预算,再筛选、压缩、排序。模型本身无状态,所谓多轮就是每次都要把历史重新打包。预算上:可用额度等于窗口上限减去 System Prompt、当前输入、预留输出空间,剩下的才是历史加工具结果加检索内容的共享额度。然后对历史做裁剪和压缩,最后按重要程度排序,最重要的放开头和结尾,因为模型对这两端利用率最高,中间容易被忽略。

💡 加分点:一上来就点出「模型无状态,多轮就是重新打包」这个本质,再讲预算→裁剪→排序三步,结构非常清晰。
面试官Agent 场景里工具返回结果很大,占 token 怎么办?
你

这是 Agent 最易爆窗口的环节。我的做法是工具结果不能原样塞进历史,要做精简:只提取关键字段,比如状态码、核心数据、错误信息;超长结果截断并提示完整结果已存储需要时可再调;或者让模型生成一个工具结果摘要替换原始返回。还要注意工具调用和结果别跟对话历史混在同一个 messages 里平铺,走单独的通道,避免一个长返回把后面的对话挤出去。

💡 加分点:能主动提到「工具结果精简 + 上下文隔离」两个点,说明你在真实 Agent 项目里踩过这个坑。
面试官用户说「回到刚才那个方案」,上下文怎么支持回溯?
你

如果只维护一条线性历史,这种回溯是做不到的。我一般维护一棵对话树,每个节点是一步;用户回溯时,从那个节点 fork 出新的分支;上下文编译器根据当前分支路径来组装 messages。这样既保留了历史路径,又能支持分支回溯。另外重试同一轮时我不会完全复用上次的上下文,会把上次模型输出的错误部分去掉或标记为此路不通,避免模型重复犯同样的错。

💡 加分点:能讲出「对话树 + fork 分支」而不是只靠线性历史,说明你处理过复杂的 Agent 交互场景。
📝 读完打卡 · 写下你的收获 读完了?来打卡吧
用一句话写下你从这篇学到的最大收获,检验自己是否真的懂了 👇
⏱ 00:00 🔥0分