08.06实操更新于 2026年9月17日

实操:把一个 SOP 做成 Skill

从人工流程到可被 Agent 调用的能力。

08.06 · 课时讲解

实操:把一个 SOP 做成 Skill

摸清缺口、搭建 Skill、分别验证触发与效果、持续迭代。

1 / 11

一句话理解:把 SOP 做成 Skill 分四步:先摸清人工流程和 Agent 的缺口,再搭文件夹、写 SKILL.md 和脚本,然后分别验证触发和效果,最后把每次纠错补回 Skill。

前面几课讲了 Skill 是什么、怎么组织、怎么加载、怎么写。这一课动手做一个完整的 Skill:把「SEO 站内诊断」这份人工 SOP,变成 Claude Code 可以自动调用的能力,并验证它会被正确触发,而且确实比不用它做得更好。

预计用时 60–90 分钟。你需要:

  • 安装好 Claude Code,并能在终端运行 claude。
  • 本机有 Python 3(示例脚本只用标准库)。
  • 一个测试项目目录,里面放 2–3 个 HTML 页面。用本地文件而不是线上网址,结果更容易复现。

整个实操分成四个阶段、十个步骤:

把 SOP 做成 Skill 的四个阶段与十个步骤 ① 摸清缺口 1 · 写下人工 SOP 2 · 无 Skill 跑基线 ② 搭建 Skill 3 · 创建目录 4 · 写 SKILL.md 5 · 写脚本 6 · 规则与模板 7 · 检查格式 ③ 分别验证 8 · 验证触发 9 · 验证效果 ④ 迭代分享 10 · 迭代与分享 真实使用中的每次纠错,补回注意事项与规则
图 1 · 四个阶段:先测出缺口,再搭建,然后分别验证触发与效果,最后持续迭代

第 1 步:写下人工 SOP

动手写 Skill 之前,先把专家现在怎么做写清楚。假设增长团队的 SEO 专员是这样检查页面的:

步骤 人工怎么做 容易出错的地方
1 打开页面源码,找 title 和 meta description 漏看、长度靠目测
2 检查 H1 是否有且只有一个 模板里藏着第二个 H1
3 检查 canonical 是否指向自己或正确的主版本 跨域 canonical 被误判为错误
4 检查 robots 是否误设了 noindex 预发布环境的配置被带上线
5 检查图片 alt 装饰性图片的空 alt 被误报
6 按团队模板写报告,按严重程度排序 格式不统一,缺少证据

右边这一列最重要,它们会变成 Skill 里的踩坑清单。

第 2 步:先不用 Skill 跑一遍,记录缺口

在测试项目里启动 claude,直接说:

帮我检查 pages/about.html 和 pages/pricing.html 的 SEO 问题,给出报告。

记下 Agent 的实际表现:报告格式是否统一?有没有用团队的判定标准?有没有把 alt="" 误报成问题?每个问题有没有给出证据?这就是基线。Skill 要弥补的,正是这里暴露出来的缺口,而不是你想象中的缺口。

第 3 步:创建目录

项目级 Skill 放在 .claude/skills/,提交到 Git 后团队成员都能用;只想自己在所有项目里用,就放在 ~/.claude/skills/。本练习用项目级:

mkdir -p .claude/skills/seo-audit/scripts .claude/skills/seo-audit/references .claude/skills/seo-audit/assets

最终结构:

.claude/skills/seo-audit/
├── SKILL.md
├── scripts/
│   └── check_page.py
├── references/
│   └── rules.md
└── assets/
    └── report-template.md

第 4 步:写 SKILL.md

---
name: seo-audit
description: 对网页做站内 SEO 诊断:提取并检查 title、meta description、H1、canonical、robots 与图片 alt,按团队口径判定问题等级,输出带证据的问题清单和修复建议。当用户要求检查页面 SEO、排查页面为什么没被收录或排名差、或在落地页上线前做审查时使用,即使用户没有明确提到 SEO。不负责关键词研究和外链分析。
---

# SEO 站内诊断

## 工作流程

复制以下清单并随进度更新:

```text
- [ ] 1. 对每个页面运行检查脚本
- [ ] 2. 按团队口径判定每一项
- [ ] 3. 汇总问题并排序
- [ ] 4. 按模板输出报告
- [ ] 5. 核对证据
```

1. 对每个页面(本地文件路径或 URL)运行:
   `python3 ${CLAUDE_SKILL_DIR}/scripts/check_page.py <页面>`
   脚本输出 JSON。多个页面逐个运行。
2. 读取 `references/rules.md`,按其中的口径把每一项判定为“错误 / 警告 / 通过”。
3. 汇总:错误在前,警告在后;同级别内按影响页面数从多到少排列。
4. 按 `assets/report-template.md` 的结构输出报告。
5. 输出前自查:报告中的每个问题都必须引用脚本输出里的具体字段和值;做不到的问题删除或标为“待人工确认”。

## 注意事项

- 图片的 `alt=""` 表示装饰性图片,是合法写法。脚本只统计完全没有 alt 属性的图片。
- canonical 指向另一个 URL 不一定是错误,可能是有意合并重复页面。先确认目标页面是否是主版本,无法确认时标为“警告”并说明原因。
- 脚本返回 `error` 字段时,在报告中标记该页面“未能检查”,并附上错误信息,不要推测结果。
- robots 含 `noindex` 时一律判为“错误”,并提醒检查是否是预发布配置被带上线。

几点说明:

  • ${CLAUDE_SKILL_DIR} 是 Claude Code 提供的变量,会被替换成 Skill 所在目录。Claude Code 通常在项目根目录执行命令,所以脚本要用这个变量定位。在其他支持该标准的产品里,按规范写相对 Skill 根目录的路径(scripts/check_page.py),并以该产品文档说明的执行方式为准。
  • 注意事项里的每一条,都来自第 1 步表格右列的真实易错点。

第 5 步:写脚本

先看清楚这个 Skill 里脚本和模型各管哪一段:

SEO 诊断 Skill 中脚本与模型的分工:脚本做确定性提取,模型做判断与汇总 脚本 确定性提取 模型 按口径判断 页面 文件或 URL check_page.py 提取标签 JSON 输出 字段 + 数值 逐项判定 按 rules.md 汇总排序 错误在前 输出报告 每条附证据
图 2 · 脚本负责每次都一样的提取,模型负责判定、排序和写报告

脚本负责确定性的提取工作,模型负责判断。把下面的代码保存为 scripts/check_page.py:

#!/usr/bin/env python3
"""提取网页的站内 SEO 信号,输出 JSON。用法:check_page.py <文件路径或 URL>"""
import json
import sys
import urllib.request
from html.parser import HTMLParser


class SEOParser(HTMLParser):
    def __init__(self):
        super().__init__()
        self.title = ""
        self.meta_description = None
        self.robots = None
        self.canonical = None
        self.h1 = []
        self.images_missing_alt = 0
        self._in_title = False
        self._in_h1 = False

    def handle_starttag(self, tag, attrs):
        a = dict(attrs)
        name = (a.get("name") or "").lower()
        if tag == "title":
            self._in_title = True
        elif tag == "h1":
            self._in_h1 = True
            self.h1.append("")
        elif tag == "meta" and name == "description":
            self.meta_description = a.get("content") or ""
        elif tag == "meta" and name == "robots":
            self.robots = a.get("content") or ""
        elif tag == "link" and "canonical" in (a.get("rel") or "").lower().split():
            self.canonical = a.get("href")
        elif tag == "img" and "alt" not in a:
            self.images_missing_alt += 1

    def handle_endtag(self, tag):
        if tag == "title":
            self._in_title = False
        elif tag == "h1":
            self._in_h1 = False

    def handle_data(self, data):
        if self._in_title:
            self.title += data
        if self._in_h1 and self.h1:
            self.h1[-1] += data


def load(target):
    if target.startswith(("http://", "https://")):
        req = urllib.request.Request(target, headers={"User-Agent": "seo-audit-skill"})
        with urllib.request.urlopen(req, timeout=15) as resp:
            return resp.read().decode("utf-8", errors="replace")
    with open(target, encoding="utf-8", errors="replace") as f:
        return f.read()


