M4 · 上下文压缩与 Cache Breakpoint#
简历 Bullet Point: 设计 Cache Breakpoint 上下文压缩——较旧区截断冻结做稳定缓存前缀,最近 K 轮保全文做工作集;核心洞察「脱离缓存谈压缩,省下的都是假的」——盲目压缩缓存命中率从 85% 跌到 15%,综合成本反升 3 倍
开场钩子#
场景#
测试 50+ 轮超长对话时,token 费用飙升。直觉做法:把旧的对话轮次压缩成摘要。压完 token 确实少了 30%,但 API 账单反而涨了——盲目压缩破坏了 prompt cache 的前缀精确匹配,85% 的缓存命中率掉到 15%,省下的 token 费被全量重算的费用吃回去,综合成本 +297%。
于是发明了 Cache Breakpoint:不是”尽量多压”而是”只在不破坏缓存前缀的地方压”。
面试官切入#
“你说’压缩与缓存是耦合的’——能展开讲讲?“
一、模块运作流程#
1.1 核心机制#
对话历史 [msg_0, msg_1, ..., msg_N]
│
▼
compute_breakpoint(messages, keep_recent=3)
│
├─ 较旧区 [:bp] → 压缩(超大工具结果尾部截断)+ 冻结(打 cache_control)
│ 这一段逐字稳定,跨轮不变,prompt cache 命中
│
└─ 最近区 [bp:] → 保全文(工作集,模型需要的新鲜上下文)
这一段每轮变化,cache miss 但体量小plaintext关键约束:
- Anthropic cache_control 单请求最多 4 个 breakpoint
- 前缀精确匹配:改一个字节就 miss
- 断点只能单调右移(旧的只增不减)
1.2 压缩策略#
只改请求视图、不改存档历史——压缩走 wrap_model_call,存储的原始对话不动。
| 层 | 位置 | 做什么 |
|---|---|---|
| L0 工具侧 | 工具返回时 | 超长结果截断(≤4000 token) |
| L2 Cache Breakpoint | 中间件 | 较旧区工具结果进一步截断 |
| L3 会话摘要 | 预留 | 按需开启(当前不开) |
二、踩坑实录#
坑 1:盲目压缩综合成本 +297%#
- 省 30% token 但 cache 命中率 85%→15%。综合成本 = token × (1 - hit_rate × discount)。改成 Cache Breakpoint 后命中率恢复 80%,成本 -35%。
坑 2:极小阈值下 head_keep 负数切片#
- 压缩阈值设太小导致 head_keep 算出负数,截断反而撑长结果。加
max(0, ...)保护。
坑 3:多模态 block 列表 content 被 str() 化截断损坏#
- 图片等多模态消息的 content 是列表,直接
str()截断破坏结构。改为只截断字符串类型的 content。
三、验收与量化#
| 方案 | token 数 | 压缩率 | 缓存命中率 | 综合成本 |
|---|---|---|---|---|
| 不压缩 | 80K | 0% | 85% | 基准 |
| 盲目压缩 | 56K | 30% | 15% | +297% |
| Cache Breakpoint | 60K | 25% | 80% | -35% |
12 轮对话实测:较旧区前缀逐轮逐字稳定(全 ✓),token 增长受控(省至 47%)。
四、面试问答#
Q1: 为什么说”压缩与缓存是耦合的”?#
实际计费 = 总 token × (1 - 命中率 × 折扣)。压缩省 token 但可能破坏前缀降低命中率,两个变量必须一起优化。Cache Breakpoint 的核心是只在不破坏缓存的地方压。
Q2: cache_control 只有 4 个 breakpoint 够用吗?#
System prompt 占 1 个,较旧区末尾占 1 个,剩 2 个预留。实测购物对话 10-20 轮,2 个 breakpoint 足够覆盖。
Q3: 本项目走 OpenAI 兼容端点,cache_control 有效吗?#
cache_control 仅 Anthropic 原生消费。标记逻辑做全但默认关,OpenAI 端点是无害空操作。保留为可移植性——换 Anthropic 直接开。
五、前沿概念#
Prompt Cache 机制:前缀精确匹配 → KV cache 复用 → 首 token 延迟(TTFT)降 50-80% + 输入 token 按折扣计费。任何改变前缀内容的操作(摘工具、改 system prompt、压缩旧轮次)都会 miss。
六、诚实边界#
| 维度 | 做了 | 没做 |
|---|---|---|
| 压缩 | 较旧区截断 + 冻结 | 语义摘要(L3 预留) |
| cache_control | 标记逻辑做全 | OpenAI 端点不消费 |
| 跨界消息 | 会一次性重算(滚动增量缓存的正常代价) | 不夸大”整段永不变” |