微信公众号图文生成与发布
> 当前版本:v2.4(多账号矩阵 + 文末推荐发布时间 + 去AI化/观点注入)
> 历史:v2.3 多账号矩阵 / v2.2 智谱LLM / v2.1 外部选题 / v2.0 全自动流水线 / v1.x 单账号
安装与安全(v2.4 特别说明)
- 本技能不包含任何真实公众号凭证,
config.example.json中的 appid/appsecret 均为空占位。 - 使用前把
config.example.json复制为config.json,填入你自己的 AppID/AppSecret 后再运行。 - 多账号玩法:
config.json的accounts数组可配置多个公众号,发布时用--account <id>选择。 - 建议将含真实凭证的
config.json加入.gitignore/ 排除在分发包之外,避免泄露。
Overview
本技能把三件事合并成一个可复用流程:写文章 → 做配图 → 发草稿。
- 写文章:根据用户提供的话题(及可选的风格、目标受众、字数要求),用自然语言生成一篇结构完整、符合公众号阅读习惯的图文,并输出为带排版的 HTML。
- 做配图:把文章的关键画面抽象成图像提示词,调用一个兼容 OpenAI 图像接口协议的 HTTP 端点生成图片(封面图 + 可选正文插图)。端点 URL 和 key 由用户配置,不内置任何厂商依赖。
- 发草稿:用用户提供的
AppID + AppSecret自动换取access_token(带本地缓存),上传封面素材换取thumb_media_id,把正文插图上传换取微信内链,最后调用draft/add接口写入草稿箱。
> 所有网络调用都封装在 scripts/ 里,SKILL.md 负责告诉模型「什么时候、按什么顺序」调用它们。文章正文由模型自身生成(你就是写手),图片与发布由脚本完成。
配置(首次使用前设好)
通过环境变量或脚本参数传入,推荐用环境变量:
| 变量 | 说明 | 示例 |
|---|---|---|
WECHAT_APPID |
公众号 AppID | wx1234567890abcdef |
WECHAT_APPSECRET |
公众号 AppSecret | a1b2c3d4... |
LLM_API_URL |
写作 LLM 基址(OpenAI /chat/completions 协议,推荐智谱) |
https://open.bigmodel.cn/api/paas/v4 |
LLM_API_KEY |
写作 LLM key | xxxx.yyyy |
LLM_MODEL |
写作模型名(默认 glm-4-flash) |
glm-4-flash |
IMAGE_API_URL |
图像生成接口基址(兼容 OpenAI /images/generations) |
https://api.openai.com/v1 |
IMAGE_API_KEY |
图像接口鉴权 key | sk-... |
IMAGE_MODEL |
模型名(可选,默认 gpt-image-1) |
gpt-image-1 |
> 写作模型默认用智谱 GLM-4-Flash(完全免费,中文优化好,兼容 OpenAI 协议)。
> 到 https://open.bigmodel.cn/ 注册,控制台「API Keys」创建 key 填入即可。
> 未配 LLM_API_KEY 时,写作由对话模型直接生成 HTML(默认方式),两者并存可随时切换。
依赖:pip install requests(脚本仅用标准库 + requests)。
工作流决策树
用户给主题(及可选参数)?
├─ 是 → 进入「生成流程」
└─ 否 → 向用户索要主题/关键词,再继续
生成流程:
1. 写文章 → 模型生成标题/摘要/正文 HTML(见下)
2. 生成配图 → generate_image.py(封面 + 正文插图)
3. 组装 HTML → 把图片嵌入排版模板 assets/article_template.html
4. 发布草稿 → publish_pipeline.py(封面上传 + draft/add)
用户已有成稿?
├─ 是 → 跳到第 2 步,直接用现成图文
└─ 否 → 从第 1 步开始
1. 生成文章
根据主题生成一篇公众号图文,质量要求:
- 标题:有吸引力、信息明确,18 字以内优先。
- 摘要(digest):一句话钩子,40 字以内,用于草稿列表预览。
- 正文:用
assets/article_template.html的 CSS 类排版(.lead导语、.h2小标题、.tip提示框、.figure图文)。段落短、多用小标题。 - 配图提示词:从正文提炼 1 张封面图 prompt + 0~N 张正文插图 prompt,描述要具体(风格、主体、构图、色调)。
文章正文以 HTML 字符串或文件形式交给后续步骤。可让用户选风格/受众/字数,缺省时自主决定。
2. 生成配图(generate_image.py)
为每张图调用脚本,封面必须生成:
python scripts/generate_image.py \
--prompt "国风插画,山水之间一座发光的数据灯塔,青绿与暖金色调,扁平矢量风" \
--output cover.png --size 1024x1024
正文插图同理,输出到不同文件。脚本会把返回的图片(base64 或 url)保存为本地文件,并打印最终路径。
3. 组装 HTML
把文章正文与图片合成一篇完整 HTML:
- 封面图在发布阶段单独上传,无需放进正文 HTML。
- 正文插图:在 HTML 中用
<img src="/abs/path/to/inline.png">(本地绝对路径)或占位符{{IMG:/abs/path/to/inline.png}}标记;publish_pipeline.py会自动上传并替换为微信内链。
可用 assets/article_template.html 作为排版骨架,填入标题、摘要占位与正文。
4. 发布到草稿(publish_pipeline.py)
端到端串联:发布前查重/爆款检测 → 上传封面拿 thumb_media_id → 上传正文插图拿内链 → 调用 draft/add。
python scripts/publish_pipeline.py \
--title "当 AI 学会写诗" \
--author "你的名字" \
--digest "一篇关于生成式模型的轻科普" \
--html article.html \
--cover cover.png \
--keywords "AI,生成式模型,写作" \ # 可选:用于查重
--compare-text old_article.md \ # 可选:对比文本/历史文章,用于查重
--appid "$WECHAT_APPID" --appsecret "$WECHAT_APPSECRET"
成功返回 media_id(草稿 ID)。脚本已内置 token 缓存(~/.cache/wechat_token.json),2 小时内复用。
加 --skip-check 可关闭发布前的查重/爆款检测。
> 仅写入草稿箱,不会群发。用户需在公众号后台确认并群发。
5. 发布前查重与爆款优化(v1.3 新增)
publish_pipeline.py 发布前会自动调用 optimize_article.py 做两件事:
- 查重(本地启发式,无需 API):对比标题/正文与
--keywords、--compare-text的相似度,
识别高重复句子并给出改写建议。风险等级分 低/中/高。 - 爆款优化:
- 规则引擎逐项检查:标题长度/吸睛要素(数字·疑问·情绪词)、开头钩子、小标题结构、
段落长度、总字数、信息密度,每项给状态与建议; - 输出爆款潜力分(0-100);
- 生成多版备选标题 / 摘要 / 开头供挑选。
也可单独运行查看完整报告:
python scripts/optimize_article.py --html article.html \
--title "原标题" --keywords "词1,词2" \
--compare-text other.md --output report.json
> 说明:公众号没有公开的免费全文查重 API;本技能采用本地相似度启发式。
> 若需更严格的原创查重,可在公众号后台「原创声明/群发」时由微信平台判定,
> 或后续对接第三方查重服务。
6. 多账号矩阵玩法(v2.3 新增)
同时运营多个公众号(如"车策局"汽车号 + "搞钱研究屋"副业号)时,可在配置文件里配置
accounts 数组,发布时用 --account 指定发到哪个号。token 按 appid 隔离,互不冲突。
配置多账号(config.example.json 示例)
{
"accounts": [
{
"id": "che", "name": "车策局", "type": "汽车",
"appid": "wx...", "appsecret": "...", "author": "车策局",
"default_time": "工作日 20:30",
"keywords": ["购置税", "新能源", "导购"]
},
{
"id": "fuye", "name": "搞钱研究屋", "type": "副业",
"appid": "wx...", "appsecret": "...", "author": "搞钱研究屋",
"default_time": "工作日 20:30",
"keywords": ["副业", "避坑", "搞钱"]
}
]
}
字段说明:
- id:账号唯一标识(发布时用);name:公众号名;type:类型(决定推荐发布时间,如"汽车"/"副业")
- appid/appsecret:公众号凭证;author:作者;default_time:默认发布时段
- keywords:该账号的选题关键词池
> 旧版单账号字段 wechat.appid/appsecret 仍兼容:未配置 accounts 时自动包裹为一个账号。
用 --account 发布到指定账号
# 直接发布到"搞钱研究屋"(自动用该账号凭证 + 自动推荐发布时间)
python scripts/publish_pipeline.py \
--title "副业避坑第一课" \
--html article.html --cover cover.png \
--config config.json --account fuye
# 查看/选择账号
python scripts/account.py --config config.json --list
python scripts/account.py --config config.json --account fuye
# 测试某账号对某文章的推荐发布时间
python scripts/account.py --config config.json --account fuye \
--check-pubtime --title "副业避坑" --html article.html
文章结尾自动推荐发布时间
content_pipeline.py 支持 --pubtime-in-body:发布时按「账号类型 + 文章类型」动态计算
推荐发布时间,追加到正文末尾(读者可见),并在终端同步打印提醒运营者。
# 全自动流水线,发布到副业号,并在文末追加推荐发布时间
python scripts/content_pipeline.py --config config.json \
--account fuye --topic "副业避坑清单" --pubtime-in-body
推荐时间规则(account.py 内置):
- 汽车号:行业分析→工作日20-22点;新闻速报→早7:30-8:30;导购→晚+周末
- 副业号:干货→工作日20:30;互动→周末上午
- 通用兜底:工作日20:30
Resources
scripts/
generate_image.py:调用图像生成接口生成图片。支持两种后端——
1. 配置了IMAGE_API_URL+IMAGE_API_KEY时用兼容 OpenAI/images/generations协议(OpenAI、智谱、通义万相、硅基流动等);
2. 未配置时自动降级到 Pollinations.ai 免费接口(无需 key,偶发 500)。make_charts.py(新增 v1.2):本机绘制配图——用 matplotlib 生成封面 + 车路云示意图 + 销量对比图。完全无需 API key,中文正常、内容严格贴合文章,是零成本最稳的兜底路径。优先推荐使用。wechat_publisher.py:微信封装模块——get_access_token()(按 appid 隔离 token 缓存 + IP 白名单自动重试)、upload_cover()(永久素材)、upload_inline_image()(临时素材,返回内链)、add_draft()/delete_draft()/get_draft()。account.py(新增 v2.3):多账号矩阵管理——读取/选择/列出多个公众号账号(兼容旧单账号),按「账号类型+文章类型」动态推荐发布时间,并可在文末追加发布时段文案。publish_pipeline.py:端到端 CLI,串联发布前查重/爆款检测 + 封面上传 + draft/add。optimize_article.py(新增 v1.3):发布前查重 + 爆款优化——本地相似度查重、
爆款规则引擎(标题/钩子/结构/字数等)、多版本标题/摘要/开头生成、爆款潜力分。content_pipeline.py(新增 v2.0):内容全自动流水线编排——把「选题→AIGC写作→
自动配图→查重/爆款→进草稿」串成一条命令(--only可只跑某阶段)。
写作需配LLM_API_URL/KEY;配图默认走本机make_charts.py;发布仅进草稿箱。topic_api.py(新增 v2.1):外部选题数据源适配层——对接 5118 / 新榜关键词指数,
统一返回{keyword, heat}热度候选,供topic_select做选题打分;
未配置 key 或调用失败时自动降级本地评分。feedback.py(新增 v2.0):数据回收与反哺分析——把群发后的阅读/在看/分享
数据(人工从后台填入feedback.json)聚合分析,找出高潜选题与高分享标题,
反哺选题关键词池和标题库,形成「越写越懂」的飞轮。
配置模板
config.example.json:content_pipeline的配置模板(公众号凭证、LLM、图像、选题关键词池、外部数据源 API 预留)。feedback.example.json:数据回收表模板。
references/
wechat_api.md:微信草稿相关接口的字段、限制与常见报错(token 有效期、封面必须是永久素材、正文图片用uploadimg内链等)。image_api.md:通用图像接口的请求/响应协议,以及如何接 OpenAI、通义万相等不同后端。
assets/
article_template.html:公众号图文 HTML 排版模板(内联 CSS,适配微信编辑器)。
注意事项
- 草稿 ≠ 群发:本技能只写草稿箱,绝不自动群发。
- 封面必须是永久素材:脚本用
material/add_material上传封面;正文插图用media/uploadimg拿内链。 - 外部选题数据源(5118 / 新榜):在
config.example.json的topic.api_mode选
xlb(5118)或newrank(新榜),并把topic.api_key填成对应平台控制台 key。 - 新榜:
api.newrank.cn,「关键词搜索公众号」接口,HeaderKey: {key},
用返回的weekNri(新榜指数)/favorite(收藏数) 做热度,并回填 top 公众号 tags 作为选题标签。 - 5118:
api.5118.com,「关键词搜索量信息APIv2」/「海量长尾词挖掘APIv2」,
鉴权 HeaderX-Key: {key},返回关键词指数/搜索量。 - 未配 key、接口报错或余额不足时自动降级本地评分,不会中断流水线。
- 图片生成:推荐优先用
make_charts.py本机绘制(无需任何 key,内容可控、中文正常);
其次用generate_image.py配IMAGE_API_URL+IMAGE_API_KEY走付费接口;
实在没有 key 时,generate_image.py会自动降级到 Pollinations 免费接口(稳定度差,仅临时用)。 - 凭证安全:
AppSecret与图像/数据源API key属敏感信息,不要写入仓库或日志。
已踩坑与规避(v1.1 优化记录)
以下问题在真实跑通过程中被发现,已在 scripts/ 中修复/规避:
| 问题 | 现象 | 规避方案 |
|---|---|---|
中文变成 \uXXXX 乱码(核心) |
草稿箱里正文显示 1300\u4e07\u8f86... 而非"1300万辆…" |
根因是 requests.post(json=...) 触发 json.dumps 默认 ensure_ascii=True,中文被转义。已改为 json.dumps(payload, ensure_ascii=False).encode('utf-8') + 显式 Content-Type: application/json; charset=utf-8。 |
| 读取响应误判乱码 | 用 r.json() 读草稿时中文变成 1300ä¸è¾ |
响应头缺 charset 时 requests 默认按 Latin-1 解析。统一用 r.content.decode('utf-8') 手动解码(见 _get_json/_post_json)。 |
| IP 白名单 40164 | 换 token 报 invalid ip xxx not in whitelist |
动态出口 IP 会漂移。get_access_token 遇 40164 自动重试最多 30 次(间隔 0.4s),抢在漂移间隙成功;仍失败则提示去后台加白名单。 |
| 作者名 45110 | 传中文 author 报 author size out of limit |
publish_pipeline 捕获 45110 自动降级为空 author 重试,并提示发布后在公众号后台手动补填作者。 |
{{IMG:...}} 占位误触发 |
HTML 注释里的示例占位被正则匹配,报"正文插图不存在"中断发布 | replace_inline_images 只在本地文件真实存在时才上传替换;不存在的占位直接跳过保留,不再中断整篇发布。 |
| 草稿重复 | 反复发布产生多条相同草稿 | 新增 delete_draft(),发布前可清理旧草稿;也提供 get_draft() 用于发布后验证内容。 |
验证建议(发布后必做)
发布成功后,用 get_draft() 读取草稿内容并按 UTF-8 检查:
- 标题/摘要/正文是正常中文(无 \uXXXX、无 ä¸ 类 mojibake);
- 若发现乱码,说明发送端编码有问题,先确认 add_draft 使用 ensure_ascii=False。