这套资源包含一份跨平台通用的 SKILL.md 说明书、30 条带 error/warn 分级与特性开关的规则注册表、白名单与阈值配置模板、两套 CI 配置,以及一份自动化覆盖不到的人工检查清单。检测脚本的实现分散在系列六篇正文中,本页只收录可直接复制的成品件。
这份资源是什么
「SEO Skills 工具箱」系列六篇正文讲了怎么从零建一套 SEO 审计体系。这一页把其中可直接复制的成品件集中起来,省去在六篇文章里翻找。
分工很清楚:
| 内容 | 在哪 |
|---|---|
| 说明书、规则表、配置、CI、检查清单 | 本页,完整给出 |
| 各项检查的实现代码与原理 | 系列正文,本页只给索引 |
不在这里重复贴检测脚本,是因为那些代码需要配着「为什么这么写」才有意义——脱离上下文抄过去,遇到第一个误报就不知道该改哪里了。
一、完整目录结构
项目根目录/
├── seo/
│ ├── rules.js # 规则注册表(本页第三节,完整)
│ ├── config.js # 特性开关 + 白名单(本页第四节,完整)
│ ├── runner.js # 执行器(系列⑥ 第二节)
│ ├── load.js # 产物加载与归一化(系列⑥ 第三节)
│ └── checks/
│ ├── index.js # 各检查函数汇总导出
│ ├── page.js # scope=page 的检查
│ ├── site.js # scope=site 的检查
│ ├── file.js # scope=file 的检查
│ └── cross.js # scope=cross 的检查
├── references/
│ ├── thresholds.json # 长度阈值(本页第五节)
│ ├── schema-rules.json # JSON-LD 必填字段表(系列③ 第六节)
│ └── consistency-map.json# 标记与页面的对照表(系列③ 第六节)
├── .claude/skills/seo-audit/
│ └── SKILL.md # 说明书(本页第二节,完整)
└── .audit/ # 产出目录,加进 .gitignore
└── seo.json
.audit/ 一定要加进 .gitignore。它是每次构建的产物,提交进仓库只会制造无意义的 diff。
二、SKILL.md 说明书(完整,可直接复制)
这份说明书是跨平台通用的。放进 .claude/skills/seo-audit/SKILL.md 即为 Claude Code 的 Skill;内容原样搬进 .cursor/rules/seo-audit.mdc 或 AGENTS.md 的一节同样能用——系列①有各平台的落地位置对照。
---
name: seo-audit
description: 解读 SEO 审计结果,定位根因并给出修复方案。当用户运行 validate
或 seo:audit 后出现失败、询问 SEO 检查报错的含义、需要修复审计问题,或要求
做上线前 SEO 检查时使用。
---
## 职责边界
**只解释,不检测。** 检测由 `seo/runner.js` 完成,本 Skill 读取它的输出。
- ✅ 读 `.audit/seo.json`,归纳根因,给修复方案
- ❌ 不自己扫描 HTML 寻找问题
- ❌ 不重新实现任何一条已有规则
原因:检测要求 100% 覆盖与结果稳定,这是脚本的强项;解释要求理解代码
结构与业务意图,这才是模型的位置。
## 执行步骤
1. 检查 `.audit/seo.json` 是否存在,以及是否晚于最近一次构建。
缺失或过期时先执行 `npm run validate`。
2. 读取 JSON。`summary` 给总量,`findings` 给明细。
3. 按 `id` 分组,再判断哪些分组属于同一个根因。
4. 对每个根因定位到**源码位置**(不是产物位置)。
5. 按下方「输出格式」生成报告。
6. 除非用户明确要求,不要直接改代码。
## 根因定位规则
判断依据是**受影响页面的分布**,不是问题类型:
| 受影响范围 | 通常的根因位置 |
|---|---|
| 全部页面 | 布局文件(BaseLayout 等) |
| 某个栏目的全部页面 | 该栏目的页面模板或 content 配置 |
| 若干页面,无明显共性 | 逐页的 Markdown / frontmatter |
| 只在动态路由页出现 | 路由参数处理逻辑 |
| sitemap / robots 相关 | 构建配置(astro.config.mjs 等) |
同一个 `id` 出现 3 次和出现 60 次,根因位置完全不同。**先看分布,再找文件。**
## 误报处理
以下情况先标记为「需确认」,不要直接当作缺陷:
- 目标域名在 `seo/config.js` 的 ALLOWLIST 中
- 外链返回 401 / 403 / 429(可能是反爬或限流,浏览器能打开即为误报)
- 页面内容需 JS 渲染后才出现(静态审计覆盖不到,应在说明中注明)
确认为稳定误报的,追加进 ALLOWLIST 并写明**日期与原因**。
## 输出格式
按根因分组。每组给出:
1. **一句话问题描述** + 影响页面数
2. **根因位置**:文件路径与行号
3. **具体改法**:能给 diff 就给 diff
4. **为什么是这些页面**:解释受影响范围的成因
5. **验证方式**:改完跑什么命令、期望看到什么
`warn` 级别默认只列出计数,不展开修复方案,除非用户问起。
不要逐条罗列 findings——JSON 里已经有了,人要的是分组后的结论。
## 拿不准时
宁可输出「影响这 3 个页面,需要确认是否共用同一模板」,
也不要猜一个具体文件路径。**猜错路径的报告比不给路径的报告更糟**——
人照着改一次发现不对,之后就不会再信任这个 Skill 了。
三、规则注册表(30 条,完整)
// seo/rules.js
export const RULES = [
// ── HTML 基础 ──
{ id: 'html-lang', level: 'error', scope: 'page', desc: '中文路径 lang="zh",其余 lang="en"', when: 'i18n' },
{ id: 'h1-exactly-one', level: 'error', scope: 'page', desc: '每页恰好一个 <h1>' },
{ id: 'title-present', level: 'error', scope: 'page', desc: 'title 存在且非空' },
{ id: 'title-duplicate', level: 'warn', scope: 'site', desc: 'title 跨页面重复' },
{ id: 'title-width', level: 'warn', scope: 'page', desc: 'title 宽度在 30–60 半角当量内' },
{ id: 'desc-exactly-one', level: 'error', scope: 'page', desc: 'meta description 恰好一个且非空' },
{ id: 'desc-duplicate', level: 'warn', scope: 'site', desc: 'description 跨页面重复' },
{ id: 'desc-width', level: 'warn', scope: 'page', desc: 'description 宽度在 70–155 半角当量内' },
{ id: 'img-alt', level: 'error', scope: 'page', desc: '每个 <img> 有非空 alt' },
// ── 索引指令 ──
{ id: 'canonical-one', level: 'error', scope: 'page', desc: 'canonical 恰好一个' },
{ id: 'canonical-self', level: 'error', scope: 'page', desc: 'canonical 等于当前正式 URL' },
{ id: 'canonical-noquery', level: 'error', scope: 'page', desc: 'canonical 不带查询参数' },
{ id: 'canonical-target', level: 'error', scope: 'cross', desc: 'canonical 目标页面存在' },
{ id: 'robots-meta-one', level: 'error', scope: 'page', desc: 'robots meta 恰好一个且无冲突指令' },
{ id: 'noindex-sitemap', level: 'error', scope: 'cross', desc: 'noindex 页面不得进入 sitemap' },
// ── 多语言(单语站关闭)──
{ id: 'hreflang-set', level: 'error', scope: 'page', desc: '含 en / zh-CN / x-default', when: 'i18n' },
{ id: 'hreflang-target', level: 'error', scope: 'cross', desc: 'hreflang 目标页面存在', when: 'i18n' },
// ── 链接 ──
{ id: 'internal-404', level: 'error', scope: 'cross', desc: '站内链接对应真实产物' },
{ id: 'anchor-exists', level: 'warn', scope: 'cross', desc: '锚点在目标页面存在' },
{ id: 'trailing-slash', level: 'warn', scope: 'cross', desc: '内链尾部斜杠与产物一致' },
{ id: 'no-param-link', level: 'error', scope: 'page', desc: '禁止遗留 ?from= 内链' },
// ── robots.txt ──
{ id: 'robots-exists', level: 'error', scope: 'file', desc: 'robots.txt 存在且非全站 Disallow' },
{ id: 'robots-sitemap', level: 'error', scope: 'file', desc: 'robots.txt 声明 sitemap 且目标存在' },
// ── sitemap ──
{ id: 'sitemap-index', level: 'error', scope: 'file', desc: 'sitemap-index 存在、命名空间正确' },
{ id: 'sitemap-clean', level: 'error', scope: 'file', desc: 'URL 不重复、不带参数或锚点' },
{ id: 'sitemap-sync', level: 'error', scope: 'cross', desc: 'sitemap 与产物双向一致' },
{ id: 'sitemap-lastmod', level: 'error', scope: 'file', desc: 'lastmod 恰好一个、格式正确、非未来时间' },
// ── 图片 sitemap(无图片 sitemap 时关闭)──
{ id: 'imgmap-ns', level: 'error', scope: 'file', desc: '图片 sitemap 命名空间正确', when: 'imageSitemap' },
{ id: 'imgmap-count', level: 'error', scope: 'file', desc: '每条目 1–1000 张图片', when: 'imageSitemap' },
{ id: 'imgmap-url', level: 'error', scope: 'file', desc: '图片 URL 为 http(s)、不重复无参数', when: 'imageSitemap' },
{ id: 'imgmap-file', level: 'error', scope: 'cross', desc: '本地图片文件真实存在', when: 'imageSitemap' },
{ id: 'imgmap-onpage', level: 'error', scope: 'cross', desc: '图片实际出现在所属页面中', when: 'imageSitemap' },
{ id: 'imgmap-lastmod', level: 'error', scope: 'cross', desc: '图片 lastmod 与页面 sitemap 一致', when: 'imageSitemap' },
{ id: 'imgmap-legacy', level: 'warn', scope: 'file', desc: 'image:title / image:caption 已弃用', when: 'imageSitemap' },
// ── 结构化数据 ──
{ id: 'jsonld-valid', level: 'error', scope: 'page', desc: '每个 JSON-LD 块是合法 JSON' },
{ id: 'jsonld-required', level: 'error', scope: 'page', desc: '按 schema-rules.json 校验必填字段' },
{ id: 'jsonld-scope', level: 'error', scope: 'page', desc: 'WebSite / Organization 仅出现在首页' },
{ id: 'article-type', level: 'error', scope: 'page', desc: '文章页含 Article 或 BlogPosting' },
{ id: 'no-searchaction', level: 'warn', scope: 'page', desc: '不声明不存在的 SearchAction' },
];
四个字段的含义:id 是稳定标识(也是白名单的键),level 决定 error 阻断构建 / warn 只报告,scope 决定执行阶段(file → page → site → cross),when 是特性开关。
四、配置模板
// seo/config.js
// 特性开关:按站点实际情况打开
export const FEATURES = {
i18n: false, // 多语言站点(启用 lang / hreflang 相关 4 条规则)
imageSitemap: false, // 有图片 sitemap(启用 imgmap-* 共 7 条规则)
};
// 白名单:键为规则 id,值为豁免的目标
// 每条必须写明【日期】和【原因】,否则半年后没人知道为什么在这里
export const ALLOWLIST = {
'title-duplicate': [
'/search/', // 2026-08-01 搜索页与首页同标题,已 noindex,可接受
],
'internal-404': [
// 示例:'/legacy/old-page/', // 2026-08-01 待迁移,跟踪于 #42
],
'img-alt': [
// 示例:'/about/', // 2026-08-01 装饰性图片,待补 alt
],
};
特性开关这一步不能省。 上面 30 条规则里有 11 条只对多语言站或有图片 sitemap 的站成立。硬塞给不适用的站点,会立刻产生 11 类必然失败——人的第一反应就是把整个检查关掉,然后这套东西就废了。
五、阈值配置
// references/thresholds.json
{
"brand": "BLKTECH",
"title": { "min": 30, "max": 60 },
"description": { "min": 70, "max": 155 },
"headline": { "max": 110 },
"clickDepth": { "max": 3 },
"redirectChain": { "max": 2 }
}
单位是半角当量不是字符数:全角字符算 2,其余算 1。中英文混排必须这样算,否则中文站的截断问题几乎全部漏检——原理和实现见系列②。
brand 用于跨页重复检测时剥离共同的品牌后缀。文章名 - 站名 这类模板,不剥后缀就永远发现不了真重复。
这些阈值是社区长期测量的经验值,不是官方规范,会随搜索引擎改版波动。放在配置文件里而不是硬编码进脚本,改阈值不该需要改代码。
六、CI 配置
两套,触发时机和阻断策略完全不同。
每次构建:内链与产物一致性
# .github/workflows/validate.yml
name: validate
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install
- run: bun run validate # build + seo:audit,error 时退出码 1
- uses: actions/upload-artifact@v4
if: always() # 失败时更需要这份 JSON
with: { name: seo-audit, path: .audit/seo.json }
定时:外链与结构指标
# .github/workflows/periodic-audit.yml
name: periodic-audit
on:
schedule:
- cron: '0 3 * * 1' # 每周一:外链探测
- cron: '0 4 1 * *' # 每月一日:内链结构
workflow_dispatch:
jobs:
periodic:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install && bun run build
- run: bun seo/check-external.mjs || true # 外链失效不阻断
- run: bun seo/link-graph.mjs || true # 结构指标不阻断
- uses: actions/upload-artifact@v4
with: { name: periodic-audit, path: .audit/ }
两个 || true 是刻意的。外链失效不是你的错、第三方站点抖动频繁;结构指标没有唯一正确答案。只要有一次因为别人的服务器打嗝导致你发不了版,这个检查就会在一周内被注释掉。
七、脚本实现索引
本页不重复贴实现代码。各检查的原理与完整实现:
| 检查范畴 | 正文 | 关键实现 |
|---|---|---|
| 内链 404、锚点、尾斜杠、外链探测 | ① 死链审计 | crawl-local.mjs、check-external.mjs |
| title / description 宽度与重复 | ② Meta 审计 | display-width.mjs、extract-meta.mjs |
| JSON-LD 三层校验 | ③ 结构化数据 | extract-jsonld.mjs |
| sitemap 集合运算与信号矛盾 | ④ Sitemap 差异 | sitemap-diff.mjs |
| 点击深度、入链分布、连通分量 | ⑤ 内链结构 | link-graph.mjs |
| runner、loader、四种 scope | ⑥ 内置 audit | runner.js、load.js |
八、分阶段落地清单
不要一次上 30 条规则。
- 阶段 1:跑通 runner + 3 条最确定的规则(
h1-exactly-one、title-present、internal-404) - 阶段 2:把现有问题全部修掉或全部加白名单,让基线归零
- 阶段 3:接进 CI,此时 CI 应该是绿的
- 阶段 4:每次加 2–3 条规则,修完再加下一批
- 阶段 5:补上 SKILL.md,让 Agent 承担解读
- 阶段 6:加定时任务,跑外链与结构指标
阶段 2 是成败关键。基线不归零就接 CI,等于 CI 从第一天起就是红的,之后所有人都会习惯性忽略它——包括你自己。宁可先把已知问题加进白名单标上 TODO,也不要让 CI 带着一屏红色上线。
九、自动化覆盖不到的部分
这是整套东西最该说清楚的一节。上面所有工具加起来,覆盖的是技术正确性,不是内容有效性。
以下事项无法自动化,需要人工定期做:
上线前抽查(每次发布)
- 用 Rich Results Test 抽查 1–2 个代表页面(无公开 API,只能手动)
- 移动端实际打开,确认渲染正常
- 确认首屏关键内容不依赖 JS 渲染(静态审计看不到 JS 之后的内容)
- 新页面的 title 与 description 自己读一遍,是不是套话
周期性复核(每月 / 每季度)
- Search Console 的「网页索引编制」报告——权威但滞后,本地审计替代不了
- Core Web Vitals 实测数据(需要真实用户环境)
- 外链失效后替代目标是否合适(脚本只知道断了,不知道该换成什么)
- 搜索引擎政策变化——尤其结构化数据的富媒体资格规则,建议每年复核并更新
references/
根本上自动化不了的
- 内容本身是否匹配搜索意图
- 内容质量与竞品的相对水平
- 内链分布是否符合你的内容战略(工具能算分布,判断不了战略)
一句实话作为收尾:
技术审计全绿的站,完全可以没有任何排名。
这套 Skill 保证的是「没有技术问题拖后腿」,不是「能拿到流量」。它的价值在于把技术问题彻底从变量列表里划掉——这样当排名不理想时,你知道该去看内容,而不是怀疑是不是哪里有个 404。
十、系列导航
「SEO Skills 工具箱」完整系列:
- 死链与断链审计
- Meta 审计:title、description、H1
- 结构化数据与 JSON-LD 校验
- Sitemap 差异审计与孤儿页发现
- 内链结构审计
- 内置 seo:audit 与 Skill 封装
- 当前:配套资源
七条原则:
- 确定性交给脚本,判断交给模型(①)
- 能量化的写成阈值表,不能量化的给判断依据(②)
- CI 的阻断线画在「有唯一正确答案」的地方(②③④⑤)
- 报告要给根因,不是给症状列表(③)
- 集合运算前先做归一化——脚本不会报错,只会安静地全错(④)
- 算图指标前先剔除模板化链接(⑤)
- 凡是能用规则表达的判断,都不该交给模型执行(⑥)
相关阅读:
RELATED / 相关推荐
接着读这些
按同一栏目、标签与技术栈为你挑选。
AI Skills 高阶:把 SEO 检查内置进项目,做成一条命令 + 一个 Skill
把分散的五类 SEO 检查合并成项目内置的 seo:audit 脚本,用统一的规则注册表管理 24 项检查与 error/warn 分级,再封装成 Skill,让检测、解析和修复方案一次给全。
AI Skills 实战:把网站死链与断链审计做成一个可复用 Skill
把死链检查的判定规则和输出格式固化成一份 Skill 说明书,配合两个确定性脚本,让 AI Agent 每次都按同一套 SOP 产出可执行的审计报告,并接入 CI 持续拦截。
全系列 AI Skills 工具箱汇总:Prompt 合集与使用指南
65 篇内容的收口。这篇把十二个 Skill 的说明书、脚本清单、以及贯穿全系列的执行顺序整理成一份可以直接照着用的索引,并给出三种落地路径——从零建站、已有站点改造、只用 Skills 工具箱。