多轮对话上下文超长怎么截断
做多轮对话的AI小助手,聊着聊着上下文就爆了。我用一个拖拽配节点的低代码平台搭了个长对话助手,被这事折腾过一阵,干脆整理成问答,把我自己摸出来的答案都写上。
Q:为什么聊久了会报错或者变笨?
A:每轮对话你都得把历史消息一起喂给模型,聊得越久历史越长,token 越堆越多。要么超过模型的上下文窗口直接报错,要么虽然没超但前面的内容把后面挤得模型"记不住",回答开始飘。我第一次遇到是聊到第三十多轮,模型突然不认识开头设定的人设了,查了才知道是历史太长被截在中间。
Q:最简单的截断办法是啥?
A:滑动窗口,只保留最近 N 轮。比如永远只带最近 10 轮对话进去,更早的直接丢。实现起来一行:
recent = history[-10:] # 只留最近10轮
简单粗暴,对大部分闲聊型场景够用。缺点是太早的信息真就丢了,用户要是聊到第 5 轮又提第 1 轮说过的事,它就懵了。
Q:那重要信息丢了怎么办?
A:加一个固定不丢的部分。我的做法是把系统提示、人设、还有用户一开始交代的关键信息(比如"我的订单号是 xxx")单独存一份,每轮都强制带上,不参与滑动窗口的丢弃。结构大概是:
[固定系统提示] + [关键事实区] + [最近N轮对话]
固定区永远在,滑动区按需丢。这样核心设定不会因为聊太久就失忆。
Q:N 设多少合适?
A:没标准答案,看你模型窗口和单轮长度。我的经验是先按 token 算,不要按轮数死磕。我实测我这场景单轮平均 300 token 左右,模型窗口我留一半给历史,算下来大概能放十几轮。我干脆不固定轮数,改成按 token 累加,从最近往前加,加到预算上限就停:
budget = 4000 # 留给历史的token预算
kept, total = [], 0
for msg in reversed(history):
t = count_tokens(msg)
if total + t > budget:
break
kept.append(msg)
total += t
kept.reverse()
按 token 比按轮数稳,因为有时候一轮特别长,按轮数算容易超。
Q:丢掉的历史完全不要了吗,太可惜?
A:可以上摘要。聊到一定长度,把要丢弃的早期对话先让模型压缩成一段简短摘要,把摘要塞进"关键事实区",再丢原文。这样早期信息以压缩形式保留下来。代价是多一次模型调用、而且摘要会丢细节。我只在比较重的客服场景才上摘要,普通闲聊不值当,加了反而慢。这是个取舍,别无脑上。
Q:有什么容易踩的坑?
A:两个。一是别把系统提示也卷进滑动窗口给丢了,我干过,丢完模型直接人格分裂。系统提示必须钉死在最前面。二是摘要这条路径,摘要本身也占 token,别让摘要越滚越长,我有次摘要套摘要,滚到比原文还长,离谱。给摘要也设个长度上限。
Q:这套搭起来麻烦吗?
A:在那个零代码平台上,截断逻辑挂在对话节点前的一个处理节点里,配好预算就行,没写多少代码。底层模型走的讯飞星辰 MaaS,现成 API 调,token 计数和对话都用它的,没自己部署算力。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)