Claude Code 工程实践手册
核心概念
Claude Code 不是聊天机器人,而是一个六层架构的 Agent 系统。核心执行模型是迭代式 Agent 循环:收集上下文 → 执行行动 → 验证结果 → [完成或循环]
六层架构
- CLAUDE.md - 项目契约层
- Hooks/Permissions - 控制层
- Skills - 工作流层
- Tools/MCP - 工具层
- Memory - 记忆层
- Subagents - 隔离执行层
关键原则:过度关注某一层会导致系统不稳定。CLAUDE.md 太长会污染上下文,工具太多会增加选择噪音,子代理太多会导致状态漂移。
上下文管理
Context 预算分配
200K 总上下文
├── 固定开销 (~15-20K)
│ ├── 系统指令: ~2K
│ ├── Skill 描述符: ~1-5K
│ ├── MCP 工具定义: ~10-20K ← 最大隐藏开销
│ └── LSP 状态: ~2-5K
├── 半固定 (~5-10K)
│ ├── CLAUDE.md: ~2-5K
│ └── Memory: ~1-2K
└── 动态可用 (~160-180K)
├── 对话历史
├── 文件内容
└── 工具调用结果
上下文分层策略
- 常驻 → CLAUDE.md: 项目契约/构建命令/禁止事项
- 路径加载 → rules: 语言/目录/文件类型特定规则
- 按需加载 → skills: 工作流/领域知识
- 隔离加载 → subagents: 重量级探索/并行研究
- 永不在上下文 → hooks: 确定性脚本/审计/阻塞性检查
关键实践
- 保持 CLAUDE.md 短小精悍 - 只包含命令、约束、架构边界
- 使用 .claude/rules/ - 路径和语言特定规则,不要让根 CLAUDE.md 处理所有变体
- 主动使用 /context - 检查消费情况,不要等待自动压缩
- 任务切换用 /clear - 同任务的新阶段用 /compact
- 编写 Compact Instructions - 决定什么保留,而不是让算法决定
Compact Instructions 示例
## Compact Instructions
压缩时按优先级保留:
1. 架构决策 (NEVER summarize)
2. 修改的文件及其关键变更
3. 当前验证状态 (pass/fail)
4. 开放 TODO 和回滚笔记
5. 工具输出 (可删除,只保留 pass/fail)HANDOFF.md 模式
在结束会话前,让 Claude 记录当前状态:尝试了什么、什么有效、什么失败、下一步应该做什么。下一个 Claude 实例可以从这个文件继续,而不依赖压缩质量。
Skills 设计模式
Hooks 设计原则
合适用途
- 保护文件阻止修改
- Edit 后自动格式化/lint/轻量验证
- SessionStart 后注入动态上下文 (Git 分支、环境变量)
- 任务完成后推送通知
不合适用途
- 需要大量上下文的复杂语义判断
- 长时间运行的业务流程
- 需要多步推理和权衡的决策 (这些属于 skills 或 subagents)
Hooks + Skills + CLAUDE.md 三层栈
- CLAUDE.md: 声明”提交前必须通过测试和 lint”
- Skill: 告诉 Claude 按什么顺序运行测试、如何读取失败、如何修复
- Hook: 关键路径上的硬验证,必要时阻止
工具设计原则
Agent 工具 vs 人类 API 的差异
人类 API 通常优化功能完整性,Agent 工具应该优化正确选择和正确使用。
实践原则
- 按系统或资源层前缀命名:
github_pr_*,jira_issue_* - 支持
response_format: concise / detailed(大响应) - 错误响应要有纠正性,不返回不透明代码
- 高级任务工具可合并时,不要暴露太多低级片段
AskUserQuestion 演进案例
Anthropic 尝试了三种方法让 Claude 停下来问用户问题:
-
V1: 在现有工具(如 Bash)中添加 question 参数
- 结果:Claude 经常忽略参数,继续执行而不暂停
-
V2: 要求 Claude 发出特定 markdown 格式,由外层解析暂停
- 问题:没有硬强制,质疑路径仍然脆弱
-
V3: 独立的 AskUserQuestion 工具
- Claude 必须显式调用它来提问,工具调用本身成为暂停信号
- 这比前两个版本稳定得多
实践启示:如果你想让 Claude 停下来提问,给它一个专用工具。标志和输出格式约定对模型来说太容易跳过。
Prompt Caching 的架构影响
Prompt caching 不只是成本优化,它塑造架构。高缓存命中率降低成本、改善延迟、使更宽松的速率限制成为可能。
缓存工作原理
通过前缀匹配工作,从请求开始到每个 cache_control 断点的内容被缓存。顺序很重要。
Claude Code 的提示顺序
- System Prompt → 静态,锁定
- Tool Definitions → 静态,锁定
- Chat History → 动态,在后面
- Current user input → 最后
破坏缓存的行为
- 在静态系统提示中放入带时间戳的内容 (每次都会变)
- 非确定性地打乱工具定义顺序
- 会话中途添加/删除工具
动态信息处理
当前时间等动态信息怎么办?不要放在系统提示中。放在后面的消息中。这保留缓存稳定性。
模型特定缓存
Prompt 缓存是模型特定的。如果你用 Opus 聊了 100K tokens,想问一个简单问题,切换到 Haiku 实际上比继续用 Opus 更贵,因为你要为 Haiku 重建整个缓存。
如果真需要切换,通过 subagent 移交: Opus 为另一个模型准备”移交消息”,解释要完成的任务。
验证层级
最低: 命令退出码、lint、typecheck、单元测试
中等: 集成测试、截图对比、合约测试、冒烟测试
更高: 生产日志验证、监控指标、人工审查检查清单
定义完成标准
## Verification
后端更改:
- 运行 `make test` 和 `make lint`
- API 更改:更新 `tests/contracts/` 下的合约测试
UI 更改:
- 如果是视觉的,捕获前后截图
完成定义:
- 所有测试通过
- Lint 通过
- 没有留下 TODO,除非显式跟踪Subagents 使用时机
何时使用
- 代码库扫描 (生成大量输出)
- 测试运行
- 审查通过 (只需要摘要,不承载所有中间输出)
何时不用
- 与主线程具有相同广泛权限的 subagent (隔离变得毫无意义)
- 输出格式不固定,主线程无法使用
- 子任务间强依赖,频繁共享中间状态
配置约束
tools / disallowedTools: 限制可用工具model: 探索任务用 Haiku/Sonnet,重要审查用 OpusmaxTurns: 防止失控isolation: worktree: 文件需要修改时隔离文件系统
Plan Mode 价值
Plan Mode 将探索与执行分离。探索保持只读,只有在确认计划后才接触文件。
- 探索阶段是只读的
- Claude 可以在提出具体计划前澄清目标和边界
- 只有在确认计划后才开始执行
对于复杂重构、迁移和跨模块更改,这种分离通常比立即编辑更好。它实质性地降低了将错误假设带入执行的可能性。
有用模式: 让一个 agent 起草计划,另一个在执行前审查。
CLAUDE.md 最佳实践
记忆位置与用途
Claude Code 提供三种记忆位置,每种都有不同用途:
| 记忆类型 | 文件位置 | 用途说明 | 使用示例 |
|---|---|---|---|
| 项目记忆(共享) | ./CLAUDE.md | 项目团队共享的指令 | 项目架构、编码规范、常用工作流程 |
| 用户记忆(全局) | ~/.claude/CLAUDE.md | 用于所有项目的个人偏好设置 | 代码风格偏好、个人工具快捷方式 |
| 项目记忆(本地) | ./CLAUDE.local.md | 项目的个人偏好设置(已废弃) | 你的沙箱地址、测试数据偏好等 |
递归读取机制:Claude Code 会从当前工作目录开始,向上递归到根目录,读取找到的所有 CLAUDE.md 文件。
应该包含
- 开头用一句话说明项目是什么 - 快速建立上下文
- 构建、测试、lint、运行命令 (最重要)
- 关键目录结构和模块边界
- 显式代码风格和命名约束
- 非显而易见的环境依赖和陷阱
- 禁止事项和高风险操作 (NEVER 列表)
- 必须在压缩中幸存的信息 (Compact Instructions)
不应该包含
- 长背景介绍
- 完整 API 文档
- 模糊原则如”写高质量代码”
- Claude 可以通过阅读仓库推断的显而易见的信息
- 大背景材料和低频任务知识 (放在 skills 里)
编写建议
- 代码风格偏好要具体、可执行 - 不要模糊描述
- 包含关键命令 - 测试、构建、Lint、部署
- 对坑点的描述要足够具体 - 真正能防止犯错
- 控制在 300 行以内 - 确保每一行都有存在价值
- 使用 @import 引用详细说明 - 保持主文件简洁
- 删除过时或冲突的内容 - 避免混淆
- 对真正关键的规则进行强调 - 使用 “IMPORTANT”、“YOU MUST” 等关键词,但只限于”真的关键”的
- 在工作过程中持续添加 - 不只是一开始写
- PR 评审中暴露约定时及时更新 - 保持同步
维护工作流
快捷维护:编码时随时按下 # 键,让 Claude 将指令自动添加到相关的 CLAUDE.md 文件中。
提示优化:可以使用 Anthropic 的 Prompt Improver 工具优化指令,提升遵循度。
避免过度堆砌:不要把所有细节都堆进 CLAUDE.md,会让 Claude 忽视关键规则。解决方法是删繁就简,保留必要内容,并测试哪些指令 Claude 执行得最好。
对于更大的项目
- 约定差异明显的子目录需要各自的
CLAUDE.md - 把规则拆分到
.claude/rules/文件中,有助于不同团队明确各自的责任边界
让 Claude 维护自己的 CLAUDE.md
更正 Claude 错误后立即更新 CLAUDE.md:
“更新你的 CLAUDE.md,这样你就不会再犯这个错误了。”
Claude 通常相当擅长为自己编写这些规则,重复错误确实会随时间变得不那么频繁。仍然,定期审查文件。条目会过时,曾经有帮助的约束可能不再值得。
思考
与已有知识的联系:
- 这补充了我对 Agent 的理解 - 单智能体 = LLM + 观察 + 思考 + 行动 + 记忆,而 Claude Code 是这个理论的具体工程实现
- 与 Code Agent 和 自动化 Agent 探索 的观察一致 - Agent 系统的设计确实是系统工程问题,不只是提示词工程
- 六层架构让我重新思考 提示词工程 - 提示词只是 CLAUDE.md 这一层,还需要 Hooks、Skills、Tools 等多层配合
新的洞察:
- 上下文管理是 Agent 系统的核心瓶颈 - 不只是 200K 限制,更是噪音问题。MCP 工具定义就占 10-20K tokens,5 个服务器就是 25K tokens (12.5%)
- Prompt Caching 塑造架构 - 缓存命中率不只是成本问题,它决定了系统设计方式。静态内容放在前面,动态内容放后面,这改变了如何组织上下文
- 验证比能力更重要 - “Claude 说完成了”没有工程价值。重要的是知道它是否真的正确、出错时能否回滚、过程是否可审计
- 渐进式披露是关键设计模式 - 不要一次性展示所有内容。先给索引和导航,然后按需加载细节。这对 Skill 设计、MCP 工具设计都适用
- Agent 工具 ≠ 人类 API - 人类 API 优化功能完整性,Agent 工具要优化正确选择和正确使用。AskUserQuestion 的演进说明了这一点
应用迁移:
- 我在日常使用 Claude Code 时,应该更主动管理上下文 - 使用 /context 检查,使用 /clear 和 /compact 控制
- 创建 Skill 时要使用渐进式披露模式 - SKILL.md 只放骨架,详细内容放 references/
- 要为我的项目写更好的 CLAUDE.md - 不只是命令,还要有 Compact Instructions 和明确的 Verification 标准
待探索的问题:
- 我当前的项目中,哪些部分应该放在 CLAUDE.md,哪些应该放在 rules,哪些应该是 Skills?
- 我的 MCP 连接中有多少是低频使用的?是否应该禁用一些以减少上下文开销?
- 如何设计一个有效的 HANDOFF.md 来保持长期项目的连续性?
出处
- 你不知道的 Claude Code:架构、治理与工程实践 - Tw93 (@HiTw93), 2026年3月
记忆扩展
六层架构口诀
Claude Has Skills To Make Systems
CLAUDE.md → Hooks → Skills → Tools → Memory → Subagents
案例:MCP 工具是最大隐藏开销
小明安装了 10 个 MCP 服务器,觉得功能很强大
结果发现 Claude 变"笨"了,经常忽略重要指令
原因:10 个 MCP × 2K tokens = 20K tokens
加上系统提示、Skill 描述,固定开销 40K+
只剩 160K 给对话,Claude 的"注意力"被稀释了
Prompt Caching 核心原则
静态放前面,动态放后面
就像盖房子:地基(静态)先打好,家具(动态)后面再搬
| 位置 | 内容 | 特点 |
|---|---|---|
| 前面 | System Prompt, Tool Definitions | 静态,可缓存 |
| 后面 | Chat History, Current Input | 动态,每次变化 |
关键实践速查
| 问题 | 解决方案 |
|---|---|
| MCP 太多吃上下文 | 禁用低频使用的 MCP |
| 动态信息破坏缓存 | 放在消息中,不要放在系统提示 |
| 任务切换丢失上下文 | 用 /clear 而不是继续堆叠 |
| 需要暂停问用户 | 用 AskUserQuestion 工具,不要用标志位 |