Claude Code 工程实践手册

核心概念

Claude Code 不是聊天机器人,而是一个六层架构的 Agent 系统。核心执行模型是迭代式 Agent 循环:收集上下文 → 执行行动 → 验证结果 → [完成或循环]

六层架构

  1. CLAUDE.md - 项目契约层
  2. Hooks/Permissions - 控制层
  3. Skills - 工作流层
  4. Tools/MCP - 工具层
  5. Memory - 记忆层
  6. 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: 确定性脚本/审计/阻塞性检查

关键实践

  1. 保持 CLAUDE.md 短小精悍 - 只包含命令、约束、架构边界
  2. 使用 .claude/rules/ - 路径和语言特定规则,不要让根 CLAUDE.md 处理所有变体
  3. 主动使用 /context - 检查消费情况,不要等待自动压缩
  4. 任务切换用 /clear - 同任务的新阶段用 /compact
  5. 编写 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 停下来问用户问题:

  1. V1: 在现有工具(如 Bash)中添加 question 参数

    • 结果:Claude 经常忽略参数,继续执行而不暂停
  2. V2: 要求 Claude 发出特定 markdown 格式,由外层解析暂停

    • 问题:没有硬强制,质疑路径仍然脆弱
  3. V3: 独立的 AskUserQuestion 工具

    • Claude 必须显式调用它来提问,工具调用本身成为暂停信号
    • 这比前两个版本稳定得多

实践启示:如果你想让 Claude 停下来提问,给它一个专用工具。标志和输出格式约定对模型来说太容易跳过。

Prompt Caching 的架构影响

Prompt caching 不只是成本优化,它塑造架构。高缓存命中率降低成本、改善延迟、使更宽松的速率限制成为可能。

缓存工作原理

通过前缀匹配工作,从请求开始到每个 cache_control 断点的内容被缓存。顺序很重要

Claude Code 的提示顺序

  1. System Prompt → 静态,锁定
  2. Tool Definitions → 静态,锁定
  3. Chat History → 动态,在后面
  4. 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,重要审查用 Opus
  • maxTurns: 防止失控
  • 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 等多层配合

新的洞察:

  1. 上下文管理是 Agent 系统的核心瓶颈 - 不只是 200K 限制,更是噪音问题。MCP 工具定义就占 10-20K tokens,5 个服务器就是 25K tokens (12.5%)
  2. Prompt Caching 塑造架构 - 缓存命中率不只是成本问题,它决定了系统设计方式。静态内容放在前面,动态内容放后面,这改变了如何组织上下文
  3. 验证比能力更重要 - “Claude 说完成了”没有工程价值。重要的是知道它是否真的正确、出错时能否回滚、过程是否可审计
  4. 渐进式披露是关键设计模式 - 不要一次性展示所有内容。先给索引和导航,然后按需加载细节。这对 Skill 设计、MCP 工具设计都适用
  5. 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 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 工具,不要用标志位