公众号排版交给开源引擎:实测十几个候选项目,附两个踩坑
上一篇留了句「下一步装 WXLayoutSkill」。真去装之前先实测了一遍候选项目,结果把那个推荐撤回了。
上一篇留了个尾巴
9 月底那篇结尾写着「下一步:装 WXLayoutSkill(开源公众号排版包),把排版这一步也交给规则」。
这次真去装了。但装之前先做了一件事——把候选项目拉出来实测。因为上一篇的排版 HTML 是我手写的,我想先确认「手写」和「引擎出」差多少,值不值得换。
实测十几个候选项目
数据是 2026-10-14 从 GitHub API 实时拉的,不是转述二手文章。
| 项目 | star | 最后提交 | 许可 | 判断 |
|---|---|---|---|---|
| aiworkskills/wechat-article-skills | 680 | 2026-09-23 | Apache-2.0 | 采用,排版纯本地 |
| geekjourneyx/md2wechat-skill | 3,699 | 2026-10-12 | Source-Available | 核心排版走付费 API |
| iniwap/AIWriteX | 2,087 | 2026-10-14 | Apache-2.0 | 完整桌面应用,过重 |
| doocs/md | 13,420 | 2026-10-12 | WTFPL | 人用的 Web 编辑器,非 Agent CLI |
| mdnice/markdown-nice | 4,681 | 2023-10-06 | GPL-3.0 | 停更 3 年 |
| lyricat/wechat-format | 4,554 | 2025-09-13 | 无 | 停更 1 年且无许可证 |
| caol64/wenyan-mcp | 1,340 | 2026-09-20 | Apache-2.0 | 发布必须公众号凭证 |
| wechatsync/Wechatsync | 6,444 | 2026-05-27 | GPL-3.0 | 浏览器扩展,需登录态 |
| TanShilongMario/WXLayoutSkill | 18 | 2026-06-25 | MIT | 体量太小,撤回推荐 |
| vercel/satori | 14,067 | 2026-10-13 | MPL-2.0 | 备选:HTML→PNG 底座 |
几条值得展开的:
WXLayoutSkill 撤回。 18 star、7 fork、4 个月没动。上一篇推荐它,是因为当时只看了介绍页,没看数据。它含三个子技能、有 9 组低饱和安全色,设计思路是对的,但体量太小,不适合当长期依赖。
md2wechat 不能用。 3,699 star,看起来最对口——面向 Agent 的公众号 CLI,README 里明确写了支持 WorkBuddy,star 也是候选里最高的。但它的核心 convert 走付费 API;免费模式只生成一段 prompt 交给外部模型继续处理,不直接返回 HTML。许可是 Source-Available,不是 OSI 开源。star 高不等于能白用。
AIWriteX 太重。 2,087 star,功能覆盖热点雷达、文章采集、洗稿、多智能体协作、排版、AI 配图、自动发布、手机端控制,是 CrewAI + FastAPI + PyWebView 的完整桌面应用。功能超需求,部署成本也超。
doocs/md 不对口。 13,420 star,候选里 star 最高、维护也活跃(最后提交就在两天前),但它是给人用的 Web 编辑器,不是给 Agent 调的 CLI。当主题 CSS 参考可以,当引擎不行。
最后选中的是 aiworkskills/wechat-article-skills。
选它不是因为 star 最高——是因为它满足一个硬条件:排版那一步是纯本地的。它的 formatting 子技能文档里写得很明确:
本 skill 为纯本地 Markdown → HTML 转换,零网络、零凭证。
我核对过 format.py:1965 行,只依赖 pyyaml,没有任何网络调用、不读任何密钥。这一点很关键,下面会讲为什么。
为什么「纯本地」是硬条件
因为公众号这一环有个绕不开的约束。
微信官方文档「发布能力」页写着:
2025 年 7 月起,个人主体账号、企业主体未认证账号及不支持认证的账号将被回收以上接口的调用权限。
我的号是个人主体,发布接口拿不到。所以任何「需要 AppID / AppSecret 才能跑」的方案,在我这里从第一步就断了。
这一条直接砍掉一批候选:wenyan-mcp(发布必须公众号凭证)、md2wechat 的草稿功能、AIWriteX 的自动发布,以及 wechat-article-skills 自己的 publish 子技能。
注意这些方案的卡点是同一个——所以在选型上它们不构成任何差异。真正有差异的只有不依赖凭证的那部分:排版、配图、内容生成。
选型结论:混着来

