博客产线(三):发布前的六道自动体检
我的博客走批量产线:文章由 agent 生成、校验器放行、构建部署全自动。产线跑得快,坑也跟着提速——前两天审计一批新文章,十几分钟翻出六类问题,每一类单看都不致命,合在一起足够让访客皱眉:首页灰块、404 链接、图文对不上的配图。
这篇把检查清单整理成六道体检,全部可以用几行脚本完成,每道附一个真实踩到的案例。
第一道:封面存在且唯一
我的主题(Butterfly)的首页缩略图只认 front matter 里的 cover 字段,缺失就显示灰块。更隐蔽的是唯一性:两篇文章用了同一张 Unsplash 图,首页上并排出现时非常刺眼——这个红线以前靠人眼盯,批量产出后必须机器查。
1 | # 封面唯一性:有输出即有重复 |
真实案例:新发的文章和三个月前的一篇用了同一张图——生成时按语义挑图,两个主题相近的文章撞车了。修复是给后发的那篇换图,顺手把”每篇封面必须全站查重”写进了校验器。
第二道:Markdown 结构——标题必须独占一行
生成类内容特有的一种损坏:标题粘在上一行末尾,比如一句话结束后直接跟 ## 加标题,中间没有换行。人眼浏览时几乎注意不到,渲染时整个章节塌成普通文字,目录直接少一节。
扫描逻辑一行正则:找”非井号字符后紧跟井号标题”的行(跳过代码块内部)。上一轮审计从 98 篇里扫出 1 篇 2 处,都是同一个批次产物的行拼接 bug——结构问题从来不是孤例,扫出第一处就该查同批全部。
产生原因也值得记下:批量生成时,agent 常把”补一个小节”实现成往某一行末尾追加文本,追加完忘了换行。源头修法是生成规范里强制”追加内容必须以换行开头”,但源头管不住的时候,扫描就是最后一道网。两类手段互补,不是二选一。
第三道:图文对应
配图有两层校验:正文引用的每个图片路径在磁盘上存在;反过来,磁盘上的图都被正文引用着——后者决定了部署体积里有没有该清没清的旧图。实现就是一个集合差:
1 | referenced = 从所有正文提取 /images/... 路径 |
这轮翻新 93 篇时靠它清了 75 张失效旧图。注意两边都要查:只查”缺失”的团队,仓库里会悄悄堆出几百张没人引用的历史包袱。
第四道:外链可达
文内链接是最容易被忽略的翻车点,因为它在写作当下”看起来对”。两个真实案例:文末指向一个 GitHub 仓库,链接看着完全正常——但仓库根本没建过,访客点开 404;另一处 npm 包链接,curl 返回 403 差点被我当死链删掉,换 registry API 一查,包好好活着。
所以外链检查有两个细则:全部链接逐个过一遍 HTTP 状态码;npm 这类反爬站点不看页面状态码,看 registry API。还有一条纪律——没建好的仓库不放链接,想表达”即将开源”就用文字说。
第五道:正文字符完整性
生成内容偶尔会混进替换符(U+FFFD)和乱码段。我逮到过一处”下知地理”变成带乱码的四个半字,肉眼校对很难发现,grep -c $'\uFFFD' 一秒定位。顺带把全站扫了一遍,确认只有那一处。
第六道:日期必须带时分秒——最隐蔽的一道
这是最近才抓到的,值得单独展开。
Hexo 的 permalink 是 :year/:month/:day/:title/,日期来自 front matter。我的新文章如果只写 date: 2026-09-12(不带时间),Hexo 会把它解析成午夜零点。问题在于在哪个时区的午夜:本机是东八区,本地构建出来的 URL 是 /2026/09/12/;而 GitHub Actions 的构建机是 UTC,同一个日期解析出来变成了 9 月 11 日,线上 URL 就是 /2026/09/11/。
两边都是”对的”,合起来就是灾难:本地验证一切正常,线上路径差一天,任何按本地路径写的内链、验证脚本、外发链接全部失效。我当时拿本地路径去 curl 线上,得到 404,差点误判成部署失败。
修复极简:日期一律写全 2026-09-12 10:00:00,时刻落在白天,两边时区解析出的日期就一致。顺带把全站扫了一遍,凡 date 只有日期的补齐时间戳——注意这会改变线上 URL,历史文章要不要动,得权衡已有外链。
收口:体检进产线
六道检查合起来不到一百行脚本,跑一遍十几秒。它们现在挂在发布流程的固定位置:生成 → 校验器 → 六道体检 → 构建 → 部署 → 线上抽查。
分工上有条清晰的线:单篇校验器管”这一篇合不合规范”——front matter 字段、篇幅口径、句法纪律;六道体检管”这一批合不合全站”——封面查重、图片集合差、全站结构扫描,都是拿一篇新文章和整个存量对答案。两层缺一不可,只做前者,问题会在”篇与篇之间”反复溜进来。
执行时机上,结构扫描和封面查重足够便宜,可以挂在每次推送前的本地钩子;外链体检涉及真实网络请求,发布流程里跑一次即可。检查越靠前,修复越便宜——这条老原则在内容工程里照样成立。
产线化写作的教训到今天可以总结成一句话:生成能力的上限决定了产量的上限,体检的下限决定了口碑的下限。前者大家都在卷,后者全靠这些不起眼的脚本。
附记:第六道规则诞生的那一晚
这篇自己的发布过程,把第六道规则原样上演了一遍。
初稿把两篇文章的日期写成了当天凌晨 5 点(东八区)。部署成功、健康检查 200,一切正常——直到按常规路径访问,全部 404。UTC 构建机把东八区的凌晨 5 点解析成了前一天的日期,线上 URL 整体漂移了一天,而本地构建毫无异样。
处置照着清单走:日期改到不跨日界的时刻;顺手全站扫了一遍 date-only 的文章补齐时间戳;校验器回填两条新检查——date 不得晚于推送时刻、东八区 08:00 之前的时刻会跨日漂移。
所以第六道的措辞才敢写”最隐蔽”:规则从来不是先知的智慧,是事故的沉淀——包括这篇自己踩的那一次。
本系列:(一)我给 AI 搭了条产线,然后被首篇文章教育了 | (二)10 个并行 agent 一天翻新 93 篇旧文章 | (三)本文 · 发布前体检