AI 写博客这件事,后来还是得做成工程(二)

blog-style-suite 怎么把风格学习和 token 成本拆开

如果 token 足够,最省脑子的办法其实很粗暴:把历史文章直接塞给模型,让它自己学。

问题在于,这种办法只适合偶尔来一篇,不适合反复写。你要是真把博客写作当成长期工作流来做,生吃历史文章这条路,很快就会从“简单直接”变成“又贵又乱”。

这组文章讲到这里,主线已经变了。上一篇 AI 写博客这件事,后来还是得做成工程(一):blog-writer 为什么会长出来 讲的是消费侧的自动化;这一篇开始讲生产侧,也就是风格数据怎么生成、怎么压缩、怎么别把 token 白白烧掉;下一篇会接着收在 AI 写博客这件事,后来还是得做成工程(三):本地模型、在线模型和 Minimax 最后怎么分工

一开始最自然的想法,就是直接喂历史文章

这条路真的太自然了。

你既然想让模型学会你的写法,那最直观的办法当然就是把旧文章喂给它。最好把历史博客里写得最像自己的那些都塞进去,让它自己总结。

单看一次任务,这么干没什么毛病。

甚至很多时候效果还不错。上下文够长,模型够强,历史文章够多,风格确实能被带出来。

但问题不是“这一篇能不能写成”,问题是“下一篇、下下篇,还要不要再来一遍”。

每次都重新喂一批旧文章,会带来几个很现实的副作用:

  • 同一批材料反复占上下文
  • token 开销和写稿次数近乎线性增长
  • 模型看到的噪音越来越多,真正有用的信号反而被稀释
  • 写稿动作和风格维护动作彻底绑死,谁都轻不下来

也就是说,token 富裕的时候,生吃当然能吃。但工程上不能老这么吃。

这也是为什么数据模块和数据生成模块必须拆开

我后来把这件事想明白,核心就一句话:消费侧和生产侧得分开。

blog-writer 负责的是消费侧。它只管读一份已经发布好的 runtime,然后把文章按固定契约写出来。

而风格数据的扫描、筛选、评分、压缩、provider 对比,这些都应该放到另一条生产侧流水线里。也就是后来长出来的 blog-style-suite

从 git 记录看,这个转折很清楚。

2026 年 4 月 1 日 21:47 的 84a06b5,已经明确把原来的 blog-style-maintainer skill 换成了仓库级 CLI 工具。这个动作很说明问题,因为一旦你有了 scan/build/rebuild、有了输出目录、有了恢复机制,事情就已经不像一个 skill 了,更像一个正常的 Python 项目。

再到 2026 年 4 月 1 日 23:05 的 9e92b8eblog-style-suite 继续被拆成 scanner.pybuilder.pycompressor.py 这些模块。到这一步,思路其实已经非常工程化了:

  • scanner.py 负责把文章从磁盘里扫出来,提取结构化特征
  • builder.py 负责评分、精选、缓存、运行时装配
  • compressor.py 负责该让模型参与的那几步压缩

这和单纯写一段超级 prompt,已经是两种完全不同的思路了。

省 token,不靠玄学,靠的是预处理和批量化

这套工程真正最值钱的地方,我觉得是 2026 年 4 月 2 日 19:41 的 bc4b950

那次提交把话说得很直白,AI 调用从大约 2000 次,直接降到了每个 provider 最多 5 次。

怎么做到的?

不是靠“把 prompt 改得更聪明”,而是靠把该预处理的东西提前做掉。

现在 blog-style-suite 的流程,已经很清楚了:

  1. scan 阶段纯启发式,0 次 AI 调用
  2. build 阶段先做启发式评分,还是 0 次 AI 调用
  3. 再按 technical / finance / essay / tooling 四个 lane 各做 1 次批量精选和打标签
  4. 最后再做 1 次作者风格压缩

这样算下来,冷启动最多也就是 5 次调用。

更关键的是,这 5 次不是分散在每一篇文章上,而是集中在已经被预处理过的、高价值的摘要材料上。

这就是预处理真正省 token 的地方。不是少省几个字,而是从“按文章逐篇调用”改成“按阶段集中调用”。

再往后,缓存也补上了。

builder.py 里有 lane 的 batch fingerprint,有 provider checkpoint 恢复,有 review_pool_per_lane = 12 这种为了本地模型上下文做的收缩。你要改一小部分数据,不会整条流水线重跑。

这类设计看起来都不炫,但每一个都很实用。因为它们解决的都是“别让同一批 token 反复烧第二次”。

现在这套数据结构,本质上就是在压缩真正有用的信号

这一套拆完以后,数据结构也就顺了。

我现在更愿意把它理解成三层。

第一层:scan.json

这是共享原料。

