你现在读的这篇,是产线写完第一篇真文章之后,回头写的第二篇。题材就是产线本身:怎么选技能、怎么编排、怎么在第一次实跑里被现实教育,又怎么把教训长回产线里。

产线长什么样

先交代结构。一条文章的生命线是四步:研究、写作、润色、发布。前三个环节各有专门技能负责。content-research-writer 是个写作伙伴技能,管研究、搭大纲、逐节写作和逐节反馈,产线用它跑博客文章工作流:先一起定大纲,再逐节写、写完一节审一节,技术教程类还要检查代码示例可不可执行。润色归 write,专职去 AI 味;humanizer-zh 从维基百科那页”AI 写作特征”清单来,专治三段式排比、夸大象征这类机器腔。这些技能都不是我写的,是现成的,产线只负责在正确的时机把它们叫起来。

我要补的是两块:一块叫编排,一块叫闸门。

编排就是一个叫 blog-pipeline 的技能,它自己不写一个字,只干一件事:看你处于哪种场景,把对应的子技能按需叫出来。写新文章走全流程;改旧文走 edit-article;只润色现成稿子就跳过前面直接进润色闸。它像个领班,不炒菜。产线的全部家当就是这张路由表:

场景 阶段序列
A. 写新文章 定题 → 研究/大纲 → 写作 → 润色 → 发布
B. 修改已发布文章 edit-article → 校验 → 发布
C. 只润色现成草稿 润色 → 发布

闸门是个脚本,后面细说。

设计决策:编排器保持薄

这个编排器模式不是我发明的,是从我的小说写作产线”李白”移植过来的。李白覆盖”8 问开书、大纲、正文、改稿、归档”的全流程,同样自己不生成内容:题材、文风、字数标准全从 liyu 服务实时调取,正文也由 liyu 生成,它只做问询编排、委派评审和验收把关,本地不存领域数据。博客产线照搬了这个骨架,把 liyu 换成写作子技能而已。

移植时纠结过的一个点是:要不要把子技能的指令复制一份进编排技能。复制了,编排技能就自包含,单独看很完整。

没复制。理由很实际:同一份指令存两处,改一处忘另一处,漂移只是时间问题。子技能升级了,编排里的副本还停在旧版,到时候跑的是哪套规则没人说得清。所以编排技能里只有路由表和场景判断,子技能内容一律现场加载。它瘦,但不会过期。

把纪律变成代码

agent 有个毛病:技能读了,执行时会飘。你跟它说 date 必须早于推送时刻,它今天记得,三周后的深夜赶稿未必记得。所以纪律不能只写在给人看的文档里,得变成跑得起来的代码。

于是有了 check_post.py,零依赖,发布前必须过它。它的每条校验都对应一次真实的疼。

最早的一条来自 Jekyll 时代:站点还是 Chirpy 模板那会儿,front matter 的 date 写得比构建时刻晚(比如中午 12 点的文章 11 点半推送),Jekyll 会静默跳过,日志里就一句 “has a future date”,文章不上线,也没有任何报错。那次排查了好一阵才定位到原因,”date 早于推送时刻”从此成了第一条机械化纪律。

第二条是 Hexo 迁移后踩的双日期坑。站点 permalink 是 :year/:month/:day/:title/,我起初想当然以为 :title 取自 front matter,拿 curl 一测才发现它取的是文件名:文件名叫 2026-09-11-xxx.md,产出的 URL 就是 /2026/09/11/2026-09-11-xxx/,日期出现两遍。发现后这条也升级成了正则拦截:

1
FILENAME_RE = re.compile(r"^(?!\d{4}-\d{2}-\d{2}-)([0-9a-z-]+)\.md$")

脚本写完不是直接上岗的。先跑了五个场景的测试:正例、未来日期、缺 title、date 与文件名不一致、5 分钟余量边界,又让一个子 agent 干跑全流程,验证路由和红线都按预期工作,全部通过才收编。如今它还管着 cover:缺缩略图拦下,缩略图和正文插图重复也拦下(这条的来历见下一节)。

FAIL 报错长这样,一次真实触发:

