wxpost · 公众号贴图全链路
> 「一条能发的贴图,八成功夫花在对齐结构上,最后一步才是点发布。」
核心理念
贴图不是「把长文配几张图」,它是一套独立的叙事形态:图负责抓住眼睛,文负责讲完事情。所以这个 skill 的活不是「调个接口」,而是把一条内容从想法推到草稿箱,中间每一步都替人做掉决策。
一个好用的贴图流水线,要同时满足五件事:
- 文案归 Agent,确定性归 CLI。 写文案是语言问题,交给模型;压图、传素材、建草稿是确定性问题,交给代码。不给 CLI 再配一个大模型 key。
- 配图是分角色的,不是复制粘贴。 封面抢点击、内容图讲信息、收尾图收情绪,三件事三种画法。
- 先看整体,再看局部。 九宫格故事板先对齐结构和画风,确认了才逐张放大。省掉「出完 9 张才发现方向全错」。
- 每个号有自己的记性。 工具号和生活方式号的调性天差地别,共用一个 prompt 一定翻车。
- 站在安全一侧。 默认只建草稿,绝不擅自推给粉丝。
关键区分:产出的是 newspic(图片消息),不是 news(图文消息)。两者 API 都走 draft/add,但字段要求和版式完全不同,混用会直接报错或排版全乱。
执行流程
Phase 0: 入口分流
先判断用户要的到底是哪种东西。别急着敲命令。
| 用户说 | 走哪条路 |
|---|---|
| 「发贴图 / 小绿书 / 公众号图片消息」 | Phase 1 起,全链路 |
| 「给这篇配几张图,发到公众号」 | Phase 2 起,文案已有,只做配图和发布 |
| 「先出个分镜 / 故事板看看」 | Phase 2.5,只出故事板 |
| 「照这条链接做一个」「这个链接参考一下」 | Phase 0.3 from-url 提取素材,再走 Phase 2 |
| 「这篇小红书稿子转成贴图」 | 先 from-md 转成 post 目录,再 Phase 2.5 |
| 「写公众号长文、要排版好看」 | ❌ 不在这里做,那是 news 图文消息,排版规则完全不同 |
| 「群发 / 推给所有粉丝」 | ⚠️ 默认不做,见 Phase 4 |
Phase 0.3: 从链接取素材(可选入口)
用户手上没有文案,只有一条别人写得好的图文时走这里。做参考,不做搬运。
$WXPOST from-url <链接> --dir ./post # 参考模式(默认)
$WXPOST from-url <链接> --dir ./post --copy # 搬运模式:原文原图直接填进 post.json
支持范围和真实可靠度:
| 链接形态 | 能抓到 | 可靠度 |
|---|---|---|
公众号文章 mp.weixin.qq.com/s/* |
标题、作者、正文、正文图 | ✅ 稳定,HTML 结构公开 |
| 微信贴图(同域名,图多文少会自动识别为 picture) | 同上 | ✅ 稳定 |
小红书 xiaohongshu.com/explore/*、xhslink.com/* |
标题、正文、图片 | ⚠️ 反爬强,常拿不到 |
参考模式的产物:
post/reference/
├── reference.md 原文标题/作者/来源/正文/图片清单
└── 01.jpg 02.jpg … 原文图片,命名按原文顺序
同时把选题写进 post.json 的 brief,接着走 Phase 2。
⛔ 小红书抓不到时不要硬编内容。 命令行会明确报错,这时把替代方案给用户:① 把正文直接粘过来 ② 截图或导出图片 ③ 换个来源。绝不允许自己编一段"大概是这个意思"的内容顶上。
Phase 0.5: 环境体检 🔴 CHECKPOINT
任何操作前先跑这一条,不许猜。
$WXPOST check --json
$WXPOST 的取值:
WXPOST="$HOME/.wxpost/wxpost.sh" # macOS / Linux / Git Bash
# Windows cmd / PowerShell:"$HOME\.wxpost\wxpost.cmd"
不确定哪个存在就先探测:ls ~/.wxpost/wxpost.sh ~/.wxpost/wxpost.cmd 2>/dev/null。若 .venv 缺失:bash ~/.wxpost/install.sh。
读完 JSON,按三个字段决定下一步:
| 字段 | 含义 | 不满足时怎么办 |
|---|---|---|
ready |
微信凭证是否可用 | 把 checks[].detail 原样转述给用户(已翻译成人话),提示跑 wxpost init |
image.ready |
本机有无生图通道 | 决定 Phase 1.5 怎么问用户 |
llm.ready |
本机有无对话模型 | 决定 Phase 2 走路 A 还是路 B |
> ⛔ 不要在体检失败时硬着头皮往下走。40164 就该先去加 IP 白名单,40013 就该重新复制 AppID。
Phase 1: 读取这个账号的记忆
$WXPOST style
输出三段东西:结构化偏好(调性、人称、句式、emoji 密度、标题风格、正文长度、标签数、配图数、封面比例、统一视觉风格)、规则清单(禁用词、惯用词、选题倾向)、历史笔记(用户过去明确要求过的)。
写文案和设计配图之前必须读过它。 用户亲口说过的偏好,优先级高于默认值,也高于你的判断。
Phase 1.5: 确认生图通道 🔴 CHECKPOINT
⛔ 必须问用户,不许自行假设。 CLI 探测不到宿主 Agent 自己有没有生图能力(Codex、WorkBuddy 内置的那种它看不见),所以只能问。
image.ready = false 时,用一句话给三选一:
> 配图这块你想怎么办?
> ① 我来出图 —— 你这环境自带生图能力的话,我直接生成好放进目录
> ② 配一个生图模型 —— 给中转站的 base_url、API Key、模型名(生图和写文案可共用一个 key),我写进配置后全自动
> ③ 你自己给图 —— 把图丢进 post/images/ 也行
| 用户选择 | 执行 |
|---|---|
| ① 宿主自带生图 | 用宿主能力出图,落到 post/images/01.jpg 起(编号即顺序)。不要调 wxpost gen |
| ② 配中转站 | $WXPOST init --image-only,按提示填,然后按 B 走 |
| ③ 手动给图 | 等用户放图,继续 Phase 2 |
image.ready = true 时也问一次,给默认值:
> 配图我直接用配置的模型({model})按方案出,还是你自己出 / 已经有图?
> 💡 生图要花钱,用户也常常早有图。别闷头就生成。
Phase 2: 内容生产
文案和配图方案必须并行产出,不许只写文案不设计配图。
路 A:配了对话模型(llm.ready = true)
$WXPOST scaffold --dir ./post --account <账号key>
$WXPOST write --dir ./post --brief "用 Claude Code 三个月踩过的 5 个坑" --count 9
write 会按账号风格一次生成 title / content / hashtags / image_prompts 并写入 post.json。要调语气加 --extra "语气更犀利,别写成教程"。
已有正文、只缺配图方案:
$WXPOST design --dir ./post --brief "主题" --count 9
路 B:没配对话模型(llm.ready = false)
你自己写,你本来就是模型。直接编辑 post.json,字段见「数据契约」。
image_prompts 必须按下面的配图设计规范写,不是随便丢几个关键词。
配图设计规范
一组贴图不是「同一种图做三张」,而是三个角色各司其职:
| 位置 | 角色 | 要什么 |
|---|---|---|
| 第 1 张(自动作封面) | 抢点击 | 强视觉冲击 + 图上直接放大字标题(3–12 字),浅色或高对比背景,主体明确,给文字留白 |
| 中间几张 | 讲信息 | 每张只承担一个信息点,形态轮换:信息图 / 对比图 / 步骤图 / 场景图 / 地图 / 清单 |
| 最后 1 张 | 收情绪 | 金句或行动引导,留白多,安静,不堆信息 |
硬约束,全部要写进提示词:
- 全系列统一配色、同一画风(插画或摄影二选一,不许混)、同一光源方向
- 图上中文 5–12 字,逐字用引号写进提示词,防模型自由发挥出乱码
- 不把正文原句照搬上图,提炼成关键词句
- 禁止真实品牌 logo、可辨认的真人肖像、复杂表格、超过 8 字的副标题
- 每条提示词写全:用途 → 画面主体 → 构图 → 风格 → 配色 → 光线 → 图上文字 → 比例 3:4
正文的 emoji 规范
贴图正文是在手机上快速滑动的,emoji 承担分段和提神的作用:
| 规则 | 说明 |
|---|---|
| 分点 | 用 1️⃣2️⃣3️⃣ 或 ✅❌⚠️💡,禁止 Markdown 的 - 和 1. |
| 段落开头 | 放一个相关 emoji,代替小标题 |
| 关键信息前 | 👉 💡 ❗ 🎯 📌 📢 |
| 密度 | 一行最多 1–2 个,不许连堆三四个 |
| 选型 | 只用常见 emoji,冷门符号在老安卓上显示成方块 |
| 底线 | 把 emoji 全删掉后正文仍要通顺——emoji 是调味不是主菜 |
密度由 style.emoji 控制,四档 none / light / medium(默认)/ heavy。
Phase 2.5: 故事板验收 🔴 CHECKPOINT
这一步不能跳。 一张九宫格把所有分镜缩略排在一起,让用户一眼看清结构、节奏、画风是否统一。
$WXPOST storyboard --dir ./post # 拆分镜 + 出九宫格
$WXPOST storyboard --dir ./post --no-image # 只出分镜表
$WXPOST storyboard --dir ./post --reuse-prompts # 不调对话模型,用现有 image_prompts 当分镜
产出三份,都在 post/storyboard/:
| 文件 | 内容 |
|---|---|
storyboard.png |
九宫格预览(按张数自适应:≤3 排一行、4 排 2×2、5–6 排 2×3、7–9 排 3×3) |
storyboard.json |
分镜表,每格含 role / headline / scene / text_on_image |
storyboard_prompt.txt |
实际用的提示词,方便手改 |
检查点长这样:
┌────┬──────────┬──────────────────────────┬────────────────┐
│ # │ 角色 │ 画面 │ 图上文字 │
├────┼──────────┼──────────────────────────┼────────────────┤
│ 01 │ 封面 │ 女生骑车冲向桥,背后摄像头 │ 「48个抓拍点」 │
│ 02 │ 内容 │ 竖排时间轴,两个节点 │ 「11月21日启用」│
│ 03 │ 内容 │ 五个圆形图标横向排列 │ 「都会拍」 │
│ … │ … │ … │ … │
└────┴──────────┴──────────────────────────┴────────────────┘
必须把故事板图和分镜表都摆给用户,明确问:「结构和风格对不对?要改哪一格?」不要自己看一眼就往下走。
按反馈改:
| 用户说 | 怎么改 |
|---|---|
| 第 3 格不对 | 编辑 storyboard.json 那一格的 scene / text_on_image,跑 storyboard --reuse-prompts |
| 整体方向不对 | 改 post.json 的 brief 或 image_prompts,重跑 storyboard |
| 风格不统一 | $WXPOST profile set layout.image_style "…" 后重跑 |
| 格子糊 / 页码乱 | 正常现象,故事板只看结构,字看不清不影响后面逐张出图 |
张数 ≤4 或用户明确说「不用看草稿直接出」可以跳过,其余情况都该走一遍。
Phase 3: 逐张生成正式图
$WXPOST gen --dir ./post
按 image_prompts 逐张出图到 post/images/,自动叠加账号记忆里的统一视觉风格。已存在的图跳过,--force 覆盖。
出图后自己看一眼(能读图就用 Read 打开),逐项过:
- [ ] 图上文字有没有乱码、缺笔、变成英文字母
- [ ] 画风和故事板是否一致
- [ ] 封面的字够不够大,手机上能不能看清
- [ ] 九张里有没有连着几张构图雷同
不合格就改对应那条 image_prompts,--force 只重跑那一张。
> 用户选了「我自己出图」就跳过这条,但仍要把 image_prompts 清单和故事板交付给他。
Phase 3.5: 发布前质检
$WXPOST publish --dir ./post --dry-run
本地校验:标题是否 ≤32 字、正文是否 ≤2 万字、比例是否合法、图片是否超 20 张、压缩后是否 ≤1.8MB。全部通过才进 Phase 4。
Phase 4: 建草稿
$WXPOST publish --dir ./post
成功会打印 media_id,让用户去「内容与互动 → 草稿箱」预览。
只有用户明确说「直接发出去」「不用我审了」才加 --submit:
$WXPOST publish --dir ./post --submit
⛔ 执行前必须复述一遍「发布后不可撤销,会推给全部粉丝」,等用户点头。
Phase 5: 记忆沉淀
发布完问一句:「这版风格要固定下来吗?有什么下次要改的?」
| 用户表达了 | 命令 |
|---|---|
| 模糊的风格偏好 | $WXPOST profile learn "标题别用问句,显得软" |
| 能落到字段的 | $WXPOST profile set style.emoji heavy |
| 配图视觉统一 | $WXPOST profile set layout.image_style "扁平插画,低饱和蓝绿" |
| 禁用词 | $WXPOST profile set rules.banned_words "赋能,抓手,闭环" |
可用字段:$WXPOST profile fields。全部记忆:$WXPOST profile show。
> 用户只要表达了偏好就立刻写进去,别等下次。这是这个 skill 越用越顺手的原因。
数据契约
post.json
{
"brief": "这次要讲什么",
"title": "标题,≤32 字,超了直接失败",
"content": "正文纯文本,可分段。不要 Markdown 标题和加粗语法。",
"hashtags": ["AI编程", "#效率工具"],
"author": "作者名",
"cover_ratio": "1_1",
"image_ratio": "3:4",
"image_prompts": ["第1张(封面)的完整提示词", "第2张的提示词"],
"images": [],
"need_open_comment": 1,
"only_fans_can_comment": 0
}
| 字段 | 必填 | 说明 |
|---|---|---|
brief |
否 | 选题原话,write / storyboard 会用 |
title |
是 | 微信硬限 32 字,超了建草稿会失败 |
content |
是 | 纯文本,Markdown/HTML 会被自动剥离 |
hashtags |
否 | 自动拼到正文末尾,带不带 # 都行 |
cover_ratio |
否 | 只支持 1_1 / 16_9 / 2.35_1 |
image_ratio |
否 | 图片裁切比例,默认 3:4 |
image_prompts |
否 | gen 用;条数 = 图片数 |
images |
否 | 留空则自动扫 images/ 目录,按文件名排序 |
目录结构
post/
├── post.json 标题/正文/标签/比例/配图提示词
├── images/ 原图,01.jpg 起,第一张即封面(不会被改动)
├── storyboard/ 九宫格预览 + 分镜表 + 提示词
├── reference/ 原文参考素材(from-url 产生)
└── .build/ 处理后的成品,自动生成
小绿书 vs 小红书
都是「图为主 + 短文案 + #标签」,但版式结构完全不同,照搬一定翻车:
| 小红书 | 微信公众号图片消息 | |
|---|---|---|
| 图文关系 | 图文交错,一图配一句 | 图在前、文在后:顶部多图轮播,下方一整段正文 |
| 正文 | 每张图下挂一小段 | 一段完整可读的纯文本,不能和图片穿插 |
| 排版能力 | 支持较多格式 | 只允许纯文本,Markdown/HTML 会被剥掉 |
| 标题 | 20 字内,可带 emoji | ≤32 字硬限,建议 ≤20 字 |
| 图片 | 最多 18 张 | 最多 20 张,首张即封面 |
| 比例 | 3:4 竖图 | 3:4 竖图最合适;封面裁剪只支持 1_1 / 16_9 / 2.35_1 |
所以不要写「图 1:…、图 2:…」的交错结构。正文要独立成篇,读完能懂。
报错与降级
| 现象 | 原因 | 一线修复 | 仍失败兜底 |
|---|---|---|---|
40164 |
IP 不在白名单 | 把报错里的 IP 加进后台「基本配置 → IP 白名单」 | 换网络环境 |
40013 / 40125 |
AppID / AppSecret 错 | 重新复制,别带空格 | 重置 AppSecret(只显示一次) |
48001 |
接口未授权 | 确认是认证的订阅号/服务号 | 未认证号只能手动发 |
45003 |
标题超 32 字 | 改短 | CLI 已在发布前拦截 |
45001 / 40006 |
图片超 2MB | CLI 已压过,换图 | 降低分辨率重出 |
40007 |
media_id 无效 | 图片没传成功,重跑 publish |
检查图片是否被占用 |
45009 |
当日配额用尽 | 等次日 | 别拿测试图反复传 |
| 生图 401/403 | Key 被拒 | 检查 api_key,注意 sk- 前缀 |
换 key |
| 生图 404 | base_url 拼错 | 通常多了或少了 /v1,CLI 会打实际请求地址 |
对照中转站文档 |
| 生图 400 size | 尺寸不支持 | 已自动降级 1024x1792 / 1024x1024 | 换模型 |
| 写文案 404 | 中转站不提供 chat | 只配生图模型 | 文案改由 Agent 写 |
write/design 返回 3 |
没配对话模型 | 不是错误,按提示自己写 | — |
两个容易忽略的坑:
- 重复草稿:
draft/add没有幂等。同一份post.json再发会多一条,CLI 会拦下来问。要覆盖先$WXPOST draft delete <media_id>。 - 故事板别放 images/:会被当成正文图一起上传,草稿里多一张九宫格。CLI 默认放
storyboard/,别手动搬。
品味守则
| 原则 | 一句话 |
|---|---|
| 先生成结构,再生成内容 | 故事板比九张精修图便宜得多 |
| 配图要能删掉任意一张而不影响阅读 | 说明每张都承担独立信息,不是凑数 |
| 封面字必须能在手机上一眼读清 | 缩到 200px 宽还看不清就是失败 |
| 正文别写成文档 | 短句、口语、分段,像说话不像写报告 |
| 报错要说人话 | 用户不关心 errcode,只关心下一步做什么 |
| 不确定就问,别猜 | 猜错一次的成本远高于问一句 |
❌ 反模式黑名单
| # | 反模式 | 为什么不行 | 换成 |
|---|---|---|---|
| 1 | 没问用户就闷头生图 | 花钱,且用户常已有图 | Phase 1.5 先问 |
| 2 | 正文里写「图1:…图2:…」 | 微信图片消息图文不交错,会变成一堆废话 | 正文独立成篇 |
| 3 | 九张图用同一个提示词改个数字 | 翻页像卡带,用户划两张就走 | 按三角色分工 |
| 4 | 跳过故事板直接出图 | 方向错了要重出全部 | Phase 2.5 必须先确认 |
| 5 | 用 Markdown 写正文 | 会被剥离,白写 | 纯文本 + emoji 序号 |
| 6 | 标题写到 40 字再说 | 微信直接拒收 | 生成时就卡 32 字 |
| 7 | 用户没说就加 --submit |
不可撤销,等于替他发了 | 默认只建草稿 |
| 8 | 图片文件名用中文或空格 | 排序错乱,封面跑偏 | 01.jpg、02.jpg |
| 9 | 忘记读 style 就写文案 |
每个号的调性不同,写出来不像这个号 | Phase 1 必读 |
| 10 | 用户说了偏好却不当场记下来 | 下次还得再说一遍 | Phase 5 立刻 profile learn |
特殊场景
只有图片,文案已定
跳过 Phase 2 的文案部分,直接编辑 post.json 的 title / content,图片丢进 images/,从 Phase 3.5 开始。
从 Markdown 转换
$WXPOST from-md ./笔记.md --dir ./post
自动抽 H1 当标题、抽 #标签 到 hashtags、剩余正文剥掉 Markdown 标记。然后补图片,从 Phase 3.5 继续。
拿到一条链接但抓不到
小红书这类反爬平台经常抓不下来。按这个顺序降级,每一步都不许编内容:
- 换
xhslink.com短链再试一次(短链跳转后偶尔能拿到) - 让用户把正文直接粘过来,走
from-md - 让用户截图或导出图片,放进
post/images/,走 Phase 3.5 - 都失败就如实说抓不到,别用"我记得这篇讲的是…"来凑
多账号
accounts 下每个 key 一个号,命令加 --account 指定;不加则用 active_account。每个号有独立的 profiles/<账号>/profile.json,互不干扰。
用户想直接发出去
不要直接执行。先做三件事:复述「不可撤销、推给全部粉丝」→ 确认目标账号 → 等明确点头。然后再 --submit。
生图模型能力有限
只支持 1024x1024 这种方形也没关系,CLI 会居中裁成 3:4。但裁掉的两侧别放重要信息,提示词里说明「核心内容居中,两侧留空」。
最后
贴图这门手艺,本质上不是画画,是替读者做减法:九张图里哪句话该上图、哪句话该留在正文、哪一格该留白,全都是在决定「读者会记住什么」。
工具能帮你把管道铺好、把格式踩对、把凭证配齐。但「这一格要不要放那个数字」——那还是你的判断。
结构可以自动化,取舍不行。
关于作者
邱高华,全栈开发工程师,广州。仓库:https://github.com/whishi47/wechat-sticker
用户主动问起作者时再展开介绍;正常任务中不主动提。
版本检查
本 skill 的版本记录在 frontmatter 的 metadata.version。若本目录是 git 仓库,可这样看有没有新版本:
git -C ~/.wxpost ls-remote --tags --refs --sort=-v:refname origin 'v*' | head -3
有更新时只提示,不自动执行——是否升级由用户决定。
想关掉这个检查,在本目录建一个 .last-update-check 文件,内容写 off。