登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  科技周边 >  人工智能

多轮对话上下文太长怎么办:消息裁剪、摘要保留与 token 预算实验

来源:17golang原创

时间:2026-08-27 03:26:28 361浏览 收藏

聊天接口上线后,最先暴露的往往不是模型“不会回答”,而是对话一长就开始忘记前面的约束:用户明明说过“只返回 JSON”,第十几轮却混入了解释文字;工具调用的参数也越来越长,最后在请求阶段就被上下文窗口拒绝。解决这类问题,关键不是盲目把窗口换大,而是先给历史消息、摘要、当前问题和输出预留明确预算。

要点速览
  • 先为输出和工具调用预留空间,再决定历史消息最多保留多少。
  • 最近几轮保留原文,较早内容压缩成带边界的摘要,不能把摘要当成绝对事实。
  • 裁剪前后都要记录 token 估算、保留轮次和摘要版本,方便复现质量波动。
  • 一旦命中窗口上限,应停止继续拼接,返回可恢复的状态,而不是无限重试。

先做一个能复现的上下文预算实验

先把一次请求拆成四块:系统约束、历史消息、当前用户问题、预留输出。设模型上下文上限为 W,工具定义和安全说明占用 T,输出预留 O,那么给历史消息的预算不是“窗口减去当前问题”这么简单,而是:

history_budget = W - T - O - current_input_tokens

例如本地测试把 W 设为 8192,系统与工具定义约 1100,输出预留 1200,当前问题约 260,那么历史预算只有 5632。这个数一开始就算清楚,后面每次裁剪才有稳定依据。

不要用字符串长度代替 token 计数。中文、代码、JSON 键名和英文标识符的切分方式不同;如果生产模型提供官方 tokenizer,应在服务端使用同一套规则。没有 tokenizer 时可以先用保守估算,但要给估算值留安全余量。

上下文预算实验中系统约束、历史消息、当前问题和输出预留的分配关系

初始化消息结构:把可丢弃内容标出来

消息不要只保存 rolecontent。至少补上时间、会话轮次、重要级别和是否已经摘要,这些字段决定了裁剪时能不能做出可解释的选择。

{
  "turn": 12,
  "role": "user",
  "content": "把订单查询改成只返回 JSON",
  "importance": "constraint",
  "summarized": false
}

建议把内容分成三档:系统约束和当前任务属于不可丢弃区;最近两到四轮属于原文保护区;更早的闲聊、已经完成的中间推理属于可摘要区。重要级别不是越多越好,生产里通常只需要 constrainttaskcontext 三种。

编写裁剪函数:先保约束,再保最近轮次

下面的伪代码使用 token 估算函数 count_tokens,重点是顺序而不是某一家 SDK 的调用方式。裁剪时先放入不可丢弃消息,再从后往前加入最近对话,最后把旧内容摘要放在历史区前面。

def fit_messages(system, old_summary, messages, current, budget):
    kept = [system]
    used = count_tokens(system) + count_tokens(current)

    if old_summary and used + count_tokens(old_summary)  budget:
            continue
        kept.insert(1, message)
        used += size

    return kept + [current], used

真实实现还要处理成对的 user/assistant 消息,以及工具调用和工具结果必须成组保留的问题。不能只保留一条工具调用而丢掉对应结果,否则模型看到的历史会像一条未完成的分支。

检查点很简单:输出最终消息列表后,再次计算总 token,确认总量小于 W - safety_margin。如果超出,宁可继续缩短摘要,也不要把超限请求交给下游。

摘要不是全文压缩:只留下能影响下一轮的事实

摘要最容易出错的地方,是把已经确认的事实、用户偏好、待办事项和模型猜测混成一句话。建议固定成四个字段:已确认事实、用户约束、未完成任务、需要追问的缺口。

已确认事实:订单状态接口返回字段为 order_id、status
用户约束:后续只输出 JSON,不添加 Markdown 说明
未完成任务:补充超时后的重试策略
待确认缺口:重试是否允许改变查询时间范围

每次摘要都带一个版本号,例如 summary_v3,并把它与被压缩的消息轮次记录在日志里。这样当用户反馈“模型好像忘了上一轮”时,可以判断是摘要丢字段,还是最近轮次根本没有进入请求。

上下文裁剪前后对照:旧消息压缩为摘要并保留最近任务约束

运行检查:用三类指标判断裁剪是否过度

不要只看请求是否成功。至少记录四个字段:input_tokensoutput_tokenshistory_turns_keptsummary_version。如果输入 token 下降了,但“违反输出格式”的比例上升,说明摘要保留了背景却丢了约束。

可以做一组固定回放:同一批 20 个问题,分别使用完整历史、只保留最近四轮、最近四轮加摘要三种策略。对比格式遵循率、工具参数正确率和平均输入 token。实验数据不需要伪装成线上结论,关键是让每次改动都能在同一批问题上复核。

  • 格式遵循率下降:优先检查系统约束是否被重复摘要或放到了可丢弃区。
  • 工具参数错误增加:检查工具调用和工具结果是否被拆开裁剪。
  • 输入 token 仍持续增长:检查摘要是否被追加而不是替换旧摘要。

相关问题与恢复边界

把上下文窗口调大就能解决吗?

不能保证。窗口变大只扩大容量,不能修复摘要错误、工具结果失配或无界追加。仍然应该保留硬预算和超限保护。

摘要应该每一轮都重新生成吗?

不必。只有当可摘要区新增内容达到阈值,或任务状态发生变化时再更新;否则频繁摘要会增加延迟,也会让事实不断被改写。

裁剪后用户还能继续对话吗?

可以,但要把摘要版本和当前任务状态作为可恢复信息保存。若关键约束无法压缩,应提示用户重新确认,而不是假装模型仍记得全部细节。

把策略落成发布前检查清单

上线前逐项核对:系统约束是否永远保留;当前问题和输出预算是否先扣除;最近消息是否按完整轮次保留;工具调用与结果是否成组;摘要是否区分事实、约束和待确认项;超限时是否停止请求并返回可恢复状态;日志是否记录裁剪数量、估算 token 和摘要版本。

多轮对话的稳定性,本质上是一个预算管理问题。把历史消息当成有生命周期的数据,给它标注重要性、摘要版本和回放指标,模型窗口变长或变短都不会让系统失去边界。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>