里面放的是文章路径、标题、日期、分类、标签、开头段落、closing stub、headings、screening 结果、lane 归类这些结构化信号。

它不是给 blog-writer 直接吃的,它是给生产侧继续往下加工的。

第二层:{provider}.source.json

这是 provider 级 checkpoint。

在共享原料的基础上,多了评分结果、lane selection、fingerprint、cache status 这些中间态。也就是说,它更像“加工过程中的半成品”,重点是可恢复、可复用、可中断继续。

第三层:{provider}.runtime.jsonpublished.runtime.json

这才是消费侧真正关心的成品。

里面保留的是:

  • author_style
  • lanes
  • samples
  • writer_guide

也就是把原来一大堆历史文章,压成一份可直接消费的运行时风格资产。

尤其是 published.runtime.json 这个发布位很关键。blog-writer 只读这一份,不回头去扫 content/post,也不关心 suite 目录下所有 provider 的完整镜像。

这个边界一旦立住,消费侧就轻了。写稿模型看到的,不再是一堆原始旧文,而是一份已经预处理好的高密度信号。

不是所有事情都该让模型做

我现在越来越觉得,这套工程里最正确的一个判断,其实不是“多接几个模型”,而是“别把不该让模型做的事也扔给模型”。

像这些事情,就很适合本地规则先做掉:

  • frontmatter 解析
  • 开头段落提取
  • headings 抽取
  • 本人/转载/模型署名判断
  • blockquote 比例检测
  • <!--more-->、嵌入 prompt、正文长度这类硬规则筛掉

这些动作让模型来做,不是不行,是浪费。

模型更适合做的,是那些带模糊性、带取舍的部分。比如一个 lane 里哪几篇更能代表当前声音,或者从高分文章里提炼作者风格标签。

所以 blog-style-suite 真正值钱的,不只是“省 token”,而是它把人、规则、模型三者各自该干的活重新分了一次。

预处理不是为了省一点 token,是为了让写稿这件事可持续

第二篇的结论,我想压得更直接一点。

token 富裕的时候,生吃历史文章当然没问题。甚至你真只写一两篇,可能还更省脑子。

但只要你想把这件事做成长期工作流,预处理就不是可选项了。因为你不做预处理,写稿模型每次都得重复看旧材料,风格维护和文章生成也永远搅在一起。

blog-style-suite 的意义,就是把这一团东西拆开。

不是为了显得系统,不是为了多一个项目名,而是为了让 blog-writer 保持轻,保持稳,保持“只干写稿这一个动作”。

到了这里,下一步的问题也就顺理成章了。

既然生产侧已经独立出来了,那到底该让什么模型来承担这部分成本?本地模型、在线模型、Minimax,分别该站在哪个工位上?这件事,留到下一篇 AI 写博客这件事,后来还是得做成工程(三):本地模型、在线模型和 Minimax 最后怎么分工

参考资料

写作附记

原始提示词

$blog-writer 本次的内容比较多,拆分成系列文章:去年就有很多稿子是通过大模型写的,那会是自己写个大纲或者问题清单,然后AI出稿子,复制内容到本地 md 文档,填写头信息,标签信息、发布文章;近期 codex 用了很多,发现 codex 里面的联网搜索能力很强,那我是不是能写个 skill,将这些事情自动化,此时诞生了 skill blog-writer 的第一稿,我还想着让 AI 学习我以前文章的风格,这就导致 blog-writer 运行的时候,很费 token,后续我针对 blog-writer 进行了多个版本的优化,拆分了 数据模块,数据生成的模块,原本数据生成的模块还是独立的 skill,写着写着,我就发现,更适合做成 Python 项目,此时就有了 blog-style-suite,然后我又发现,训练风格数据,也是比较费 token,我就想着用本地的大模型,对接了本地的大模型,我又想到了对比下本地大模型和在线版本的区别,又对接了 minimax;blog-style-suite 和 blog-writer 的演化历史可以分析的 git 提价记录。顺带基于本地 blog-writer、blog-style-suite 的代码,可以讲讲里面的设计思路,是如何做到了节约 token,数据结构是如何设计的,核心的设计思路。Token 富裕完全能生吃历史文章,预处理能节约很多 token

写作思路摘要

  • 这一篇把重点从写稿动作切到数据工程,核心回答“为什么必须拆模块”。
  • 开头直接承认“生吃历史文章能用”,这样后面的拆分理由才更有说服力。
  • 重点展开了 scan.jsonsource.jsonruntime.json 这三层结构,避免空讲架构。
  • bc4b950 被放在中间当转折点,因为“从约 2000 次到 5 次”最能说明预处理的价值。
  • 结尾把消费侧和生产侧重新分开,为第三篇的模型分工做铺垫。
金融IT程序员的瞎折腾、日常生活的碎碎念
使用 Hugo 构建
主题 StackJimmy 设计