1. 为什么说模板才是Claude Code的隐藏生产力
很多人拿到claude-code之后,第一反应是把它当成一个普通的对话式AI编码助手来用——打开终端敲几句指令,让它写个函数、修个bug、补个测试,感觉挺爽,但用上一两周就会发现问题:每次开新项目,都要把项目背景、技术栈、代码规范、注意事项重新口头交代一遍;每换一个仓库,AI就像失忆了一样,之前项目里总结过的坑、约定过的风格,全部不复存在。
这其实就是典型的"裸奔式使用"。claude-code真正的分水岭,不在于你会不会用它的聊天界面,而在于你有没有建立一套属于自己的templates——模板体系。这里的模板不是指代码脚手架那种"生成一个项目结构"的模板,而是指一套和AI之间稳定的协作契约:告诉它你的项目长什么样、按什么规则工作、遇到什么情况用哪种策略。
我自己的体会非常深。一开始我用Claude Code做一个小型内部工具,每天光是在对话里重复解释项目结构和技术栈,就要花掉将近二十分钟。后来我把这些说明固化成一个CLAUDE.md文件放进项目根目录,第二天打开终端一进项目,AI就自动"进入状态"了,直接能回答"这个项目里订单状态流转逻辑在哪"这种问题,不再需要我重新"培训"它。
这篇文章我打算把我从零搭建claude-code模板体系的过程完整拆开来讲。内容包括:CLAUDE.md文件的层级设计、如何用自定义commands把高频动作封装成斜杠命令、hooks机制如何让AI在关键节点自动收敛行为,以及一套可以复用的agents协作模式。内容偏工程实践,适合已经用claude-code写过几段代码、但觉得"差点意思"的开发者,也适合那种团队里准备统一AI编码规范的负责人——这套东西拿回去就能用,不用再从零摸索。
2. CLAUDE.md的三层结构:从"口头约定"到"写入契约"
2.1 先搞清楚CLAUDE.md到底管什么
CLAUDE.md是claude-code的"记忆文件",也可以理解成一份给AI看的项目说明书。它决定了AI每次启动时默认拥有什么样的上下文。没有这个文件,AI就是"裸奔"状态——只有你当前对话里提到的内容,其他一概不知。有了它,AI在进入项目目录的第一时间就能读到项目背景、技术栈、目录结构、运行命令、代码风格约定,甚至你踩过的坑。
我第一次用的时候很天真,以为CLAUDE.md只在项目根目录放一份就够了。实际用下来发现,这套机制分为三层,每一层的优先级和作用范围都不一样:
| 层级 | 文件位置 | 作用范围 | 覆盖时机 |
|---|---|---|---|
| 用户级 | ~/.claude/CLAUDE.md | 所有项目、所有会话 | 全局默认 |
| 项目级 | <项目根目录>/CLAUDE.md | 当前项目的全部会话 | 进入项目时 |
| 会话级 | 对话中追加说明 | 当前会话 | 实时动态补充 |
用户级适合放你自己的通用偏好,比如"代码注释用中文""优先使用函数式写法""不要动测试文件除非我明确要求"。项目级适合放这个仓库特有的信息,比如模块划分、启动命令、部署流程。会话级是临时补充,类似"这次只改登录逻辑,其他别碰"。
我见过不少人只用了项目级这一层,而且要么写得像流水账,要么写了一堆AI根本用不上的信息。真正好用的CLAUDE.md,结构应该高度结构化,让AI能在几百毫秒内提取出关键约束。
2.2 项目级CLAUDE.md的黄金段落
以我维护的一个Go后端项目为例,我当前的CLAUDE.md长这样(核心段落摘录):
# Project: order-service # Tech stack: Go 1.22 / Gin / PostgreSQL / Redis # Monorepo layout: services/order | services/user | pkg/common ## Commands - dev: docker compose up -d && go run ./cmd/server - test: go test ./... -race - lint: golangci-lint run ## Coding rules - 错误处理:禁止吞错,所有error必须向上返回或显式log - 日志:使用pkg/common里的logger,禁止直接fmt.Println - 数据库:所有SQL必须走repository层,禁止在handler里拼SQL - 测试:公开函数必须补单元测试,覆盖率目标80%+ - 状态码:业务错误统一返回{code, message, data}结构 ## Gotchas - Redis缓存过期策略改动过,涉及cache包的修改先看README的DECISIONS.md - 用户服务的phone字段已废弃,新代码不要复用 - mockery生成mock的路径在internal/mocks,不要手写mock这一段写法有三个要点:极致压缩、约束明确、直击痛点。不要写"我们是一个电商订单系统,为用户提供优质服务"这种废话,AI不需要情怀,它需要的是"技术栈+命令+规则+坑"。
写Gotchas这一点特别重要。AI在生成代码时,最怕的就是它按照通用常识去写,结果用上了项目里已经淘汰的方案。我在实际项目里踩过最惨的一次:AI帮我重构缓存模块,按它理解的"标准做法"直接用了全局缓存键前缀,结果把线上用户数据串了。事后我把"所有缓存键必须带业务前缀+版本号"写进Gotchas,再没出过同类问题。
2.3 用户级CLAUDE.md的个性化沉淀
用户级CLAUDE.md不需要覆盖具体项目,但要把你的"个人工作习惯"固化下来。这个东西的价值在于跨项目一致性和个人风格沉淀。
我的~/.claude/CLAUDE.md内容不长,但每一条都持续生效:
# 编码偏好 - 变量命名:不用单字母,除非是循环索引 - 注释:中文,解释为什么而不是是什么 - 函数长度:尽量控制在50行以内,过长必须拆分 - Commit message: 遵循 conventional commits 格式 - 生成代码时,如果存在安全隐患(SQL注入、越权、敏感信息硬编码),必须提醒我 # 交互规则 - 每次修改前先说明改动计划,等我确认后再动手 - 涉及删代码的操作,先列出将删除的文件和函数,得到确认再执行 - 不确定的需求点要主动提问,禁止自作主张"涉及删代码的操作要确认"这条,是我吃过亏之后加上的。有一次它帮我"清理无用代码",一口气删了三个文件,其中一个老接口的兼容层直接被移除,导致联调环境里的旧客户端全部报错。从那以后我规定:AI可以提议删除,但不能执行删除,除非我明确点头。
这个机制本质上是在给AI划定"授权边界"。Claude Code本身能力很强,但越强的工具越需要清晰的边界。CLAUDE.md的核心价值不是让AI更聪明,而是让AI在你的项目里表现得"像团队里一个靠谱的同事"——不该碰的不碰,该做的主动做。
3. commands自定义命令:把高频动作变成斜杠指令
3.1 从一条老长老长的prompt说起
CLAUDE.md解决的是"AI知不知道背景"的问题,但实际操作中,我还有另一层烦恼:很多动作是需要反复执行的,比如"审查一下我改过的diff""给这段代码补测试""帮我检查goroutine有没有泄漏"。这些动作每次都靠手打一大段prompt来触发,效率太低,而且每次描述细枝末节的内容都不一样,AI返回的质量也飘忽不定。
claude-code的解决办法是/commands。在项目根目录的.claude/commands/下,每个.md文件就对应一个斜杠命令。你只要在对话里输入/review,AI就会读取这个文件里的完整指令来执行。
这玩意儿本质上是把prompt模板化、符号化。它解决的不只是效率问题,更是"一致性"问题——团队里大家用同一个/review命令,AI执行的审查标准就是同一套,输出质量就有了下限保障。
3.2 我的命令文件结构拆解
一个典型的命令文件会分成两段:开头的YAML frontmatter(配置元数据)和正文(prompt内容)。以我常用的.claude/commands/review.md为例:
--- description: 审查当前分支的代码改动 allowed-tools: Read, Grep, Glob model: sonnet --- 你是一位资深代码评审专家。请执行以下步骤: 1. 先用 git diff 获取当前分支相对 main 的全部改动 2. 按文件逐个审查,重点关注: - 错误处理是否完备 - 是否有明显的并发问题(竞态、死锁、未关闭的资源) - 是否存在SQL注入 / 越权访问等安全隐患 - 命名与代码风格是否符合项目CLAUDE.md约定 3. 输出格式: - 先给总体结论(通过 / 有问题) - 按严重程度列出问题,每条问题标注文件位置 - 修复建议必须给出可操作的代码片段frontmatter里的description是命令菜单里显示的一句话说明;allowed-tools约束了AI在执行这个命令时能用哪些工具——限制工具范围能防止它越权执行一些危险操作;model参数可以指定这个命令用哪个模型跑,重活累活可以分配更强的模型,轻量任务用快的模型省时间。
正文部分是我的核心经验:命令文件不是把prompt怼上去就完事,它应该是一份执行清单(checklist)。AI逐条按清单走,输出质量就稳定。我见过有人把命令文件写成一段散文风格的prompt,效果时好时坏,原因就是AI在解析多段自然语言时,对"必须做的事"和"可选做的事"的权重判断不一致。清单式的写法,括号里明说"必须""禁止",AI的执行确定性会高得多。
3.3 命令模板是"半成品",用参数让它活起来
固定写法的命令用久了,你会发现一个问题:有些命令的变体很多。比如补测试,有时候要补的是单测,有时候是集成测试;有时候只需要补某个文件,有时候是整个包。把全部分支写成不同命令会爆炸,一个新增特性就可能要新建三四个command文件。
claude-code支持在命令文件里声明参数,用$ARGUMENTS占位符接收用户在/cmd xxx后面的额外输入。我实际维护的.claude/commands/test.md长这样:
--- description: 为指定代码编写单元测试并运行 --- 用户对 `$ARGUMENTS` 指定的代码进行测试补全。 要求: 1. 先用 Glob 定位用户指定文件的对应测试文件,判断是否已存在 2. 如果存在,在原有基础上追加用例;如果不存在,新建 _test.go 文件 3. 测试用例需覆盖:正常路径、边界值、错误分支 4. 测试命名遵循 函数名_场景_期望结果 的格式 5. 写完后执行 go test ./路径/... -run 对应用例,把结果反馈 如果用户没有指定具体文件,列出当前分支改动的文件清单,让我选择。这样我平时用/test services/order/repository.go就能直接对目标文件补测试,不用每次重复交代"什么格式、覆盖哪些场景、跑哪个命令"。命令模板沉淀之后,团队里新人上手,也不需要我先给TA培训一小时的"AI协作规范",直接用命令就知道AI会怎么干活。
4. hooks机制:让AI在关键节点自动收敛行为
4.1 hooks到底是干什么的
命令和CLAUDE.md都是"被动"的:AI只有在读上下文或者你敲斜杠命令时,才会用到它们。但真实开发中有很多场景,你希望AI主动在某个行为发生之前或之后,执行一次检查或者拦截。比如:它正在往代码里写入一个危险命令时,最好能自动拦一下;它每次打开文件时,能自动确认这个文件是不是在改动范围内。
这些就是hooks的活。claude-code的hooks机制,本质上是事件触发的脚本钩子。在项目的.claude/hooks/目录下,你可以放一系列可执行脚本,并在settings.json里把脚本绑定到对应的claude-code事件上。事件类型主要有:
PreToolUse——某个工具被调用之前触发,可以阻止调用PostToolUse——工具执行结束后触发,可以做校验Notification——Claude Code发出通知时触发SessionStart——会话开始时触发UserPromptSubmit——用户提交消息时触发
我用得最多的是PreToolUse和PostToolUse。前者管"准入",后者管"质检"——正好对应我前面说的授权边界和质量收敛。
4.2 一个实战:拦截危险命令的写入
先说PreToolUse。有一次AI在帮我修复容器启动问题时,自作主张往代码里塞了一个docker stop $(docker ps -aq)的组合命令,我当时没有注意到,结果本地所有在跑的服务容器全被停了。之后我在.claude/hooks/pre_tool_use.sh里写了一个简单的拦截逻辑:
#!/bin/bash # 从stdin读取JSON事件数据 input=$(cat) # 提取工具名和工具输入部分(粗略解析) tool_name=$(echo "$input" | jq -r '.tool_name') # 只对Bash或Edit操作检查 if [[ "$tool_name" == "Bash" || "$tool_name" == "Edit" ]]; then content=$(echo "$input" | jq -r '.tool_input.command // .tool_input.file_path // ""') # 危险命令黑名单检查 if echo "$content" | grep -qE 'docker stop|docker rm|git push --force|rm -rf /'; then # 输出拦截结果并阻止调用 echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionReason\":\"检测到高危操作,已拦截。请确认后再单独执行。\"}}" exit 0 fi fi # 放行 echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"allow\"}}" exit 0这里有个细节要提醒你:hooks脚本的标准输出必须是特定格式的JSON,claude-code才能正确解析。如果你在脚本里print了调试信息,会把协议打乱,导致AI误判。排查的时候可以用printf打到stderr,避免污染协议。我第一次写hooks时就是在这里踩了坑,脚本明明执行了,但Claude Code完全没反应,后来才发现是stdout被调试文本污染了。
PreToolUse的permissionDecision有三个值:allow、deny、ask。ask比较柔和,会弹出确认请求。我建议不要一上来就全deny,很多"危险命令"其实是合法的,比如docker stop有时候就是要停掉某个容器。先用ask做一个软拦截,让AI和你在对话里确认,比自己写死deny要好。
4.3 PostToolUse做自动校验:让每次改动都过一遍lint
再说PostToolUse。这个事件在AI执行完工具后触发,适合做"写入后的质量校验"。我当前项目里的.claude/hooks/post_tool_use.sh会在AI编辑完Go文件后,自动跑一次golangci-lint run --fast,把语法错误和lint问题抓出来反馈给AI,让它自己修正:
#!/bin/bash input=$(cat) tool_name=$(echo "$input" | jq -r '.tool_name') if [[ "$tool_name" == "Edit" || "$tool_name" == "MultiEdit" ]]; then file_path=$(echo "$input" | jq -r '.tool_input.file_path') if [[ "$file_path" == *.go ]]; then cd /path/to/project lint_output=$(golangci-lint run --fast 2>&1) if [[ $? -ne 0 ]]; then echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"hookEventName\":\"PostToolUse\",\"renderedContent\":\"Lint检查发现问题,请优先修复:<br>${lint_output}\"}}" fi fi fi exit 0注意我上面的示例最后一行是exit 0,这很重要。PostToolUse的脚本无论检查是否通过,整体退出码都应该是0,否则会干扰Claude Code的主流程。lint结果通过renderedContent字段回传给对话,AI看到之后就能自己着手修。
有了这个钩子,我明显感觉到一个变化:AI写完代码后提交给我审查时,代码质量比没有钩子时高一大截。因为它在交卷之前,已经被lint折磨过几轮了。我相当于给AI的代码输出加了一个"自动质检岗",省掉了我手动review低级错误的时间。
4.4 settings.json里怎么挂载hooks
hooks脚本写好后,需要在.claude/settings.json里注册。以我上面两个脚本为例,对应的配置是:
{ "permissions": { "allow": ["Bash", "Read", "Edit", "Glob", "Grep"] }, "hooks": { "PreToolUse": [ { "matcher": "Bash|Edit|MultiEdit", "hooks": [ { "type": "command", "command": "bash .claude/hooks/pre_tool_use.sh" } ] } ], "PostToolUse": [ { "matcher": "Edit|MultiEdit", "hooks": [ { "type": "command", "command": "bash .claude/hooks/post_tool_use.sh" } ] } ] } }matcher字段用来限定这个hook匹配哪些工具,比如上面PreToolUse只对Bash、Edit、MultiEdit生效,其他操作不拦截,避免性能浪费和误伤。command字段里可以写任何可执行命令或脚本路径,不限于bash,Python脚本、Node脚本都可以。
还有一点:这个settings.json还承担了权限白名单的功能。上面我配置了permissions.allow,意思是Bash、Read、Edit这些基础工具允许直接使用,不需要每次弹窗确认。团队统一模板时,这一项可以大大减少AI操作时的等待成本。不过别把所有工具都加进去,像Write这种可以追加写入但不可覆盖的、或者一些影响范围大的重工具,建议保留默认的询问逻辑。
5. agents协作模式:把一个大活拆给"虚拟团队"
5.1 单线程对话的天花板
用claude-code做复杂任务时,你会遇到一个很尴尬的瓶颈:单个会话里的上下文窗口是有限的,而AI处理复杂任务的能力会随着对话轮数增加和上下文膨胀而下降。比如让AI从零重构一个模块,它既要理解旧代码逻辑,又要设计新架构,还要保证测试不挂——这些活全塞在一个session里,到后面AI经常顾此失彼,改了这个忘了那个。
我的解决方案是agents——子代理。你可以把agent理解成"给AI戴上不同的专业帽子"。claude-code默认会在.claude/agents/里定义一系列子代理,还是在CLAUDE.md里通过特定的标记让AI知道可以调度它们。每个子代理有自己的system prompt、工具权限和行为边界。这就相当于你组建了一个虚拟小团队:架构师agent负责整体设计、实现agent负责写代码、审查agent负责挑毛病。
5.2 我常用的agent配置文档
我当前项目里的.claude/agents/目录长这样:
architect.md——负责阅读现有代码、输出技术方案implementer.md——负责按方案写代码,偏向快速执行reviewer.md——负责代码评审和找漏洞,和implementer互相制衡
以implementer.md为例,它的核心配置片段:
# role: 实现工程师 # model: sonnet # tools: Read, Grep, Glob, Edit, MultiEdit 你是项目中的实现工程师。接到任务后,严格按照方案文档执行。 工作方式: 1. 先读相关文件,理清改动点,不允许凭印象直接改 2. 改动前,用列表输出你准备修改的文件和改动摘要 3. 改动时,只改方案涉及的部分,禁止顺手重构无关代码 4. 完成后,更新相关测试 禁止事项: - 禁止修改与任务无关的文件 - 禁止在未确认方案的情况下直接开始写代码 - 禁止跳过测试直接交付 - 禁止忽略CLAUDE.md中记录的Gotchas和命令文件一样,agent配置文件也支持frontmatter。tools字段决定了这个agent能调用哪些工具。我故意不给implementer配Bash权限,这样它就不能自己乱跑命令,所有构建、测试都得通过主对话来执行——这相当于人为加了一道关卡,防止它"闷头乱改还跑出一堆副作用"。
reviewer.md则反过来,我给它加了Read、Grep、Glob这类只读工具,明确不允许它直接改代码,只负责在代码里找问题:
# role: 代码评审专家 # model: opus # tools: Read, Grep, Glob 你是代码评审专家。收到代码后,执行严格审查。 每个提交必须回答: 1. 逻辑正确性:是否存在边界条件未处理、并发安全问题、资源泄漏? 2. 安全性:是否存在注入、越权、敏感信息泄露? 3. 可维护性:命名、结构、注释是否符合团队约定? 4. 测试完整性:关键分支是否有对应的测试覆盖? 输出格式: - 结论:通过 / 需修改 - 每个问题按 严重(必须改)/ 中等(建议改)/ 轻微(可选) 分级 - 每条问题必须给出文件路径和行号区间 - 修复建议用可执行代码片段说明在CLAUDE.md里,我会加一段调度说明,告诉AI什么时候该用哪个agent:
## Agent dispatch - 任务涉及跨模块设计或重构时,先调用 /architect 输出方案 - 方案确认后,由 /implementer 执行代码改动 - 代码改动完成后,必须调用 /reviewer 做一轮评审 - 评审发现问题后,回到 /implementer 修复,形成循环,直到reviewer给出通过结论这个调度说明是整个协作模式里最关键的部分。因为AI主对话本身也是一个agent,你可以在CLAUDE.md里明确"你是技术负责人,负责调度和最终决策"。这样一来,复杂任务就不再是"一个AI从早干到晚",而是"架构师出图、工程师实现、评审员把关"的闭环。运行下来我的直接体验是:生成质量的稳定性明显提升了,尤其是在大型重构任务上,因为任何一环的输出都要被另一环审查,相当于在AI内部也引入了一次"代码评审"流程。
5.3 agent协作的边界和注意点
要提醒的是,agent协作不是银弹,用不好反而会拖慢速度。我的经验是:优先用单会话+CLAUDE.md解决简单任务,只有任务复杂度超过"一个session能干完"的量级时,才上agents。划分标准很简单——如果这个任务你预估自己需要一个下午连续工作才能完成,那它就值得上agent;如果只是改个函数,直接让主AI改完就行,上agent反而多几轮调度开销。
另外,agents之间的信息传递也值得留意。implementer在改代码时,architect的方案文档你是希望它能读到的。最简单的方式是把方案写进一个临时文件(比如docs/plan.md),让implementer先读这个文件再动手,避免大量依赖上下文传播。这个流量控制细节,能避免子代理之间"你说你的、我干我的"的脱节问题。
6. 一套完整可复用的模板实战:从零搭建一个项目的规范体系
6.1 初始化步骤清单
前面四章讲的是零部件,这一章我把它们组装起来,给一个完整的最小模板。假设我现在要开一个全新的Node.js + TypeScript项目,我会按这个顺序做一遍:
第一步,创建目录骨架:
mkdir -p .claude/commands .claude/hooks .claude/agents touch CLAUDE.md .claude/settings.json第二步,写CLAUDE.md主体。内容包括技术栈、常用命令、目录结构、代码风格和已知坑。这个文件不求一次写全,先写目前确定的,后续项目推进过程中不断追加。
第三步,把项目特有命令封装成commands。启动服务、跑测试、lint、build,这四类是最基础的。写完之后在对话里敲一遍/,看命令是否出现在列表里。
第四步,配置hooks。先做PreToolUse的ask级拦截,把rm -rf、git push --force、docker stop这种命令先拉进观察名单;再做PostToolUse的lint自动校验。
第五步,按需配置agents。项目前期可以只配一个reviewer,用于代码完成后的自动评审。等复杂度上来,再加architect和implementer。
第六步,所有文件入库,先小范围跑一个真实任务验证效果。千万不要第一天就铺全量规则,规则太多AI反应不过来,人也维护不过来。
6.2 模板文件的最小清单参考
我整理了一份我目前生产环境里正常运行的最小文件清单,可以直接抄:
CLAUDE.md .claude/ ├── settings.json ├── commands/ │ ├── review.md │ ├── test.md │ └── fix.md ├── hooks/ │ ├── pre_tool_use.sh │ └── post_tool_use.sh └── agents/ ├── architect.md ├── implementer.md └── reviewer.md每个文件尽量控制在几十行内,不要写成几百行的巨型文档。模板的价值在于"该imp的时候imp、该省的时候省"。规则写得太满,AI每次操作都要掂量一堆约束,生成速度会变慢,而且经常出现"为了满足规则A而违反规则B"的窘境。我个人的经验是:CLAUDE.md控制在40-60行,单个command文件在30行以内,单个agent文件在40行以内,信息密度高、指向性强,AI执行效果最好。
6.3 一套模板如何跨项目复用
最后说下复用。模板的最佳载体不是"复制粘贴",而是你用户级目录下的一套基准模板加上每个项目级的增量定制。
我自己的做法是,在~/.claude/下维护一个templates/目录,里面按技术栈放好基准模板:
templates/node-ts/templates/go-server/templates/python-fastapi/
开新项目时,把对应技术栈的.claude/目录拷过去,再根据当前项目的特殊性改CLAUDE.md和commands。这样既保证了团队内所有项目的基础协作规范一致,又保留了每个项目独立演化的空间。
这套方法论我在两个团队里推行过,效果都很明显。新手第一天用claude-code就能直接享受到"老手调教过"的AI行为模式,不用再从零体验AI的"野性"。而我自己,也把大量重复性的口头交代从日常工作中彻底删掉了。
最后分享一个我个人的小经验:模板不是一次性建完就完事的。每隔两到三周,回看一次CLAUDE.md和commands,把最近踩过的坑、新增的约定、临时加过的prompt,收编进模板里。这套东西的价值会随着项目的演进越滚越大,它记录的不仅是AI的协作方式,更是你对这个项目技术债务和最佳实践的全部理解。