聊天历史越滚越长怎么办?3 种压缩策略 AB 对比

Claude 中文知识站 Lv4

一次线上事故引发的思考

去年有个事故我记到现在。

客户是家做法律咨询的,AI 助手用的 Claude Sonnet。用户在对话第 15 轮说自己有一笔 48 万的遗产纠纷,要咨询继承顺序。第 16 到第 40 轮中间聊了一堆背景——父母离异、兄弟几个、有一个继父、继父带来的两个子女。

到第 42 轮,用户问了一句”那我应该起诉谁”。

我们那会儿做了简单的历史截断——超过 30 轮就只保留最近 20 轮。结果 AI 回答里说成了”您只需要和两位兄弟协商即可”——那个继父和继子女的信息早就被截掉了。

那一次差点吃官司。从那之后我再也不敢用纯截断策略。

策略 1:朴素截断(千万别用)

最朴素的做法:设一个轮数上限,超过就从头砍。实现十分钟能搞定。

信息损失率实测:我们跑过一个基准测试集,120 个 case,每个 case 40-60 轮对话。纯截断(保留最近 15 轮)的信息召回率是 **43.7%**。几乎一半关键信息丢了。

为什么这么惨:用户往往在对话早期交代背景,后期只抛出具体问题。截掉早期就是截掉上下文。

什么时候可以用:真的不建议用。除非你的场景是客服——每次对话独立,不跨 session。那用户如果第二次来咨询,你反正也不需要记得第一次。

说句实话我现在看到代码里有 messages[-N:] 这种直接切片的,都会把人叫过来谈话。

策略 2:分级压缩(当前我用得最多)

分三层:

L1 近期原文:最近 6-10 轮原封不动保留。

L2 中期要点:倒数第 11-30 轮,每 3-4 轮生成一个小摘要,约 150 token 左右。这一层保留关键 entity、用户意图转折点、AI 已做的承诺。

L3 远期摘要:30 轮以前整段压缩,约 500-800 token。这一层只保留”这场对话是关于什么、用户的核心诉求、已经解决和未解决的事项”。

信息损失率实测:同样那 120 个 case,分级压缩的召回率 **84.2%**。比截断好太多。

实现关键

第一,L2 和 L3 的压缩 prompt 要完全不同。L2 是”请保留所有数字、日期、人名和具体诉求”,L3 是”请概括对话主题和已达成的共识,不超过 800 字”。粒度不一样。

第二,entity 白名单机制。压缩时显式提取出金额、时间、人名、地点、医疗术语、法律条文号,单独存成一个 entity list,和摘要一起喂回 context。这个机制救了我好几次。

第三,L2 不是一次性生成的,是增量更新。每 3 轮新对话来了之后,用 Haiku 对这 3 轮做小摘要,append 到 L2。而不是每次都重新压缩整段——太贵。

成本:Haiku 4.5 做压缩一次 300 token 左右,成本 0.00012 美刀。一个 60 轮对话全程大概压 15 次,总成本 0.0018 刀。相比 Sonnet 的 input 费用节省 70% 以上。

我踩的坑:最开始 L2 的摘要让 Haiku 自由发挥,结果它经常”优化”掉用户的原话。用户说”我觉得这个方案不错但有点贵”,压缩完变成”用户对方案表示满意”——“有点贵”这个关键反馈丢了。后来我在 prompt 里明确要求”保留用户的原始措辞,尤其是带情感倾向的表达”,才好转。

策略 3:Entity-centric compression(进阶)

这个策略是我从一个客户那里学来的,他们团队里有个做知识图谱出身的工程师。

核心思想:不压缩对话,而是从对话里抽取事实

每轮对话结束后,跑一个抽取任务,产出形如:

1
2
3
4
5
6
<facts>
<fact id="f1" type="user_profile">用户年龄 34 岁</fact>
<fact id="f2" type="preference">偏好低风险投资</fact>
<fact id="f3" type="constraint">每月最多投入 5000 元</fact>
<fact id="f4" type="decision">用户已拒绝方案 A</fact>
</facts>

每个 fact 带 id、类型、原始对话轮次引用。

再下次对话时,context 里不放对话历史本身,而是放 fact list + 最近 3-5 轮原文。

