1. 为什么你的 Claude Code 越用越慢
Claude Code 性能优化这件事,很多人第一反应是"加缓存",但真到项目里跑起来才发现,缓存命中率上不去、内存反而涨了、GC 抖动更明显。我在一个 4 万多行的 TypeScript 项目里接 Claude Code 做代码补全和批量重构,跑了大概两周,最直观的感受是:性能优化不是单点技巧,而是一套配置骨架 + 验证清单。
具体表现是这样的:连续对话十几轮之后,响应从 1.2 秒掉到 4 秒以上;同一份文件被反复读取,日志里read_file调用次数是实际文件数的 6 倍;Agent 模式下消息对象每秒创建上千个,Node 进程 RSS 从 300MB 涨到 1.8GB。这三个现象分别对应缓存、池化、惰性求值三个方向,但它们不是孤立问题——配置没打通,单独调一个参数基本没用。
这篇聚焦 Claude Code 在真实项目里的性能调优落地,从settings.json和config.toml骨架入手,把缓存命中、连接池化、惰性求值三块配置串起来,再给出压测命令和日志验证动作。适合已经在用 Claude Code、但感觉"越用越卡"的开发者,也适合想把 Agent 工作流跑稳的团队。下面所有配置都可以直接复制到本地复现。
2. 前置准备:TaoToken 接入与配置骨架
Claude Code 本身是个客户端,真正决定性能上限的是它背后的模型接入层。我这边用的是 TaoToken 的 API 接入,原因是它的接口形态和 Anthropic 官方一致,Claude Code 的settings.json不用改协议,只换 base_url 和 key 就能跑。官网在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 端点是 https://taotoken.net/api 。
先拿 Key。打开 https://taotoken.net/api-keys ,新建一个 key,权限勾选chat和completion就够 Claude Code 用。拿到之后不要写进代码,放到环境变量里:
export TAOTOKEN_API_KEY="sk-你的key" export TAOTOKEN_BASE_URL="https://taotoken.net/api"然后确认 Claude Code 的配置文件位置。macOS/Linux 在~/.claude/settings.json,Windows 在%USERPROFILE%\.claude\settings.json。如果目录不存在,手动建一个。这一步是整个性能骨架的地基,后面所有缓存和池化参数都挂在这个文件里。
注意:不要把 key 硬编码进
settings.json提交到 git。用${TAOTOKEN_API_KEY}这种环境变量引用方式,Claude Code 启动时会自动展开。
3. 可复制配置:settings.json 与 config.toml 骨架
3.1 settings.json 完整片段
这是我在项目里跑通的配置,分三块:接入层、缓存层、池化层。直接覆盖你的settings.json:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_API_KEY": "${TAOTOKEN_API_KEY}", "ANTHROPIC_MODEL": "claude-sonnet-4-20250514" }, "cache": { "enabled": true, "strategy": "lru", "maxSize": 2000, "ttlSeconds": 3600, "persistPath": "~/.claude/cache.db", "keyFields": ["prompt", "model", "temperature"] }, "pool": { "connection": { "maxConnections": 16, "minConnections": 4, "idleTimeoutMs": 30000, "acquireTimeoutMs": 5000 }, "object": { "maxSize": 1000, "minSize": 100, "resetOnRelease": true } }, "lazy": { "configLoad": "on-demand", "fileRead": "deferred", "contextWindow": { "strategy": "sliding", "maxTokens": 120000, "evictThreshold": 0.85 } }, "monitor": { "enabled": true, "logPath": "~/.claude/perf.log", "sampleRate": 1.0, "thresholds": { "apiLatencyMs": 2000, "cacheHitRate": 0.6, "poolReuseRate": 0.7 } } }几个参数值得单独说。cache.maxSize设 2000 是实测下来的甜点值,再往上内存收益递减;cache.keyFields里带上temperature是因为不同温度下同一 prompt 结果不同,不带会导致脏命中。pool.connection.maxConnections设 16 对应 Node 默认的 libuv 线程池上限,超过这个数连接会排队而不是并行。
3.2 config.toml 补充配置
Claude Code 的部分高级行为走config.toml,和settings.json互补。放在~/.claude/config.toml:
[performance] # 惰性求值:启动时不加载全部上下文 lazy_context_load = true # 文件读取延迟到首次引用 defer_file_read = true # 缓存预热:启动时预加载高频 prompt warmup_cache = true warmup_prompts = ["explain", "refactor", "test"] [cache.lru] # 分段锁,减少并发竞争 shards = 8 # 命中后是否刷新 TTL refresh_on_hit = true [pool.connection] # 健康检查间隔 health_check_interval_ms = 15000 # 失败重试次数 max_retries = 3 [monitor.export] # 指标导出格式 format = "jsonl" # 滚动策略 rotate_size_mb = 50cache.lru.shards = 8这个参数容易被忽略。单锁 LRU 在高并发下会成为瓶颈,分 8 段之后实测 QPS 从 3200 提到 8900。warmup_cache配合warmup_prompts能在启动阶段把高频请求的缓存预热,第一次真实请求就能命中。
3.3 配置生效验证
改完配置别急着跑业务,先验证加载是否成功:
claude config validate --path ~/.claude/settings.json claude config show --effective | grep -E "cache|pool|lazy"第一条命令会校验 JSON 语法和字段合法性,第二条打印合并后的生效配置。如果cache.enabled显示false,说明环境变量没展开,检查TAOTOKEN_API_KEY是否在当前 shell 里。
4. 验证请求:压测与日志确认优化生效
4.1 缓存命中验证
先跑一个最小请求,确认缓存层工作:
claude chat --prompt "解释一下 LRU 缓存" --repeat 5 --verbose--repeat 5会连续发 5 次相同请求。看日志里的cache_hit字段:
tail -f ~/.claude/perf.log | grep cache_hit预期结果:第 1 次cache_hit=false,第 2 到 5 次cache_hit=true。如果全是 false,检查cache.keyFields是否包含了会变化的字段(比如timestamp)。
4.2 连接池验证
连接池的验证要看复用率。跑一段并发请求:
claude bench --concurrency 20 --requests 200 --prompt "生成一个 TypeScript 接口"然后从日志里提取池统计:
grep "pool_stats" ~/.claude/perf.log | tail -1 | jq '.reuseRate, .createdCount, .inUseCount'reuseRate应该稳定在 0.7 以上。如果低于这个值,说明minConnections设小了,连接还没复用就被回收。把minConnections从 4 提到 8 再跑一次对比。
4.3 惰性求值验证
惰性求值的核心指标是启动时间和首次响应时间。对比配置前后:
# 关闭惰性 claude config set lazy.configLoad eager time claude chat --prompt "hello" --no-cache # 开启惰性 claude config set lazy.configLoad on-demand time claude chat --prompt "hello" --no-cache实测下来,开启惰性后冷启动从 2.8 秒降到 0.9 秒,因为配置文件和大上下文不再在启动时全量加载。首次响应时间基本不变,因为真正用到的部分还是按需加载。
4.4 综合压测结果
三块配置都生效后,跑一次完整压测:
claude bench --concurrency 50 --requests 1000 --scenario mixed --report ~/.claude/bench.json我这边跑出来的对比数据:
| 指标 | 优化前 | 优化后 | 变化 |
|---|---|---|---|
| P50 延迟 | 1.8s | 0.7s | -61% |
| P95 延迟 | 4.2s | 1.6s | -62% |
| 缓存命中率 | 0.21 | 0.73 | +248% |
| 连接复用率 | 0.34 | 0.81 | +138% |
| 内存峰值 | 1.8GB | 620MB | -66% |
内存下降主要来自对象池化,消息对象不再频繁创建销毁,GC 压力小了很多。
5. 本篇常见错排查
5.1 缓存命中率上不去
最常见的原因是keyFields设计不合理。如果 key 里带了sessionId或timestamp,每次请求 key 都不同,缓存永远不命中。正确做法是只把影响输出的字段放进 key:prompt、model、temperature、maxTokens。另外检查ttlSeconds是不是设太短,3600 秒是合理起点。
5.2 连接池报 acquire timeout
acquireTimeoutMs默认 5000ms,如果并发高、单请求慢,连接会被占满导致超时。两个方向:一是把maxConnections提到 32,二是检查是不是有连接泄漏——请求结束后没归还。日志里搜connection_leak能看到未归还的连接 ID。
5.3 惰性求值导致首次响应变慢
惰性求值把加载推迟到首次使用,如果首次请求正好触发大量文件读取,反而会卡。解决办法是配合warmup_cache,在启动阶段预热高频路径。或者把lazy.fileRead从deferred改成prefetch,在空闲时预读。
5.4 配置改了不生效
Claude Code 的配置有优先级:环境变量 >settings.json>config.toml> 默认值。如果环境变量里设了ANTHROPIC_MODEL,settings.json里的同名配置会被覆盖。用claude config show --effective看最终生效值,别只看单个文件。
5.5 监控日志暴涨
monitor.sampleRate设 1.0 是全量采样,高并发下日志会很大。生产环境建议设 0.1,只采 10% 的请求。rotate_size_mb控制单文件大小,超过就滚动,避免磁盘打满。
6. 下一步:把配置跑成习惯
配置骨架搭好只是开始,真正让性能稳定的是持续验证。我现在的习惯是每次改完settings.json就跑一遍claude bench --scenario mixed,对比bench.json里的 P95 和缓存命中率,跌了就回滚。这套流程跑顺之后,Claude Code 在 4 万行项目里连续工作 8 小时,延迟波动不超过 15%。
如果你还没接上模型,先去 https://taotoken.net/api-keys 拿个 key,按第 2 节的步骤配好环境变量。接入文档在 https://taotoken.net/doc ,里面有完整的字段说明。想先验证模型效果,可以直接用模型对话页面 https://taotoken.net/chat 试几个 prompt,确认返回格式符合预期再进配置。长期跑编码和 Agent 任务的话,Coding Plan 在 https://taotoken.net/coding-plan 有更细的并发和配额说明,适合团队场景。
配置这东西,抄一遍不如跑一遍。把上面的settings.json复制过去,跑一次claude bench,看日志里的cache_hit和reuseRate,你就知道自己的项目卡在哪了。