- 直接用开源的:排版引擎、配图工作流。这两块是通用工程,自己造不值得。
- 保留自研的:选题库、写作风格、发布契约。牙科选题库(33 条种子 + 四道筛子)和从 8.1% 分享率倒推的写作风格是垂类积累,通用开源包给不了。
- 自己写一层薄的:串联。不改开源代码,只做调用和校验。
装的时候沿用之前的约定:整个仓库复制进 ~/.workbuddy-ai/skills/,保留完整目录结构,根目录放一份路由 SKILL.md 索引 9 个子技能。9 个里只启用 formatting 一个,其余 8 个因为需要凭证或不符定位而不激活。
一次跑通
选的是选题库里未发过的 B1「洗牙后牙缝变大是真的吗」。产物:
公众号发布/2026-10-14-洗牙后牙缝变大是真的吗/
├── article.md 620 字
├── cover.png 1800×766(21:9 两倍图)
├── images/
│ ├── 01.png 洗牙前后对比
│ └── 02.png 1–2 周缓解清单
├── 排版.html 粘贴级片段
├── 预览.html 375px 手机宽度预览
└── .aws-article/presets/formatting/牙科.yaml 自定义主题
命令就四条:
# 出图
chrome --headless=new --force-device-scale-factor=2 \
--window-size=900,383 --screenshot=cover.png _render/cover.html
# 排版
format.py article.md --theme 牙科 --scheme 松绿 -o 排版.html
# 预览页
make_preview.py .
# 校验
check_publish_dir.py . # 退出码 0
配图为什么不用 AI 生图
AI 生图一张 5–10 额度,三张就是 15–30。这次换了个零额度的做法:把卡片写成 HTML,用 Chrome 无头模式截图。
三个好处:
- 零额度,不调任何生图模型
- 风格与排版一致,卡片和正文用同一套色值(松绿
#4E7D6B),放一起不打架 - 没有 AI 感,牙科内容用 AI 生成牙齿图片容易翻车,纯几何加文字的卡片反而干净
--force-device-scale-factor=2 出两倍图,手机上不糊。
两个真踩过的坑
坑一:主题把图片写死成 16:9
内置主题的 img 样式里写着:
img: display:block; width:100%; height:auto; aspect-ratio:16/9; object-fit:cover; border-radius:16px;
object-fit:cover 配合固定比例,会把我 1.55:1 的要点卡切头切尾。
正确做法是不动上游代码,复制内置主题 YAML 到本期 .aws-article/presets/formatting/牙科.yaml,删掉那两条。
但这里有个更隐蔽的坑:我一开始用 --export-theme 亲和 导出来改,结果换配色失效了。原因是导出会把派生色值(primary-fill、primary-ink)烘焙成字面量写进 YAML,之后 --scheme 松绿 只改 primary-color,那两个值不动,页面主色还是默认的蓝。
改成直接复制内置主题文件再改,问题消失。导出适合看结构,不适合当编辑起点。
坑二:校验脚本不认图注语法
check_publish_dir.py 是之前写的素材校验脚本,它用正则抓 Markdown 图片引用:
REF_MD = re.compile(r"!\[[^\]]*\]\(([^)]+)\)")
问题在于  这种带图注的写法,括号里被抓出来的是 images/01.png "图注",整段被当成文件名,于是误报「引用缺失」。
修法是加一个 ref_to_path(),剥掉可选 title 和 ? / # 后缀:
def ref_to_path(raw: str) -> str:
raw = raw.strip()
if raw.startswith("<") and raw.endswith(">"):
raw = raw[1:-1].strip()
m = re.search(r"\s+[\"']", raw) # title 一定跟在空白之后且以引号开头
if m:
raw = raw[: m.start()]
return raw.split("?")[0].split("#")[0].strip()
新旧目录都回归跑过,都通过。
「发布」这一环没变
结论还是上一篇那句:个人主体拿不到发布接口,就别在这上面耗。
公众号自带的定时发布不需要任何 API,人工设一次就到点自己发。所以自动化真正的价值在排版和内容生成——调字号、插图片、对位置,那一套才是十几二十分钟的体力活。发布那 3 分钟手动点,性价比最高,风险为零。
这次全链路跑完,从选题到校验通过,我自己动手的只有两处:挑了一个选题,改了三处措辞。
沉淀
wechat-article-skills(aiworkskills/wechat-article-skills)—— 装了 9 个子技能,只启用 formatting(纯本地排版引擎)wechat-mp-auto-publish更新 —— 新增make_preview.py(把粘贴级片段包成 375px 预览页);修了check_publish_dir.py的图注 bug.aws-article/presets/formatting/牙科.yaml—— 牙科线专用主题(亲和骨架 + 松绿配色 + 不裁图)
下一步
- 用分享率数据验证 B1 这个选题——低于 3% 说明选题没触发转发动机,该换题而不是改标题
- 把这条链路串成一条命令,省掉中间四次手动调用
- 视数据决定要不要注册个体工商户——不只是为了发布接口,是为了分销和诊所合作的资质
记于 2026.10.14。这次学到的:选开源项目时 star 数最不重要,许可和「要不要凭证」才决定它能不能用。
— 完 —
返回首页
留言
还没有留言,来当第一个吧。
加载留言中…