让 AI 给博客做体检:10 个坏链接、1.7MB 封面图和一个被推翻的假设
网站能打开、CI 一直是绿的,当成待测系统体检后却找到了 10 个点不动的目录链接、两张超大封面图和一段多余的 174KB 代码。记录一次由 AI 编码 Agent 执行、按测试标准验收的网站体检:被推翻的两个判断,和一条拦住 AI 改动的校验规则。
网站能打开、页面能点、CI 一直是绿的。可当我真的把它当成一个待测系统来体检,第一天就找到了 10 个点不动的目录链接、两张 1.7MB 和 1.4MB 的封面图,以及一段几乎每个知识库页面都要多下载的 174KB 代码。
博客从 9 月中旬之后停更了一个多月。重新开始写之前,我决定先做一件测试工程师最熟悉、却很少对自己的东西做的事:把它当成一个待测系统,完整体检一遍。
这次体检由 AI 编码 Agent 执行:读代码、跑构建、写脚本爬全站、提修复方案、改代码、跑回归。我负责三件事:定规则、审方案、决定合不合并。
这也正好是上一篇《AI 写完了,也说修好了:测试人员如何验收编码 Agent?》的一次实战。那篇讲的是“怎么验收 Agent 的交付”,这一篇是我把同一套标准用在自己的网站上,看看会发生什么。
先说结论:问题不少,但最有价值的不是修掉了哪些 Bug,而是体检过程中两个被推翻的判断,和一条两个多月前写下、这次拦住了 AI 改动的校验规则。
一、先定规则,再动手
让 Agent 直接“优化一下网站”,得到的往往是一堆看起来合理、却无法验收的改动。所以开始前,我先把规则定下来:
- 先诊断,后修改。 第一轮只读不改,输出问题清单和可优化点,由我决定做哪些。
- 每个改动都要有前后数字。 “应该更快了”不算,要给出改动前后的体积、数量或耗时。
- 每个修复都要能证明没有副作用。 尤其是改动公共逻辑时,要证明其他页面的输出没有变。
- 不能只看 CI 是否变绿。 CI 只能证明“没有破坏已有断言”,证明不了“新改动是对的”。
对应到具体手段,这次用了四类检查:
| 检查方式 | 用来回答的问题 |
|---|---|
| lint + 生产构建 + 站点校验脚本 | 有没有破坏已有约定 |
| 关闭 JavaScript 后爬取全站 | 搜索引擎和 AI 爬虫实际看到了什么 |
| 真实浏览器自动化 | 搜索、目录、图表、站内跳转这些交互还正常吗 |
| 改动前后输出逐字节对比 | 公共逻辑的改动有没有误伤其他页面 |
其中最后一项最容易被省略,却是这次让我最放心的一项,后面会讲到。
二、跑通了,不等于对:10 个点不动的目录链接
体检里最典型的一个问题,藏在一篇五月份的文章里。
《用 AI 维护十年老项目》这篇文章的右侧目录有 21 项,看起来很完整。但其中 10 项点了没有任何反应,比如“技术栈”“核心模块”“高风险区域”“回归测试”。
原因是:文章里有几段示例,演示 AI 输出的项目摘要格式,这些示例放在代码块里,内容本身就带着 Markdown 的二级标题标记:
## 技术栈
## 核心模块
## 高风险区域
页面渲染时,代码块里的内容被正确地当成代码展示;但目录组件用的是另一套提取逻辑,它直接从原文里找以 ## 开头的行,没有跳过代码块。于是代码块里的 10 行被当成了标题,生成了 10 个指向不存在锚点的目录项。
为什么一直没人发现?因为这类缺陷在视觉上完全正常:目录看起来很专业,项目数量还“更丰富”了,只有真的去点,才知道跳不过去。
再往下查,问题的根源更值得警惕:“标题的锚点 id 怎么生成”这条规则,在渲染器和目录组件里各写了一遍。 两份实现今天碰巧一致,明天任何一边改了,目录就会悄悄失效。
修复做了三件事:
- 把标题提取和锚点生成合并到同一个模块,渲染器和目录共用;
- 提取标题时跳过代码块;
- 同名标题自动追加序号,避免两个标题生成同一个锚点。
验证是这次我最想强调的部分。改的是全站共用的 Markdown 渲染逻辑,所以只看这一篇文章修好了是不够的。Agent 写了一个对比脚本,把全部 59 份 Markdown 内容(文章、RAG 专题章节、项目页)分别用新旧两版渲染器跑一遍:
- 渲染出的 HTML 逐字节一致,59 份里 0 份变化;
- 新的目录里,每一项都能在页面中找到对应的锚点,0 个断链。
这就是“改了公共逻辑”时最朴素也最可靠的回归手段:新旧版本对同一批输入跑一遍,比较输出。
三、性能问题藏在依赖链里,而不是在源码表面
1. 每个知识库页面都在下载整份 RAG 教程
知识库页面共用一个布局组件,负责搜索、章节导航和“上一篇 / 下一篇”。它需要知道各个页面的标题和更新时间,于是引用了知识库的页面注册表。
问题出在注册表的下一层:为了列出 RAG 专题的 14 个章节,注册表又引用了 RAG 专题的数据文件。而这个文件里,除了章节标题,还有 14 章的完整正文。
布局组件(在浏览器端运行)
→ 知识库页面注册表
→ RAG 专题数据(含 14 章全文,约 150KB)
布局组件要在浏览器端运行,所以这条链上的东西都会被打包进前端代码。结果是:57 个知识库页面里,有 56 个都要多下载一个 174KB(压缩后 67KB)的文件,而这些页面根本用不到 RAG 正文。
只看源码很难发现这个问题,每一行引用都“合理”。它是在检查构建产物时才暴露的:Agent 在打包后的前端文件里搜索 RAG 正文中的一句话,发现它出现在几乎所有知识库页面加载的文件里。
修复方式是把章节的元数据(标题、更新时间)和正文拆成两个文件,注册表只引用元数据。改完后,这个共享文件从 174KB 降到 44KB(压缩后从 67KB 降到 14KB),并再次确认:知识库页面加载的文件里,已经找不到任何 RAG 正文。
2. 两张没有压缩的封面图
博客文章里的图片,是由 Markdown 解析器直接输出成普通图片标签的,不经过框架自带的图片优化。也就是说,放进去多大的图,读者就下载多大的图。
两张 PNG 封面分别是 1.7MB 和 1.4MB。转成同尺寸的 WebP 后,分别是 70KB 和 75KB,肉眼看不出区别。
3. 本可以提前生成的页面
博客列表页、文章分享图、RSS 和图片站点地图,原本都是“有人访问时服务器现做”。原因各不相同:列表页在服务端读取了搜索参数;分享图没有声明要预先生成哪些文章……
改成构建时生成后,访问这些地址只需要直接返回现成文件。这里有一个约束必须守住:列表页之前专门做过“全部文章在服务端渲染”,方便搜索引擎抓取。所以改造后,Agent 专门验证了:静态 HTML 里仍然包含当时全部 43 篇文章的链接,带搜索参数访问时,浏览器端也能正确筛选。
这一节的经验是:性能测试要看构建产物和网络请求,而不是只读源码。 源码层面每一处都说得通,问题出在它们组合起来的结果上。
四、一个被推翻的假设
这是整次体检里我最想记录的一段。
文章里的流程图用 Mermaid 渲染。Agent 读代码时发现,渲染组件在监听整个页面的所有 DOM 变化:只要页面上任何地方插入或删除了节点,就重新检查一遍有没有需要渲染的图表。
它据此提出了一个听起来很严谨的推断:
如果某张图的语法有误,渲染失败时会把源码写回页面;这次写回本身是一次 DOM 变化,会再次触发渲染,再次失败,再次写回……形成无限循环。
推理链条每一步都说得通,修复方案也写好了。按很多人的习惯,这时候就可以在提交说明里写上“修复了渲染失败时的无限循环”。
但它在提交前多做了一步:用改动前的旧代码复现这个问题。
具体做法是,在一篇有流程图的文章页面里,用浏览器自动化插入一张故意写错语法的图,然后统计 4 秒内这张图被重新渲染了几次。
结果:1 次。 没有循环。
回头再看代码才发现,渲染组件里本来就有一个“正在渲染中”的标记。失败时写回源码发生在渲染过程中,由此触发的那次检查会被这个标记直接拦下。推断里漏掉了这一层。
最终这个改动还是保留了,因为它确实让组件少做了很多无用功:只在出现新图表时才处理,不再对评论区加载、图片预览这类无关变化做全量扫描。但它的定位从“修复严重缺陷”降成了“小幅减少无用开销”,PR 说明也照实改写。
同一次体检里还有第二次类似的修正。讨论“教程目录页要不要改成静态生成”时,Agent 一开始说“会影响 SEO,因为搜索引擎抓不到分页内容”;后来核对页面元数据才发现,所有带筛选参数的教程页,规范地址本来就指向目录页本身,搜索引擎原本就不会单独收录分页。于是它主动更正:改成静态的 SEO 代价其实很小,真正要放弃的主要是“关闭 JavaScript 也能翻页”这一点。
这两件事让我更确定一件事:AI 的推理写得再像样,在被复现之前,也只是一个假设。 这和验收人写的结论没有区别:“我认为是这样”和“我复现过是这样”,是两个不同等级的证据。
五、自己写的校验规则,拦住了 AI 的改动
体检的最后一部分是 SEO。Agent 写了一个爬虫,在本地生产构建上关闭 JavaScript,从站点地图出发,顺着站内链接爬完了 134 个地址,模拟搜索引擎和 AI 爬虫看到的原始页面。
先说好消息:死链、重复标题、重复描述、规范地址错误、缺少主标题、图片缺少替代文本,全部是 0。
需要改进的主要有四项,改动前后的对比如下:
| 项目 | 改动前 | 改动后 |
|---|---|---|
| 站点地图里缺少更新日期的地址 | 76 / 122 | 4 / 122 |
| 有面包屑结构化数据的页面 | 0 | 116 |
| 有文章类结构化数据的页面 | 43 | 113 |
| 描述过短的页面 | 23 | 6 |
| 图片站点地图收录的图片 | 43 张(仅分享图) | 62 张(新增 19 张正文配图) |
这里有一个插曲。
Agent 最初的方案里,除了给每个知识页面补上更新日期,还给知识库首页取了“所有子页面中最新的那个日期”。改完一跑站点校验,失败了,报错是:
Knowledge sitemap entry must not claim an unverified lastModified date.
这条断言是 8 月加进站点校验脚本的。当时站点地图里所有页面的更新日期都是构建时间,等于每次部署都对搜索引擎说“所有页面今天都更新了”。那次提交删掉了这些日期,同时加了这条断言,防止以后再有人写回去。
Agent 去翻了这条断言的提交历史,理解了它要防的是什么,然后调整了方案:
- 知识库首页仍然不声明日期,遵守原有约定;
- 各个知识页面只使用数据里人工维护的“最近更新”日期,也就是页面底部展示给读者的那个日期,不做任何推断;
- 再进一步,把日期的取值顺序改成与页面底部完全一致,并验证站点地图、结构化数据和页面底部三处的日期 0 处不一致。
这件事让我意识到:把判断写成自动化断言,它的寿命比记忆长得多。 两个多月前的一个决定,在今天准确地拦住了一个“看起来合理”的改动,而且它对人和 AI 一视同仁。
如果你的项目里有“不能这样做”的经验,别只写在文档里,尽量把它变成一条会失败的检查。
六、还没做的,和决定不做的
体检不是为了把所有问题一次清零。这次有几项我看过评估后,决定暂缓或不做:
- 教程目录页保持服务端渲染。 改成静态生成能省掉一点服务器开销,但会失去“关闭 JavaScript 也能筛选翻页”的能力;个人站的访问量下,这点开销读者基本感觉不到。
- 24 篇文章的标题在搜索结果里会被截断。 加上站名后缀后超出了显示宽度。搜索引擎本来就常改写标题,收益有限,暂不处理。
- 旧文章的站内链接偏少。 有 5 篇文章,全站只有文章列表页链向它们。这是内容层面的问题,需要我自己慢慢补,而不是让程序自动生成链接。
- 线上环境的检查。 这次的检查都在本地生产构建上完成;域名跳转、真实的页面速度和搜索引擎的收录情况,需要我在线上和站长平台上自己确认。
七、给个人网站做体检,可以照着这张清单走
| 维度 | 检查什么 | 怎么查 |
|---|---|---|
| 功能 | 目录、锚点、站内链接能不能跳 | 对每个目录项断言页面中存在对应锚点;爬全站找非 200 的地址 |
| 功能 | 搜索、筛选、图表在站内跳转后是否正常 | 用浏览器自动化走一遍真实交互 |
| 性能 | 浏览器端到底下载了什么 | 在构建产物里搜索“不该出现的内容”,比如正文、全量数据 |
| 性能 | 图片是否经过压缩 | 列出静态资源里最大的文件,逐个确认是否必要 |
| 性能 | 哪些页面每次访问都在现做 | 看构建输出里每条路由的渲染方式 |
| SEO | 爬虫看到的原始页面 | 关闭 JavaScript 爬全站,检查标题、描述、规范地址、结构化数据 |
| SEO | 站点地图的日期是否真实 | 日期必须来自可核实的数据,不要用构建时间 |
| 工程 | 公共逻辑改动是否误伤 | 新旧版本对同一批输入跑一遍,逐字节比较输出 |
| 工程 | 重要的约定会不会被遗忘 | 把“不能这样做”写成会失败的校验 |
写在最后
这次体检前后一共开了两个 PR,改了十几处问题。但回头看,真正值得记下来的是三个习惯:
- 改公共逻辑,就用新旧输出对比来证明没有误伤;
- 任何“我认为”,都先复现成“我看到”,再写进结论;
- 把踩过的坑写成会失败的断言,让它替你记住。
这三条对人适用,对 AI 也一样适用。AI 编码 Agent 让体检的执行成本降得很低,这次从读代码、爬全站到逐字节对比,几乎都是它完成的;但哪个问题值得修、哪个结论可以相信,仍然要靠证据来定,而不是靠它说得多有道理。
博客恢复更新了。下一篇,我想接着聊聊另一类“说法和证据对不上”的情况:AI 自己报告的效果,和独立复核出来的结果,到底能差多少。
参考资料与延伸阅读
- 本次体检的性能与正确性修复(PR #30):目录锚点、知识库前端包、封面图、静态生成等改动及验证记录。
- 本次体检的 SEO 改进(PR #31):站点地图日期、结构化数据、页面描述和图片站点地图的改动及前后对比。
- AI 写完了,也说修好了:测试人员如何验收编码 Agent?:本文所用验收思路的出处。
- 别光写代码不写 meta 标签:我给博客做了 SEO 和 GEO 优化的全过程:这个网站第一轮 SEO 优化的记录。
- 同一个模型,换套评测脚本就差 80 分:AI Benchmark 到底测到了什么?:另一个“测量方式本身需要被测试”的例子。
版权与声明
本站所有内容仅代表作者个人观点,与作者供职的公司、客户或其他关联机构无关。
本文除特别声明外,采用 CC BY-NC-SA 4.0 许可协议。转载或改编请署名、附原文与许可证链接,并标明改动;不得用于商业用途,演绎作品须以相同许可发布。
评论