站点 bug 大多不刺激——缺 alt、内链断了、200 字的页面根本不该发出去。给 AI 一个真实 checklist 并交叉验证,它在这件事上极强。下面是 prompt 和验证脚本。
问题背景
站点 QA 没人愿意做,所以总被跳过。让 AI Agent 按既定 checklist 读你的构建产物,10 分钟能扫完 200 页,比人工 review 抓得还全。关键是 checklist 要具体:“找出 400 字以下的页面”是好任务,“帮我审站点”不是。
判断标准
- 60+ 天没做过完整站点检查。
- 最近改过路由、重命名了 slug、迁移过内容。
- Search Console 报告 coverage 问题增多。
- 最近加了一批新页面,怀疑有些带 bug 上线了。
快速结论
build、指 AI 看 dist/、问六个窄问题、shell 抽查、分批修。
开始前准备
- 本地
dist/已 build。 - Codex / Claude Code 能读文件。
grep、linkinator、xmllint、jq装好。
实操步骤
- 本地 build:
npm run build
ls dist/ # 确认页面在
- 死链 QA。Prompt:
[CONTEXT] 构建产物在 dist/(Astro 静态)。内部链接形如 /en/articles/<slug>/ 或 /zh/articles/<slug>/。
[TASK] 列出 dist/**/*.html 里所有 href 指向 dist/ 中没有对应 index.html 的内部链接。
输出:file, broken_href
shell 验证(真理来源):
# 活 URL 集合
find dist -name 'index.html' | sed 's|dist||;s|/index.html|/|' | sort > /tmp/live-urls.txt
# 每页用到的内链
grep -RhoE 'href="(/[a-z]+/articles/[a-z0-9-]+/)"' dist | sed 's/href="//;s/"//' \
| sort -u > /tmp/used-urls.txt
# 用到的 - 活的 = 死链
comm -23 /tmp/used-urls.txt /tmp/live-urls.txt | head
- 缺 alt。Prompt:
[TASK] dist/**/*.html 里列出无 alt 或 alt="" 的 <img>。
输出:file, line_excerpt
验证:
grep -RHn '<img[^>]*>' dist | grep -v 'alt="[^"]\+"' | head
- 薄页。Prompt:
[TASK] dist/**/index.html 每页提取可见正文(剥掉 nav/header/footer/scripts)并计数字数。
列出正文字数 < 400 的页面。
输出:file, word_count
- 孤儿检测。Prompt:
[TASK] dist/ 里每篇文章页,统计其它页面中指向它的内链数量。
列出 0 入链的页面(孤儿)。
输出:file, incoming_count
验证:
# 从未被链作目标的页面
for url in $(cat /tmp/live-urls.txt); do
count=$(grep -RlE "href=\"$url\"" dist | grep -v "dist$url" | wc -l)
[ "$count" -eq 0 ] && echo "ORPHAN: $url"
done | head
- title sanity。Prompt:
[TASK] dist/**/*.html 列出 <title>:
- 缺失或空
- > 65 字符
- 跨页面重复
输出:file, issue, title_text
- 源文件 frontmatter 一致性。 10 行脚本:
// scripts/frontmatter-consistency.mjs
import { readdirSync, readFileSync } from 'node:fs';
import matter from 'gray-matter';
const REQUIRED = ['title', 'description', 'urlSlug', 'category', 'tags',
'publishedAt', 'lang', 'translationKey'];
for (const lang of ['en', 'zh']) {
for (const cat of readdirSync(`src/content/articles/${lang}`)) {
for (const f of readdirSync(`src/content/articles/${lang}/${cat}`)) {
if (!f.endsWith('.mdx')) continue;
const { data } = matter(readFileSync(`src/content/articles/${lang}/${cat}/${f}`, 'utf8'));
const missing = REQUIRED.filter((k) => data[k] === undefined || data[k] === '');
if (missing.length) console.log(`${lang}/${cat}/${f}: 缺 ${missing.join(',')}`);
}
}
}
- 按类别开 issue。 把关键项接到 CI 闸门:
# .github/workflows/qa.yml(节选)
- name: QA gates
run: |
node scripts/frontmatter-consistency.mjs # 永远跑
npx linkinator dist --skip 'http' --silent # 内链闸门
node scripts/audit-pillars.mjs # 孤儿闸门
执行检查清单
- AI prompt 都指向
dist/,每个一类问题。 - 6 类里至少 3 类有 shell 验证。
- frontmatter 一致性接 CI。
- 内链闸门:新出现的死链直接挂构建。
- issue 按类别开,不按文件。
上线后验证
- 修完后重跑每个 prompt 返回 0(或近 0)。
- 4-8 周后 Search Console coverage 问题下降。
- 抽样 Lighthouse SEO + Accessibility 都 100。
容易踩的坑
- 让 AI 看源码而不是构建产物。很多 bug 渲染后才出现:null frontmatter 导致 meta 为空、
:key插值断了等等。 - 相信 AI 一句”看起来没问题”。一定让它出明确列表,没列表就没法验证。
- 改完不记账。一个月后会重犯。
- 跳过孤儿检测。重构后特别容易出孤儿,悄悄拖排名。
- 只看字数判质量。200 字能答清问题的页面也行,字数标记是起点不是结论。
FAQ
- 能放进 CI 自动化吗: 可以。写脚本输出列表,关键类别(内部死链、缺 canonical)直接 fail build。AI 适合第一遍,定型之后就用代码固化。
- 外链失效呢: 用专门的工具(如 lychee、linkinator),AI 在这上面又慢又费。
- 多久做一次完整 QA: 活跃站每月一次,否则每季度。每次大改后必做。
- 缺 alt 真的影响排名吗: 间接影响——无障碍和图片搜索受损。要修但不必慌。
- AI 数到 23 条死链、我的验证脚本数到 18 条,信谁?: 永远信脚本。AI 列表用于探索,脚本用于闸门。