信息损失率实测:同样基准,召回率 **88.9%**。比分级压缩略高。但注意这个数字是在”精确引用”测试集上——如果测试集是”语气一致性””理解对话的情感脉络”这类软指标,entity-centric 反而不如分级压缩,因为它丢了语气。

为什么强

第一,可审计。每个 fact 可以追溯到原始对话。用户质疑”AI 为什么说我不吃辣”,你能精确定位。

第二,可修改。用户说”我改主意了,现在可以接受高风险”,你直接把 f2 覆盖掉。朴素摘要做不到这种精细更新。

第三,可组合。fact 库可以跨会话累积,变成用户画像。但这需要额外做冲突检测,上一篇文章讲过。

什么时候别用

任务如果高度依赖”情感连续性””语气”——比如陪伴聊天、创意写作——entity 抽取会把灵魂抽没。

实现成本:比分级压缩高一倍。每轮后要跑一次抽取 + 每次请求前要拼接 fact list。但长期看收益更大,尤其配合 CLAUDE.md 这种项目级配置 一起用。

三种策略的成本-效果对比

做了张表,是我这半年几个项目的实测均值(60-80 轮对话):

策略 信息召回率 单会话压缩成本 实现复杂度 适用场景
朴素截断 43.7% 0 极低 基本别用
分级压缩 84.2% ~$0.002 大部分通用场景
Entity-centric 88.9% ~$0.004 严肃决策场景

我现在的默认选择:通用场景分级压缩,严肃场景 entity-centric。偶尔也会混用——近期用分级,远期用 entity 化的 fact list 兜底。

对 Claude.ai 做法的一点推测

Anthropic 没公开过 Claude.ai 里”记得住几个月前对话”功能的技术细节。但从我观察到的行为来看,我的猜测是这样的:

第一,用了 entity-centric 为主的记忆层。因为 Claude.ai 能精确回忆”你三周前跟我说你在学 Rust”这种细节,而且跨会话都能命中。这不像纯摘要能做到的。

第二,近期会话保留更多原文。同一个会话内前后文连贯性极好,断层罕见。这说明近期没过度压缩。

第三,有 用户可见的记忆管理入口。用户能看到、删除、修改记忆。这种 UX 暗示底层是结构化的 fact,不是黑盒摘要。

第四,召回是 LLM 主导的。对话中 Claude 会自己判断”要不要调用记忆”,而不是每次机械检索。这点和我上一篇写的 agent 记忆架构思路一致。

当然这都是我瞎猜,Anthropic 哪天发工程 blog 估计会把我打脸。但八九不离十。

最后三条实操经验

第一,压缩模型要比主模型小一档。 主力用 Sonnet,压缩用 Haiku。用 Opus 做压缩是浪费。

第二,摘要的 prompt 要版本化。 我在 repo 里专门有个 prompts/summarization_v3.xml,改动要走 review。因为摘要 prompt 直接决定了你的”记忆质量”,不是随便改的。

第三,留一个”压缩旁路”。 关键决策点(用户下单、签约、投诉)的那几轮对话永远保留原文,不进压缩池。这种关键轮次的识别靠 Haiku 实时打标。

做记忆这件事最忌讳聪明过头,把简单问题做复杂。但更忌讳偷懒,把复杂问题做简单。压缩这一块,属于后者。

延伸阅读

压缩策略上游是 [Context 预算拆桶](/posts/prompt-to-context-engineering/),下游对接 [长期记忆架构](/posts/claude-md-project-config/)。压缩 prompt 的结构化输出强烈建议用 [XML 标签](/posts/prompt-xml-tags-claude-special/) 而非 Markdown。想省调用费,[Prompt Caching 深度指南](/posts/prompt-caching-deep-guide/) 配合分级压缩效果拔群——中期摘要那一段正好适合做缓存。

  • 标题: 聊天历史越滚越长怎么办?3 种压缩策略 AB 对比
  • 作者: Claude 中文知识站
  • 创建于 : 2026-04-19 08:50:00
  • 更新于 : 2026-04-19 20:15:00
  • 链接: https://claude.cocoloop.cn/posts/context-compression-summarization/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论