这组文章讲到这里,主线已经变了。上一篇 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 的 9e92b8e,blog-style-suite 继续被拆成 scanner.py、builder.py、compressor.py 这些模块。到这一步,思路其实已经非常工程化了:
scanner.py负责把文章从磁盘里扫出来,提取结构化特征builder.py负责评分、精选、缓存、运行时装配compressor.py负责该让模型参与的那几步压缩
这和单纯写一段超级 prompt,已经是两种完全不同的思路了。
省 token,不靠玄学,靠的是预处理和批量化
这套工程真正最值钱的地方,我觉得是 2026 年 4 月 2 日 19:41 的 bc4b950。
那次提交把话说得很直白,AI 调用从大约 2000 次,直接降到了每个 provider 最多 5 次。
怎么做到的?
不是靠“把 prompt 改得更聪明”,而是靠把该预处理的东西提前做掉。
现在 blog-style-suite 的流程,已经很清楚了:
scan阶段纯启发式,0 次 AI 调用build阶段先做启发式评分,还是 0 次 AI 调用- 再按
technical / finance / essay / tooling四个 lane 各做 1 次批量精选和打标签 - 最后再做 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.json 和 published.runtime.json
这才是消费侧真正关心的成品。
里面保留的是:
author_stylelanessampleswriter_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 最后怎么分工。
参考资料
- 仓库提交:
84a06b5dc743f2e9bc6e788d53496a1261bc63ae - 仓库提交:
9e92b8e6a15d03e6392aff7f3b2dcb0992fe5043 - 仓库提交:
bc4b950cbb13e37d1fdb16a9d23325cfefa6f90e - 仓库文件:
scripts/blog-style-suite/README.md - 仓库文件:
scripts/blog-style-suite/style_pipeline/scanner.py - 仓库文件:
scripts/blog-style-suite/style_pipeline/builder.py - 仓库文件:
scripts/blog-style-suite/style_pipeline/compressor.py - 生效运行时:
.agents/data/blog-writing/published.runtime.json
写作附记
原始提示词
$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.json、source.json、runtime.json这三层结构,避免空讲架构。 bc4b950被放在中间当转折点,因为“从约 2000 次到 5 次”最能说明预处理的价值。- 结尾把消费侧和生产侧重新分开,为第三篇的模型分工做铺垫。