Appearance
Q55 · 超长多轮对话如何兼顾不断档和节省 Token?
用户与售后助手聊了 30 轮:先问订单 A123 的耳机是否能自动退款,又聊了设备偶发杂音、换货和其他话题。现在用户只说:“那副耳机还能检测吗?”如果助手只保留最后一句,就不知道“那副耳机”是哪笔订单;如果把 30 轮消息、每次工具返回的长文本都送给模型,每轮都会重复消耗 token,还可能超出这次调用的容量。所谓“不断档”,是让助手带着当前问题真正需要的旧信息继续,而不是把全部旧文字反复重放。
本例的日期和政策均为教学假设,不是商家的真实承诺:咨询日为 2026 年 9 月 25 日,可信订单工具确认 A123 于 9 月 20 日签收、已拆封;用户自述“耳机偶发杂音”,故障原因尚未核实。现行政策 v3 规定“签收后 7 天内且未拆封可自动退款;签收后 30 天内可申请质量问题检测,具体维修或退货需按检测与订单结果确定;已拆封不自动排除质量问题检测”。所以本轮能说的是:A123 目前仍可申请检测,但不能保证退款,杂音是否属于故障要核实。这和先前“已拆封不符合自动退款”并不矛盾。
设计上要同时照顾两件事:完整历史如何安全留存,以及本次模型调用实际看见什么。OpenAI 的对话状态文档说明上下文窗口是单次请求容量,输入、输出及适用模型的推理 token 都可能占用它;即使用会话 ID 串起多轮,先前内容也不会变成免费的无限记忆。LangChain 的短期记忆文档把裁剪和摘要列为处理长会话的常见方法,并提醒完整历史即使放得下也可能增加成本、延迟和无关干扰。OpenAI:Conversation state、LangChain:Short-term memory
术语与符号
| 术语或符号 | 零基础解释 | A123 例子中的对应物 |
|---|---|---|
| 一轮对话 | 通常指用户一次发言及助手随后的处理或回复 | 用户问“能退吗”,助手答复一次 |
| token | 模型计量输入、输出的单位,不等于汉字数 | 聊天、工具结果、政策条款都要占容量 |
| 上下文窗口 | 一次模型调用可处理内容的总容量,具体规则随模型变化 | 当前问题、历史片段、政策和回答的共同空间 |
| 对话历史 | 按时间保存的原始消息与相关工具记录 | 30 轮售后往来 |
| 本次上下文 | 应用为眼前问题实际送给模型的材料 | 最近消息、A123 事实卡、政策 v3 |
| 裁剪 / 滑动窗口 | 只保留最近若干消息或一定 token 内的消息 | 最近 4 轮原话 |
| 摘要 / 压缩 | 用较短文字表达较早对话中仍重要的信息;可能漏掉条件 | “先前已讨论自动退款,但未执行退款” |
| 事实卡 / 结构化状态 | 把易错关键事实分字段保存,标明来源与时效 | A123 已拆封,来源为订单工具 |
| 短期记忆 | 同一会话或执行线程内保存的状态和历史 | 这 30 轮的主题、近期消息 |
| 长期记忆 | 跨会话可按身份与权限取回的信息 | 用户偏好或旧售后案例索引 |
| 检索 | 按当前问题从档案中找相关内容 | 若本轮信息不够,从旧案例取回 A123 记录 |
W / H | W 是教学假设的本次总容量;H 是扣掉其他内容后留给历史与记忆的空间 | 下文假设 W=8,000,算出 H=3,750 token |
| 来源与时效 | 某条事实从哪里来、何时核实,便于重新检查 | 订单工具 9 月 25 日确认拆封;政策 v3 当日有效 |
保存原始历史与送模时的裁剪不是同一操作:应用可以按业务和隐私要求留有完整记录,但每次只选择必要片段组成请求。若在状态里永久删除消息,之后可能无法回查;若仅对本次输入做裁剪,旧消息仍可留在受控存储。LangChain 文档把“调用前 trim”与“从图状态中 delete”分为不同做法。LangChain:Trim / Delete / Summarize messages
让本轮读得懂,又不装下 30 轮

