使用 Skill 优化我的博客发布流程

20260427144654_2cu1sqDG.webp

前言

这篇我想聊一下最近折腾博客发布流程的过程。

不是那种“我写了一个脚本,所以效率提升了”的记录,而是从一个很实际的问题开始:我发现自己写文章的时间还好,发布文章反而越来越烦。

我的博客主站是 Hexo,另外还有一个 dev-site。文章写完之后,并不是点一下发布就结束了。我要补 front matter、生成列表页封面、处理正文头图、同步到 dev-site、转换图片、检查路径、跑构建。每一步都不难,但每一步都容易漏。

一开始我想的是:那就写脚本吧。后来发现,脚本只能解决一部分问题。再后来我开始把 AI 加进来,让它不只是“帮我写正文”,而是参与整个发布流程。最后这套东西慢慢固化成几个 skill,再用一个总控 skill 串起来。

这篇文章就按这个脉络写:我遇到了什么痛点,中间尝试过哪些方案,为什么最后会变成多个 skill 协作,以及这套流程最后带来了什么收益。


痛点:发布文章比写文章还容易出错

先说我原来的发布方式。

一篇文章写完后,我通常会按这个顺序处理:

  1. 手动补 front matter:titleabbrlinkdescriptionkeywordstagscategoriescover
  2. 手动检查正文第一张图,占位图要替换,路径要改;
  3. 手动把文章同步到 dev-site,对齐两边 front matter;
  4. 手动复制文章资源目录,顺便处理图片格式;
  5. 本地构建,看到报错再回去补洞。

这些步骤本身没有什么技术含量,但它有两个很烦的特点。

第一个是碎。

每个动作都只占一两分钟,但它们散在不同地方:Markdown 文件、文章资源目录、Hexo 配置、dev-site 内容目录、构建命令。有时候写完文章已经很晚了,脑子里想的是“赶紧发布”,结果越赶越容易漏。

第二个是依赖记忆。

我自己经常遇到这些情况:

  • cover 写了,但不是 cover-generator 的标准 URL;
  • random-pic-api 还留在正文里,忘了替换;
  • dev-site 同步时 banner 没跟着走本地图片;
  • 图片还是 .svg.png,到 Astro 侧才发现元数据或路径问题;
  • 都改完了,回头一看 front matter 又漏了一个字段。

这种错误最烦的地方不是难修,而是很低级。你知道它不应该发生,但只要流程还靠人脑记,就一定会发生。

所以真正浪费时间的不是写文章,而是发布前这段机械劳动。


第一轮尝试:用脚本减少重复劳动

最早我想得很简单:既然重复,那就脚本化。

比如:

  • 批量补缺失的 cover
  • 把某些图片统一转成 WebP;
  • 复制文章资源目录;
  • 把 Hexo front matter 转成 dev-site front matter;
  • 构建前跑一轮检查。

脚本确实解决了一部分问题,尤其是那些输入输出非常确定的动作。比如“把 SVG 转成 WebP”、“把文件复制到另一个目录”、“把日期格式从 YYYY-MM-DD HH:mm:ss 转成 YYYY-MM-DD”,这类任务脚本很合适。

但我很快发现,发布流程里还有很多事情不是纯粹的字符串替换。

比如正文头图。它不是随便找一张图就行,而是要理解文章主题,再生成一张风格合适的图。再比如 descriptionai 摘要,它们也不是从标题里截几个关键词就能解决。还有分类和标签,它们需要贴合文章语义,但又不能乱造新分类。

这些事情脚本能做,但会越来越别扭。你要么写一堆规则,最后变成一个难维护的规则引擎;要么把很多判断留给自己,脚本只做半截自动化。

这就是我后来引入 AI 的原因。


第二轮尝试:让 AI 参与发布,而不只是写正文

以前我用 AI,更多是让它帮我润色、整理提纲、补一段说明。后来我意识到,AI 更适合接管发布流程里那些“需要理解上下文,但又有明确输出格式”的工作。