def main():
    if len(sys.argv) != 2:
        print("用法:check_page.py <文件路径或 URL>", file=sys.stderr)
        sys.exit(2)
    target = sys.argv[1]
    try:
        html = load(target)
    except Exception as exc:
        # 把失败原因作为结构化结果交给 Agent,而不是抛出一长串堆栈
        print(json.dumps({"target": target, "error": str(exc)}, ensure_ascii=False))
        sys.exit(1)

    parser = SEOParser()
    parser.feed(html)
    title = " ".join(parser.title.split())
    description = parser.meta_description
    print(json.dumps({
        "target": target,
        "title": title,
        "title_length": len(title),
        "meta_description": description,
        "meta_description_length": len(description) if description is not None else 0,
        "h1": [" ".join(h.split()) for h in parser.h1],
        "canonical": parser.canonical,
        "robots": parser.robots,
        "images_missing_alt": parser.images_missing_alt,
    }, ensure_ascii=False, indent=2))


if __name__ == "__main__":
    main()

先手动运行一次,确认输出正常:

python3 .claude/skills/seo-audit/scripts/check_page.py pages/about.html

第 6 步:补上参考规则与报告模板

references/rules.md 写团队自己的判定口径。下面的数值只是示例,请换成你们团队实际采用的标准:

# SEO 判定口径(团队内部标准)

| 检查项 | 错误 | 警告 | 通过 |
| --- | --- | --- | --- |
| title | 缺失或为空 | 长度少于 15 或多于 60 个字符 | 其他情况 |
| meta description | 缺失 | 长度少于 50 或多于 160 个字符 | 其他情况 |
| H1 | 没有 H1 | 多于 1 个 | 恰好 1 个 |
| canonical | 缺失 | 指向其他 URL | 指向自身 |
| robots | 含 noindex | 含 nofollow | 未设置或 index,follow |
| 图片 alt | — | 存在缺少 alt 属性的图片 | 全部具备 alt 属性 |

assets/report-template.md 固定报告结构:

# SEO 诊断报告:{站点或页面范围}

## 结论摘要
{一句话:共检查 N 个页面,发现 X 个错误、Y 个警告,最优先处理的是……}

## 问题清单
| 等级 | 页面 | 检查项 | 证据(脚本输出) | 修复建议 |
| --- | --- | --- | --- | --- |

## 未能检查的页面
{页面与错误信息;没有则写“无”}

第 7 步:检查格式

在项目根目录运行:

claude plugin validate .claude/skills

Claude Code 文档说明,这条命令可以找出元数据无法解析的 SKILL.md(需要较新版本的 Claude Code,旧版本没有该命令时跳过即可)。如果元数据格式有误,Claude Code 仍会加载正文,但元数据是空的,此时 /seo-audit 可以手动调用,Claude 却无法根据描述自动触发。启动时加 --debug 可以看到解析错误。

第 8 步:验证触发

Claude Code 会监测 Skill 目录的变化,新建或修改的 Skill 通常在当前会话中就能生效;如果 .claude/skills/ 是会话启动后才新建的,需要重启 Claude Code。

1. 确认被识别。 在 claude 里问:

What skills are available?

列表里应该出现 seo-audit。

2. 手动调用。 输入 /seo-audit pages/about.html,确认流程能完整跑通。手动调用成功,说明 Skill 本身可用。

3. 测自动触发。 每条提问都在新会话里运行,观察 Agent 是否加载了 seo-audit:

提问 预期
上线前帮我看看 pages/pricing.html 有没有什么会影响搜索排名的问题 触发
为什么 about 页面在 Google 上搜不到?帮我查查页面本身 触发
检查一下这两个落地页的标题和描述写得合不合规 触发
帮我给 pricing 页面写一段更吸引人的产品介绍 不触发
帮我修一下 about.html 里图片错位的 CSS 问题 不触发
帮我调研一下竞品主要在投哪些关键词 不触发(关键词研究不在范围内)

