目 录CONTENT

文章目录
AI

Claude Code + 本地 LLM 的 KV Cache 复用失效问题排查全记录

DarkAthena
2026-05-06 / 0 评论 / 0 点赞 / 8 阅读 / 0 字

Claude Code + 本地 LLM 的 KV Cache 复用失效问题排查全记录

环境信息

  • Claude Code: v2.1.114 (macos)
  • LLM 后端: LM Studio (v0.4.12) / llama.cpp (8853)
  • 模型: Qwen3-Coder-Next-Q4_K_M.gguf(Hybrid 注意力架构,含 SWA 层)
  • 硬件: AMD AI MAX 395(128GB vRAM )
  • 操作系统: Windows 11
  • 上下文窗口: 262144 tokens

一、问题现象

在使用 Claude Code 连接本地 LLM 后端进行日常开发时,发现每次对话轮次都存在严重的性能问题:

正常预期:Claude Code 采用增量对话模式,每一轮仅向 prompt 末尾追加几十到几百个 token。LLM 服务端应复用上一轮已计算的 KV Cache,仅对新增 token 进行 prompt evaluation,耗时应在亚秒级。

实际表现:每一次请求都触发了完整的 prompt 重新处理,约 24K tokens 的 prompt evaluation 耗时长达 55 秒,而新增 token 仅 30-50 个。这意味着 KV Cache 完全未被复用,每轮对话都在白白浪费算力。

llama.cpp 服务端日志中的关键报错:

slot 0 :KV cache reuse failed. Forcing full prompt re-processing.
n_past = 21, n_checkpoint = 8191
reason: no matching checkpoint found. common_prefix = 0

这表明尽管前后请求的 prompt 有极高的前缀重叠(理论上 >99%),但缓存复用机制完全失效。


二、初步分析:理解 KV Cache 与 Prompt Cache 复用机制

在深入排查之前,先梳理 LLM 推理中的关键概念:

2.1 KV Cache

Transformer 模型在推理时,每一层的 Key 和 Value 矩阵可以被缓存。当新请求的 prompt 前缀与之前请求相同时,已缓存的 KV 状态可以直接复用,无需重新计算。

2.2 LCP (Longest Common Prefix) 相似度匹配

llama.cpp 使用 LCP 相似度算法来决定是否复用某个 slot 的缓存:

  • sim:两个 prompt 的最长公共前缀占比(0~1),阈值默认为 0.1
  • f_keep:可保留的缓存 fraction(考虑 checkpoint 位置)
  • sim >= 0.1f_keep > 0 时,才会尝试复用缓存

2.3 Context Checkpoint

KV Cache 在特定 token 位置建立检查点(snapshot),默认每 8192 tokens 一个。复用时只能从最近的检查点位置开始,检查点之前的部分可以完整复用,之后的需要重新计算。

2.4 Slot 管理

llama.cpp 默认分配多个 slot(通常 4 个)以支持并行请求。Slot 选择策略有:

  • LRU (Least Recently Used):选择最久未使用的 slot
  • LCP Similarity:选择与当前请求前缀最相似的 slot

三、故障诊断:缩小排查范围

3.1 初步使用的模型——Qwen3.6-35B-A3B

排查初期,我们首先使用的模型是 Qwen3.6-35B-A3B(MoE 架构)。LM Studio 日志中出现了一条令人困惑的信息:

cache reuse is not supported - ignoring n_cache_reuse = 256

初看这条日志,很容易得出"该模型不支持 KV Cache 复用"的结论。但后续验证表明这是一个误导

实际验证:在对话达到 91 条消息(~50K+ tokens)时,prompt processing 从 0% 到 100% 仍在 <1 秒内完成,完全不可能是全量重处理。日志也确认 LCP slot 选择正常命中(sim_best = 0.999, f_keep = 1.000),连续多轮请求都是如此。

cache reuse is not supported 的真实含义:这条日志指的是不支持 llama.cpp 的 n_cache_reuse 批量化 KV 复用机制(一种更激进的跨批次缓存复用优化),而非不支持 KV Cache 本身。LM Studio 内部的 checkpoint-based KV state 恢复仍然生效——只要 LCP 选中同一个 slot 且 f_keep = 1.000,KV 状态就会从上一个 checkpoint 位置直接恢复,跳过已处理部分。

教训:日志中 cache reuse is not supported 不等于"完全不能用 cache"。需要结合实际 prompt processing 耗时和 sim_best/f_keep 指标综合判断。

3.2 第二步:观察 llama.cpp 日志

切换到 llama.cpp 后端运行,日志显示:

slot 0: evaluating [  0, 8191) ... done.
slot 0: checkpoint at 8191
slot 0: evaluating [  8191, 16383) ... done.
slot 0: checkpoint at 16383
slot 0: evaluating [ 16383, 24510) ... done.
slot 0: checkpoint at 24510