博客发布正好符合这个特征。

它不是让 AI 自由发挥,而是让 AI 在一组约束里做事:

  • 读文章内容,补合理的摘要和关键词;
  • 根据标题生成符合规则的 cover URL;
  • 根据正文生成头图提示词;
  • 判断哪些字段该同步到 dev-site,哪些字段不该带过去;
  • 在转换正文时避开代码块,不要误改 Shell 里的 [[ ... ]]
  • 最后跑构建,确认流程没有破坏站点。

但是这里也有一个坑:如果只写一句“帮我发布这篇文章”,AI 会把很多职责混在一起。今天改 front matter,顺手生成图;明天同步 dev-site,又顺手改 Hexo 正文。短期看很方便,长期就会变成另一个版本的“靠记忆发布”。

所以我没有把它做成一个巨大的 prompt,而是把发布流程拆成多个 skill。


为什么要拆成多个 Skill

我现在的发布流程拆成三段:

  1. 先处理 Hexo front matter 和 cover
  2. 再生成正文头图;
  3. 最后同步到 dev-site,并优先用正文头图做 banner

对应的 skill 是:

  • hexo-frontmatter-prepare
  • hexo-body-cover-generate
  • hexo-devsite-sync

这个拆法其实跟写代码很像。

你不会把所有逻辑都塞进一个 2000 行的函数里,而是先拆出基础工具类:一个类负责解析配置,一个类负责处理图片,一个类负责同步数据。它们各自可以独立测试,也可以被别的业务复用。

然后再写一个业务服务,把这些基础能力按顺序编排起来,对外提供一个完整动作。

我的这几个 skill 也是这个关系:

阶段输入输出不做什么
front matter 准备一篇 Hexo Markdown合法元数据 + 线上 cover不生成正文图,不碰 dev-site
正文头图生成同一篇 Markdown + 正文内容本地 cover.webp + 正文引用替换 + 提示词记录不改 front matter cover
dev-site 同步已处理过的 Hexo 文章dev-site 文章 + banner + 资源目录不反向改 Hexo 写作规则

这样拆有几个好处:

  • 每个 skill 的上下文更小,AI 不容易跑偏;
  • 每个 skill 的输出契约更明确,后续流程可以依赖;
  • 单独某一步失败时,能立刻知道问题在哪;
  • 以后只想重做正文头图,不需要重新跑 dev-site 同步;
  • 新增目标站点时,可以复用前两段,只换同步阶段。

我现在越来越觉得,自动化流程最怕的不是步骤多,而是边界混在一起。只要边界混了,AI 越“聪明”,后面越难排查。


Skill 1:先把 front matter 变成稳定入口

第一个 skill 解决的是文章元数据问题。

在 Hexo 里,front matter 是很多功能的入口。分类、标签、摘要、封面、链接、时间都在这里。这里如果不稳定,后面所有自动化都会被拖累。

所以 hexo-frontmatter-prepare 只做一件事:把 Hexo front matter 补全,并把 cover 规范成可直接渲染的线上地址。

我现在固定用 cover-generator 这个开源项目来生成封面 URL。它的好处很直接:

  • 风格统一,不用每篇单独找图;
  • 参数化,标题变了可以重生一版;
  • 对 Hexo 来说就是一个普通图片 URL,不改主题逻辑。

在这段里,skill 会完成:

  • 检查并补齐 titleabbrlinkdate
  • 根据正文补 descriptionkeywordsai
  • 从白名单里选择分类和标签;
  • 生成或修正 cover-generator URL;
  • 保持 YAML 可解析,避免后续构建时炸 front matter。

这里我特意把 cover 定义成「列表页封面」,而不是「文章正文头图」。这个区别非常重要。

Hexo 主题里的 cover 通常会出现在首页卡片、归档列表、相关文章推荐、Open Graph 图片等位置。它更像一个站点级展示字段,适合用一个稳定的远程 URL。正文头图则是文章内容资产,应该跟文章放在一起,方便迁移和版本管理。

