sync
This commit is contained in:
741
1 - Inbox/你不知道的 Claude Code:架构、治理与工程实践.md
Normal file
741
1 - Inbox/你不知道的 Claude Code:架构、治理与工程实践.md
Normal file
@@ -0,0 +1,741 @@
|
||||
---
|
||||
title: "你不知道的 Claude Code:架构、治理与工程实践"
|
||||
source: "https://x.com/HiTw93/article/2032091246588518683"
|
||||
author:
|
||||
- "[[Tw93 (@HiTw93)]]"
|
||||
published: 2026-03-12
|
||||
created: 2026-03-31
|
||||
description:
|
||||
tags:
|
||||
- "clippings"
|
||||
---
|
||||
今天这篇文章源于最近半年深度使用 Claude Code、两个账号每月 40 刀氪金换来的一些踩坑经验,希望能给大伙一些输入。
|
||||
|
||||
刚开始我也把它当 ChatBot 用,后来很快发现不对劲:上下文越来越乱、工具越来越多但效果越来越差、规则越写越长却越不遵守,折腾了一段时间,研究了 Claude Code 本身之后才意识到,这不是 Prompt 问题,而是这套系统的设计就是这样的。
|
||||
|
||||
这篇文章想和大伙聊聊这几个事:Claude Code 底层怎么运作、上下文为什么会乱以及怎么治理、Skills 和 Hooks 应该怎么设计、Subagents 的正确用法、Prompt Caching 的架构影响,以及怎么写一个真正有用的 CLAUDE.md。
|
||||
|
||||
我觉得最直接的理解方式,是把 Claude Code 拆成六层来看:
|
||||
|
||||

|
||||
|
||||
只强化其中一层,系统就会失衡,CLAUDE.md 写太长,上下文先污染自己了;工具堆太多了,选择就搞不清楚了;subagents 开得到处都是,状态就漂移了;验证这步跳过了,出了问题根本不知道是哪里挂的。
|
||||
|
||||
# 1\. 它底层是怎么运行的
|
||||
|
||||

|
||||
|
||||
Claude Code 的核心不是"回答",而是一个反复循环的代理过程:
|
||||
|
||||
```text
|
||||
收集上下文 → 采取行动 → 验证结果 → [完成 or 回到收集]
|
||||
↑ ↓
|
||||
CLAUDE.md Hooks / 权限 / 沙箱
|
||||
Skills Tools / MCP
|
||||
Memory
|
||||
```
|
||||
|
||||
用了一段时间才意识到,卡住的地方几乎从来不是模型不够聪明,更多时候是给了它错误的上下文,或者写出来了但根本没法判断对不对,也没法撤回。
|
||||
|
||||
## 真正要关注的五个层面:
|
||||
|
||||

|
||||
|
||||
对着这几个面看,很多问题就好排查了。结果不稳定,查上下文加载顺序,不是模型的事;自动化失控,看控制层有没有设计,不是 agent 太主动;长会话质量下降,中间产物把上下文污染了,换个新会话比反复调 prompt 有用得多。
|
||||
|
||||
# 2\. 概念边界:MCP / Plugin / Tools / Skills / Hooks / Subagents
|
||||
|
||||

|
||||
|
||||
简单记:给 Claude 新动作能力用 Tool/MCP,给它一套工作方法用 Skill,需要隔离执行环境用 Subagent,要强制约束和审计用 Hook,跨项目分发用 Plugin。
|
||||
|
||||
# 3\. 上下文工程:最重要的系统约束
|
||||
|
||||
很多人把上下文当"容量问题",但卡住的地方通常不是不够长,而是太吵了,有用的信息被大量无关内容淹没了。
|
||||
|
||||
## 真实的上下文成本构成
|
||||
|
||||

|
||||
|
||||
Claude Code 的 200K 上下文并非全部可用:
|
||||
|
||||
```text
|
||||
200K 总上下文
|
||||
├── 固定开销 (~15-20K)
|
||||
│ ├── 系统指令: ~2K
|
||||
│ ├── 所有启用的 Skill 描述符: ~1-5K
|
||||
│ ├── MCP Server 工具定义: ~10-20K ← 最大隐形杀手
|
||||
│ └── LSP 状态: ~2-5K
|
||||
│
|
||||
├── 半固定 (~5-10K)
|
||||
│ ├── CLAUDE.md: ~2-5K
|
||||
│ └── Memory: ~1-2K
|
||||
│
|
||||
└── 动态可用 (~160-180K)
|
||||
├── 对话历史
|
||||
├── 文件内容
|
||||
└── 工具调用结果
|
||||
```
|
||||
|
||||