-- 下一轮请求 --
slot 0: KV cache reuse failed. n_past = 21

关键发现:

  1. 检查点分别在 8191、16383、24510 位置,但 n_past 仅为 21(这意味着只有 21 个 token 被认为可复用)
  2. common_prefix = 0,说明前缀匹配完全失败
  3. 日志中提到 "SWA/hybrid memory" 作为原因

3.3 第三步:怀疑 Claude Code 的 Attribution Header

Claude Code ≥2.1.36 版本会在每次请求的 prompt 前端注入一个 Attribution Header,包含时间戳等信息:

[Antropic] Claude Code v2.1.114 (2026-04-20T14:30:00+08:00)

由于时间戳每秒变化,这会导致 prompt 的最前面几个 token 就不同,直接破坏前缀匹配。

3.4 第四步:资料检索——发现社区已知问题

通过 Web 搜索,找到了关键参考:

syoyo 的文章提到使用 --checkpoint-every-nb 3 参数(注意:这个参数名后来被证明是过时的)。


四、测试验证:逐步定位有效参数

4.1 第一次尝试:--checkpoint-every-nb 3(失败)

.\llama-server.exe `
  -m "Qwen3-Coder-Next-Q4_K_M.gguf" `
  --port 1234 --host 0.0.0.0 `
  --no-context-shift --swa-full `
  -c 262144 -ngl 99 `
  --parallel 1 `
  --checkpoint-every-nb 3

结果:报错 error: invalid argument: --checkpoint-every-nb

教训:社区文章中的参数名可能已过时。通过 llama-server.exe --help | findstr checkpoint 查到正确参数名为 --checkpoint-every-n-tokens

4.2 第二次尝试:完整参数组合(成功)

.\llama-server.exe `
  -m "Qwen3-Coder-Next-Q4_K_M.gguf" `
  --port 1234 --host 0.0.0.0 `
  --no-context-shift --swa-full `
  -c 262144 -ngl 99 `
  --parallel 1 `
  --checkpoint-every-n-tokens 3

结果:Cache 复用成功!

slot 0: sim_best = 1.000, f_keep = 1.000
slot 0: n_past = 23881, new tokens = 124
prompt eval: 786ms  (之前: 55s)

Prompt evaluation 从 55 秒骤降至 786 毫秒,验证了 KV Cache 正常工作。


五、参数必要性分析:哪些是真正需要的?

问题解决后,回头审视——我们调整了很多东西(切换后端、改并发度、加参数),真的都需要吗?

5.1 逐个分析

