---
title: "Claude Code 调用国内模型的缓存大坑：一个环境变量省 90% 的钱"
author: deletexiumu
pubDatetime: 2026-02-15T18:00:00+08:00
modDatetime: 2026-02-16T20:00:00+08:00
featured: false
draft: false
tags:
  - Claude Code
  - 国内模型
  - API
description: "Claude Code 2.1.42 版本缓存命中率低的原因分析，以及通过一行环境变量节省 90% 费用的解决方案。附 DeepSeek/Kimi 验证脚本。"
---

用 Claude Code 接入国内模型（Kimi、智谱、DeepSeek 等），你有没有觉得：响应特别慢、费用特别高、经常 429？

这篇文章告诉你原因，以及怎么一行配置解决。

---

## 💡 本文内容概览

- Claude Code 的缓存机制为什么对成本至关重要
- 2.1.42 版本到底修了什么、没修什么
- 实测数据对比：修复前后的惊人差异
- 一行环境变量的解决方案

**适合人群**：使用 Claude Code 接入第三方 API（国内模型、中转站）的用户。

---

## 一、背景：一个让所有第三方平台头疼的问题

2 月 14 日，Kimi 团队的 @Hx1u0 在 X 上发了一条长帖，提到了一个关键信息：

> Claude Code 在一次神秘更新后，缓存命中率从 90% 降到不足 20%，这不仅消耗了大量额度，还让用户等待时间变长，429 变多。

与此同时，第三方 API 平台也发现了类似问题：

> 新版本的 Claude Code CLI 会在 system prompt 中注入一个 `cch=xxx` 的字段，这个值每次都变化，导致每次请求都无法命中缓存。

**缓存为什么这么重要？**

Claude Code 每次请求都会发送一个庞大的 system prompt（约 6.8 万 tokens），包含所有的系统指令、工具定义、项目上下文等。如果这些内容能被缓存（KV Cache），后续请求就只需要处理新增的用户消息部分，速度快、成本低。

反之，如果缓存失效，每次都要重新处理 6.8 万 tokens——相当于每说一句话都要把整本说明书重读一遍。

---

## 二、2.1.42 版本声称修复了什么？

Claude Code 2.1.42 的 changelog 里有这么一条：

> Improved prompt cache hit rates by moving date out of system prompt

听起来问题解决了？我决定逆向 2.1.42 的二进制文件，看看到底改了什么。

---

## 三、源码逆向分析

Claude Code 是编译后的 Mach-O 二进制文件，但通过 `strings` 命令可以提取其中的 JavaScript 源码。以下是关键发现：

### 发现 1：billing header 的结构

Claude Code 会在 system prompt 的**第一个 text block** 注入一段「账单头」：

```
x-anthropic-billing-header: cc_version=2.1.42.abc; cc_entrypoint=claude-code; cch=00000;
```

这段文字不是 HTTP 头，而是直接嵌入在发送给模型的 system prompt 文本中。

### 发现 2：cch 字段已固定 ✅

在 2.1.42 中，`cch` 已经硬编码为 `00000`，不再随机变化：

```javascript
B = `x-anthropic-billing-header: cc_version=${R}; cc_entrypoint=${A}; cch=00000;`
```

对比 2.1.37 时代 `cch` 每次请求都随机生成，这确实是一个修复。

### 发现 3：cc_version 后缀仍然是动态哈希 ❌

但问题没完全解决。`cc_version` 的格式是 `2.1.42.abc`，其中 `abc` 是一个 3 字符哈希，由以下逻辑生成：

```javascript
function o7A(T, R) {
    // 从用户首条消息中取第 4、7、20 位字符
    let _ = [4, 7, 20].map(($) => T[$] || "0").join("");
    // 拼接盐值 + 字符 + 版本号，SHA256 取前 3 位
    let B = `59cf53e54c78${_}${VERSION}`;
    return crypto.createHash("sha256").update(B).digest("hex").slice(0, 3);
}
```

**这意味着**：每开一个新会话，第一条消息不同，就会生成不同的哈希 → billing header 不同 → system prompt 前缀不同 → **跨会话的前缀缓存无法共享**。

需要注意：在**同一个会话内**，哈希由第一条消息算出后就不再变化，后续多轮对话的前缀是稳定的，缓存可以正常工作。**真正受影响的是跨会话场景**——频繁开新会话、并行开发多个任务、定时任务等，每次新会话都会生成不同的哈希，导致 68K 的 system prompt 前缀无法复用。

### 发现 4：日期已移出 system prompt ✅

旧版本在 system prompt 中直接写入 `Today's date is 2026-02-15`，每天凌晨 0 点缓存就会失效。

