你不知道的 Claude Code:架构、治理与工程实践
作者: @HiTw93
原文: https://x.com/i/status/2032091246588518683
字数: 8000+ 字深度长文
点赞: 2510 | 转推: 566 | 浏览: 41万+
太长不读
今天这篇文章源于最近半年深度使用 Claude Code、两个账号每月 40 刀氪金换来的一些踩坑经验。
刚开始我也把它当 ChatBot 用,后来很快发现不对劲:上下文越来越乱、工具越来越多但效果越来越差、规则越写越长却越不遵守。折腾了一段时间,研究了 Claude Code 本身之后才意识到,这不是 Prompt 问题,而是这套系统的设计就是这样的。
这篇文章想和大伙聊聊这几个事:
- Claude Code 底层怎么运作
- 上下文为什么会乱以及怎么治理
- Skills 和 Hooks 应该怎么设计
- Subagents 的正确用法
- Prompt Caching 的架构影响
- 怎么写一个真正有用的 CLAUDE.md
1. 它底层是怎么运行的
Claude Code 的核心不是”回答”,而是一个反复循环的代理过程:
用户输入 → 工具选择 → 工具执行 → 结果解析 → 下一步决策 → ...
用了一段时间才意识到,卡住的地方几乎从来不是模型不够聪明,更多时候是:
- 给了它错误的上下文
- 写出来了但根本没法判断对不对
- 也没法撤回
真正要关注的五个层面:
| 层面 | 关注点 | 出问题时的表现 |
|---|---|---|
| 上下文层 | 加载顺序、污染、压缩 | 结果不稳定,同一条指令前后不一致 |
| 控制层 | 循环、决策、安全边界 | 自动化失控,停不下来或越界操作 |
| 验证层 | 测试、lint、类型检查 | 改完代码跑不过,不知道哪里挂 |
| 工具层 | 数量、schema、返回格式 | 选错工具、传错参数、返回解析失败 |
| 持久层 | 文件读写、状态管理 | 状态漂移,文件被意外修改或覆盖 |
对着这几个面看,很多问题就好排查了。
2. 概念边界:MCP / Plugin / Tools / Skills / Hooks / Subagents
| 概念 | 核心区别 | 使用场景 |
|---|---|---|
| Tool | 单个原子能力 | 给 Claude 新动作能力 |
| MCP | 标准化协议下的工具集合 | 连接外部系统(GitHub、数据库等) |
| Plugin | 跨项目分发的打包方案 | 发布到社区共享 |
| Skill | 工作流 + 知识 + 约束 | 特定任务的标准化方法 |
| Hook | 强制拦截点 | 阻断、校验、通知 |
| Subagent | 隔离执行环境 | 复杂任务分解、并行处理 |
简单记:给 Claude 新动作能力用 Tool/MCP,给它一套工作方法用 Skill,需要隔离执行环境用 Subagent,要强制约束和审计用 Hook,跨项目分发用 Plugin。
3. 上下文工程:最重要的系统约束
很多人把上下文当”容量问题”,但卡住的地方通常不是不够长,而是太吵了——有用的信息被大量无关内容淹没。
真实的上下文成本构成
Claude Code 的 200K 上下文并非全部可用:
System Prompt: ~2,000 tokens (固定)
Tools/MCP: ~4,000-6,000 tokens 每 Server
CLAUDE.md: ~500-2,500 tokens
对话历史: 动态增长
文件引用: 按需加载
一个典型 MCP Server(如 GitHub)包含 20-30 个工具定义,每个约 200 tokens,合计 4,000-6,000 tokens。接 5 个 Server,光这部分固定开销就到了 25,000 tokens(12.5%)。
推荐的上下文分层
System (固定) → 不频繁变化的核心约束
CLAUDE.md → 项目级通用约定
Rules (.claude/rules/) → 路径/语言特定规则
Skills (按需加载) → 任务特定工作流
对话历史 → 当前会话状态
文件引用 → 正在处理的代码
说白了:偶尔用的东西就不要每次都加载进来。
上下文最佳实践
- 保持 CLAUDE.md 短、硬、可执行
- 优先写命令、约束、架构边界
- Anthropic 官方 CLAUDE.md 约 2.5K tokens,可以参考
- 大型参考文档拆到 Skills 的 supporting files
- 不要塞进 SKILL.md 正文
- 使用 .claude/rules/ 做路径/语言规则
- 不让根 CLAUDE.md 承担所有差异
-
任务切换优先 /clear,同一任务进入新阶段用 /compact
- 把 Compact Instructions 写进 CLAUDE.md
- 压缩后必须保留什么由你控制,不由算法猜
压缩机制的陷阱
默认压缩算法按”可重新读取”判断,早期的 Tool Output 和文件内容会被优先删掉,顺带把架构决策和约束理由也一起扔了。两小时后再改,可能根本不记得两小时前定了什么。
解决方案:在 CLAUDE.md 里写明 Compact Instructions。
还有一种更主动的方案:HANDOFF.md —— 在开新会话前,让 Claude 写一份交接文档,把当前进度、尝试过什么、哪些走通了、哪些是死路、下一步该做什么写清楚。下一个 Claude 实例只读这个文件就能接着做。
Plan Mode 的工程价值
Plan Mode 的核心是把探索和执行拆开:
- 探索阶段以只读操作为主
- Claude 可以先澄清目标和边界,再提交具体方案
- 执行成本在计划确认之后才发生
对于复杂重构、迁移、跨模块改动,这样做比”急着出代码”有用得多。
进阶玩法:开一个 Claude 写计划,再开一个 Codex 以”高级工程师”身份审这个计划,让 AI 审 AI,效果很好。
4. Skills 设计:不是模板库,是用的时候才加载的工作流
一个好 Skill 应该满足:
- 描述要让模型知道”何时该用我”,而不是”我是干什么的”
- 有完整步骤、输入、输出和停止条件,别写了个开头没有结尾
- 正文只放导航和核心约束,大资料拆到 supporting files 里
- 有副作用的 Skill 要显式设置
disable-model-invocation: true
Skill 的三种典型类型
类型一:检查清单型(质量门禁)
# release-checklist.skill
name: release-checklist
description: 在发布前执行完整检查清单,确保不遗漏关键步骤
steps:
- 检查版本号已更新
- 确认 CHANGELOG 已更新
- 运行完整测试套件
- 检查文档是否同步
- ...
类型二:工作流型(标准化操作)
# migration-workflow.skill
name: migration-workflow
description: 执行高风险配置迁移,包含显式回滚步骤
steps:
- 创建备份
- 在隔离环境测试
- 执行迁移
- 验证结果
- 如失败则执行回滚
类型三:领域专家型(封装决策框架)
# debug-framework.skill
name: debug-framework
description: 运行时出问题按固定路径收集证据,不要瞎猜
steps:
- 收集错误日志
- 检查环境状态
- 定位问题范围
- 提出修复方案
Skills 反模式
- ❌ 描述过短:
description: help with backend(任何后端工作都能触发) - ❌ 正文过长:几百行工作手册全塞进 SKILL.md 正文
- ❌ 一个 Skill 覆盖 review、deploy、debug、docs、incident 五件事
- ❌ 有副作用的 Skill 允许模型自动调用
5. 工具设计:怎么让 Claude 少选错
给 Claude 的工具和给人写的 API 不是一回事。给人用的 API 追求功能齐全,但给 agent 用,重点不是功能堆得多完整,而是让它更容易用对。
好工具 vs 坏工具
| 好工具 | 坏工具 |
|---|---|
| 名称前缀按系统分层 | 命名随意,模型分不清 |
| 参数语义清晰 | 参数模糊,模型传错 |
| 错误响应教模型修正 | 只抛 opaque error code |
| 高层任务工具 | 过多底层碎片工具 |
从 Claude Code 内部工具演进学到的
AskUserQuestion 工具的诞生:
- 第一版:给已有工具加 question 参数 → Claude 经常忽略
- 第二版:要求特定 markdown 格式 → Claude 经常”忘了”写
- 第三版:独立的 AskUserQuestion 工具 → 调用即暂停,没有歧义
核心原则:既然你就是要 Claude 停下来问一句,那就直接给它一个专门的工具。加个 flag 或者约定一段输出格式,很多时候它一顺手就略过去了。
6. Hooks:在 Claude 执行操作前后,强制插入你自己的逻辑
Hooks 不是”自动运行的脚本”,而是把不能交给 Claude 临场发挥的事情,重新收回到确定性的流程里。
当前支持的 Hook 点
SessionStart- 会话开始时BeforeToolUse- 工具调用前AfterToolUse- 工具调用后BeforeEdit- 文件编辑前AfterEdit- 文件编辑后
适合 vs 不适合放到 Hooks
适合:
- 阻断修改受保护文件
- Edit 后自动格式化/lint/轻量校验
- SessionStart 后注入动态上下文
- 任务完成后推送通知
不适合:
- 需要读大量上下文的复杂语义判断
- 长时间运行的业务流程
- 需要多步推理和权衡的决策
Hooks + Skills + CLAUDE.md 三层叠加
CLAUDE.md: 声明"提交前必须通过测试和 lint"
↓
Skill: 告诉 Claude 在什么顺序下运行测试、如何看失败、如何修复
↓
Hook: 对关键路径执行硬性校验,必要时阻断
三样少任何一层都会有漏洞。只写 CLAUDE.md 规则,Claude 经常当没看见;只靠 Hooks,细节判断又做不了。
7. Subagents:派一个独立的 Claude 去干一件具体的事
Subagent 就是从主对话派出去的一个独立 Claude 实例:
- 有自己的上下文窗口
- 只用你指定的工具
- 干完汇报结果
核心价值不是”并行”,而是隔离 —— 扫代码库、跑测试、做审查这类会产生大量输出的事,交给 Subagent 做,主线程只拿摘要,不会被中间过程污染。
配置时要显式约束
subagent:
tools: [read, grep, bash] # 限定能用什么工具
disallowedTools: [edit, write] # 禁止使用的工具
model: claude-3-5-sonnet # 探索任务用 Haiku,重要审查用 Opus
maxTurns: 50 # 防止跑飞
isolation: worktree # 需要动文件时隔离文件系统
常见反模式
- ❌ 子代理权限和主线程一样宽,隔离没有意义
- ❌ 输出格式不固定,主线程拿到没法用
- ❌ 子任务之间强依赖,频繁要共享中间状态
8. Prompt Caching:Claude Code 内部架构的核心
“Cache Rules Everything Around Me” —— 对 agent 同样如此。
Claude Code 的整个架构都是围绕 Prompt 缓存构建的。高缓存命中率不只降低成本,也帮助创造更宽松的速率限制。Anthropic 甚至会对命中率运行告警,太低直接宣布 SEV。
为缓存设计的 Prompt Layout
Prompt 缓存是按前缀匹配工作的:
[ System Prompt - 缓存 ]
[ Tools - 缓存 ]
[ CLAUDE.md - 缓存 ]
[ 对话历史 - 动态 ]
↓ cache_control 断点
[ 当前消息 - 不缓存 ]
破坏缓存的常见陷阱:
- 在静态 System Prompt 中放入带时间戳的内容
- 非确定性地打乱工具定义顺序
- 会话中途增删工具
解决方案:动态信息(如当前时间)放到用户消息里传进去,系统 Prompt 不动,缓存就不会被打坏。
defer_loading:工具的延迟加载
Claude Code 有数十个 MCP 工具,每次请求全量包含会很贵。解决方案是发送轻量级 stub,只有工具名,标记 defer_loading: true。模型通过 ToolSearch 工具”发现”它们,完整的工具 schema 只在模型选择后才加载。
9. 验证闭环:没有 Verifier 就没有工程上的 Agent
「Claude 说完成了」其实没啥用,你得能知道:
- 它做没做对
- 出了问题能退回来
- 过程还能查
这才算数。
Verifier 的层级
最低层: 命令退出码、lint、typecheck、unit test
↓
中间层: 集成测试、截图对比、contract test
↓
更高层: 生产日志验证、监控指标、人工审查清单
核心原则
假如一个任务你都说不清楚「Claude 怎么才算做对了」,那它大概率也不适合直接丢给 Claude 自动完成。
10. 如何写一个好的 CLAUDE.md
CLAUDE.md 是你和 Claude 之间的协作契约,不是团队文档,也不是知识库,里面只放那些每次会话都得成立的事。
应该放什么
- ✅ 怎么 build、怎么 test、怎么跑(最核心)
- ✅ 关键目录结构与模块边界
- ✅ 代码风格和命名约束
- ✅ 那些不明显的环境坑
- ✅ 绝对不能干的事(NEVER 列表)
- ✅ 压缩时必须保留的信息(Compact Instructions)
不该放什么
- ❌ 大段背景介绍
- ❌ 完整 API 文档
- ❌ 空泛原则,如”写高质量代码”
- ❌ Claude 通过读仓库即可推断的显然信息
- ❌ 大量背景资料和低频任务知识(这些放到 Skills)
高质量模板
# CLAUDE.md
## 构建与测试
- 构建: `make build`
- 测试: `make test`
- 运行: `make dev`
## 关键目录
- `/src/core` - 核心逻辑,禁止直接调用外部 API
- `/src/adapters` - 外部集成,必须 interface 隔离
- `/tests` - 测试文件,命名必须 `*_test.go`
## 代码风格
- 函数名用 verbNoun 格式
- 错误处理必须 wrap context
- 禁止在 core 层 import adapters
## 环境坑
- 需要 Go 1.21+
- 本地开发需要 Docker Compose 启动依赖服务
- `make setup` 会配置好一切
## NEVER
- 不要直接修改 generated 文件
- 不要在 core 层打日志(用中间件)
## Compact Instructions
压缩时必须保留:
1. 构建和测试命令
2. 目录边界规则
3. NEVER 列表
让 Claude 维护自己的 CLAUDE.md
每次纠正 Claude 的错误后,让它自己更新 CLAUDE.md:
“Update your CLAUDE.md so you don’t make that mistake again.”
Claude 在给自己补这类规则时其实还挺好用,用久了确实越来越少犯同样的错。
11. 高频命令的工程意义
| 命令 | 核心作用 |
|---|---|
/clear |
清空对话历史,重新开始 |
/compact |
压缩上下文,保留关键信息 |
/context |
查看当前上下文消耗 |
/simplify |
三维检查代码(复用、质量、效率) |
/rewind |
回到某个 checkpoint 重新总结 |
/btw |
快速旁路问答,不打断主任务 |
/insight |
让 Claude 分析当前会话,提炼值得沉淀的内容 |
双击 ESC |
回溯到上一条输入重新编辑 |
12. 配置健康检查
作者开源了一个 Skill 项目 tw93/claude-health,可以一键检查 Claude Code 配置状态:
npx skills add tw93/claude-health
装好之后在任意会话里跑 /health,它会自动识别项目复杂度,对 CLAUDE.md、rules、skills、hooks、allowedTools 和实际行为模式各跑一遍检查,输出一份优先级报告。
13. 结语
用 Claude Code 大概会经历三个阶段:
第一阶段:当 ChatBot 用,问问题、改代码
↓
第二阶段:意识到它是 agent,开始折腾工具、rules、skills
↓
第三阶段:关注点变成「怎么让 agent 在约束下自己跑起来」
到了第三阶段,关注点会悄悄变掉,从「这个功能怎么用」变成「怎么让 agent 在约束下自己跑起来」,两件事感觉差很多。
有一个问题挺值得想的:假如一个任务你说不清楚「什么叫做完」,那大概率也不适合直接扔给 Claude 自主完成。 验证标准本身都没有,Claude 再聪明也跑不出正确答案。
这些是半年折腾下来的一些总结,肯定还有很多没有挖掘到的地方,如果大伙有用得更 6 的技巧,欢迎交流。
原文作者: @HiTw93
整理时间: 2026-03-13
原文链接: https://x.com/i/status/2032091246588518683