|
||||
|
||||
一个典型 MCP Server(如 GitHub)包含 20-30 个工具定义,每个约 200 tokens,合计 **4,000-6,000 tokens**。接 5 个 Server,光这部分固定开销就到了 **25,000 tokens(12.5%)**。我第一次算出这个数字的时候,真没想到有这么多,在要读大量代码的场景,这 12.5% 真的很关键。
|
||||
|
||||
## 推荐的上下文分层
|
||||
|
||||
```text
|
||||
始终常驻 → CLAUDE.md:项目契约 / 构建命令 / 禁止事项
|
||||
按路径加载 → rules:语言 / 目录 / 文件类型特定规则
|
||||
按需加载 → Skills:工作流 / 领域知识
|
||||
隔离加载 → Subagents:大量探索 / 并行研究
|
||||
不进上下文 → Hooks:确定性脚本 / 审计 / 阻断
|
||||
```
|
||||
|
||||
说白了,偶尔用的东西就不要每次都加载进来。
|
||||
|
||||
## 上下文最佳实践
|
||||
|
||||
- 保持 CLAUDE.md 短、硬、可执行,优先写命令、约束、架构边界。Anthropic 官方自己的 CLAUDE.md 大约只有 2.5K tokens,可以参考
|
||||
- 把大型参考文档拆到 Skills 的 supporting files,不要塞进 SKILL.md 正文
|
||||
- 使用 .claude/rules/ 做路径/语言规则,不让根 CLAUDE.md 承担所有差异
|
||||
- 长会话主动用 /context 观察消耗,不要等系统自动压缩后再补救
|
||||
|
||||