参数作用对 Cache 复用的贡献
CLAUDE_CODE_ATTRIBUTION_HEADER=0禁用 Claude Code 注入的时间戳 header唯一关键配置。消除 prompt 前缀变化,解决根因
--parallel 1单 slot 运行❌ 不需要。llama.cpp 新版本已内置 LCP 相似度 slot 选择,能自动路由到缓存命中的 slot
--checkpoint-every-n-tokens 3缩小检查点间隔至 3 tokens❌ 不需要。默认 8192 间隔配合 LCP 匹配已足够(f_keep = 1.000
--no-context-shift禁止上下文滚动❌ 无关。25K/262K 远未触达上下文限制
--swa-fullSWA 层使用完整上下文❌ 影响模型质量,不影响缓存机制
从 LM Studio 切换到 llama.cpp调试需要❌ 两者都仅需 CLAUDE_CODE_ATTRIBUTION_HEADER=0

5.2 回到 LM Studio 的验证

切回 LM Studio 后端,开启 4 个并行 slot仅配置 CLAUDE_CODE_ATTRIBUTION_HEADER=0

sim_best = 0.997, f_keep = 1.000
prompt eval: 786ms, 124 tokens (之前: 53s, 23881 tokens)

结论:Cache 复用完美工作。

5.3 回到 llama.cpp 的验证

切回 llama.cpp 后端,去掉所有额外启动参数,使用最简配置,仅配置 CLAUDE_CODE_ATTRIBUTION_HEADER=0

.\llama-server.exe `
  -m "Qwen3-Coder-Next-Q4_K_M.gguf" `
  --port 1234 --host 0.0.0.0 `
  -c 262144 -ngl 99
# 第一轮(冷启动)
prompt eval time = 55531 ms / 24479 tokens    ← 全量处理

# 第二轮起
task 44:  prompt eval =  994 ms /  34 tokens  ← sim_best=0.999, f_keep=1.000
task 70:  prompt eval =  278 ms /  25 tokens  ← sim_best=0.999, f_keep=1.000
task 117: prompt eval =  456 ms /  59 tokens  ← sim_best=0.998, f_keep=1.000
task 176: prompt eval =  466 ms /  59 tokens  ← sim_best=0.998, f_keep=1.000

结论:同样完美工作! llama.cpp 新版本已内置 LCP 相似度 slot 选择和 prompt cache 机制(启动时可见 prompt cache is enabled),无需手动 --parallel 1 或调小 checkpoint 间隔。

5.4 最终确认

无论 LM Studio 还是 llama.cpp,无论 4 并行 slot 还是默认配置,KV Cache 复用的唯一必要条件就是 CLAUDE_CODE_ATTRIBUTION_HEADER=0


六、最终结论

6.1 根因

Claude Code v2.1.36+ 默认在每次请求中注入包含时间戳的 Attribution Header,导致 prompt 前缀逐次变化,LCP 相似度降至 0.086(远低于 0.1 阈值),KV Cache 复用完全失效。

6.2 修复方案

仅需一个配置:(macos)在 ~/.zshrc 中添加:

export CLAUDE_CODE_ATTRIBUTION_HEADER=0

6.3 性能提升

指标修复前修复后提升
Prompt Eval 时间~55s (23881 tokens)~786ms (124 tokens)70x
每轮对话响应延迟>60s<5s12x+
GPU 利用率低效(重复计算)高效(增量计算)

七、踩坑经验总结

  1. cache reuse is not supported 不等于完全不能用 cache:Qwen3.6-35B-A3B 日志中输出 cache reuse is not supported - ignoring n_cache_reuse = 256,但实际上 checkpoint-based KV state 恢复仍然生效,配置 CLAUDE_CODE_ATTRIBUTION_HEADER=0 后 prompt processing <1s 完成。这条日志仅表示不支持 n_cache_reuse 批量化优化,需结合实际耗时和 sim_best/f_keep 指标综合判断
  2. 社区文章的参数名可能过时--checkpoint-every-nb--checkpoint-every-n-tokens,永远以 --help 输出为准。社区方案中建议的 --parallel 1--checkpoint-every-n-tokens 3 在 llama.cpp 新版本中也已不再必要
  3. 不要过度工程化:调试时一次性加了 5 个参数,最后发现只需 1 个环境变量。解决问题后必须做减法验证每个参数的必要性
  4. llama.cpp 新版本已内置 LCP slot 选择和 prompt cache:启动时可见 prompt cache is enabled,slot 选择日志显示 selected slot by LCP similarity,无需手动 --parallel 1
  5. Claude Code 的 Attribution Header 是个隐蔽的坑:它只改变了 prompt 最前面几个 token,但足以让整个缓存机制瘫痪(sim 从 0.999 降至 0.086)

附录:关键日志对比

修复前(Cache 失效)

slot 0: KV cache reuse failed. Forcing full prompt re-processing.
n_past = 21, n_checkpoint = 8191
reason: no matching checkpoint found. common_prefix = 0
slot 0: evaluating [  0,  8191) ... done.     ← 重新处理全部
slot 0: evaluating [  8191,  16383) ... done.
slot 0: evaluating [ 16383,  24510) ... done.
prompt eval time: 55.123s

修复后 - llama.cpp(最简配置,Cache 命中)

# 冷启动
prompt eval time = 55531 ms / 24479 tokens

# 后续轮次
slot 1: selected slot by LCP similarity, sim_best = 0.999, f_keep = 1.000
slot 1: n_tokens = 24564, new_tokens = 25
prompt eval time = 278 ms / 25 tokens      ← 仅处理新增部分

修复后 - LM Studio(Cache 命中)

slot 3: sim_best = 0.997, f_keep = 1.000
prompt eval: 786ms, 124 tokens (之前: 53s, 23881 tokens)

本文基于 2026-04-20 的实际调试过程整理,Claude Code v2.1.114,模型 Qwen3-Coder-Next-Q4_K_M.gguf。

笔者补充:

本文是真实的AI对故障进行排查诊断的记录,且文章完全由AI自行整理完成。
笔者后续查了相关资料,claude code接非官方api,最好配上这三个环境变量

export CLAUDE_CODE_ATTRIBUTION_HEADER=0 #关闭计费头
export CLAUDE_CODE_ENABLE_TELEMETRY=0 #关闭使用情况统计
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 #停止后台ping

如果未生效,则在.claude/settings.json里配置

{...

"env": {
    "CLAUDE_CODE_ENABLE_TELEMETRY": "0",
    "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
    "CLAUDE_CODE_ATTRIBUTION_HEADER" : "0"
},
...}

这个问题如果一开始AI就去网上搜索去找相关资料,而不是质疑环境问题或者模型问题,很快就能定位根因。AI折腾半天,自以为是的各种改,而且中间得到了很多错误结论,是靠笔者通过实际测试来确认中间很多修改都是无用功,浪费了很多时间。好在还是有所收获,对kv cache的认知更清楚了一点。

0
AI
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin
博主关闭了所有页面的评论