所以这一段的契约非常简单:

1
2
3
4
Markdown front matter
-> title / date / tags / categories / description / keywords / ai 补齐
-> cover 写成 cover-generator 的远程 PNG URL
-> 正文保持不动

我还加了几个约束,防止自动化越跑越散:

  • 分类只能从站内白名单里选一个,不让每篇文章临时造分类;
  • 标签控制在 1 到 5 个,不把所有关键词都塞进 tags;
  • cover URL 必须 URL 编码,中文标题不能裸奔;
  • cover 只写线上 PNG,不在这一步创建本地图片;
  • 不为了校验 URL 去请求线上 API,避免发布流程被网络抖动打断。

这些规则看着琐碎,但它们解决的是同一个问题:front matter 必须变成一个可预测的结构化入口,而不是一堆靠记忆维护的字段。

这一段完成之后,我至少能确认:这篇文章已经是一个合法的 Hexo 文章,列表页封面也有了。后面再生成正文头图、同步 dev-site,都不用回头猜元数据。


Skill 2:正文头图单独处理,而不是混进 cover

这段是我体感提升最大的部分。

我以前会在文首留一个占位:

1
![random-pic-api](https://api.dong4j.site/pc?spm={{spm}})

发文前要么手工找图,要么临时生成,再自己改引用路径。现在这段交给 skill 跑:

  • 基于文章内容生成正文头图;
  • 输出到文章同名目录的 cover.webp
  • 自动把 random-pic-api 替换为本地相对路径;
  • 保留提示词,方便后续重绘或风格统一。

结果是两件事同时解决:

  1. 正文第一屏不再是随机占位图;
  2. 图片资产和文章绑定在一起,迁移、同步都更稳。

这里最关键的设计是:我没有让 skill 去「猜」正文里哪张图是头图,而是只识别一个固定占位符:

1
![random-pic-api](https://api.dong4j.site/pc?spm={{spm}})

这个占位符有点像一个发布前 TODO。写草稿时它可以先占住文首位置,真正发布时再由 skill 替换成本地图片:

1
![cover](./article-slug/cover.webp)

最终文件结构大概是这样:

1
2
3
source/_posts/2026/article-slug.md
source/_posts/2026/article-slug/cover.webp
source/_posts/2026/article-slug/prompts/01-cover-article-slug.md

我这里还做了一个看似多余、实际很有用的动作:保存提示词。

AI 生成图片很容易变成一次性操作。今天生成一张图看着不错,过两个月想换风格、换模型、统一全站视觉时,才发现只剩一张结果图,完全不知道当初是怎么来的。提示词单独落盘之后,正文头图就不再是不可追溯的「魔法产物」,而是一个可以重新生成的构建结果。

这段还有一个硬边界:不修改 front matter 里的 cover

原因很简单,cover 是 Hexo 主题展示字段,正文头图是文章内容字段。两个字段可以来自同一个标题,也可以视觉风格接近,但不能互相覆盖。否则后面同步 dev-site 时,你很快就会分不清某张图到底是给首页卡片看的,还是给文章详情页看的。

如果正文里没有固定占位符,我的策略也不是自动插入,而是停止并提示。因为「要不要在文首放图」本身是写作决策,不应该由工具悄悄替作者决定。


Skill 3:同步 dev-site,本质是内容模型转换

第三段我不再把它当「复制文章」,而是「内容模型转换」。

Hexo 和 dev-site 的 front matter 不同,这里必须按目标站 schema 输出。现在的规则很清楚:

  • 主体内容从 Hexo 转过去;
  • 字段做映射,不硬搬无关字段;
  • banner 优先使用正文头图 cover.webp
  • 如果是图片资源,顺便做必要转换(比如统一到 webp);
  • 同步后跑构建验证,确保 dev-site 可发布。

也就是说,我不需要再手动决定「banner 用哪张图」。只要第二段有产出,第三段就直接复用正文头图,视觉和内容保持一致。

这里最容易踩坑的是把同步理解成复制。实际上我现在把它当成一次 schema 转换。

Hexo 文章里的 front matter 是给 Hexo 主题和插件看的,dev-site 的 front matter 是给 Astro 内容集合看的。两个站点字段看起来相似,但语义不完全一样:

Hexo 字段dev-site 字段说明
titletitle直接映射
descriptiondescription直接映射
datepubDate转成 dev-site 需要的日期格式
categories[0]category映射成 dev-site 分类 slug
tagstags直接映射
正文头图 cover.webpbanner复制到 dev-site 资源目录后写成本地路径
coverfallback banner只在单独同步且没有正文头图时兜底
abbrlink / ai / keywords不携带目标站点不需要就不硬塞

正文也要做转换。比如 Hexo 里开头的 referrer meta、随机图占位符、已经被 banner 复用的正文头图,在 dev-site 里都应该移除。否则详情页顶部会先渲染一个 banner,正文第一屏又出现同一张图,视觉上很重复。

资源同步也不是全量复制。我会复制文章本地资源,但跳过 prompts/。提示词对源码维护有价值,对公开站点没有价值,没必要发布出去。

完整流水线里,我要求 dev-site 同步必须优先使用第二段生成的 cover.webp。如果没有这张图,就说明正文头图阶段没有完成,此时应该失败并报告,而不是悄悄退回到 Hexo cover

但如果我单独运行 dev-site 同步,用来迁移一些历史文章,没有本地 cover.webp 也可以接受 fallback。区别在于最终报告里必须说明:这篇文章的 banner 用的是远程 cover,不是本地正文头图。

这就是我现在给自动化流程定的一条原则:完整流水线要严格,独立工具可以兜底,但兜底必须说出来。


为什么还要一个总控 Skill

做到这里,其实三个 skill 已经能独立解决问题了。

但如果每次发布都要我手动记住顺序:

  1. 先跑 hexo-frontmatter-prepare
  2. 再跑 hexo-body-cover-generate
  3. 最后跑 hexo-devsite-sync
  4. 同步后再构建;
  5. 最后再看结果。

那它还是有一点“半自动”。我只是把原来手动操作的步骤换成了手动调用 skill。

所以我又加了一个总控 skill:hexo-publish-pipeline

它本身不负责具体细节,而是负责编排。

这就像代码里的业务服务层。底下已经有了几个基础工具类:

  • FrontmatterService 负责元数据;
  • CoverImageService 负责正文头图;
  • DevSiteSyncService 负责同步转换。

那业务层就可以封装一个 publishPost()

1
2
3
4
5
6
publishPost(article)
-> prepareFrontmatter(article)
-> generateBodyCover(article)
-> syncToDevSite(article)
-> runBuildCheck()
-> reportResult()

总控 skill 的好处是:

  • 固定执行顺序,不靠我临时记;
  • 统一检查每一步的输出是否满足下一步输入;
  • 完整流水线里可以采用更严格的失败策略;
  • 最终汇总 Hexo 和 dev-site 的改动;
  • 以后流程增加新步骤,只需要改编排层,不必改每个基础 skill。

举个例子,单独运行 dev-site 同步时,缺少本地 cover.webp 可以 fallback 到 Hexo cover。但在完整发布流水线里,第二步本来就应该生成 cover.webp,如果第三步没看到这张图,就必须报错。

这个规则就应该放在总控 skill 里。因为它不是某个基础能力的规则,而是整个业务流程的规则。


现在的完整工作流

改造后,我的发布动作变成了:

  1. 在 Hexo 写完文章内容;
  2. 触发总控 skill;
  3. 检查结果并发布。

剩下这些重复任务基本都自动完成:

  • front matter 生成与修正;
  • cover 生成(cover-generator);
  • random-pic-api 替换;
  • 正文头图本地化;
  • dev-site 同步;
  • banner 复用正文头图;
  • 图片转换与兼容性处理。

以前这套流程至少半小时。现在稳定在 5 分钟左右,差不多是一个数量级的提升。

这不是「快一点」的问题,而是「发布心智负担」被砍掉了。以前每次发布都像做检查清单,现在我只需要把文章写好,流程自己把剩下的脏活干完。

现在一篇文章从写完到可发布,大概是这个状态流:

1
2
3
4
5
6
7
草稿完成
-> front matter 检查通过
-> Hexo cover 就绪
-> 正文头图生成并替换
-> dev-site 内容转换完成
-> dev-site build 通过
-> 人工确认 commit / push / deploy

最后一步我仍然保留人工确认。自动化可以帮我做重复动作,但不应该默认替我发布。尤其是文章这种内容资产,构建通过不代表表达就没问题,发布前还是应该有一次人眼确认。


这套流程带来的收益

这次优化对我最大的收益,不只是省了时间。

时间当然省了。以前一篇文章发布前至少要折腾二三十分钟,现在大部分情况几分钟就能完成。

但更重要的是确定性。

以前我每次发布都在心里过一遍 checklist:cover 生成了吗?正文头图换了吗?dev-site banner 对了吗?图片复制了吗?构建跑了吗?

现在这些问题变成了流程的一部分:

  • front matter 不完整,第一段就会处理;
  • 正文占位图没替换,第二段会处理或报错;
  • dev-site 字段不匹配,第三段会转换;
  • 构建失败,流程最后会暴露出来;
  • 哪一步使用了 fallback,最终报告会说清楚。

第二个收益是可维护。

如果哪天我想换正文头图生成模型,只需要改 hexo-body-cover-generate。如果我想把 dev-site 从 Astro 换成别的框架,主要改 hexo-devsite-sync。如果我想给发布流程加一个“自动生成社交媒体摘要”的步骤,就在总控 skill 里多插一段。

这比写一个超级大脚本舒服很多。


这套思路怎么迁移到别人的博客

如果你不是 Hexo,也不是 Astro,其实也能照着这个思路拆。

先别急着写脚本,先把三个问题回答清楚:

  1. 当前站点的列表封面字段是什么?
  2. 正文头图是否应该成为文章本地资源?
  3. 目标站点或目标渠道需要什么内容模型?

比如你用的是 VitePress,列表封面可能只是 front matter 里的 image;你用的是 Next.js + MDX,可能需要 ogImageheroImage 分开;你同步到公众号、语雀、Notion,可能根本没有 banner,只有正文第一张图。

工具不同没关系,边界可以照搬:

1
2
3
4
5
6
7
8
当前站点元数据
-> 当前站点可消费的封面字段

正文内容资产
-> 本地图片 + 可追溯生成记录

目标渠道同步
-> 目标 schema + 资源转换 + 构建/预览验证

真正值得复制的是这件事:不要让一个字段承担太多职责,也不要让一个脚本同时决定所有上下游细节。


最后的结论

回头看这次优化,路径其实很自然:

一开始是手工发布太碎,靠记忆容易漏;然后我尝试用脚本解决确定性动作;再后来发现脚本不适合处理语义判断,于是把 AI 引入发布流程;最后为了让 AI 不乱跑,又把流程拆成多个职责单一的 skill,并用一个总控 skill 编排。

这次最关键的,不是某个工具多强,而是职责边界终于清楚了:

  • covercover
  • 正文头图归正文;
  • banner 在同步阶段决定,并优先复用正文头图。

把这三件事拆开后,skill 才真正能自动化,而不是把手工步骤换个壳再跑一遍。

对我来说,这是一次质变:从「写文 + 手工运维」变成「写文 + 自动发布」。

更准确地说,是从「靠记忆发布」变成「靠契约发布」。只要契约稳定,后面不管是换图片生成模型、换静态站框架,还是再同步一个新站点,都不需要推翻整套流程。