4. 批量测试(可选)。 提问多了,可以用非交互模式批量运行,从输出中查找 Skill 调用记录:

claude -p "上线前帮我看看 pages/pricing.html 有没有影响搜索排名的问题" \
  --output-format stream-json --verbose \
  | jq -c 'select(.type == "assistant") | .message.content[]? | select(.type == "tool_use" and .name == "Skill") | .input'

输出里出现 seo-audit,说明本次触发了。输出的字段结构可能随版本变化,第一次使用时先去掉 jq 部分,人工看一眼原始输出,确认 Skill 调用长什么样。每条提问至少跑 3 次,计算触发率。

5. 调整。 应该触发却没触发,就在描述里补充用户的真实说法;不该触发却触发了,就把描述写得更具体,或写明不负责的范围。改完重新测试所有提问,而不只是失败的那几条。

第 9 步:验证效果

触发只说明 Agent 找到了 Skill,不说明它做得更好。用第 2 步的同一个任务做对比:

  1. 在新会话里运行,装着 Skill。
  2. 暂时关闭 Skill 再运行一次。可以在 /skills 菜单里把它设为关闭,也可以暂时把文件夹移出 .claude/skills/。
  3. 按下表对比:
维度 无 Skill 有 Skill
报告结构是否符合模板
判定是否符合团队口径
是否误报 alt=""
每个问题是否都有证据
耗时与 Token 消耗

第 8、9 步合起来,就是 Skill 验证的两道关:

第 8 步验证触发与第 9 步验证效果的流程 第 8 步 验证触发 第 9 步 验证效果 确认被识别 列出可用 Skill 手动调用 /seo-audit 跑通 自然提问 新会话 · 每条 3 次 算触发率 应触发 > 50% 调整描述,全部重测 同一任务 第 2 步的基线 装着 Skill 关闭 Skill 逐项对比 格式、口径、证据 明显更好 才算有效
图 3 · 先验证会不会被触发,再验证触发后是否真的做得更好

想要更系统的评测,可以安装官方的 skill-creator 插件(/plugin install skill-creator@claude-plugins-official),让它管理测试用例、隔离运行有无 Skill 的对比,并给出通过率报告。

第 10 步:迭代与分享

  • 在真实使用中每纠正一次 Agent,就把这条纠正补进“注意事项”。
  • 规则口径变了,只改 references/rules.md,不必动流程。
  • 验证通过后,把 .claude/skills/seo-audit/ 提交到仓库,团队成员拉取代码就能用。
  • 同一个文件夹也可以用在其他支持 Agent Skills 标准的产品里,但要调整脚本路径的写法,并确认目标环境允许运行 Python、能访问需要检查的页面。

常见误区

  • 只测手动调用。 /seo-audit 能用,只说明 Skill 本身没有问题,不代表用户自然提问时会触发。
  • 在写 Skill 的同一个会话里测试。 之前的对话已经告诉了 Agent 很多背景,会掩盖 Skill 本身的缺口。
  • 只看有没有触发,不看效果。 一个被触发却没带来改进的 Skill,只是在白白消耗上下文。

验收清单

  • 手动调用 /seo-audit 可以完整跑通
  • 3 条应触发的提问,触发率都高于 50%
  • 3 条近似负例都没有误触发
  • 与无 Skill 相比,报告格式、判定口径和证据完整性都有明显改善
  • 注意事项至少有一条来自测试中发现的新问题
  • Skill 已提交到仓库,并写明负责人

练习

用同样的方法,把「客服工单排查」SOP 做成 Skill(约 60 分钟):

  1. 写出人工排查的 5–6 个步骤和各自的易错点。
  2. 没有真实系统时,用几个 JSON 文件模拟工单、历史回复和订单数据,写一个计算各环节响应与停留时长的脚本。
  3. 完成 SKILL.md,设计 6 条触发测试提问,完成有无 Skill 的效果对比。

延伸阅读

下一步

Skills 模块到此结束。下一个模块从「Workflow 与 Agent」开始:什么时候该让模型决定下一步,什么时候应该把路径预先写死。

试试:上下文、MCP、沙箱、Prompt 注入、外链