wechat-article-pipeline について
wechat-article-pipelineは、完成済み記事をWeChat公式アカウントの下書きに保存するための定型処理を自動化します。アカウント確認、レンダリング、画像アップロード、下書き作成を行い、本文の執筆や一斉配信は行いません。
主な機能
- 01処理前の対象アカウント確認
- 02MarkdownからWeChat HTMLへの変換
- 03ローカル画像の前処理とアップロード
- 04カバー画像と挿絵の生成
- 05記事を下書きとしてのみ保存
- 06下書きのmediaIdと処理情報を返却
活用シーン
- 01完成した記事を公式アカウントの下書きに保存
- 02Markdownを変換してローカル画像を準備
- 03下書き用のカバー画像や挿絵を作成
- 04下書きの保存先アカウントを確認
MCP 対応
このスキルはDOUBAOYA_API_KEYを使ってdoubaoya.comの機能エンドポイントを呼び出します。READMEにはMCPサーバーまたはMCPクライアントのインターフェースは記載されていません。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ zizhanovo/ doubaoya-community --skill wechat-article-pipelineインストール後、Claude Code で/wechat-article-pipelineを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
.versionconfig.example.jsonconfig.example.README.mdLICENSEpipeline.jsonREADME.mdassets/ip/README.mdassets/styles/index.jsonprofiles/example-ip.jsonREADME.mdschemas/design-config.schema.jsonscripts/account-verify.mjsdesign-studio.mjsdesign-studio.page.htmlextract-theme.mjsfetch-article.mjsgen-image.mjsimport-theme.mjspipeline.mjsplan-figures.mjspreprocess-and-publish.mjsrender-wechat-html.mjsselfcheck-remote-theme.mjsvalidate-theme.mjsthemes/benya-clean.jsonbenya-premium.jsonCREDITS.mddoocs-classic.jsondoocs-grace.jsondoocs-simple.json詳細
公众号图文流水线(都爆鸭)
本鸭帮你把一篇已经写好的图文,走一串确定性的机械步骤,最终存进你自己公众号的草稿箱——
只存草稿,绝不群发。存完给你 mediaId,你再去公众号后台亲眼确认、手动群发。
📍 接的是哪一棒:用户说「帮我写一篇公众号文章」时,正文先由你(agent)写 (选题、写作、起标题、封面套路、合规检测现在都由
doubaoya按意图路由承接)。 正文落地之后再看用户要的终态: 只要成稿就到那里为止;要排版好的公众号 HTML 或要文章进自己的草稿箱,才回到这里。 用户没表达过后一种意图时先问一句——这一步会写进他自己的公众号后台。
⚠️ 写入能力:会写到你自己的公众号后台。所以只做「存草稿」这一步,群发的手一定在你自己。 走 doubaoya.com 一条线,鉴权用你自己的密钥
DOUBAOYA_API_KEY(形如dyh_…)。
分工:正文由你(agent)依需求撰写;本流水线不代写正文,只自动化后续那些确定性的运维步骤 (校验账号、渲染、传图、存草稿)。
单一事实源:pipeline.json
10 步 SOP 与全部硬规则声明在 pipeline.json(steps[] + hardRules[])。
本 SKILL.md 与编排脚本 scripts/pipeline.mjs 都以它为准——改流程先改 pipeline.json,别在各处硬编码。
其中第 6 步「引导式设计」由 agent 执行(选风格 / 生封面 / 生配图 / 排版确认,见下方引导式设计), 它把产出(
--cover本地封面 + Markdown 里的本地<img>)喂给后面的机械步骤;pipeline.mjs本身仍是渲染→传图→存草稿的确定性执行器。
10 步 SOP
- 识别任务类型 — 确认是「把已写好的文章推进公众号草稿箱」。
- 读取身份上下文 — 加载并回显 IP/身份 profile(名称 / 别名 /
isNot消歧 / 语气)。 - whoami 校验账号 —
GET /api/agent/whoami,把本地 key 解析成目标账号那一条(key 只在内存)。 - 草稿前置检查 —
GET /api/skills(断言wechat-draft-publish存在)+GET /api/wechat/status(确认公众号、解析 appid/昵称)。 - md→HTML —
--md时渲染成公众号内联样式 HTML(原样保留<img src>);--html时直接用。 - 引导式设计 — 选风格 → AI 生封面(
--cover-guard,1536x1024)→ 生配图(1024x1024,落进 Markdown 源后回到第 5 步重渲染)→ 排版确认。引导默认,「你全权定」是逃生舱。见下方引导式设计。 - 图片预处理 — 扫描
<img>,本地图片客户端预上传到图床(>1MB 先压缩)并改写 HTML;外链原样保留。 - 封面 — 本地封面作为 thumb 预上传;没有则走都爆鸭兜底封面。
- 保存草稿 —
POST /api/wechat/publish(draft/add)。 - 验证回报 — 标题 / 公众号 / 正文图上传数 / 封面 / 使用风格 / mediaId / 群发:否。
硬规则(hardRules,代码里强制)
- 只存草稿绝不群发 — 没有任何群发路径;流水线拒绝任何
--mass-send/--broadcast/群发 参数。 - 发布前必须 whoami 校验目标账号 — 第 3 步不过,第 8 步不跑。
- 先加载身份上下文再做内容判断。
- 发现走
/api/skills,执行走/api/wechat/status+/api/wechat/publish,不走/invoke。 - 本地图片必须客户端预上传(服务端读不到你本机的文件)。
呼び出しは全部このプロトコルで(doubaoya-gateway から転載)
この Skill で使う機能(機能名と詳細エンドポイントのみを記載します。入力パラメーターはここには記載せず、呼び出しのたびに取得します):
| operationKey | 詳細エンドポイント | 使用するステップ |
|---|---|---|
skill.wechat.render ⚠️専用 | GET /api/skills/wechat-render | 第 5 ステップ md→HTML(サーバー側で組版する経路) |
skill.ai.imageGen | GET /api/skills/gpt-image-gen | 第 6 ステップ カバー画像/挿絵の生成(scripts/gen-image.mjs はこの薄いラッパーです) |
skill.wechat.draftPublish ⚠️専用 | GET /api/skills/wechat-draft-publish | 第 9 ステップ 下書きの保存 |
- 認証:すべての呼び出しエンドポイントで
Authorization: Bearer $DOUBAOYA_API_KEYが必要です。 まず環境変数DOUBAOYA_API_KEYを読み込みます。環境にない場合は、ユーザーに一度だけ尋ねます。以降は再度尋ねません。 🔴 一文字も表示、出力、ログへの記録をしてはいけません。プレフィックスもキーの一部です。 状態を報告する場合は 「設定済み/未設定」とだけ伝え、途中までの形式(${KEY:0:6}のような書き方もキーの出力にあたります)で表示してはいけません。 ベース URL はhttps://doubaoya.comです。 - 先に仕様を取得してから、パラメーターを組み立てる:
GET <詳細エンドポイント>(認証不要、無料)。inputContract→inputUiSchemaのfields→requestSchema(例示値であり、仕様ではありません)の順に確認し、最初に取得できた情報を使って止めます。 🔴 記憶や本文にあるフィールド名をもとに入力パラメーターを組み立ててはいけません。ここにフィールド名を一切書かないのは、転記できないようにするためです。 execution.targetの指示どおりに呼び出し、URLを自分で組み立てないでください:同じ詳細レスポンスにexecution.target.methodとexecution.target.pathが含まれており、ベース URL の後ろに付けたものが呼び出し先です。execution.modeがdedicatedの場合、メソッドは必ずしもPOSTではありません(PUTの場合もあります)。unavailableの場合はtargetがなく、呼び出してはいけません。この機能は現在利用できないと、そのままユーザーに伝えてください。 ⚠️ 上記 3 つのうち 2 つは専用ルートです。詳細エンドポイントとは呼び出し先がまったく関係なく、推測できません。targetを読む以外に方法はありません(これがこの Skill の厳格なルール「/invokeを使わない」の理由です)。- 2 つのルートは互いにフォールバックしません:
/api/skills/<slug>/invokeと/api/apis/<platform>/<slug>/callは重複しない別々の集合の入口です。違う集合の slug を使って呼び出すと、必ず 404 になります。方法を変えて再試行しても無駄です。 したがって、第 3 条は推奨ではありません。呼び出し先は詳細レスポンスからのみ取得してください。 - レスポンスのエンベロープを確認する:成功・失敗とも、同じ階層の
{ success, requestId, data, error }形式です。 まずsuccessを確認します。trueならdataを取得し、falseならerror.code/error.messageを確認します。 成功時のエンベロープには、次の任意フィールドが追加される場合もあります(含まれないのが通常で、異常ではありません):noResult:クエリは正しいものの、データがありません。課金対象外です。失敗として再試行せず、結果がないことをユーザーに伝え、条件の変更を提案してください。notice:この Skill に関する更新のお知らせです。そのままユーザーに伝えてください。今回の結果には影響しません。detailUrl:今回の結果の doubaoya.com 上の詳細ページです。ユーザーにリンクを案内できます。
- エラーへの対処(
HTTP/error.code): 401MISSING_API_KEY/UNAUTHORIZED→ ユーザーにキーセンターでキーを生成または再作成し、環境変数を更新するよう案内します。 400VALIDATION_ERROR→messageに従って入力パラメーターを修正し、修正前に仕様を再取得します。 400DEDICATED_ROUTE→ 汎用プロキシを誤って使用しています。messageに正しい呼び出し先が記載されているため、execution.targetに従って再送信します。 402INSUFFICIENT_CREDITS→ ユーザーにチャージを案内します。 404SKILL_NOT_FOUND/ENDPOINT_NOT_FOUND→ 第 4 条を参照し、もう一方の集合の検出エンドポイントを確認します。slug を推測してはいけません。 検出エンドポイントにも該当する機能がない場合、ローカルの Skill が古くなっている可能性が高いです(その Skill が指定する機能がすでに提供終了しています)。 ユーザーに「お使いの「本鸭」Skill は古くなっている可能性があります」と伝え、/dby-updateを一度実行するよう案内します(または「都爆鸭を更新」と伝えます)。 その後、この一度だけ再試行します。🔴 再試行しても 404 の場合は、機能が提供終了したことをそのまま伝えます。再度更新したり、ループしたりしてはいけません。 503CAPABILITY_UNAVAILABLE→ 再試行せず、別の機能に切り替えるか、利用できないことをそのまま伝えます。 502PROVIDER_FAILED→ 上流側の一時的な失敗です。クレジットは自動返却済みなので、そのまま再試行できます。 🔴 「先に更新して再試行」の手順を行えるのは、上記の 404 の場合だけです。それ以外のエラーを理由に更新してはいけません。 401 はキーの問題、400 は入力パラメーターの問題、402 は残高の問題であり、Skill を更新しても解決しません。
料金の数字はここには一切記載しません。 料金は予告なく変更されるため、ここに転記するとユーザーに誤った料金を伝えることになります。各機能の現在の料金は詳細レスポンスに記載されています。料金を伝える場合は、その場で取得して伝えてください。価格に左右されないこととして覚えておくのは 2 点だけです。下書きの保存と画像の生成は有料、サーバー側の組版レンダリングは無料です。有料の 2 つのステップを実行する前に、必ずユーザーに確認してください。
doubaoya-gateway を読むタイミング
上記のプロトコルだけで必要な情報はすべて揃っており、この Skill の 3 つの呼び出しを実行できます。通常の流れでは、ほかの情報を読む必要はありません。 ゲートウェイの Skill を読むのは、次の 2 つの場合だけです。
| 状況 | 読むもの |
|---|---|
| ユーザーの要望がこのパイプラインの 3 つの機能の範囲外で、別の機能を使いたい場合。まず利用可能な機能を確認する必要があります | doubaoya-gateway の references/capability-index.md(経路の選択専用。入力パラメーターは含まれません) |
| 機能を選んだ後、実際にリクエストを送る前に、その機能について既知の注意点を確認したい場合 | doubaoya-gateway の references/routing-pitfalls.md |
🔴 索引をこのファイルに転載してはいけません。機能一覧は週ごとに変わるため、転載したその日から古くなり始めます。
本文を書く前に
本文は**あなた(エージェント)**が執筆します。次の 2 点によって、実機での表示が決まるため、執筆前に確認してください。
🔴 本文にタイトルを書かないでください
公式アカウントは、下書きの title フィールドを使って記事ページの大見出しを必ず表示します。本文にも同じタイトルがあると、実機では2 回表示されます。
- Markdown で書く場合:本文は最初の段落から始め、見出しの最高レベルは
##までにします。タイトルは--titleパラメーターだけで指定し、本文には書かないでください。 - 完成した HTML を直接公開する場合(
--html):HTML の冒頭に<h1>(または大見出しとして使う<h2>/太字の大きな文字)を置かないでください。 この経路ではファイルがそのまま公開され、重複を取り除く処理はありません。 - レンダラーだけを実行する場合:
render-wechat-html.mjs --title "标题"は本文の先頭に**<h1>を挿入**します。これはローカルプレビューで 記事全体の仕上がりを確認するためのものです。この生成物をそのまま公開してはいけません。公開にはpipeline.mjsを使用してください。
pipeline.mjs --mdの経路では、元ファイル冒頭の frontmatter と単一の#見出しはnormalizeDraftMarkdownによって取り除かれ、--titleは本文に挿入されません。ただし、これは最終的な保護策であり、許可ではありません。本文中の 2 つ目のタイトルや、##でタイトルをもう一度書くことまでは防げません。
先拉一份写作规范:GET /api/wechat/writing-spec
✅ 接口已上线,正常拉取即可。拿到 401 说明
DOUBAOYA_API_KEY缺失或不对——提示用户检查 密钥配置,别跳过。只有遇到网络错误或真 404 时才降级:跳过这一步照常写 (上面那条 + 正文组件语法 已经够用),别死循环重试、别当故障报给用户。
写正文前拉一次,按它组织结构再动笔。它把「什么内容该写成什么 markdown 结构」和「平台会整篇打回 / 静默丢内容的硬约束」写成一段可直接照做的文字。
curl -sS https://doubaoya.com/api/wechat/writing-spec \
-H "Authorization: Bearer $DOUBAOYA_API_KEY"
只读、免费、不扣点(这条路径根本不进记账),也不改用户的任何配置。鉴权与其它接口一致
(Bearer 密钥,或网页端登录态);未鉴权 401。
成功信封的 data 里带一段 markdown 写作规范(要照着写的就是它)、这套排版的元信息,
以及去哪自定义排版的入口。具体有哪些字段照这一次的实际响应读,别照记忆或本文档读——
本文档故意不列字段表,理由见上面协议第 2 条。
没设置过排版的用户照样拿到可用规范(默认主题 + 只出「结构建议」那块),不返空、不报错; 响应会告诉你这份规范用的是不是默认排版,是的话把自定义入口转达给用户就好,别当成错误处理。
规范正文分两块,成立条件不一样:
- 结构建议 + 平台硬约束 —— 与排版无关,永远成立,照做。
- 本主题的呈现 —— 只在这篇真用服务端那套排版渲染时成立。本流水线在你没有显式钉本机主题
(不带
--theme,config.json也没把mdTheme写成路径)时,会用同一条DOUBAOYA_API_KEY自动拉取服务端默认排版的编译版(GET /api/wechat/theme?format=compiled)来渲染—— 此时第二块适用,照着写。显式--theme/config.mdTheme钉了本机主题,或没拉到服务端主题 而回退本机主题时,第二块不适用,只照第一块写(流水线日志会打出本次实际用了哪份主题)。
主题来源与优先级
渲染用哪份主题,按下面顺序取第一个成立的(详见 themes/README.md):
--theme <path|neutral>(含--design折算的主题)——显式指定,永远赢;config.json里显式写成路径的mdTheme——钉死本机主题,不发任何请求;- 服务端编译主题
GET /api/wechat/theme?format=compiled——你在 doubaoya.com 排版工作室设置的 默认排版(engine-2 主题服务端已编译成本机渲染器认识的全字面量形状)。拉取带 5s 超时, 拉不到就优雅回退:401 告警提示检查密钥(不中断),404/网络错误只打一句提示,不重试; - 项目默认主题
themes/benya-clean.json。
一处例外:规范里「
:::组件语法与> [!NOTE]提示块渲染器不解析、别写」那条,说的是服务端 渲染器。本流水线的render-wechat-html.mjs解析这两套语法(见下节),走本流水线时照常用。 排版改过之后重新拉一次——别把这段文字当长期事实缓存。
两条渲染路,判据是「有没有本机的东西要处理」
第 5 步 md→HTML 有两条路,不是随便挑一条:
| 什么时候走 | 走哪条 |
|---|---|
正文里有本机图片,或用了上面那套 ::: 组件语法,或用户没有密钥只想先看排版 | 本机渲染器 scripts/render-wechat-html.mjs(也就是 pipeline.mjs 内部走的那条) |
| 只是把一段干净 markdown 变成公众号 HTML,没有本机图片、没用组件语法 | 平台渲染能力 skill.wechat.render(详情端点 GET /api/skills/wechat-render),照上面协议第 2、3 条调 |
平台渲染那条不花钱,而且用的就是用户在 doubaoya.com 排版工作室设的那套排版,免去主题双源
对不上的整类问题;本机渲染器则是在服务端够不着的地方(本机文件、组件语法、无密钥)才不可替代。
⚠️ 它是专用路由:调用地址跟详情端点毫无关系,只能读详情响应里 execution 的 target。
正文组件语法(组件层)
正文 markdown 里可直接用下面这套组件语法,渲染时自动套用当前排版主题的配色(主色 / 标题色 / 正文色),不用手写 HTML:
:::关注卡…:::(别名:::follow)——引导关注卡片,卡内文案可自定义(留空用默认「点击上方名片,关注我们」)。> [!NOTE] 标题、> [!TIP]、> [!WARN]——三级提示框(信息 / 贴士 / 警示);标题可选,下面接正文。NOTE 用主题主色,TIP 绿、WARN 橙。:::金句…:::(别名:::quote-card)——居中大字金句卡,标题色 + 浅主题底。:::标题 小节标题(别名:::title)——带徽章 + 底部渐隐线的花式小标题。:::分割(别名:::divider)——居中小图标 + 两侧渐隐线的花式分割线。
示例:
:::关注卡
点击上方名片,关注我们
:::
> [!TIP] 小贴士
> 先定选题,再动笔。
:::金句
真正的高手,都在偷偷做时间的朋友
:::
:::标题 一、为什么
:::分割
未知组件名(如 :::xxx)不会报错——原样输出并给出提示。组件产出的 HTML 纯内联样式、无 class / id,符合公众号红线。主题 JSON 可选加 components 段覆盖内置模板(进阶)。
组合结构(不重复造轮子)
scripts/pipeline.mjs 是编排者,它组合三个零依赖模块:
| 阶段 | 模块 | 说明 |
|---|---|---|
| 账号解析 | scripts/account-verify.mjs | resolveAccountKey({account, baseUrl}):多来源(env / ~/.doubaoya / Keychain)候选 → 逐个 whoami → 按目标账号挑对 key,key 只在内存。多 key 指向不同账号且未指定 --account 时,报出各 key 对应账号并停。 |
| md→公众号 HTML | scripts/render-wechat-html.mjs | renderWechatHtml(md,{title}):零依赖内联样式渲染,原样保留图片 src。 |
| 封面/配图生图 | scripts/gen-image.mjs | generateImage({prompt,size,out,styleId,coverGuard,referenceImage}):零依赖,是能力 skill.ai.imageGen(详情端点 GET /api/skills/gpt-image-gen)的薄壳,同步返回、计费。传 referenceImage(本地路径/URL/data:/裸 base64,CLI --reference-image)时走 operation:"edit" 条件化,保留参考图里的 IP 形象;不传则文生图。另导出 resolveReferenceImage(ref)(本地图 → data: URL 小工具)。风格库 assets/styles/index.json,用 env DOUBAOYA_API_KEY(无需额外密钥)。产出本地 jpeg → 喂 --cover 或以 <img src> 落进正文,不碰发布契约。由 agent 在引导式设计里调用(不由 pipeline.mjs 机械触发)。 |
| 配图自动布局 | scripts/plan-figures.mjs | planFigures(markdown,{maxFigures,minChars}) → {figures[],meta}:确定性规则(不接 LLM)决定在哪些 h2 小节末尾配图 + 画面建议。按小节有效字数过阈值(默认 160)挑,张数按总字数分档(<1800→3、1800–3000→4、>3000→5)封顶。CLI node plan-figures.mjs --md <文章> [--max-figures N] [--min-chars N] [--json]。工作台「自动配图」调它,产出直接填 design-config.images[](afterHeading 锚点),由现有 pipeline 注入逻辑消费,不改发布链路。 |
| 传图 + 存草稿 | scripts/preprocess-and-publish.mjs | vendored 自 wechat-draft-publish skill(两份需保持同步)。本地图预上传 + >1MB 压缩 + 存草稿(draft/add,无群发)。 |
编排者把这三步串起来,并加上身份上下文加载、前置检查、硬门与结构化回报。
引导式设计(封面 / 配图 / 排版)
第 6 步——渲染前后完成视觉设计。引导是默认:在下面 4 处停下来问用户;逃生舱:用户若说
「封面配图你全权定 / 我赶时间」,就跳过所有停顿,用 config.defaultStyleId 自动出一版。
生图走能力 skill.ai.imageGen(详情端点 GET /api/skills/gpt-image-gen),无需额外密钥
(用发布本就在用的 DOUBAOYA_API_KEY)。想在对话里逐张生就用零依赖薄壳 scripts/gen-image.mjs,
缺密钥时它报清晰错误、不崩。这一步花钱,动手前先问用户(现价现拉,本文不写数字)。
- 选风格 — 把
assets/styles/index.json的 6 个风格(name+id)和各自样图assets/styles/<id>.jpg列给用户挑(或用户说「你定」)。6 个起手风格:杂志编辑风(magazine-editorial)、极简大字(minimal-bigtype)、真实摄影感(photo-real)、扁平插画(flat-illustration)、国潮中式(guochao-chinese)、商务信息图(biz-infographic)。 - 封面 — AI 读文章提炼一个封面概念(主体 + 氛围),用选定风格生 1 张
1536x1024,展示给用户 → 选 / 重生 / 自己传 / 用兜底。定了就设进--cover <本地jpeg>。封面必须加--cover-guard(把主体压在水平中带、上下留氛围背景,防公众号 2.35:1 居中裁切切掉关键内容):node scripts/gen-image.mjs --prompt "<封面概念>" --style <风格id> --cover-guard \ --size 1536x1024 --out <暂存目录>/cover.jpg - 配图 — 扫文章结构(一般每个
##小标题下 1 张),提议张数与各自画面,逐张生成1024x1024并以<img src=本地路径>落进 Markdown 源(不是渲染后的 HTML——放进源里才会被主题套上图注/圆角/间距)。
配图落进 Markdown 后回到第 5 步重渲染。这些本地图会被现有node scripts/gen-image.mjs --prompt "<该段画面>" --style <风格id> \ --size 1024x1024 --out <暂存目录>/fig1.jpgpreprocess-and-publish.mjs走image上传, 无需改动任何发布链路。 - 排版 — 确认用哪套主题(默认按主题来源与优先级自动取:有 key 时先拉
服务端默认排版的编译版,或用
--theme/config.mdTheme钉一套本机主题;写主题见下方 「复刻参考排版风格」),并确认渲染器真被调用。
gen-image.mjs生成的本地 jpeg 路径,封面喂pipeline.mjs --cover、配图以<img src>落进正文—— 两者都不触碰微信侧发布契约。上游生图密钥只在 doubaoya 服务端,skill 端只用密钥。
用设计工作台(可视化替代)
不想在命令行里逐步选风格 / 生图,可起本地网页工作台一次点完,产出一个 design-config.json,再交给
pipeline.mjs --design 消费。工作台零依赖(Node 内置 http + 全局 fetch),只绑 127.0.0.1,只写本地产物,不发布、不提交。
export DOUBAOYA_API_KEY="dyh_你的密钥"
node scripts/design-studio.mjs --md <文章.md> --title "<标题>" \
[--out <默认同目录 文章.design.json>] [--port 4599]
注册卡通 IP(可选,保持全篇形象统一):把你的卡通 IP 形象图放进 assets/ip/(或页面顶部「上传 IP」),
并在 config.json 里把 ipImage 指向它。注册后,封面与配图默认走参考图条件化生成
(operation:"edit" + referenceImage),保留同一形象让全篇视觉统一;未注册则退回文生图。
见 assets/ip/README.md。
页面三区:①排版 = 主题卡片实时换肤预览(左侧 375px 手机公众号外框);②封面 = 选生图风格 →
生成候选(默认套用当前 IP 参考图,可再生 / 上传自己的)→ 挑一张;③配图(自动布局) = 点「自动配图」→
后端 plan-figures.mjs(确定性规则,不接 LLM)自动挑好位置(信息量大的 h2 小节末尾、张数按字数分档)→
逐张用 IP 参考图生成并自动摆好,用户只做「换一张 / 删除 / 整体重生」,不手选锚点。顶部「保存配置」
写出 design-config.json(含 ip 与自动填充的 images[],过 schemas/design-config.schema.json 校验)。
生成的封面/配图 jpeg 落 design-config 同目录的 .design/assets/。
拿到 design-config.json 后进流水线(套主题 + 设封面 + 按 h2 锚点注入配图):
node scripts/pipeline.mjs --md <文章.md> --title "<标题>" --design <文章.design.json> --dry-run
--design的主题 / 封面是默认值;显式--theme/--cover与之冲突时命令行优先并告警。配图按afterHeading锚点插在对应 h2 小节末尾,找不到锚点则追加文末并告警。工作台 +--design与上面的命令行 引导等价,二选一即可,都不触碰微信侧发布契约。
上手:配置 + 身份 profile
# 1. 复制配置模板,填你自己的值(见 config.example.README.md 逐字段说明)
cp config.example.json config.json
# 2. 复制身份 profile 模板,改成你自己账号的身份卡
cp profiles/example-ip.json profiles/my-ip.json
# 再在 config.json 里把 ipProfile 指向 profiles/my-ip.json
config.json 关键字段:targetAccount(多 key 时挑账号)、appid / publicAccountName(选/校验公众号)、
ipProfile(身份卡路径)、coverFallback(兜底封面标记)。null = 自动探测。config.json 属于你个人,别提交到公共仓库。
身份上下文优先(通用规律,不是某个人的故事)
一个账号名 / IP 名很可能和某个通用名词或产品品类同名。若不先加载身份上下文,agent 可能把这个
专有名词误读成字面意思的通用名词,导致选题、配图、封面全跑偏。profile 里的 isNot 就是把这条
消歧规则外化成数据:流水线第 2 步先读它、回显它,明确「这是账号名,不是那个通用名词」。
示例 profile(profiles/example-ip.json,虚构的 示例·日常号)演示了 schema——请照它写你自己账号的身份卡。
详见 profiles/README.md。
CLI 用法
export DOUBAOYA_API_KEY="dyh_你的密钥" # 或放 ~/.doubaoya/key、Keychain(account-verify 会找)
# A. 从 Markdown 开始(渲染 → 传图 → 存草稿)
node scripts/pipeline.mjs --md article.md --title "标题" --config ./config.json
# B. 已有排好版的 HTML,直接发
node scripts/pipeline.mjs --html article.html --title "标题"
# C. 指定账号 + 公众号 + 本地封面 + 摘要
node scripts/pipeline.mjs --md a.md --title "标题" \
--account [email protected] --appid wx0123... --cover cover.png --digest "本期摘要"
# D. 干跑:只渲染+校验+扫描本地图,什么都不发
node scripts/pipeline.mjs --md a.md --title "标题" --dry-run
# E. 起可视化设计工作台选主题/封面/配图 → 产出 design-config.json(见「用设计工作台」)
node scripts/design-studio.mjs --md a.md --title "标题" # 网页里点完「保存配置」
# F. 用设计工作台产出的 design-config 跑流水线(套主题 + 设封面 + 按 h2 锚点注入配图)
node scripts/pipeline.mjs --md a.md --title "标题" --design a.design.json --dry-run
参数:--md | --html(二选一)、--title(必填)、--account、--appid、--cover、--digest、
--config、--profile、--theme、--design、--output-processed-html、--base-url、--dry-run、--help。
只存草稿:本流水线没有任何群发参数。传
--mass-send/--broadcast/带「群发」字样的 flag 会被直接拒绝。
复刻参考排版风格 → 可复用主题
想让排版长得像某个你欣赏的公众号,或某种描述得出的风格?把它一次性萃取成一个 theme.json,
之后永久复用(每次渲染只需 --theme my-theme.json,见下方 CLI)。主题契约的权威是
themes/THEME-SCHEMA.md(top-level 只有 meta/palette/page/elements/decorations)。
校验器是 scripts/validate-theme.mjs,套用器是 scripts/render-wechat-html.mjs --theme(或 pipeline.mjs --theme)。
写主题是一次性的活;产出的
theme.json之后一直用。默认主题是themes/benya-clean.json(本鸭精品「知识清爽」风,推荐)。不想从零写?先从内置主题themes/benya-clean.json(默认/推荐)/themes/magazine.json/themes/minimal.json/themes/knowledge.json里挑一个最接近的复制再改。
路径 A:复刻一篇公众号文章的排版(给 URL)
流程 = 抓取 →(零 token 启发式)萃取草稿 → LLM 精修 → 校验 → 渲染。 其中「萃取草稿」是一次快速的零 token 首过(用启发式把配色/排版扒出来), 真正把它做到「精修」的是你(LLM)对草稿的refine——这正是我们相对纯启发式工具的优势所在。
启发式萃取算法来自 oaker-io/wewrite(MIT © 2026 OpenClaw) 的
analyze_styles(),零依赖 Node 重写移植进scripts/extract-theme.mjs(署名见文件头 +meta.notes)。
-
抓取参考正文(一次性风格学习,抓的是一篇公开文章、不登录、不批量):
node scripts/fetch-article.mjs --url "https://mp.weixin.qq.com/s/..." --out ref.html它提取正文
#js_content,保留所有 inlinestyle="…"(这些内联样式就是我们要分析的数据), 去掉<script>/<style>/注释,并打印风格指纹:各标签数量、出现最多的颜色、用到的字号。若该链接被反爬/已过期而抓不到,脚本会明确提示你:在浏览器里打开文章、查看源码,把正文 HTML 贴进本地文件来分析(授权步骤对任何公众号正文 HTML 都适用,不只限本抓取器)。
-
萃取候选主题草稿(
extract-theme.mjs,零 token 快速首过):node scripts/extract-theme.mjs --html ref.html --name "参考风格" --out my-theme.json # 或一步到位(内部复用 fetch-article 抓正文): node scripts/extract-theme.mjs --url "https://mp.weixin.qq.com/s/..." --name "参考风格" --out my-theme.json它按标签分组内联样式,扒出
text/text_light/ 主色 accent(strong/section/h1-3/span 的非灰色加权计数,font-size≥20px权重 ×5)/ 背景 / 排版(字号·行高·字距)/ 引用边框与底色 / 代码色 / 圆角, 盖进一套中性基底模板(用{{token}}注色),产出一份通过validate-theme.mjs的theme.json草稿。信号弱时(135/秀米 导出把色写在
span而非p上等)它会回落到中性默认并告警「低置信度」——正常,交给下一步精修。 -
你(LLM)对着参考精修草稿(我们的核心价值——启发式看不到的东西由你补齐): 按下面的 CHECKLIST 逐项核对
my-theme.json,修正主色、规整脏值(2em→具体行高、把色从 span 归到text等)、 补上装饰分割线 / 标题处理:- 标题 h1–h3:色条 / 背景块 / 是否居中 / 字号 / 字重 / 字色(→
elements.h1..h3.style,装饰条用wrapBefore)。 - 正文
p:font-size/line-height/color/letter-spacing/ 段间距margin(→elements.p.style与page)。 - 引用
blockquote:左边框 / 背景 / 字色(→elements.blockquote.style)。 - 列表 marker:项目符号样式(→
elements.li.marker+ul/ol/li.style)。 - 图片:圆角 / 阴影 / 居中 / 图注(→
elements.img.style+figureStyle/captionStyle)。 - 强调 / 链接色:
strong/em/a的处理与主色(→elements.strong/em/a+palette.accent/link)。 - 调色板:核对萃取出的 3–5 个颜色是否合理(
text/heading/accent/accent2/muted/bgSoft/border/link); 启发式常把某个高频装饰色误当主色——对照抓取器指纹「出现最多的颜色」改回真正的主色。 - 分隔装饰:文中的花式分割线 →
elements.hr.html;整篇卡片/边框背景 →decorations.articleWrap; 命名分隔片段 →decorations.sectionDivider(这些启发式扒不出来,靠你补)。
- 标题 h1–h3:色条 / 背景块 / 是否居中 / 字号 / 字重 / 字色(→
-
校验 → 渲染:
node scripts/validate-theme.mjs my-theme.json # 有硬错误就按提示改 node scripts/render-wechat-html.mjs --md a.md --title "标题" --theme my-theme.json # 或直接进流水线: node scripts/pipeline.mjs --md a.md --title "标题" --theme my-theme.json
诚实预期:公众号编辑器(秀米 / 135 等)导出的 HTML 很吵——满是一次性的内联样式。
extract-theme.mjs是快速首过,只保证扒出大致配色骨架;把它调到「像」靠的是第 3 步你的精修。 只保留反复出现的那套规律,别把每一处 one-off 样式都当成主题。
路径 B:从一段文字风格描述直接写主题
不需要参考文章:你(agent)按描述的调性直接照 schema 填 theme.json,再校验、渲染。
例:「性冷淡杂志风」→ 低饱和 palette、细 border/hairline hr、充裕留白(大 margin/line-height)、
克制近 small-caps 的标题(大字距、非高饱和色)。同样先 validate-theme.mjs 再 render --theme。
起步同样建议复制 themes/benya-clean.json(默认/推荐)/ magazine.json(杂志风)/ minimal.json(极简)/ knowledge.json(知识卡片)之一再改。
一切以 themes/THEME-SCHEMA.md 为准;主题索引见 themes/README.md。
前置条件(分层:不是每一步都要绑公众号)
统一前置:Node ≥ 18(内置 fetch),零外部依赖。除此之外按你要做的事分三层——
只想看排版效果、写/换主题、规划配图位置的用户,没有密钥、没绑公众号也能干活:
| 想做的事 | 除 Node 外还需要 | 怎么跑 |
|---|---|---|
| md → 公众号内联样式 HTML(本地出稿 / 看排版效果) | 无 | node scripts/render-wechat-html.mjs --md a.md --theme themes/benya-clean.json --out a.html |
| 校主题 / 写主题 / 导入外部主题格式 | 无 | scripts/validate-theme.mjs、scripts/import-theme.mjs、scripts/extract-theme.mjs --html ref.html |
| 复刻某篇公开文章的排版 | 公网(不要密钥) | scripts/fetch-article.mjs --url …、scripts/extract-theme.mjs --url … |
| 配图自动布局规划(确定性规则,不接 LLM) | 无 | node scripts/plan-figures.mjs --md a.md |
起本地设计工作台:实时预览、换肤、自动配图排位、存 design-config | 无(只有页面里点「生成」才要密钥) | node scripts/design-studio.mjs --md a.md --title "标题" |
| AI 生封面 / 生配图 | 一条 DOUBAOYA_API_KEY(花钱,现价现拉) | scripts/gen-image.mjs,或工作台里点生成 |
| 用你在 doubaoya.com 设置的默认排版渲染 | 一条 DOUBAOYA_API_KEY(拉不到会静默回退本机主题,不中断) | 流水线自动拉 GET /api/wechat/theme?format=compiled |
跑 pipeline.mjs(含 --dry-run) | 密钥 + 已在 doubaoya.com 绑定公众号 | node scripts/pipeline.mjs --md a.md --title "标题" --dry-run |
| 本地图预上传 / 存草稿 | 同上(存草稿花钱,失败自动退回) | pipeline.mjs、wechat-draft-publish |
⚠️
--dry-run不是免密钥预览。它虽然什么都不发,但 whoami 校验账号与草稿前置检查 (GET /api/wechat/status)都排在它前面:没有密钥会停在「本地没有可用的DOUBAOYA_API_KEY」, 有密钥但没绑号会停在「目标账号没有已绑定的公众号」。 还没绑号、只想先看这篇排出来什么样:走render-wechat-html.mjs或设计工作台(都纯本地)。 注意单跑渲染器时--title会往正文顶部插一个<h1>(本地预览用),那份产物别拿去发布——见正文不要写标题。
绑好号、配好密钥之后,发布前先跑一次 --dry-run,确认身份上下文、目标账号、公众号、本地图扫描都对,再正式存草稿。
下一步(草稿存好之后)
草稿进箱,用户「要一篇能发的公众号图文」这个终态就已经达成了——这里通常就是终点。 群发的手始终在用户自己:本 skill 没有任何群发路径,请他去公众号后台亲眼确认草稿 (排版、封面、图片都对)再手动群发。
发布之后如果用户还想往下走,可选:
| 用户接着想要什么 | 下一步 |
|---|---|
| 攒几天数据后看这个号的发文表现 / 做体检 | wechat-account-analyzer |
| 盯自己或竞品的发文节奏 | doubaoya(打公众号发文列表端点) |
| 把已发布的文章拉正文归档 | doubaoya |
| 用复盘信号挖下一轮选题 | doubaoya(挖选题 / 追热点,也从这儿拉样本开写) |
| 说不清要到哪一步 | dby(公众号飞轮的逐跳导航) |
更新本技能
npx skills update wechat-article-pipeline # 全局安装的加 -g
最近变更:
- 调用知识改成网关委托形态:本 Skill 用到的三条能力现在 operationKey 与详情端点一起点名, 调用协议逐字内联(见调用都爆鸭), 入参规格一律调用前从详情端点现拉——原来烤在正文里的返回字段表与计价数字已整段删掉 (烤进分发物的契约必然漂,而价格会静默调整)。十步 SOP 与终态判断一步没动。 同时把第 5 步说清成两条路:干净 markdown 走免费的平台渲染能力,本机图片 / 组件语法 / 无密钥才走本机渲染器。
- 未显式钉本机主题时,流水线现在会优先拉取服务端编译主题(你在 doubaoya.com 设置的默认排版,
GET /api/wechat/theme?format=compiled;拉不到就优雅回退本机)。想钉死本机主题的,--theme或config.mdTheme写成路径即可。同时validate-theme.mjs对 engine-2 主题 (meta.engine:2/tokens/ 带点号 token)从告警升级为硬错误——这类主题只能用服务端编译版。- 默认 Markdown 排版主题已切为
benya-clean(本鸭 · 知识清爽)。想沿用旧版magazine(杂志风)的,在config.json里把mdTheme指回themes/magazine.json,或渲染时加--theme themes/magazine.json。