1
2
3
4
[FAIL] /tmp/test.md
✗ date 晚于当前时刻(2026-09-11 20:32 +0800)+5min 余量: 2026-09-11 23:00:00 +0800 → 纪律红线:date 必须早于推送时刻
✗ front matter 缺少 cover → 每篇文章必须单独配缩略图(Butterfly 首页/侧栏缩略图只认 cover 字段,缺省显示灰块)
⚠ tags/categories 均缺失,归档页将不收录

FAIL 就是 push 不了,没有商量;警告类(比如缺 tags)才允许带着说明通过。

首篇实战:产线被教育

设计得再顺,第一次真跑才知道哪疼。首篇是重写我 2024 年在 CSDN 的一篇 CentOS 旧文,全过程挑几个说。

技能口径过期了。这套技能最早按 Jekyll/Chirpy 的规矩写,而站点前一天刚迁到 Hexo/Butterfly。发布前一对,校验器、front matter 模板、流程说明三处全是旧口径。好在闸门的哲学就是”发布前必查”,问题在发布前暴露,回头把三件套全改到 Hexo 版。这也反过来验证了那句话:文档过期了没人知道,代码过期了会当场报警。

配图的”真实输出”原则。文章讲 Linux 命令,截图里的数值不能编。我的机器是 macOS,于是起 Docker 容器实跑命令抓真输出;容器选了 Rocky Linux 9 而不是 Ubuntu,因为文章语境是 CentOS 的后继者,图里出现什么发行版是有讲究的。输出有了,还要渲染成统一的终端窗风格。渲染脚本吃一份 JSON spec,每行标注类型(命令、输出、注释),要高亮的子串单独标出,比如这张图的其中一行:

1
{ "t": "cmd", "s": "uptime -p   # 跑了多久,人话版" }

渲染脚本写了两版:第一版中文全是方块,后来才明白 Menlo 这类等宽字体压根没有中文字形,注释里的中文得自动切到中文字体渲染,ASCII 和中文分段各用各的。还有个小坑:拿视觉模型给渲染结果做质检时,它会把方块区域一本正经地”脑补”成一段根本不存在的中文。报告里说”有方块”可信,引用它认出的具体文字之前,得先对着源文件核实。

缩略图的教训最直接。我给首篇配了张终端风格的题图当封面,自我感觉良好。发布之后收到反馈:缩略图不要用文章里的图。换了一张 Unsplash 的照片,然后这条要求当场固化:每篇文章必须单独配缩略图,禁止复用正文插图,写进 front matter 模板,校验器加了两条 FAIL。到这篇发文时规则又长了一层:图还必须贴题,读者看到图要能猜到文章讲什么。我自己就翻过车,”产线”两个字直译成仓库货架的照片,读者看到的只有快递。选图的标准从此写明是读者视角,这条靠脚本拦不住,只能靠每次选图时先自问一句。

发布之后还有一课

push 完、构建绿、文章 URL 返回 200,我以为结束了。去首页一看,没有新文章,吓一跳,回头一查,其实首页早就有,是 GitHub Pages 的 CDN 缓存还挂着 10 分钟窗口。这不算 bug,但值得记住:部署验证要直接 curl 文章 URL,别只看首页。这条也进了技能的检查清单。

产线是长出来的

回头看重心:这套产线没有一天是”设计完成”状态。选技能和编排是它的骨架,骨架搭完只能算能用;让它开始可靠的是背后的循环:每次实跑暴露一个盲区,修掉,然后三层同步沉淀。技能文件改掉旧口径;结论写进记忆系统,下次任务动手前先查记忆,同一个坑不踩第二遍;够通用的结论固化成校验器的新规则。人会忘,文档会过时,只有跑在发布必经之路上的脚本不会放过任何人。

这篇是产线的第二次实跑,也是第一个”由产线记录产线成长”的产物。写它的过程本身又验证了一次这个循环:文章初稿漏了编排器的来历和好几段踩坑记录,读者一眼看了出来,回头补上。下一疼出现的时候,大概又是一条新红线的出生证明。


本系列其他篇目:(二)10 个并行 agent 一天翻新 93 篇旧文章 | (三)发布前的六道自动体检