2.1.42 改为在 `system-reminder`（user message 层）注入日期，system prompt 本身不再包含每日变化的内容。WebSearch 工具描述中保留了 `February 2026` 格式的月份，但只按月变化，影响可接受。

### 发现 5：有环境变量可以关闭 billing header

```javascript
function hgD() {
    if (process.env.CLAUDE_CODE_ATTRIBUTION_HEADER) return false;  // 设了就关闭
    return true;  // 默认开启
}
```

设置 `CLAUDE_CODE_ATTRIBUTION_HEADER=false` 可以让 `pHR` 函数直接返回空字符串，billing header 不再注入 system prompt。

---

## 四、实测数据

光看代码不够，从两个维度验证：Python API 控制变量测试（精确隔离）+ Claude CLI 真实调用测试（贴近实际）。

### 4.1 控制变量测试（Python API）

直接调用 DeepSeek API（deepseek-chat），精确控制 system prompt 内容，排除其他干扰。

**实验设计**：构造两组 system prompt，仅 billing header 中的哈希不同（`abc` vs `def`，3 字符差异），其余完全一致。

```
实验 1: 相同 system prompt，不同 user message
  [1a] prompt=1822, cached=0     → 首次请求，无缓存
  [1b] prompt=1822, cached=1792  → 不同消息，命中缓存 ✅

实验 2: 不同 system prompt（仅改哈希），相同 user message
  [2a] prompt=1817, cached=1792  → 前缀 A 已有缓存，命中
  [2b] prompt=1816, cached=0     → 改了哈希，未命中 ❌
```

**结论**：

| 测试 | 前缀 | 消息 | 缓存 | 说明 |
|------|------|------|:---:|------|
| 1a → 1b | 相同 | 不同 | **命中** | 前缀一致即可复用，不需要全文相同 |
| 2a → 2b | 不同（仅 3 字符） | 相同 | **未命中** | 前缀有差异，即使消息一样也不行 |

这证明了：**缓存匹配的是前缀（Prefix），不是全文（Full Match）。** 动态哈希在 system prompt 开头，改变了前缀起点，后面所有内容的缓存全部失效。

> 验证脚本支持同时测试 Kimi 和 DeepSeek，下载地址：
> https://blog.deepai.wiki/downloads/verify_prefix_cache_multivendor.py
>
> 用法：
> ```bash
> export DEEPSEEK_API_KEY="your-key"
> python3 verify_prefix_cache_multivendor.py --provider deepseek
> ```

### 4.2 Claude CLI 真实调用测试

用真实 `claude` CLI（2.1.42），通过 `CLAUDE_CODE_ATTRIBUTION_HEADER` 环境变量控制 billing header 开关，对比费用和缓存。

| # | 条件 | 问题 | Input Tokens | Cache Read | 费用 | API 延迟 |
|---|------|------|-------------|------------|------|---------|
| 1 | billing header ON（冷启动） | hello | 58,531 | 9,728 | $0.179 | 16.3s |
| 2 | billing header ON（重复） | hello | 67,747 | 512 | $0.204 | 17.5s |
| 3 | billing header ON | 1+1 | 67,749 | 512 | $0.204 | 7.7s |
| **4** | **billing header OFF** | **hello** | **163** | **68,096** | **$0.021** | **2.1s** |
| 5 | billing header OFF | 1+1 | 68,261 | 68,261 | $0.225 | 1.8s |
| 6 | billing header ON（缓存已热） | hello | 68,259 | 68,259 | $0.225 | 2.0s |

**测试 2 vs 测试 4**（相同问题 "hello"，唯一区别是 billing header 开关）：

| 指标 | header ON | header OFF | 差距 |
|------|-----------|------------|------|
| Cache Read | 512 | 68,096 | **133 倍** |
| 费用 | $0.204 | $0.021 | **省 90%** |
| API 延迟 | 17.5s | 2.1s | **快 8 倍** |

数据很直观：**关掉 billing header 后，费用降到 1/10，速度快 8 倍**。

### 4.3 两种场景的区别

需要明确：**动态哈希的影响取决于使用场景**。

**场景 A：同一会话内多轮对话**

哈希由第一条消息算出，整个会话期间不变。第 2 轮起前缀稳定，缓存正常工作：

```
Turn 1: [SP + hash_abc] [user msg 1]                     → 首次，可能 miss
Turn 2: [SP + hash_abc] [msg1] [reply1] [msg2]           → 前缀不变，HIT
Turn 3: [SP + hash_abc] [msg1] [reply1] ... [msg3]       → 前缀不变，HIT
```

**场景 B：跨会话（本文重点）**

每开一个新会话，第一条消息不同 → 哈希不同 → system prompt 前缀不同 → 无法共享缓存：