图从左向右读:长卷代表完整 30 轮记录,中间只挑出近期原文和有来源的 A123 事实卡;底下的旧对话档案可按需取回相关记录;右侧本次上下文还放入“还能检测吗?”以及刚核实的现行政策 v3。图中的 Token 预算条表示容量约束,没有画某个真实模型的固定规格。过去的对话不因未送给模型就自动消失,但模型只依据实际进入本次上下文的内容回答。
第一层是保留近期原话。这里可以保留最近几轮用户与助手消息,尤其本轮“那副耳机”前后的指代和刚发生的纠正;具体保留几轮应由 token 预算和任务测试决定,不是永远固定 4 轮。裁剪要以完整消息和工具交互为单位:有的模型接口要求工具调用消息与对应工具结果配对,切在中间会让历史无效或产生误解。系统或开发者指令也应由应用按当前规则提供,不能因它们较早就当作普通旧聊天丢掉。LangChain:删除消息的有效性要求
第二层是把早期关键事实从叙事里单列出来。这张事实卡可以是:
| 字段 | 本例保留的值 | 来源与注意 |
|---|---|---|
case_id | A123 耳机售后 | 已登录用户有权访问的订单;不是凭模糊指代猜出 |
signed_on | 2026-09-20 | 订单工具于 9 月 25 日查得;本轮必要时复核 |
opened | true | 订单工具确认;不能摘要成“未拆封” |
reported_issue | “偶发杂音” | 用户自述,不能写成“已检出故障” |
prior_action | 先前解释过自动退款条件;未执行退款 | 来自对话/操作记录,保留“未”字 |
事实卡是压缩时的保真锚点,不是永不更新的真相。如果用户随后明确说“刚才说错了,是 A124”,要更新当前指代并重新查 A124;不能把 A123、A124 合成同一订单。订单状态和政策又可能变化,历史卡片只告诉应用“过去核实过什么”,做新的资格判断仍应查当前权威来源。对用户自述、工具核实、模型推断分别标明身份,能减少摘要把不同可信度的信息混在一起。LangGraph:Memory overview
第三层是对较早、低精度的信息写短摘要,例如“此前已比较自动退款与质量检测;用户更关心当前能否送检;没有发起退款操作”。摘要用于维持话题连贯,不能替代拆封限制、日期、政策版本等决定性条件。若旧对话很长,可在达到阈值时压缩较早部分并保留近期消息;LangChain 当前提供 SummarizationMiddleware 做这类处理。OpenAI Responses API 也有可选的压缩机制,它返回携带先前状态的压缩项;该项是不供人直接阅读的内部表示,所以需要审计的业务事实仍宜由应用单独维护。两者是不同平台的实现选项,均不能保证摘要绝不失真。LangChain:Summarize messages、OpenAI:Compaction
第四层是按需取回旧记忆。如果新的会话里用户只说“上次那副耳机”,应用可在授权范围内按用户与案例标识检索 case-A123 的旧记录,再读取足够的原文核对;不必把这个用户所有旧售后对话都注入提示词。同一会话里若摘要缺少决定性原话,也可回查旧消息。LangGraph 把线程内状态与跨线程 Store 区分开;Store 可按命名空间隔离,并在配置相应索引后做语义检索。语义搜索不是自动具备的,没有相应索引/实现时应使用明确的案例 ID 或结构化过滤。LangGraph:Stores、BaseStore.search 参考
本轮针对“还能检测吗”,应用最后应拿到现行 v3 的 30 天检测条款以及订单签收日期。由 9 月 20 日到 9 月 25 日为 5 天,在教学政策的检测申请窗口内;但“偶发杂音”尚未核验,不能把“可申请检测”讲成“必定故障、必定退款”。如果政策工具没返回生效版本或订单权限校验失败,就停止资格判断并说明还缺什么,而不是靠旧对话摘要补出结论。
给每次调用算一笔可检查的账
下面数字只为解释预算方法,不是任何真实模型的窗口、价格或 token 规则。假设目标调用的总容量 W=8,000 token,完整 30 轮历史本身约 9,500 token,显然不能原样放入。先为输出预留 1,200,为不确定开销预留 500,再扣掉应用指令与工具定义 1,500、现行政策短证据 900、当前用户问题 150,那么留给近期历史、摘要和记忆的 H 为:
H = 8,000 − 1,200 − 500 − 1,500 − 900 − 150 = 3,750 token。
| 本次送模材料 | 假设占用 |
|---|---|
| 应用指令和工具定义 | 1,500 |
| 当前用户问题 | 150 |
| 现行政策短证据 | 900 |
| 最近消息原文 | 1,800 |
| A123 事实卡 | 250 |
| 早期对话摘要 | 500 |
| 按需取回的相关旧片段 | 300 |
| 为模型回答预留 | 1,200 |
| 安全余量 | 500 |
| 合计 | 7,100 |
历史相关材料合计 1,800 + 250 + 500 + 300 = 2,850,小于 H=3,750;总计 7,100,也留下 900 的进一步余量。现实中应按目标模型和完整请求计数,而不是用“多少汉字”估算;工具定义、文件等也可能占输入预算。OpenAI 当前计数接口可对其 Responses 请求中的消息、工具、文件等计数,并提醒输出 token 不一定全都可见。其他供应商应使用相应计数规则,模型窗口和计费方式也可能不同。OpenAI:Counting tokens、Q23:Token 与上下文窗口
账还要算总成本和延迟。摘要本身可能多一次模型调用,长期记忆检索需要索引、存储与读取,反复查政策也有工具耗时;但每轮重发 30 轮历史会重复占输入,而且可能让模型注意力落在过期信息上。对 OpenAI Responses 的 previous_response_id 方式,官方明确说明链中先前输入 token 仍按输入计费;把历史保存在服务器或引用一个 ID,不能据此断言后续调用已免费。应记录压缩前后实际 token、额外摘要调用、检索耗时、回答质量,再选择何时摘要和何时检索。OpenAI:Conversation state
何时会“省了 token,却断了档”
只留最后几轮:本轮“那副耳机”失去指代,模型可能猜成另一笔订单。处理方式是保留最近指代,或从有权限的案例记录取回 A123;仍不明确就向用户确认。
摘要丢掉否定和条件:把“已拆封,不能自动退款;可申请质量检测”压成“耳机可退”,会直接改变业务结论。事实卡须保留 opened=true、动作“未执行退款”、政策来源与有效期,并用原文和工具记录校验。摘要冲突时不要拿较流畅的一版覆盖可核实的字段。
旧记忆与新事实冲突:旧卡片写 A123,用户刚纠正为 A124;或旧政策 v2 与现行 v3 不同。应使用户明确纠正与当前权威数据优先,并给旧记忆标记失效或版本;无法核实时请求补充或转人工。
检索失败或串用户:相关的拆封例外没有召回,或者拿到另一个用户的售后记录。检索必须先按登录身份、租户和授权范围过滤,再考虑语义相关性;无权限或无结果不能编造记忆。检索命中后还要读原文确认它是否真的回答当前问题。LangGraph:Stores 的命名空间与检索
裁剪破坏消息结构:把模型工具调用留下,却删掉对应的工具返回;或者把最新的用户纠正剪掉。应用要检查消息边界和所用 API 的历史格式要求,不能只按字符从头截断。LangChain:Delete messages
评测时准备一组同一售后流程的完整对话:关键事实在 20 轮前、用户最后一句只用代词、用户中途纠正订单号、政策版本变化、工具结果很长、摘要漏掉“未”、跨会话检索为空或越权。把“完整且能放入窗口时的核对答案”与“压缩后答案”逐项比较:指代是否正确、关键事实来源是否保留、是否误许诺退款、是否知道资料不足、每轮输入输出 token 与延迟。只看“没有超窗”或“平均 token 降了”不足以证明对话不断档。真实系统可抽样人工复核高影响结论。
面试时可以这样回答
我先区分“服务端保存的完整历史”和“本次送给模型的上下文”。每轮按目标模型计算完整输入预算并预留回答空间;近期几轮原话保留,早期低价值内容裁剪或摘要,把订单号、已拆封、用户自述与已核实事实等关键字段做有来源和时间的事实卡。需要跨会话或追溯很久以前的内容,再按身份和当前问题从长期记忆或原始档案取回少量片段,同时重新查会变化的订单和政策。压缩后要评测长距离指代、否定词、用户纠正、版本变化、越权和检索失败;证据不够就澄清或转人工。最后用实际 token、成本、延迟和错误率调裁剪与摘要阈值。
如果追问“长窗口模型是否可以一直塞全部历史”,回答是:窗口容量随模型而变,能放下也会产生重复输入成本和旧内容干扰;需要用实测决定何时压缩。如果追问“摘要是否能代替原始记录”,回答是:不能;摘要可能漏否定词或来源,重要事实应能回到原文或权威工具核对。