先说一个我踩了挺久的坑:Claude Code 本身已经很能打,但落到真实项目上,真正的瓶颈往往不是它不会写代码,而是它不知道你项目里有一套约定俗成的流程必须遵守。比如代码评审要看哪几个文件、接口文档要按什么模板输出、数据库变更要跑哪几条校验脚本。这些知识如果每次靠你手打一遍提示词,很快变成一篇又长又臭的说明书,而且模型不一定按你的流程执行。后来我换了个思路:把流程固化成 Skill,再让 OpenClaw 在任务启动和运行过程中自动决定该不该调用这些 Skill。这篇文章就把这套实战方案完整拆开讲一遍,从 Skill 怎么写、OpenClaw 怎么配,到日志怎么看、命中率怎么调,全部给到可以直接抄作业的细节。
1. 为什么要把 Skill 调用自动化
1.1 Skill 机制解决的真实问题
Claude Code 的 Skills 本质上是一组可复用的“操作手册”。它通常以.claude/skills/<技能名>/SKILL.md的形式存在,每个技能目录里可以放说明文档、辅助脚本、模板文件。模型在项目根目录启动时,会自动扫描这些技能目录,把每个技能的 description 加载进决策上下文。等任务真正匹配到某个技能时,再去读取 SKILL.md 的完整正文并按里面的步骤执行。
把这个机制理解透之后,你会发现它解决的是一个很实际的问题:模型的能力是通用的,但你项目里的流程是特有的。比如“评审代码之前必须先跑一遍静态检查”,“接口文档必须包含请求示例”,“发布前要检查依赖锁定文件”。这些规则写进系统提示词会很占地方,而且一旦项目多了,根本没法维护。放到 Skill 里,描述就是索引,正文就是详细操作标准,模型按需调用,上下文也不会被无关内容撑爆。
打个不严格的比方:这就像给一个很聪明但没经验的新同事准备了一排文件夹。平时他只看到文件夹外面的标签,不动里面的内容;接到对应任务时,他会自己抽出那份文件夹,照着里面的步骤做。Skill 就是这个文件夹。
1.2 手动调用流程的四个痛点
在把调度交给 OpenClaw 之前,我试过更原始的方式:每次手动告诉 Claude Code“先做评审”“按模板生成文档”。这种手动模式有四个痛点很致命。
第一是遗忘。任务一多,你根本不可能记得每个操作节点该调用哪个流程。尤其是那些低频但重要的流程,比如“每季度清理一次日志目录”,等你想起来的时候,它已经不在了。
第二是上下文污染。直接把完整流程贴进对话,每轮对话都会消耗大量 token,而且真正关键的指令会被其他内容淹没。我见过最夸张的一次,光流程说明就占了上下文的一半,模型开始“忘”掉前面的任务目标。
第三是时机滞后。很多 Skill 该在写代码之前调用,比如先评审设计方案再动手。但人手盯根本盯不过来,等你发现流程没走,代码都已经写完了。
第四是不可度量。手动调用没有日志,没有统计,你根本不知道哪个流程经常被跳过,也没法针对性地优化。说白了,流程好不好,完全靠感觉。
1.3 OpenClaw 在中间做了什么
OpenClaw 在这里起的作用,简单说就是四个动作:解析、注入、监听、回传。
它先把用户的原始请求解析成结构化的任务类型,比如“代码评审”“接口文档生成”“数据库变更检查”。然后根据任务类型,决定本次运行要暴露哪些 Skill,把这些 Skill 的描述和 SKILL.md 路径注入到 Claude Code 的启动上下文中。在 Claude Code 运行的过程中,它负责监听输出日志,判断技能是否真的被读取、是否产生了预期产物。任务结束后,它把结果和调用记录汇总回传,方便你复盘。
这套设计的价值在于:OpenClaw 管“什么时候用哪个流程”,Claude Code 管“怎么把流程执行完”。二者各管一段,责任边界非常干净。这也是我后来遇到问题能快速定位的关键,日志里一眼就能看出是调度层没命中,还是执行层没按流程走。
2. 整体架构与工具选型思路
2.1 调度层、执行层、资源层三层划分
整套方案我习惯分成三层来看。调度层是 OpenClaw,负责任务解析、Skill 注册、触发判定、会话生命周期和日志回收。执行层是 Claude Code 本身,它负责真正读取代码、分析逻辑、调用命令行工具,并按 SKILL.md 的步骤产出结果。资源层是项目里的.claude/skills目录,里面每个技能都包含入口文档、辅助脚本和静态资源。
这种分层看起来是常识,但这个常识很容易在实操中被忽略。我见过不少人的做法:把技能脚本直接写在系统提示词里,或者把模板文件硬编码到 Skill 正文中。一两个技能的时候还好,技能一多,资源层就变成了一个汤锅,入口文档里全是代码,辅助脚本里全是注释。分层不只是为了好看,更是为了让你能独立测试每一层。SKILL.md 能不能被正确解析,脚本能不能跑通,OpenClaw 的触发规则是否命中,三个问题分开排查,效率会高很多。
从我实践下来的感受看,资源层的目录结构最值得一开始就定好。我的习惯是每个技能目录下固定分三个子目录:scripts放可执行脚本,assets放模板和参考文档,其余内容都放进 SKILL.md。命名统一用 kebab-case,比如code-review、api-doc,不要用中文名,免得某些工具链处理路径时出问题。
2.2 为什么选 OpenClaw 做编排
你可能会问,为什么不让 Claude Code 自己管理 Skill 调用,非要再加一个 OpenClaw?这个问题我一开始也纠结过。Claude Code 确实具备根据任务自动加载 Skill 的能力,但它有两个短板。
第一,它只能感知任务内容,感知不到更全局的上下文。比如你想根据“当前分支是 release 分支”来决定是否调用发布检查 Skill,这在单纯的任务提示里是表达不清楚的。OpenClaw 这一层可以读取环境变量、分支名、文件变更列表,把这些外部信息统一解析后,再决定触发哪个技能。
第二,Claude Code 的调用过程缺少统一的可观测出口。你当然可以在终端里盯着输出,但要做命中率统计、误触发分析,就麻烦了。OpenClaw 把所有调用记录沉淀成结构化日志,字段统一,后面接看板、接告警都很方便。
除此之外,OpenClaw 还有一个很实用的优势:换引擎成本低。今天你用 Claude Code 做执行层,明天想换成其他命令行 Agent,理论上只要改引擎配置,调度规则和 Skill 资源都可以原样复用。当然,我这里讲的 OpenClaw 是社区里一个开源 Agent 调度项目,我本地用的是 0.4.x 版本,后面讲的配置细节都对齐这个版本的字段风格。
2.3 环境准备与依赖清单
动手之前先确认环境,别到一半发现某个依赖没装。我列一份我在模拟项目X上用的完整清单,你可以对照检查。
- 操作系统:Linux 或 macOS 都行,Windows 建议开 WSL 环境,文件路径处理更省心。
- 运行时:Node.js 18 以上,Python 3.10 以上。Claude Code 和部分 Skill 脚本依赖这两套运行时。
- CLI 工具:Claude Code 已登录,并且能正常使用无头模式跑任务。
- OpenClaw:本地安装 0.4.x 版本,启动命令能正常打印版本号。
- 项目:准备一个干净的模拟项目X。不要直接用生产项目练手,先在小项目上把链路跑通,再复制到真实环境。
我踩过的坑是环境变量。Claude Code 在无头模式下会读取一组密钥相关的环境变量,OpenClaw 启动子进程时,如果没把这些变量传进去,任务会一直卡在授权环节。我的习惯是把密钥文件路径写进 OpenClaw 的配置文件里,启动前先跑一次冒烟测试,用一条最简单的命令确认链路是通的,再开始做复杂流程。
3. Skill 的定义规范与编写要点
3.1 目录结构是第一步
先看一个我常用的目录结构,以模拟项目X为例。
模拟项目X/ └── .claude/ └── skills/ ├── code-review/ │ ├── SKILL.md │ ├── scripts/ │ │ └── review_summary.py │ └── assets/ │ └── review_template.md └── api-doc/ ├── SKILL.md ├── scripts/ │ └── gen_openapi.py └── assets/ └── api_template.yaml目录名就是技能名,SKILL.md 是入口,这个文件名不能改,改了 Claude Code 就识别不到。脚本文件建议放在scripts下,模板放在assets下,这样 SKILL.md 里只需要写“运行什么脚本、读取什么模板”,而不是把一大段代码塞进正文。
别小看这个结构的规范性。我碰到过一次很奇怪的问题:代码评审技能一直不生效,排查半天发现是 SKILL.md 被放到了.claude/skills/code-review/docs/下面。目录层级错了,Claude Code 整棵扫描树都找不到入口文件,自然也就不会触发。
3.2 frontmatter 决定模型会不会用
SKILL.md 的 frontmatter 是这个技能能不能被正确唤起的关键。里面最重要的字段就两个:name和description。
name是技能的唯一标识,要和目录名保持一致,这个基本没争议。真正决定生死的,是description。Claude Code 会把 description 当作索引,在决策时根据它判断当前任务是否匹配。所以 description 必须写清楚三件事:什么时候用、怎么用、什么时候不要用。
我对比过一个反面例子和正面例子,效果差距非常明显。反面例子是:“代码评审。”太笼统,模型根本不知道评审的范围、产出格式和触发条件,很容易在用户只是问“这段代码写得怎么样”的时候也触发。正面例子应该这样写:
--- name: code-review description: 对指定文件或 git diff 进行代码评审,输出按严重级别分组的问题清单、原因说明和可执行的修改建议。当任务包含 review、评审、走查、代码质量检查、合并前检查等意图时使用;当用户只是要求解释某段代码或写注释时不要使用。 version: 1.0.0 ---注意最后那句“当用户只是要求解释某段代码时不要使用”,这种反例约束非常重要。模型是基于语义匹配做判断的,你不给它负样本,它就会把“解释代码”和“评审代码”混在一起。
3.3 一个可直接复制的 code-review Skill
下面这份 SKILL.md 是我在模拟项目X上实际用过的版本,你可以直接复制到自己的项目里改一改。它的特点是步骤明确、输出格式固定、还带了容量保护。
--- name: code-review description: 对指定文件或 git diff 进行代码评审,输出按严重级别分组的问题清单、原因说明和可执行的修改建议。当任务包含 review、评审、走查、代码质量检查、合并前检查等意图时使用;当用户只是要求解释某段代码时不要使用。 version: 1.0.0 --- # 代码评审 目标:在 10 分钟内完成对本次变更的评审,输出一份可交给开发者的问题清单。 ## 步骤 1. 先获取变更范围: - 若用户指定文件,则直接使用这些文件。 - 否则执行 `git diff --name-only HEAD~1` 获取最近一次提交的变更文件。 2. 对每个变更文件逐行阅读,重点检查: - 是否有明显的空指针、越界、类型错误 - 是否缺少错误处理和日志 - 是否引入了不必要的复杂度 - 是否存在硬编码的敏感信息 3. 生成问题清单,按严重级别分为 P0 / P1 / P2。 - P0:会导致崩溃、数据错误或安全问题的缺陷 - P1:明显的逻辑问题,或违背既有架构约定的写法 - P2:风格、命名、可读性建议 4. 输出模板如下: ## 评审结果({commit}) ### P0 - [文件:行号] 问题描述。原因:...。建议:... ### P1 - ... ### P2 - ... 5. 若涉及文件超过 10 个,只评审前 10 个,并在结果末尾说明已跳过的文件清单。这份 SKILL.md 最有价值的地方不在代码,而在于第五条的容量保护。没有这条限制,模型会把所有变更文件都读一遍,遇到大项目直接上下文爆炸。加上之后,它就知道资源不够时要主动裁剪范围并说明情况。
3.4 编写 Skill 的五个避坑点
写 SKILL.md 这件事,看着简单,实际操作中坑不少。我总结了五个最常见的坑。
第一,description 写得太宽泛。只要包含“代码质量”这种抽象词,模型就可能在各种场景触发。正确的做法是把触发意图细化,最好给出具体动词。
第二,正文写得比论文还长。SKILL.md 是执行手册,不是知识库。大段背景说明、原理科普完全可以删掉,只保留步骤、检查点、输出格式。
第三,路径写死。Skill 里的脚本路径如果写成/home/xxx/scripts/run.sh,换一台机器就废了。正确做法是脚本里基于 SKILL.md 所在目录动态定位,或者通过环境变量注入项目根目录。
第四,只说“要做什么”,不说“不做什么”。模型是概率决策,没有负向约束,就容易跑偏。上一节的 code-review 就是典型例子。
第五,没有退出条件。Skill 正文里要写明什么时候算干完、输出到哪里。否则模型会一直“润色”结果,任务永远结束不了。比如我那个 code-review 就明确写了“只输出问题清单,不要直接修改代码”,这一句话能省掉无数次来回。
4. OpenClaw 自动调度 Skill 的实战配置
4.1 两层触发判定:规则兜底,模型决策
OpenClaw 配置的核心是触发规则。我的思路是两层判定,先粗后细。粗粒度用规则,比如关键词匹配、文件后缀匹配,把明显需要某个 Skill 的任务先框出来。细粒度交给模型,把命中的 Skill 描述注入上下文,让 Claude Code 判断是否真的需要按流程走。
这样设计的原因很简单:规则太死,模型太活,单用任何一种都有问题。纯规则会有大量漏网之鱼,比如用户说“帮我看下代码有没有坑”,这里面没有关键词“评审”,但语义上就是一次评审。纯模型又会有不确定性,同一个任务跑两次结果可能不一样。规则负责兜底,模型负责弹性,两者结合最稳。
下面是我在模拟项目X上的配置示例,字段风格基于 OpenClaw 0.4.x:
# openclaw.yml project: /workspace/模拟项目X agent: engine: claude-code headless: true working_dir: /workspace/模拟项目X skills: dir: .claude/skills auto_load: true triggers: - skill: code-review when: message_contains: ["评审", "review", "走查", "merge", "合并"] file_suffix: [".py", ".js", ".ts", ".go"] - skill: api-doc when: message_contains: ["接口文档", "api doc", "openapi", "swagger"]配置文件里的重点不是字段本身,而是你的触发条件要跟 Skill 的描述一一对应。规则对应的语义范围应当小于等于 description 的覆盖范围。否则会出现规则命中了但模型觉得不匹配的尴尬局面。
4.2 上下文注入方式与 Prompt 拼装
OpenClaw 在启动 Claude Code 之前,会根据命中的 Skill 拼出一段“任务说明”,然后通过无头模式把这段说明交给 Claude Code。这里的关键是:不要让模型凭印象执行,必须让它去读 SKILL.md 文件本身。
我常用的一段注入模板长这样:
你正在处理的项目:模拟项目X。 任务:{用户原始请求} 可用的项目流程: - code-review:对代码变更做评审,输出 P0/P1/P2 问题清单。 - api-doc:根据接口代码生成 OpenAPI 格式文档。 在开始执行前,先判断任务是否需要使用上述流程。 如果需要 code-review,必须按 .claude/skills/code-review/SKILL.md 的步骤执行。 如果需要 api-doc,必须按 .claude/skills/api-doc/SKILL.md 的步骤执行。最后一句“必须按 .../SKILL.md 的步骤执行”非常关键。Claude Code 对本地文件路径的引用是很敏感的,你只要把路径指对,它就会去读这个文件,并把里面的步骤当作最高优先级指令来处理。如果不加这句,模型可能会根据自己的预训练知识“自由发挥”,那就完全乱套了。
注入之后,OpenClaw 还要把任务类型、触发原因、注入时间记录到日志里。这些信息在后面的调优阶段非常重要,你能通过日志看到每一次调用到底是被规则命中的,还是被模型主动选择的。
4.3 端到端跑通一次完整任务
配置写好了,我们来实际跑一遍。假设我现在要对订单模块做一次代码评审,完整操作分六步。
第一步,初始化项目并创建技能目录。
cd /workspace/模拟项目X git init mkdir -p .claude/skills/code-review/scripts第二步,把 3.3 节的 SKILL.md 内容写入.claude/skills/code-review/SKILL.md。
第三步,编写openclaw.yml,把触发规则配好。
第四步,启动 OpenClaw 并传入任务。
cd /workspace/模拟项目X openclaw run --config openclaw.yml --task "帮我对订单模块做一次代码评审"第五步,观察日志。一个正常的调用链路应该是这样的:
[openclaw] task parsed: code_review [openclaw] trigger matched: code-review (message contains "评审") [openclaw] injecting skill hint into context [openclaw] launched claude-code (headless) [claude-code] reading .claude/skills/code-review/SKILL.md [claude-code] got diff: src/order/create.go src/order/query.go [claude-code] generate review result [openclaw] task finished, exit 0第六步,检查产出物。评审结果文件被写到项目目录下,里面包含 P0/P1/P2 三个分级的问题清单。到这里,一次完整的自动调用就结束了。
日志里最需要盯的是reading .claude/skills/code-review/SKILL.md这一行。看到这行,说明 Claude Code 确实把技能文件当作执行规范读取了,而不是自己凭空编了一套评审标准。
4.4 组合 Skill 的顺序编排
单个 Skill 跑通之后,自然就会遇到组合场景。比如“帮我把订单模块的接口文档补上,然后做一次代码评审”,这个请求会同时命中api-doc和code-review两个 Skill。如果放任模型自由选择顺序,八成会出问题:它可能先评审再改文档,或者两个流程并行互相干扰。
我在 OpenClaw 里用 workflow 配置来管理顺序,核心原则是先考虑依赖关系。接口文档和代码评审之间就有明显的依赖:评审过程中如果修改了接口签名,生成的文档就过期了。所以我会强制先生成文档,再做评审,并且在评审后加一步“检查接口签名是否变更,如有变更同步更新文档”。
配置示例:
workflow: - stage: 1 skill: api-doc description: 先生成接口文档 - stage: 2 skill: code-review description: 评审代码变更,若修改了接口签名,需同步更新 api-doc 输出这个顺序编排的思路可以扩展到更多场景。只要是多个技能一起触发,我建议你先把它们的依赖关系画在草稿纸上,再写进配置。硬编码顺序看起来笨,但对结果稳定性是巨大的提升。模型自由发挥的时候,输出质量方差非常大,这一点我在多次尝试后体会很深。
5. 效果验证与调优方法
5.1 先定义命中率和误触发率
自动调用上线之后,你必须先定义“成功”的标准,否则没法判断方案好不好。我用的核心指标有两个:
命中率 = 应该触发某个 Skill 的任务中,实际正确触发的比例。误触发率 = 全部触发中,本不该用却用了的比例。
这两个指标需要配套使用。只看命中率,可能漏过误触发;只看误触发率,可能忽略漏触发。我给自己定的目标线是命中率 90% 以上、误触发率 5% 以下。达不到就说明 Skill 描述或触发规则还有问题。
具体怎么统计?我是靠 OpenClaw 的结构化日志,每条日志都带上任务类型、触发原因、技能名称、产物路径。每天结束后汇总一次,按技能维度分组,就能看到哪个流程容易被跳过、哪个流程频繁误伤。这个数据整洁度,是手动调用时代完全不敢想的。
5.2 从日志里看懂调用链路
OpenClaw 的日志字段不多,但每个字段都有明确含义。我整理成一个速查表,方便你对照:
| 日志关键字 | 含义 | 正常状态 |
|---|---|---|
task parsed | 任务类型被解析成功 | 应出现在每次任务开头 |
trigger matched | 至少一条触发规则命中 | 期望技能命中时应有此条 |
skill hint injected | Skill 描述已注入上下文 | 应在启动 Claude Code 前出现 |
reading SKILL.md | Claude Code 实际读取了技能文件 | 这是最关键的成功信号 |
task finished | 任务正常结束 | 应返回 exit 0 |
排查问题时,就按链路从上往下看:缺了trigger matched,问题在触发规则;缺了reading,问题在上下文注入或者路径配置。我基本靠这个链路定位问题,比瞎猜效率高多了。
5.3 命中率调优的三板斧
调优手段归纳下来就是三板斧:改 description、调 trigger、加阶段校验。
第一,改 description 是收益最大的手段。description 是模型做语义匹配的依据,你给它一个含糊的描述,它就只能含糊地判断。前面提到的 api-doc 技能,我一开始把描述写成“生成接口文档”,结果用户在讨论接口设计时也被触发。后来改成“当任务要求生成 OpenAPI / Swagger 或接口文档文件时使用,日常讨论接口设计不需要”,误触发率明显降下来。
第二,调 trigger。当规则覆盖面太宽导致频繁误触发,就加否定词;当覆盖面太窄导致漏触发,就加同义词或文件后缀。这里要特别提醒,触发规则是模型判断之前的第一道闸门,规则没命中,模型再聪明也没有用。
第三,加阶段校验。OpenClaw 可以在任务结束后检查产物文件是否存在。比如 code-review 应该产出review_result.md,如果文件不存在,就自动补发一轮重试。这种校验不能提高首次命中率,但能把“没干成”变成“有一次补救机会”。
6. 常见问题与排查技巧实录
6.1 问题速查表
把我在实操中遇到的高频问题整理成表格,你可以直接按图索骥。
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
| Skill 完全没有被读取 | 目录层级不对或名称不是 SKILL.md | 检查.claude/skills/<name>/SKILL.md路径 |
| 唤起了但没按流程走 | description 语义不匹配 | 重写 description,补充正例和反例 |
| 频繁误触发 | trigger 条件太宽 | 收窄关键词,加否定词 |
| 脚本报路径不存在 | 脚本里写死了绝对路径 | 改为基于 SKILL.md 目录的相对路径 |
| 上下文超长 | SKILL.md 正文塞了太多代码 | 把脚本移到 scripts 目录,正文只写调用方式 |
| 多个 Skill 互相覆盖 | 没有定义执行顺序 | 在 workflow 里配置 stage 顺序 |
| 任务卡在授权环节 | 子进程没继承密钥环境变量 | 将密钥文件路径传入 OpenClaw 配置并做冒烟测试 |
6.2 两次真实排错记录
第一次排错是“SKILL.md 没被读取”。当时日志显示trigger matched和injecting skill hint into context都正常,但 Claude Code 端完全没有reading SKILL.md的痕迹。我花了一个下午排查,最后发现是 OpenClaw 配置里skills.dir写成了相对路径.claude/skills,而实际项目根目录在/workspace/模拟项目X/submodule,相对路径解析后指向了错误位置。改成绝对路径之后问题立刻消失。这个教训是:涉及 Agent 调度的配置,路径一律用绝对路径,别图省事写相对路径。
第二次排错是“code-review 变成重构方案”。日志显示技能被正确读取,但 Claude Code 评审到一半开始直接改代码,最终输出变成了一整套重构方案,而不是问题清单。原因在 SKILL.md 里,我确实写了“输出问题清单”,但没有明确写“只输出建议,不要直接修改代码”。模型默认把“评审”理解成“边评边改”。我在 SKILL.md 里加了一行强行约束,并且把这句话同时写进 OpenClaw 的注入模板里,双保险之后现象就消失了。
这两次排错的共性是:问题根源都不是模型能力,而是文档规范没写清楚或者路径配置不严谨。Agent 自动化调试,本质上就是在调试人和工具的沟通边界。
6.3 给新手的三个建议
如果你准备在自己的项目里落地这套方案,我给三个建议。
第一,从最少的技能开始。别一上来就规划十几个 Skill,模型在十几个候选里做决策,压力很大,误触发率也会升高。先挑一个最高频、最痛点的流程做透,比如代码评审,跑通之后再复制到其他流程。
第二,把 skills 目录纳入版本管理。SKILL.md 是可以迭代的资产,改坏了要能回滚。我在模拟项目X里就是把.claude/skills目录每次变更都单独提交,commit message 写成技能名称加改动原因,方便回溯。
第三,不要迷信自动化。自动调 Skill 解决的是“该用的时候用上”的问题,解决不了“流程本身写得烂”的问题。先把 SKILL.md 的步骤写清楚、可执行,再谈自动调度。流程是混蛋,自动化只会把混蛋流程放大十倍。
最后说点个人体会。这套方案最有价值的地方其实不是“自动化”这三个字,而是它逼着我把项目里那些只可意会不可言传的流程,变成了一堆可以测试、可以迭代的文档。我在实际使用中最明显的收益是,换一个人来跑同一个流程,产出质量能稳定在同一个水平上。我后面还计划把 Skill 的命中日志接到一个简单看板上,按周看哪个流程经常被跳过去,这通常就说明文档描述和实际情况开始脱节了。如果你也在折腾 Agent 自动化,建议从最小的一个评审 Skill 开始,跑通之后再说别的。