|
||||
|
||||
- 任务切换优先 /clear,同一任务进入新阶段用 /compact
|
||||
- **把 Compact Instructions 写进 CLAUDE.md**,压缩后必须保留什么由你控制,不由算法猜
|
||||
|
||||
## Tool Output 噪声:另一个隐形上下文杀手
|
||||
|
||||
前面算的是 MCP 工具定义的固定开销,但动态部分同样有个坑容易被忽视:Tool Output。cargo test 一次完整输出动辄几千行,git log、find、grep 在稍大的仓库里也能轻松塞满屏幕。这些输出 Claude 并不需要全看,但只要它出现在上下文里,就是实实在在的 token 消耗,同样会挤掉对话历史和文件内容的空间。
|
||||
|
||||
后来看到 [RTK(Rust Token Killer)](https://www.rtk-ai.app/) 这个思路觉得挺对的,它做的事很简单:在命令输出到 Claude 之前自动过滤,只留决策需要的核心信息。比如 cargo test:
|
||||
|
||||
```text
|
||||
# Claude 看到的原始输出
|
||||
running 262 tests
|
||||
test auth::test_login ... ok
|
||||
...(几千行)
|
||||
|
||||
# 走 RTK 之后
|
||||
✓ cargo test: 262 passed (1 suite, 0.08s)
|
||||
```
|
||||
|
||||
Claude 真正需要知道的就是「过了还是挂了,挂在哪里」,其他都是噪声。它通过 Hook 透明重写命令,对 Claude Code 来说完全无感。
|
||||
|
||||
后面第 6 节会提到 | head -30 这种手动截断,RTK 干的就是这件事,只是覆盖面更广,不用每条命令自己加,项目 [开源在 GitHub](https://github.com/rtk-ai/rtk)。
|
||||
|
||||
## 压缩机制的陷阱
|
||||
|
||||
默认压缩算法按"可重新读取"判断,早期的 Tool Output 和文件内容会被优先删掉,顺带把**架构决策和约束理由**也一起扔了。两小时后再改,可能根本不记得两小时前定了什么,莫名其妙的 Bug 就是这么来的。
|
||||
|
||||

|
||||
|
||||
解决方案就是在 CLAUDE.md 里写明:
|
||||
|
||||
```markdown
|
||||
## Compact Instructions
|
||||
|
||||
When compressing, preserve in priority order:
|
||||
|
||||
1. Architecture decisions (NEVER summarize)
|
||||
2. Modified files and their key changes
|
||||
3. Current verification status (pass/fail)
|
||||
4. Open TODOs and rollback notes
|
||||
5. Tool outputs (can delete, keep pass/fail only)
|
||||
```
|
||||
|
||||
除了写 Compact Instructions,还有一种更主动的方案:在开新会话前,先让 Claude 写一份 HANDOFF.md,把当前进度、尝试过什么、哪些走通了、哪些是死路、下一步该做什么写清楚。下一个 Claude 实例只读这个文件就能接着做,不依赖压缩算法的摘要质量:
|
||||
|
||||
在 HANDOFF.md 里写清楚现在的进展。解释你试了什么、什么有效、什么没用,让下一个拿到新鲜上下文的 agent 只看这个文件就能继续完成任务。
|
||||
|
||||
写完后快速扫一眼,有缺漏直接让它补,然后开新会话,把 HANDOFF.md 的路径发过去就行。
|
||||
|
||||
## Plan Mode 的工程价值
|
||||
|
||||

|
||||
|
||||
Plan Mode 的核心是把探索和执行拆开,探索阶段不动文件,确认方案后再执行:
|
||||
|
||||
- 探索阶段以只读操作为主
|
||||
- Claude 可以先澄清目标和边界,再提交具体方案
|
||||
- 执行成本在计划确认之后才发生
|
||||
|
||||

|
||||
|
||||
对于复杂重构、迁移、跨模块改动,这样做比"急着出代码"有用多了,在错误假设上越跑越偏的情况会少很多。按两下 Shift+Tab 进入 Plan Mode,**进阶玩法是开一个 Claude 写计划,再开一个 Codex 以"高级工程师"身份审这个计划,让 AI 审 AI,效果很好**。
|
||||
|
||||
# 4\. Skills 设计:不是模板库,是用的时候才加载的工作流
|
||||
|
||||
Skill 官方描述是"按需加载的知识与工作流",描述符常驻上下文,完整内容按需加载,用起来和"保存的 Prompt"差别挺大的。
|
||||
|
||||
## 一个好 Skill 应该满足什么
|
||||
|
||||
- 描述要让模型知道"何时该用我",而不是"我是干什么的",这两个差很多
|
||||
- 有完整步骤、输入、输出和停止条件,别写了个开头没有结尾
|
||||
- 正文只放导航和核心约束,大资料拆到 supporting files 里
|
||||
- 有副作用的 Skill 要显式设置 disable-model-invocation: true,不然 Claude 会自己决定要不要跑
|
||||
|
||||
## Skill 怎么做到按需加载
|
||||
|
||||
Claude Code 团队在内部设计中反复强调 "progressive disclosure",意思不是让模型一次性看到所有信息,而是先获得索引和导航,再按需拉取细节:
|
||||
|
||||
- SKILL.md 负责定义任务语义、边界和执行骨架
|
||||
- supporting files 负责提供领域细节
|
||||
- 脚本负责确定性收集上下文或证据
|
||||
|
||||
一个比较稳定的结构长这样:
|
||||
|
||||
```text
|
||||
.claude/skills/
|
||||
└── incident-triage/
|
||||
├── SKILL.md
|
||||
├── runbook.md
|
||||
├── examples.md
|
||||
└── scripts/
|
||||
└── collect-context.sh
|
||||
```
|
||||
|
||||
## Skill 的三种典型类型
|
||||
|
||||
下面几个例子都来自我在开源 terminal 项目 [Kaku](https://github.com/tw93/Kaku) 里的实际 Skill,比较直观。
|
||||
|
||||
**类型一:检查清单型(质量门禁)**
|
||||
|
||||
发布前跑一遍,确保不漏项:
|
||||
|
||||
```yaml
|
||||
---
|
||||
name: release-check
|
||||
description: Use before cutting a release to verify build, version, and smoke test.
|
||||
---
|
||||
|
||||
## Pre-flight (All must pass)
|
||||
- [ ] \`cargo build --release\` passes
|
||||
- [ ] \`cargo clippy -- -D warnings\` clean
|
||||
- [ ] Version bumped in Cargo.toml
|
||||
- [ ] CHANGELOG updated
|
||||
- [ ] \`kaku doctor\` passes on clean env
|
||||
|
||||
## Output
|
||||
Pass / Fail per item. Any Fail must be fixed before release.
|
||||
```
|
||||
|
||||
**类型二:工作流型(标准化操作)**
|
||||
|
||||
配置迁移高风险,显式调用 + 内置回滚步骤:
|
||||
|
||||
```yaml
|
||||
---
|
||||
name: config-migration
|
||||
description: Migrate config schema. Run only when explicitly requested.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
## Steps
|
||||
1. Backup: \`cp ~/.config/kaku/config.toml ~/.config/kaku/config.toml.bak\`
|
||||
2. Dry run: \`kaku config migrate --dry-run\`
|
||||
3. Apply: remove \`--dry-run\` after confirming output
|
||||
4. Verify: \`kaku doctor\` all pass
|
||||
|
||||
## Rollback
|
||||
\`cp ~/.config/kaku/config.toml.bak ~/.config/kaku/config.toml\`
|
||||
```
|
||||
|
||||
**类型三:领域专家型(封装决策框架)**
|
||||
|
||||
运行时出问题时让 Claude 按固定路径收集证据,不要瞎猜:
|
||||
|
||||
```yaml
|
||||
---
|
||||
name: runtime-diagnosis
|
||||
description: Use when kaku crashes, hangs, or behaves unexpectedly at runtime.
|
||||
---
|
||||
|
||||
## Evidence Collection
|
||||
1. Run \`kaku doctor\` and capture full output
|
||||
2. Last 50 lines of \`~/.local/share/kaku/logs/\`
|
||||
3. Plugin state: \`kaku --list-plugins\`
|
||||
|
||||
## Decision Matrix
|
||||
| Symptom | First Check |
|
||||
|---|---|
|
||||
| Crash on startup | doctor output → Lua syntax error |
|
||||
| Rendering glitch | GPU backend / terminal capability |
|
||||
| Config not applied | Config path + schema version |
|
||||
|
||||
## Output Format
|
||||
Root cause / Blast radius / Fix steps / Verification command
|
||||
```
|
||||
|
||||
描述符写短点,每个 Skill 都在偷你的上下文空间,每个启用的 Skill,描述符常驻上下文,优化前后差距很大:
|
||||
|
||||
```yaml
|
||||
# 低效(~45 tokens)
|
||||
description: |
|
||||
This skill helps you review code changes in Rust projects.
|
||||
It checks for common issues like unsafe code, error handling...
|
||||
Use this when you want to ensure code quality before merging.
|
||||
|
||||
# 高效(~9 tokens)
|
||||
description: Use for PR reviews with focus on correctness.
|
||||
```
|
||||
|
||||
还有一个很重要的 disable-auto-invoke 使用策略:
|
||||
|
||||
- 高频(>1 次/会话)→ 保持 auto-invoke,优化描述符
|
||||
- 低频(<1 次/会话)→ disable-auto-invoke,手动触发,描述符完全脱离上下文
|
||||
- 极低频(<1 次/月)→ 移除 Skill,改为 AGENTS.md 中的文档
|
||||
|
||||
## Skills 反模式
|
||||
|
||||
- 描述过短:description: help with backend(任何后端工作都能触发,哈哈)
|
||||
- 正文过长:几百行工作手册全塞进 SKILL.md 正文
|
||||
- 一个 Skill 覆盖 review、deploy、debug、docs、incident 五件事
|
||||
- 有副作用的 Skill 允许模型自动调用
|
||||
|
||||
# 5\. 工具设计:怎么让 Claude 少选错
|
||||
|
||||
我后面越用越觉得,给 Claude 的工具和给人写的 API 不是一回事。给人用的 API 往往会追求功能齐全,但给 agent 用,重点不是功能堆得多完整,而是让它更容易用对。
|
||||
|
||||
## 好工具 vs 坏工具
|
||||
|
||||

|
||||
|
||||
**几个实用设计原则**
|
||||
|
||||
- 名称前缀按系统或资源分层:github\_pr\_\*、jira\_issue\_\*
|
||||
- 对大响应支持 response\_format: concise / detailed
|
||||
- 错误响应要教模型如何修正,不要只抛 opaque error code
|
||||
- 能合并成高层任务工具时,不要暴露过多底层碎片工具,避免 list\_all\_\* 让模型自行筛选
|
||||
|
||||
## 从 Claude Code 内部工具演进学到的
|
||||
|
||||

|
||||
|
||||
我看到 Claude Code 团队内部工具的这段演进时,感觉还挺有意思。像这种需要在任务中途停下来问用户的场景,他们前后试了三种做法:
|
||||
|
||||
- **第一版**:给已有工具(如 Bash)加一个 question 参数,让 Claude 在调用工具时顺带提问。结果 Claude 大多数时候直接忽略这个参数,继续往下跑,根本不停下来问。
|
||||
- **第二版**:要求 Claude 在输出里写特定 markdown 格式,外层解析到这个格式就暂停。问题是没有强制约束,Claude 经常"忘了"按格式写,提问逻辑非常脆弱。
|
||||
- **第三版**:做成独立的 AskUserQuestion 工具。Claude 想提问就必须显式调用它,调用即暂停,没有歧义,比前两版靠谱多了。
|
||||
|
||||
下面这张图刚好能解释,为什么第三版明显更稳:
|
||||
|
||||

|
||||
|
||||
左边(markdown 自由输出)太松,模型格式随意、外层解析脆弱;右边(ExitPlanTool 参数)太死,等到退出计划阶段提问已经太晚;AskUserQuestion 独立工具落在中间,结构化且随时可调用,是这三者里最稳定的设计。
|
||||
|
||||
说白了,既然你就是要 Claude 停下来问一句,那就直接给它一个专门的工具。加个 flag 或者约定一段输出格式,很多时候它一顺手就略过去了。
|
||||
|
||||
**Todo 工具的演进**
|
||||
|
||||

|
||||
|
||||
早期用 TodoWrite 工具 + 每 5 轮插入提醒让 Claude 记住任务。随着模型变强,这个工具反而成了限制,Todo 提醒让 Claude 认为必须严格遵循,无法灵活修改计划。挺有意思的教训:当初加这个工具是因为模型不够强,模型变强之后它反而变成了枷锁。值得过段时间回来检查一下,当初加的限制还成不成立。
|
||||
|
||||
**搜索工具的演进**:最初用 RAG 向量数据库,虽然快但需要索引、不同环境脆弱,最重要的是 **Claude 不喜欢用**。改成 Grep 工具让 Claude 自己搜索后,好用很多。后来又发现一个顺带的好处:Claude 读 Skill 文件,Skill 文件又引用其他文件,模型会递归读取,按需发现信息,不需要提前塞进去,这个模式后来被叫做"渐进式披露"。
|
||||
|
||||
**什么时候不该再加 Tool**
|
||||
|
||||
- 本地 shell 可以可靠完成的事情
|
||||
- 模型只需要静态知识,不需要真正与外部交互
|
||||
- 需求更适合 Skill 的工作流约束,而不是 Tool 的动作能力
|
||||
- 还没验证过工具描述、schema 和返回格式能被模型稳定使用
|
||||
|
||||
# 6\. Hooks:在 Claude 执行操作前后,强制插入你自己的逻辑
|
||||
|
||||
Hooks 很容易被当成"自动运行的脚本",但我自己用下来,觉得它更像是把一些不能交给 Claude 临场发挥的事情,重新收回到确定性的流程里。
|
||||
|
||||
比如格式化要不要跑、保护文件能不能改、任务完成后要不要通知,这些事真不要指望 Claude 每次都自己记得。
|
||||
|
||||
当前支持的 Hook 点
|
||||
|
||||

|
||||
|
||||
## 适合 vs 不适合放到 Hooks 的
|
||||
|
||||
**适合**:阻断修改受保护文件、Edit 后自动格式化/lint/轻量校验、SessionStart 后注入动态上下文(Git 分支、环境变量)、任务完成后推送通知。
|
||||
|
||||
**不适合**:需要读大量上下文的复杂语义判断、长时间运行的业务流程、需要多步推理和权衡的决策,这些该在 Skill 或 Subagent 里。
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Edit",
|
||||
"pattern": "*.rs",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "cargo check 2>&1 | head -30",
|
||||
"statusMessage": "Running cargo check..."
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"Notification": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "osascript -e 'display notification \"Task completed\" with title \"Claude Code\"'"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Hooks:越早发现错误,越省时间
|
||||
|
||||

|
||||
|
||||
在 100 次编辑的会话中,每次节省 30-60 秒,累积节省 1-2 小时,还挺可观的。**注意限制输出长度**(| head -30),避免 Hook 输出反而污染上下文。如果不想在每条命令后面手动加截断,可以看看第 3 节提到的 RTK,它把这件事系统化了。
|
||||
|
||||
Hooks + Skills + CLAUDE.md 三层叠加
|
||||
|
||||
- CLAUDE.md:声明"提交前必须通过测试和 lint"
|
||||
- Skill:告诉 Claude 在什么顺序下运行测试、如何看失败、如何修复
|
||||
- Hook:对关键路径执行硬性校验,必要时阻断
|
||||
|
||||
用下来感觉,三样少任何一层都会有漏洞。只写 CLAUDE.md 规则,Claude 经常当没看见;只靠 Hooks,细节判断又做不了,放在一起才比较稳。
|
||||
|
||||
# 7\. Subagents:派一个独立的 Claude 去干一件具体的事
|
||||
|
||||
Subagent 就是从主对话派出去的一个独立 Claude 实例,有自己的上下文窗口,只用你指定的工具,干完汇报结果。我用下来觉得它最大的价值不是"并行",而是隔离,扫代码库、跑测试、做审查这类会产生大量输出的事,塞进主线程很快就把有效上下文挤没了,交给 Subagent 做,主线程只拿一个摘要,干净很多。
|
||||
|
||||
Claude Code 内置了三个:**Explore**(只读扫库,默认跑 Haiku 省成本)、**Plan**(规划调研)、**General-purpose**(通用),也可以自定义。
|
||||
|
||||
## 配置时要显式约束
|
||||
|
||||
- tools / disallowedTools:限定能用什么工具,别给和主线程一样宽的权限
|
||||
- model:探索任务用 Haiku/Sonnet,重要审查用 Opus
|
||||
- maxTurns:防止跑飞
|
||||
- isolation: worktree:需要动文件时隔离文件系统
|
||||
|
||||
另一个实用细节:长时间运行的 bash 命令可以按 Ctrl+B 移到后台,Claude 之后会用 BashOutput 工具查看结果,不会阻塞主线程继续工作。subagent 同理,直接告诉它「在后台跑」就行。
|
||||
|
||||
## 几个常见反模式
|
||||
|
||||
- 子代理权限和主线程一样宽,隔离没有意义
|
||||
- 输出格式不固定,主线程拿到没法用
|
||||
- 子任务之间强依赖,频繁要共享中间状态,这种情况用 Subagent 不合适
|
||||
|
||||
# 8\. Prompt Caching:Claude Code 内部架构的核心
|
||||
|
||||
这块我之前在很多教程里都没怎么看到有人展开讲,但它其实很影响 Claude Code 的成本结构和很多设计取舍。
|
||||
|
||||
工程界有句话 "Cache Rules Everything Around Me",对 agent 同样如此,Claude Code 的整个架构都是围绕 Prompt 缓存构建的,**高命中率不光省钱,速率限制也会松很多**,Anthropic 甚至会对命中率跑告警,太低直接宣布 SEV。
|
||||
|
||||
## 为缓存设计的 Prompt Layout
|
||||
|
||||

|
||||
|
||||
Prompt 缓存是按**前缀匹配**工作的,从请求开头到每个 cache\_control 断点之前的内容都会被缓存。所以这里的顺序很重要:
|
||||
|
||||
```markdown
|
||||
Claude Code 的 Prompt 顺序:
|
||||
1. System Prompt → 静态,锁定
|
||||
2. Tool Definitions → 静态,锁定
|
||||
3. Chat History → 动态,在后面
|
||||
4. 当前用户输入 → 最后
|
||||
```
|
||||
|
||||
**破坏缓存的常见陷阱**
|
||||
|
||||
- 在静态系统 Prompt 中放入带时间戳的内容(让它每次都变)
|
||||
- 非确定性地打乱工具定义顺序
|
||||
- 会话中途增删工具
|
||||
|
||||
那像当前时间这种动态信息怎么办?别去动系统 Prompt,放到下一条消息里传进去就行。Claude Code 自己也是这么做的,用户消息里加 <system-reminder> 标签,系统 Prompt 不动,缓存也就不会被打坏。
|
||||
|
||||
## 会话中途不要切换模型
|
||||
|
||||
Prompt 缓存是模型唯一的。假如你已经和 Opus 对话了 100K tokens,想问个简单问题,**切换到 Haiku 实际上比继续用 Opus 更贵**,因为要为 Haiku 重建整个缓存。确实需要切换的话,用 Subagent 交接:Opus 准备一条"交接消息"给另一个模型,说明需要完成的任务就行。
|
||||
|
||||
**Compaction 的实际实现**
|
||||
|
||||

|
||||
|
||||
上图是 Compaction(上下文压缩)的执行流程:左边是上下文快满时的状态,中间是 Claude Code 开一个 fork 调用,把完整对话历史喂给模型,加一句"Summarize this conversation",这一步命中缓存所以只需 1/10 的价格,右边是压缩完之后,原来几十轮对话被替换成一段 ~20k tokens 的摘要,System + Tools 还在,再挂上之前用到的文件引用,腾出空间继续新的轮次。
|
||||
|
||||
直觉上 Plan Mode 应该切换成只读工具集,但这会破坏缓存。实际实现是:EnterPlanMode 是模型可以自己调用的工具,检测到复杂问题时自主进入 plan mode,工具集不变,缓存不受影响。
|
||||
|
||||
**defer\_loading:工具的延迟加载**
|
||||
|
||||
Claude Code 有数十个 MCP 工具,每次请求全量包含会很贵,但中途移除会破坏缓存。解决方案是发送轻量级 stub,只有工具名,标记 defer\_loading: true。模型通过 ToolSearch 工具"发现"它们,完整的工具 schema 只在模型选择后才加载,这样缓存前缀保持稳定。
|
||||
|
||||
# 9\. 验证闭环:没有 Verifier 就没有工程上的 Agent
|
||||
|
||||
「Claude 说完成了」其实没啥用,你得能知道它做没做对、出了问题能退回来、过程还能查,这才算数。
|
||||
|
||||
## Verifier 的层级
|
||||
|
||||
- 最低层:命令退出码、lint、typecheck、unit test
|
||||
- 中间层:集成测试、截图对比、contract test、smoke test
|
||||
- 更高层:生产日志验证、监控指标、人工审查清单
|
||||
|
||||
在 Prompt、Skill 和 CLAUDE.md 中显式定义验证
|
||||
|
||||
```markdown
|
||||
## Verification
|
||||
|
||||
For backend changes:
|
||||
|
||||
- Run \`make test\` and \`make lint\`
|
||||
- For API changes, update contract tests under \`tests/contracts/\`
|
||||
|
||||
For UI changes:
|
||||
|
||||
- Capture before/after screenshots if visual
|
||||
|
||||
Definition of done:
|
||||
|
||||
- All tests pass
|
||||
- Lint passes
|
||||
- No TODO left behind unless explicitly tracked
|
||||
```
|
||||
|
||||
写任务 Prompt 或 Skill 的时候,最好把验收标准提前说清楚。哪些命令跑完算完成,失败了先查什么,截图和日志看到什么才算过,这些越早讲明白,后面越省事。
|
||||
|
||||
我自己有个很简单的判断:假如一个任务你都说不清楚「Claude 怎么才算做对了」,那它大概率也不适合直接丢给 Claude 自动完成。
|
||||
|
||||
# 10\. 高频命令的工程意义
|
||||
|
||||
这些命令说白了就干一件事:主动管理上下文,别等系统自己处理。
|
||||
|
||||
## 上下文管理
|
||||
|
||||
```bash
|
||||
/context # 查看 token 占用结构,排查 MCP 和文件读取占比
|
||||
/clear # 清空会话,同一问题被纠偏两次以上就重来
|
||||
/compact # 压缩但保留重点,配合 Compact Instructions
|
||||
/memory # 确认哪些 CLAUDE.md 真的被加载了
|
||||
```
|
||||
|
||||
## 能力与治理
|
||||
|
||||

|
||||
|
||||
```bash
|
||||
/mcp # 管理 MCP 连接,检查 token 成本,断开闲置 server
|
||||
/hooks # 管理 hooks,控制平面入口
|
||||
/permissions # 查看或更新权限白名单
|
||||
/sandbox # 配置沙箱隔离,高自动化场景必备
|
||||
/model # 切换模型:Opus 用于深度推理,Sonnet 用于常规,Haiku 用于快速探索
|
||||
```
|
||||
|
||||
## 会话连续性与并行
|
||||
|
||||
```bash
|
||||
claude --continue # 恢复当前目录最近会话,隔天接着做
|
||||
claude --resume # 打开选择器恢复历史会话
|
||||
claude --continue --fork # 从已有会话分叉,同一起点不同方案
|
||||
claude --worktree # 创建隔离 git worktree
|
||||
claude -p "prompt" # 非交互模式,接入 CI / pre-commit / 脚本
|
||||
claude -p --output-format json # 结构化输出,便于脚本消费
|
||||
```
|
||||
|
||||
## 几个不常见但很好用的命令
|
||||
|
||||
**/simplify**:对刚改完的代码做三维检查,代码复用、质量和效率,发现问题直接修掉。特别适合改完一段逻辑后立刻跑一遍,代替手动 review。
|
||||
|
||||
**/rewind**:不是"撤销",而是回到某个会话 checkpoint 重新总结。适合:Claude 已沿错误路径探索太久;想保留前半段共识但丢掉后半段失败。
|
||||
|
||||
**/btw**:在不打断主任务的前提下快速问一个侧问题,适合"两个命令有什么区别"这类单轮旁路问答,不适合需要读仓库或调用工具的问题。
|
||||
|
||||
**claude -p --output-format stream-json**:实时 JSON 事件流,适合长任务监控、增量处理、流式集成到自己的工具。
|
||||
|
||||
**/insight**:让 Claude 分析当前会话,提炼出哪些内容值得沉淀到 CLAUDE.md。用法是会话做了一段之后跑一次,它会指出"这个约定你们反复提到,但没有写进契约"之类的盲点,是迭代优化 CLAUDE.md 的好手段。
|
||||
|
||||
**双击 ESC 回溯**:按两次 ESC 可以回到上一条输入重新编辑,不用重新手打。Claude 走偏了、或者上一句话没说清楚,双击 ESC 修改后重发,比重新开会话省事得多。
|
||||
|
||||
**对话历史都在本地**:所有会话记录存放在 ~/.claude/projects/ 下,文件夹名按项目路径命名(斜杠变横杠),每个会话是一个 .jsonl 文件。想找某个话题的历史,直接 grep -rl "关键词" ~/.claude/projects/ 就能定位,或者直接告诉 Claude「帮我搜一下之前关于 X 的讨论」,它会自己去翻。
|
||||
|
||||
# 11\. 如何写一个好的 CLAUDE.md
|
||||
|
||||
CLAUDE.md 在我看来更像是你和 Claude 之间的协作契约,不是团队文档,也不是知识库,里面只放那些每次会话都得成立的事。
|
||||
|
||||
我自己的建议其实很简单,一开始甚至可以什么都不写。先用起来,等你发现自己老是在重复同一件事,再把它补进去。加法也不复杂,输入 # 可以把当前对话里的内容直接追加进 CLAUDE.md,或者直接告诉 Claude「把这条加到项目的 CLAUDE.md 里」,它会知道该改哪个文件。
|
||||
|
||||

|
||||
|
||||
## 应该放什么
|
||||
|
||||
- 怎么 build、怎么 test、怎么跑(最核心)
|
||||
- 关键目录结构与模块边界
|
||||
- 代码风格和命名约束
|
||||
- 那些不明显的环境坑
|
||||
- 绝对不能干的事(NEVER 列表)
|
||||
- 压缩时必须保留的信息(Compact Instructions)
|
||||
|
||||
## 不该放什么
|
||||
|
||||
- 大段背景介绍
|
||||
- 完整 API 文档
|
||||
- 空泛原则,如"写高质量代码"
|
||||
- Claude 通过读仓库即可推断的显然信息
|
||||
- 大量背景资料和低频任务知识(这些放到 Skills)
|
||||
|
||||
## 高质量模板
|
||||
|
||||
```markdown
|
||||
# Project Contract
|
||||
|
||||
## Build And Test
|
||||
|
||||
- Install: \`pnpm install\`
|
||||
- Dev: \`pnpm dev\`
|
||||
- Test: \`pnpm test\`
|
||||
- Typecheck: \`pnpm typecheck\`
|
||||
- Lint: \`pnpm lint\`
|
||||
|
||||
## Architecture Boundaries
|
||||
|
||||
- HTTP handlers live in \`src/http/handlers/\`
|
||||
- Domain logic lives in \`src/domain/\`
|
||||
- Do not put persistence logic in handlers
|
||||
- Shared types live in \`src/contracts/\`
|
||||
|
||||
## Coding Conventions
|
||||
|
||||
- Prefer pure functions in domain layer
|
||||
- Do not introduce new global state without explicit justification
|
||||
- Reuse existing error types from \`src/errors/\`
|
||||
|
||||
## Safety Rails
|
||||
|
||||
## NEVER
|
||||
|
||||
- Modify \`.env\`, lockfiles, or CI secrets without explicit approval
|
||||
- Remove feature flags without searching all call sites
|
||||
- Commit without running tests
|
||||
|
||||
## ALWAYS
|
||||
|
||||
- Show diff before committing
|
||||
- Update CHANGELOG for user-facing changes
|
||||
|
||||
## Verification
|
||||
|
||||
- Backend changes: \`make test\` + \`make lint\`
|
||||
- API changes: update contract tests under \`tests/contracts/\`
|
||||
- UI changes: capture before/after screenshots
|
||||
|
||||
## Compact Instructions
|
||||
|
||||
Preserve:
|
||||
|
||||
1. Architecture decisions (NEVER summarize)
|
||||
2. Modified files and key changes
|
||||
3. Current verification status (pass/fail commands)
|
||||
4. Open risks, TODOs, rollback notes
|
||||
```
|
||||
|
||||
用起来其实不复杂:每次都要知道的放 CLAUDE.md,只对部分文件生效的放 rules,只在某类任务中需要的放 Skills。
|
||||
|
||||
## 让 Claude 维护自己的 CLAUDE.md
|
||||
|
||||
我最喜欢的一个技巧:每次纠正 Claude 的错误后,让它自己更新 CLAUDE.md:
|
||||
|
||||
> "Update your CLAUDE.md so you don't make that mistake again."
|
||||
|
||||
Claude 在给自己补这类规则时其实还挺好用,用久了确实越来越少犯同样的错。不过也要定期 review,时间一长总会有些条目慢慢过时,当初有用的限制现在未必还适合,这件事后面第 14 节有个更系统的做法。
|
||||
|
||||
# 12\. 最近自己折腾中得到的新经验
|
||||
|
||||
春节放假时,我用 Claude Code 做了一个开源 terminal 项目 [Kaku](https://github.com/tw93/Kaku),底层是 Rust + Lua,也带了一些 AI 能力。混合语言加上自定义配置系统,实际折腾下来反而暴露出不少典型的 agent 协作问题,顺手聊几个对我帮助比较大的经验。
|
||||
|
||||
## 环境透明比你想象中重要
|
||||
|
||||
Claude Code 调用的都是真实的 shell、git、package manager 和本地配置。这里面只要有一层不透明,它就只能开始猜,一猜可靠性就掉。这不是 Claude Code 特有的问题,很多 agent 都一样。
|
||||
|
||||
所以我后来很快就在 terminal 里加了个 doctor 命令,把环境状态、依赖和配置情况先统一收上来,输出一份结构化的健康报告。Claude Code 开始做事前先跑一次 doctor,确实能省掉很多"环境没搞清楚就开干"的问题。
|
||||
|
||||
另外我还发现,假如 CLI 本身就有 init、config、reset 这类语义清楚的子命令,Claude Code 用起来会稳不少,比让它自己去猜配置文件怎么摆要靠谱。先把状态收敛住,再暴露编辑入口,顺序一反过来就很容易乱。
|
||||
|
||||
## 混合语言项目的 Hooks 实践
|
||||
|
||||
两套语言、两套检查,其实挺适合用 Hooks 按文件类型分别触发:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Edit",
|
||||
"pattern": "*.rs",
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": "cargo check 2>&1 | head -30",
|
||||
"statusMessage": "Checking Rust..."
|
||||
}]
|
||||
},
|
||||
{
|
||||
"matcher": "Edit",
|
||||
"pattern": "*.lua",
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": "luajit -b $FILE /dev/null 2>&1 | head -10",
|
||||
"statusMessage": "Checking Lua syntax..."
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
每次编辑完立刻知道有没有编译错误,比"跑了一堆才发现最开始就挂了"舒服得多。
|
||||
|
||||
## 完整的工程化布局参考
|
||||
|
||||
假如有同学想给自己项目配一套比较完整的 Claude Code 工程布局,可以参考这个结构,不用全做,按需裁剪:
|
||||
|
||||
```plaintext
|
||||
Project/
|
||||
├── CLAUDE.md
|
||||
├── .claude/
|
||||
│ ├── rules/
|
||||
│ │ ├── core.md
|
||||
│ │ ├── config.md
|
||||
│ │ └── release.md
|
||||
│ ├── skills/
|
||||
│ │ ├── runtime-diagnosis/ # 统一收集日志、状态和依赖
|
||||
│ │ ├── config-migration/ # 配置迁移回滚防污
|
||||
│ │ ├── release-check/ # 发布前校验、smoke test
|
||||
│ │ └── incident-triage/ # 线上故障分诊
|
||||
│ ├── agents/
|
||||
│ │ ├── reviewer.md
|
||||
│ │ └── explorer.md
|
||||
│ └── settings.json
|
||||
└── docs/
|
||||
└── ai/
|
||||
├── architecture.md
|
||||
└── release-runbook.md
|
||||
```
|
||||
|
||||
全局约束(CLAUDE.md)、路径约束(rules)、工作流(skills)和架构细节各归各位,Claude Code 跑起来会稳很多。假如你同时维护多个项目,可以把稳定的个人基线放在 ~/.claude/,各项目的差异放在项目级 .claude/,通过同步脚本分发,不同项目之间就不会互相污染了。
|
||||
|
||||
# 13\. 常见反模式
|
||||
|
||||

|
||||
|
||||
# 14\. 配置健康检查
|
||||
|
||||
基于文章里的六层框架,我把这套检查整理成了一个开源 Skill 项目 [tw93/claude-health](https://github.com/tw93/claude-health),可以一键检查你的 Claude Code 配置现在处于什么状态。
|
||||
|
||||
> npx skills add tw93/claude-health -a claude-code -s health -g -y
|
||||
|
||||
装好之后在任意会话里跑 /health,它会自动识别项目复杂度,对 CLAUDE.md、rules、skills、hooks、allowedTools 和实际行为模式各跑一遍检查,输出一份优先级报告:需要立刻修 / 结构性问题 / 可以慢慢做。
|
||||
|
||||
如果你读完这篇文章想知道自己的配置离这些原则差多远,跑一次 /health 是最快的方式。
|
||||
|
||||
# 15\. 结语
|
||||
|
||||
用 Claude Code 大概会经历三个阶段:
|
||||
|
||||

|
||||
|
||||
到了第三阶段,关注点会悄悄变掉,从「这个功能怎么用」变成「怎么让 agent 在约束下自己跑起来」,两件事感觉差很多。
|
||||
|
||||
有一个问题挺值得想的:假如一个任务你说不清楚「什么叫做完」,那大概率也不适合直接扔给 Claude 自主完成,验证标准本身都没有,Claude 再聪明也跑不出正确答案。
|
||||
|
||||
这些是半年折腾下来的一些总结,肯定还有很多没有挖掘到的地方,如果大伙有用得更 6 的技巧,欢迎告诉我。
|
||||
Reference in New Issue
Block a user