会话与历史裁剪:省 token 的那个方案,贵了 54%
五种策略实测——全量历史又便宜又不丢信息,因为缓存
多轮对话有个人人都知道的问题:每多说一轮,你都要把之前所有内容重新发一遍、重新付一遍钱。
于是几乎所有教程都会告诉你:裁剪历史——滑动窗口、摘要压缩。听起来天经地义。
我实测了 5 种策略跑同一场 15 轮对话。结果是:
A 全量历史 成本 0.0025 元 ( +0%) 输入 5486 tok 缓存命中 82% ✅ 记得
B 滑动窗口(最近 6 条) 成本 0.0038 元 ( +54%) 输入 2595 tok 缓存命中 0% ❌ 忘了
滑动窗口把 token 砍掉一半,成本反而贵了 54%,而且忘了用户说过自己对花生过敏。
这一节讲清楚为什么,以及真的装不下时该怎么裁。
你去同一家餐馆吃饭,老板认识你,你一坐下他就知道你不吃辣。这是"缓存"——他不用每次重新了解你。
现在你为了"节省老板的记忆",每次只告诉他最近三次点的菜。结果他每次都得重新琢磨你是谁——你没帮他省事,你把他的记忆清空了。
为什么全量历史反而更便宜
关键在 ap1.4 讲过的前缀缓存:模型服务按"你这次的输入,前面有多长的一段和上次完全相同"来命中缓存,命中部分只收十分之一的价。
- 全量历史:每一轮的输入 = 上一轮的输入 + 新的两条消息。前缀完全没变 → 缓存命中 82%;
- 滑动窗口:每一轮都从头部丢掉最老的一条。前缀一变,后面全部作废 → 缓存命中 0%。
A 全量历史 输入 5486 tok(命中 4480 / 未命中 1006) 成本 0.0025 元
B 滑动窗口 输入 2595 tok(命中 0 / 未命中 2595) 成本 0.0038 元
B 的 token 只有 A 的 47%,但它们全是全价。
这是一条很容易被忽略的通用规律:任何"从提示词开头动刀"的优化,都会让缓存失效。动态插入的时间戳、每次重排的示例、轮换的系统提示——都是同一个坑。要改就改尾部。
五种策略的完整对比
A 全量历史 成本 0.0025 元 ( +0%) 输入 5486 tok 缓存命中 82% ✅ 记得
B 滑动窗口(最近 6 条) 成本 0.0038 元 ( +54%) 输入 2595 tok 缓存命中 0% ❌ 忘了
C 摘要压缩(只压一次) 成本 0.0042 元 ( +70%) 输入 3206 tok 缓存命中 4% ❌ 忘了
D 滚动摘要(每次掉出就更新) 成本 0.0067 元 (+169%) 输入 4561 tok 缓存命中 3% ❌ 忘了
E 滑窗 + 用户档案 成本 0.0070 元 (+182%) 输入 6146 tok 缓存命中 12% ✅ 记得
记忆测试很直白:第 2 轮用户说"我对花生严重过敏",第 15 轮问"同事送了我一罐花生酱,可以每天抹面包吃吗"。
- A:"绝对不行!你对花生严重过敏,必须立即拒绝或转送他人。"
- B:"可以,但注意量。花生酱热量高,每天 1 勺即可……" ← 在过敏场景下,这是一次严重事故
- D(滚动摘要):"可以,但选无糖无盐的天然花生酱……" ← 摘要里把这条丢了
- E(用户档案):"绝对不行!您对花生严重过敏……早餐可选杏仁酱,但需确认无交叉污染。"
D 最值得注意:它是"正确实现"的摘要压缩——每次有内容掉出窗口就增量更新摘要,提示词里明确写了"必须保留用户的个人情况、忌口"。它还是丢了。
摘要是有损压缩,而你无法预测它会丢什么。在忌口、过敏、身份、金额这类信息上,这个风险不能接受。
结论:三条决策线
① 上下文装得下,就别裁。 15 轮对话累计 5486 token,离任何现代模型的上下文上限都还很远。在这个阶段裁剪,是在解决一个你还没有的问题,同时制造一个你本来没有的问题(缓存失效 + 丢信息)。
② 真的装不下时,裁尾不裁头。 优先丢掉的应该是最老的那几轮的助手回复(通常信息密度最低),保留用户说过的话。别动系统提示和开头——那是缓存的前缀。
③ 关键事实必须单独存,不能靠摘要。 这是 E 唯一在裁剪场景下答对的原因:它把"小林、花生严重过敏"抽成结构化的用户档案,每轮固定注入,不参与压缩、不会被摘要吃掉。
# 用户档案:和对话历史分开存,放数据库
profile = {
"name": "小林",
"allergies": ["花生"], # ← 这类字段永远注入,永远不压缩
"preferences": ["不喜欢牛奶"],
"goals": ["调整饮食"],
}
成本上它确实更贵(+182%),但这笔钱买的是"不会把过敏的人送进医院"。该花。
一套实用的会话结构
再加两条工程细节:
- 会话要有上限:一场对话超过 N 轮或 M token,提示用户"要不要开个新话题"——比悄悄丢信息诚实;
- 会话要能过期:用户三天前的对话还挂在内存里,是成本也是隐私风险(ap6.5)。设 TTL,到期归档或删除。
🔧 动手做:量一下你自己的对话成本(15 分钟)
- 跑本节代码,重点看 B 的 token 少一半却贵 54%,和 D 的"正确摘要"照样丢了过敏。
- 打开 ap1.6 的流水,统计你的真实对话长度分布:P50 多少轮?P95 多少 token?——先看数据,再决定要不要裁。
- 检查缓存命中率:如果你的多轮对话缓存命中率很低,去找"每轮都在变的前缀"(时间戳?随机示例?)。
- 列出你产品里"绝不能丢"的事实:忌口、身份、金额、已确认的选择……把它们从对话历史里抽出来,存成结构化档案。
- 设会话上限和 TTL,并写好达到上限时给用户的提示文案。
✅ 做到这里你应该有:真实的对话长度分布 + 缓存命中率 + 一张用户档案表 + 会话上限与 TTL。
为什么:多轮对话是 AI 产品最常见的形态,也是成本最容易失控的地方——因为它的成本随轮数平方级增长(每轮都要重发之前所有内容)。而本节最反直觉的发现是:最朴素的做法(什么都不裁)配合缓存,在很长一段时间内是最优解。先测,再优化——不然你的"优化"很可能像 B 那样,又贵又坏。
实测里 D(每次有内容掉出窗口就增量更新摘要)是教科书式的正确实现,提示词里也明确写了"必须保留用户的个人情况、忌口、偏好"。它还是把"花生严重过敏"丢了。
为什么会丢:摘要模型面对的是"把 N 条对话压成 80 字",它会按"这段对话在讲什么"来取舍,而用户三轮前顺口提的一句忌口,在"这段对话在讲什么"里权重很低。
所以规矩是:能结构化的事实,永远不要交给摘要。摘要用来压"上下文氛围"(聊过哪些话题、讨论到什么程度),关键事实走独立字段、独立存储、每轮固定注入。判断标准很简单:这条信息丢了会不会出事?会,就别让它进摘要。
我的 AI 产品是【描述】,对话轮数分布是【P50/P95】,单轮输入约【X】token,用的模型上下文上限【X】。请帮我设计会话管理:1) 现在到底需不需要裁剪历史(结合前缀缓存算一笔账);2) 哪些信息必须抽成结构化用户档案、字段怎么设计;3) 真要裁剪时的顺序(先丢什么、保留什么、怎么不破坏缓存前缀);4) 会话上限和 TTL 的建议值,以及达到上限时给用户的提示文案;5) 需要监控的指标(缓存命中率、平均轮数、单会话成本)。
会话三句话:全量历史 + 前缀缓存往往是最优解(实测最便宜且不丢信息);任何动提示词开头的操作都会让缓存失效(滑窗砍半 token 却贵 54%);关键事实要抽成结构化档案,别交给摘要(正确实现的滚动摘要照样丢了过敏)。
下一节回到用户能直接感受到的地方:流式 UI——ap1.2 测过 TTFT 快 8.2 倍,这一节讲怎么把这个"快"真正做进界面。
🔎 来源与核验· 3 条,点开核对