```
会话 A（"帮我写单测"）:  [SP + hash_a3f] ...
会话 B（"检查这个bug"）: [SP + hash_7b2] ...
会话 C（"重构代码"）:    [SP + hash_e91] ...
定时任务 1（"日报"）:    [SP + hash_d4c] ...
定时任务 2（"巡检"）:    [SP + hash_f08] ...
```

五个会话，五个不同前缀，互相无法共享。每个会话的第一轮都要重新处理 68K tokens。

**没有动态哈希时**，所有会话共享同一个 system prompt 前缀，只要任意一个先跑过，其余都能直接命中 68K 的缓存。

如果你的使用模式是频繁开新会话（并行开发、定时任务、改 bug 开一个写功能开一个），关掉 billing header 能节省大量 token 消耗。

---

## 五、问题总结

| 问题 | 2.1.37（出问题的版本） | 2.1.42（当前版本） | 状态 |
|------|----------------------|-------------------|------|
| `cch` 每次随机变化 | 是 | 已固定为 `00000` | ✅ 已修复 |
| `cc_version` 后缀动态哈希 | 是 | 仍然变化 | ❌ 未修复 |
| 日期在 system prompt 中 | 每天变 | 移到 user message 层 | ✅ 已修复 |
| 月份在 WebSearch 描述 | — | 每月变一次 | ⚠️ 可接受 |

**结论**：2.1.42 改善了，但没完全解决。`cch` 和日期的问题修了，但 `cc_version` 动态后缀仍然会导致**跨会话**的缓存无法共享。同一会话内多轮对话不受影响。

---

## 六、解决方案

### 方案 1：环境变量（推荐）

在你的 shell 配置文件中加入：

```bash
# ~/.zshrc 或 ~/.bashrc
export CLAUDE_CODE_ATTRIBUTION_HEADER=false
```

或者在 `~/.claude/settings.json` 中配置：

```json
{
  "env": {
    "CLAUDE_CODE_ATTRIBUTION_HEADER": "false"
  }
}
```

重启终端后生效。

### 方案 2：第三方平台侧处理

如果你是 API 中转平台的运营者，可以在代理层做处理：

1. **剥离 billing header**：在收到请求后，从 system prompt 的第一个 text block 中移除以 `x-anthropic-billing-header:` 开头的文本
2. **设置 prompt_cache_key**：Kimi 团队建议在 OpenAI 兼容接口中设置 `prompt_cache_key` 为 session_id，帮助调度到相同缓存集群
3. **Anthropic 兼容接口**：把 session_id 放到 `metadata.user_id` 中

### 方案 3：等待官方修复

这个问题已经在 GitHub 上有讨论（Issue #24168）。期待后续版本中 Anthropic 能：

- 将 billing header 从 system prompt 文本移到 HTTP header
- 或者固定 cc_version 后缀，不再基于用户消息生成动态哈希

---

## 七、延伸：Kimi 团队的缓存最佳实践

Kimi 的 @Hx1u0 在帖子中还分享了几条实用建议：

**1. 日期信息的放置位置**

不要把日期放在 system prompt 的开头，而是：

- 以 System Message 的形式，放在最后一个 Turn 之前
- 或者在每个 Turn 的 User Message 中，以 Reminder 形式提供：
  ```
  <reminder>Current DateTime: 2026-02-14 15:55:38</reminder>
  ```

**2. 避免整点集中请求**

如果你在用定时任务调用 API（比如 OpenClaw 养龙虾），尽量避免设在整点（10:00、10:30），错峰可以显著降低 429 概率。

**3. 使用 prompt_cache_key**

在 OpenAI 兼容接口中显式设置 `prompt_cache_key` 字段（推荐用 session_id），有助于提高缓存命中率。

---

## 🎯 快速回顾

**问题**：Claude Code 在 system prompt 中注入动态 billing header，导致第三方 API **跨会话**缓存无法共享。

**影响场景**：频繁开新会话、并行开发、定时任务等跨会话场景，费用高 10 倍，速度慢 8 倍。同一会话内多轮对话不受影响。

**解决**：一行环境变量。

```bash
export CLAUDE_CODE_ATTRIBUTION_HEADER=false
```

**2.1.42 修了什么**：`cch` 固定了，日期移走了。

**2.1.42 没修什么**：`cc_version` 动态后缀还在，跨会话缓存仍无法共享。

**验证脚本**（支持 Kimi / DeepSeek）：https://blog.deepai.wiki/downloads/verify_prefix_cache_multivendor.py

---

## 相关阅读

- [多模型共识：让 Claude Code 调用 DeepSeek、通义千问、KIMI 的完整配置教程](/posts/multi-model-consensus/) — 国内多模型完整接入方案，与本篇缓存配置配合使用
- [Claude Code 完整安装与配置教程（支持国内模型）](/posts/claude-code-installation/) — 基础配置回顾
