大型代码库与成本管理
本教程共 34 篇 · 第 31 篇 · 更新于 2026-07-26 · 约 8 分钟阅读
31. 大型代码库与成本管理
本节目标:学会在 monorepo 或百万行级代码库中让 Claude Code 只关注你正在处理的代码,同时掌握 token 成本跟踪和优化技巧,让每一分钱花在刀刃上。
当你的项目从几百个文件增长到几万个文件,Claude Code 的默认配置就开始力不从心了。根目录一个巨大的 CLAUDE.md 塞满了所有子系统的规则,Claude 每次读取文件都在无关代码上浪费 token,成本也跟着飙升。这一章就是解决这两个问题:让 Claude 专注于该干的活,同时把成本压下来。
为什么大型代码库需要特殊配置
想象你在一个大型商场找一家店。如果商场没有分区地图,你只能挨个逛,费时费力。Claude Code 在大型代码库里面临同样的问题——默认会读取大量无关文件,上下文窗口(Context Window)被无关内容填满,真正需要的信息反而被挤掉。
核心思路就一句话:限制 Claude 看到的范围,让它只处理你当前任务的代码。
分层 CLAUDE.md:按目录组织指令
小项目里一个根目录 CLAUDE.md 就够了。但 monorepo 有多个包,每个包技术栈不同,把所有规则塞一个文件里,Claude 每次启动都要加载全部,白白浪费上下文。
两级分层是最常见的做法
monorepo/
CLAUDE.md # 根指令:通用规则
packages/
api/
CLAUDE.md # API 包的专属指令
src/
web/
CLAUDE.md # 前端包的专属指令
src/
shared/
CLAUDE.md # 共享库指令
src/
根 CLAUDE.md 写全局规则:
这是一个 monorepo,在 packages/ 下有三个包:
- packages/api:使用 Express、TypeScript 和 PostgreSQL 的 Node.js REST API
- packages/web:使用 Vite、TypeScript 和 TailwindCSS 的 React 前端
- packages/shared:由 api 和 web 都使用的共享 TypeScript 实用程序
从包目录运行命令,而不是从 monorepo 根目录。
每个包都有自己的 tsconfig.json、package.json 和测试套件。
子目录的 CLAUDE.md 只写本区域的规则,比如 packages/api/CLAUDE.md:
这个包是 REST API 服务器。
- 运行测试:npm test(使用 Vitest)
- 运行开发服务器:npm run dev(端口 3001)
- 数据库迁移:npm run migrate
API 路由在 src/routes/ 中。每个路由文件导出一个 Express 路由器。
数据库查询在 src/db/ 中使用 Knex。永远不要在路由处理程序中写原始 SQL 字符串。
当你从 packages/api/ 启动 Claude,它加载根 CLAUDE.md 加上 packages/api/CLAUDE.md,不会加载 packages/web/ 的指令。
Tip从哪个目录启动
claude很关键。从子目录启动,Claude 只能访问该子树的文件,只加载该目录和祖先目录的CLAUDE.md。从根目录启动,则能访问所有文件,但子目录的CLAUDE.md只在 Claude 读取该目录文件时才按需加载。
排除不相关的 CLAUDE.md
如果你从根目录启动,但从不碰某些包(比如别的团队维护的),可以用 claudeMdExcludes 跳过它们:
{
"claudeMdExcludes": [
"**/packages/admin-dashboard/**",
"**/packages/legacy-*/**"
]
}
放在 .claude/settings.local.json 里只对自己生效,放在 .claude/settings.json 里对整个团队生效。
减少 Claude 读取的文件
光控制指令还不够,文件读取是另一个 token 消耗大户。
阻止读取构建产物和供应商代码
Claude 的搜索默认尊重 .gitignore,所以 node_modules/、dist/、build/ 这些不会出现在搜索结果里。但有些路径已经检入了 git,比如 vendor 目录或生成的代码,需要手动阻止:
{
"permissions": {
"deny": [
"Read(./**/dist/**)",
"Read(./**/build/**)",
"Read(./**/*.generated.*)",
"Read(./vendor/**)"
]
}
}
这些 deny 规则会阻止 Claude 用内置工具或 cat、head、grep、find 等命令读取这些路径。
用代码智能插件替代文件扫描
在大型代码库里找一个函数的定义,传统方式是 grep 搜索加读取多个候选文件,费时费 token。代码智能插件(Code Intelligence Plugin)把 Claude 连到语言服务器,直接跳转定义、查找引用。
# 安装 TypeScript 代码智能插件
/plugin install typescript-lsp@claude-plugins-official
官方市场有 TypeScript、Python、Go、Rust 等语言的插件。一次”转到定义”调用替代了好几次 grep 加文件读取,省下的 token 相当可观。
稀疏工作树:只检出需要的目录
--worktree 标志会在新的 git worktree 中启动会话,让改动与主检出隔离。默认检出整个仓库,在大型仓库里又慢又占空间。
worktree.sparsePaths 用 git sparse-checkout 只检出你指定的目录:
{
"worktree": {
"sparsePaths": [
".claude",
"packages/api",
"packages/shared"
],
"symlinkDirectories": [
"node_modules"
]
}
}
这样 worktree 只包含 .claude/、packages/api/、packages/shared/ 和根级文件(如 package.json、锁文件),node_modules 通过符号链接指向主仓库的副本,不重复占用磁盘。
Note
sparsePaths里列目录而不是单个文件。根级文件(package.json、tsconfig.base.json、锁文件)始终会检出,但根级目录不会——如果你需要根目录的.claude/,记得在列表里加上.claude。
跨包和跨仓库访问
从 packages/api/ 启动时,Claude 只能访问这个目录。如果任务需要改 packages/shared/ 里的共享类型,得额外授权。
两种方式:
# 方式一:启动时传参
claude --add-dir ../shared
# 方式二:写在设置里(对整个团队生效)
{
"permissions": {
"additionalDirectories": [
"../shared",
"../web"
]
}
}
--add-dir 会加载目标目录的 skills,additionalDirectories 只授予文件读写权限,不加载 skills 和 CLAUDE.md。
按目录放 Skills
每个子目录可以有自己的 skills,只在 Claude 处理该区域文件时按需加载:
packages/api/.claude/skills/api-testing/SKILL.md
packages/web/.claude/skills/component-patterns/SKILL.md
API 的测试 skill 在前端工作时不会加载,反之亦然。这比把所有指令塞进一个 CLAUDE.md 高效得多。
Tip目标是把
CLAUDE.md控制在 200 行以内。特定工作流的详细指令(如 PR 审查流程、数据库迁移步骤)移到 skills 里,只在需要时加载。
成本跟踪:钱花哪了
说完了怎么省,先看看怎么查。不知道钱花在哪,优化就无从谈起。
/usage 查看当前会话
Total cost: $0.55
Total duration (API): 6m 19.7s
Total duration (wall): 6h 33m 10.2s
Total code changes: 0 lines added, 0 lines removed
在 Pro、Max、Team 或 Enterprise 计划上,/usage 还会显示计划使用量明细,按 skills、subagents、plugins、MCP 服务器分别统计占比。按 d 看过去 24 小时,按 w 看过去 7 天。
Note
/usage里的美元数字是从 token 计数本地估算的,可能与实际账单有出入。权威计费以 Claude Console 使用情况页面为准。
设置支出限制
Pro 和 Max 计划可以用 /usage-credits 管理使用额度:
- 启用使用额度
- 购买更多额度(固定套餐或自定义金额)
- 设置每月支出限制
- 配置余额低于阈值时自动充值
Teams 和 Enterprise 通过管理控制台设置组织级支出限制。
组织级成本管理
不同接入方式,查看和限制成本的地方不同:
| 接入方式 | 查看支出 | 限制支出 |
|---|---|---|
| Teams/Enterprise | 组织分析支出报告 | 管理员设置支出限制 |
| Claude Console (API) | Console 使用情况页面 | 工作区支出限制 |
| 云提供商(Bedrock 等) | 云计费控制台 | 云预算控制 |
如果需要近实时的每用户指标,OpenTelemetry 导出是唯一选择,能把 token 和成本指标流式传到你自己的监控系统。
Token 优化实战策略
成本随上下文大小增长:Claude 处理的上下文越多,token 消耗越大。以下是经过验证的降本策略。
主动管理上下文
# 切换到不相关任务前清除上下文
/clear
# 压缩对话时指定保留重点
/compact Focus on code samples and API usage
也可以在 CLAUDE.md 里写默认的压缩指令:
# Compact instructions
When you are using compact, please focus on test output and code changes
Warning长会话从不清除是成本失控的头号原因。每条消息都带着之前的完整历史,对话越长,单次请求的 token 越多。在任务之间用
/clear重新开始,用/rename给会话起名方便后续/resume找回来。
选对模型
Sonnet 处理大多数编码任务效果不错,成本远低于 Opus。把 Opus 留给复杂的架构决策和多步推理。
# 会话中途切换模型
/model sonnet
# 在配置里设默认模型
/config
子代理(Subagent)做简单任务时,可以在配置里指定 model: haiku,更省钱。
减少 MCP 服务器开销
MCP 工具定义默认被延迟加载,只有工具名称进入上下文。但还是建议:
- 优先用 CLI 工具(
gh、aws、gcloud)而非 MCP 服务器,因为 CLI 不添加工具列表 - 用
/mcp查看配置的服务器,禁用没在用的 - 用
/context查看什么占了上下文
用 Hooks 预处理数据
Hook 可以在 Claude 看到数据之前先过滤。比如 Claude 要读 10000 行日志找错误,hook 先 grep 出 ERROR 行,只把匹配的几十行返回,token 从几万降到几百。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "~/.claude/hooks/filter-test-output.sh"
}
]
}
]
}
}
过滤脚本把测试命令改成只输出失败项:
#!/bin/bash
input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command')
if [[ "$cmd" =~ ^(npm test|pytest|go test) ]]; then
filtered_cmd="$cmd 2>&1 | grep -A 5 -E '(FAIL|ERROR|error:)' | head -100"
echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"allow\",\"updatedInput\":{\"command\":\"$filtered_cmd\"}}}"
else
echo "{}"
fi
把冗长操作委托给子代理
跑测试、拉文档、处理日志这类操作输出量大,委托给子代理执行,冗长输出留在子代理的上下文里,只有摘要返回主对话。
调整扩展思考
扩展思考默认启用,对复杂推理很有帮助,但思考 token 按输出 token 计费,一次请求可能消耗数万个 token。简单任务可以降低推理强度或设思考预算上限:
# 降低推理强度
/effort
# 设置思考 token 上限
# 在环境变量中设置
MAX_THINKING_TOKENS=8000
写具体的提示
模糊的请求(如”改进此代码库”)会触发大范围扫描。具体的请求(如”向 auth.ts 中的 login 函数添加输入验证”)让 Claude 精准定位,文件读取最少。
Prompt Caching:自动省钱机制
Prompt caching 是 Claude Code 自动管理的功能,但理解它的工作原理能帮你避免意外让缓存失效。
缓存怎么工作
每次发消息,Claude Code 都重新发送完整上下文给 API。没有缓存的话,每次都要重新处理全部历史。有了缓存,API 重用已处理过的内容,只处理新增部分。
请求内容按层组织,越靠前越少变动:
| 层 | 内容 | 什么时候变 |
|---|---|---|
| 系统提示 | 核心指令、工具定义、输出样式 | 工具集合变化或 Claude Code 升级 |
| 项目上下文 | CLAUDE.md、自动内存 | 会话开始或 /clear、/compact 后 |
| 对话 | 你的消息、Claude 响应、工具结果 | 每个回合 |
对话层的变化不影响前面两层的缓存。系统提示变了,后面全部失效。
哪些操作会让缓存失效
以下操作会导致下一个请求缓存未命中,变得更慢更贵:
- 切换模型:每个模型有独立缓存,
/model切换后整个对话历史重新处理 - 更改推理强度:同模型每个 effort 级别也有独立缓存
- 启用快速模式(Fast Mode):添加了请求头,缓存键改变
- 连接或断开 MCP 服务器:工具定义变化(延迟加载的工具除外)
- 启用或禁用插件:提供 MCP 服务器的插件会触发失效
- 拒绝整个工具:如添加裸
Bash到 deny 规则,会从系统提示中移除该工具定义 - 压缩对话:
/compact用摘要替换历史,对话层失效 - 升级 Claude Code:新版本通常更新系统提示
Tip在会话开始时就选好模型和推理强度,然后在任务之间的自然中断处执行
/compact。任务中途做的改变越少,缓存命中率越高。
哪些操作不破坏缓存
好消息是,这些常见操作是缓存安全的:
- 编辑仓库中的文件(文件内容只在 Claude 读取时进入上下文)
- 会话中途编辑
CLAUDE.md(不会失效,但也不会生效,需重启或/clear) - 更改输出样式(同上)
- 切换权限模式(除非用 opusplan 模型设置)
- 调用 skills 和命令(在调用点注入,不影响前面的内容)
- 运行
/recap(摘要附加到对话末尾) - 重绕对话(
/rewind截断回之前的回合,命中更早的缓存) - 生成子代理
缓存生命周期
缓存条目在不活动后过期。每次命中缓存会重置计时器,所以持续工作时缓存保持温暖。
两种 TTL(生存时间):
- 5 分钟:API 密钥和第三方提供商默认,更便宜
- 1 小时:Claude 订阅默认自动启用,更长的中断也能保持缓存
API 用户可以用环境变量选择 1 小时 TTL:
ENABLE_PROMPT_CACHING_1H=1
Note缓存限定在一台机器和一个目录。系统提示嵌入了工作目录、平台、shell 等信息,所以不同目录的会话构建不同前缀,无法共享缓存。同一目录的并行会话可以共享。
Agent 团队的成本控制
Agent 团队生成多个 Claude 实例,每个维护自己的上下文窗口。团队在 Plan Mode 运行时,token 消耗大约是标准会话的 7 倍。
控制成本的要点:
- 队友用 Sonnet,平衡能力和成本
- 保持团队规模小,token 使用与团队规模成正比
- 生成提示要精简,队友会自动加载 CLAUDE.md、MCP 和 skills
- 工作完成后关闭队友,活跃的队友持续消耗 token
把所有配置整合在一起
一个 packages/api/ 区域的完整配置示例:
{
"worktree": {
"sparsePaths": [
".claude",
"packages/api",
"packages/shared"
],
"symlinkDirectories": [
"node_modules"
]
},
"permissions": {
"additionalDirectories": [
"../shared"
],
"deny": [
"Read(./**/dist/**)",
"Read(./**/build/**)"
]
}
}
加上按目录的 CLAUDE.md 和 skills,从 packages/api/ 启动 Claude 时:
- 加载根
CLAUDE.md和packages/api/CLAUDE.md,跳过packages/web/ - 能读写
packages/api/和packages/shared/ - 跳过
dist/和build/的读取 - api-testing skill 按需可用
- worktree 只检出需要的目录
这套配置让 Claude 在大型代码库里依然精准高效,不会在无关代码上浪费 token。
小结
大型代码库和成本管理是两个紧密相关的话题。限制 Claude 看到的范围就是在省钱,而省下的每一 token 都让 Claude 在真正重要的代码上表现更好。记住几个关键习惯:从子目录启动、分层放指令、用 hooks 过滤、选对模型、任务间清除上下文。这些加起来,能让你的成本和企业平均值(约每个开发者每个活跃日 13 美元)持平甚至更低。