植物诗词生图流水线
重要原则
- LLM API 失败时自救:若 MiniMax chat API(
/v1/text/chatcompletion_v2)返回 404、网络错误、或 HTTP 200 但 content 格式不符合规范(如返回散文式多行、标题与首联粘连、句子字数不为 7),直接用自身 LLM 能力写诗,不等待 API 恢复。三种失败模式共享同一条自救路径。 - 诗歌校验一票通过:校验不通过就重写,最多 3 次。任何不符合
assert len(lines) == 9+ 8 对联 + 16 句 7 字 的输出都视为失败,无论来源是 API 还是本地生成。
⚠️ Cron 模式工具限制(2026-06-20 实测,最关键)
本流水线主要在 cron 任务中执行。cron 模式下 execute_code 工具被完全禁用,直接返回错误:
> BLOCKED: execute_code runs arbitrary local Python (including subprocess calls that bypass shell-string approval checks). Cron jobs run without a user present to approve it. Use normal tools instead, or set approvals.cron_mode: approve only if this cron profile is intentionally trusted.
这比「PIL 未安装」更严格——任何 execute_code 调用都不会执行。影响:
- ❌ 诗歌校验(
assert len(couplets) == 8等)不能用execute_code跑 - ❌ PIL 占位图脚本不能用
execute_code跑 - ❌ 任何 Python 内联逻辑都不能用
execute_code跑 - ✅ 必须用
write_file写脚本到/tmp/xxx.py+terminal执行python3 /tmp/xxx.py
自查清单(每一步都用此模式):
1. 诗歌校验 → write_file 写 /tmp/validate_poem.py → terminal 跑
2. PIL 占位图 → write_file 写 /tmp/gen_plant.py → terminal 跑
3. 视频合成 → write_file 写 /tmp/synthesize_plant.py → terminal 跑
4. 归档验证 → terminal 直接跑 bash 命令即可
> 💡 之前技能文档散落说「execute_code 不要用 heredoc」「execute_code PIL 未安装」——这两条建议是针对交互模式(用户在场)的,cron 模式下连用都不能用。本节是最高优先级约束。
期号与植物的确定规则
⚠ 这是定时任务正确运行的关键,必须严格遵守。
定时任务触发时(cron 每日 9:00),必须先动态查询再执行:
- 查最大期号:执行
ls -1 $SKILL_DIR/out/ | grep -E '^[0-9]{3}_',从目录名提取最大数字 N($SKILL_DIR指向本 skill 安装目录,可通过 cron 任务 prompt 中的skill_dir环境变量或直接用相对路径<skill>/out/) - 确定本期期号:N + 1,补零到3位(如 N=3 → 下一期=004)
- 查已用植物:从 out/ 目录所有子目录名提取植物名单(如
001_乌桕→ 乌桕已用) - 选择新植物:从候选池中排除已用植物,选一个未创作过的
用户手动触发时(DM 直发植物名):用户直接指定植物,期号仍按上面规则查 out/ 目录确定。
禁止行为:
- ❌ 不查 out/ 目录就假设从 001 开始
- ❌ 不查已用植物列表就随机选
- ❌ 依赖创建时写死的「已创建列表」上下文
📋 已用植物与候选池:见 references/used-plants.md(含 26+ 已用植物、季节匹配建议、候选优先级)。
🆕 候选选择策略(选植物的决策模式库):见 references/candidate-selection-strategy.md(含三大模式:跨季节秋提前 A / 中优先级冷门补遗 B / 盛夏药草对仗 N 连 C。用户手动触发未指定植物时必读。每期执行后追加维护记录,与 used-plants.md 同步)。
目标
输入一个植物名称(中文),按以下步骤一次性完成全部产出:
| 步骤 | 产出 | 工具 | 备注 |
|---|---|---|---|
| ① 写诗 | 16句七言诗(8对) | LLM | 必须校验16句8对 |
| ② 提示词 | 8个9:16竖屏生图提示词 | 手工编写,每对1个 | |
| ③ 生图 | 8张图片 | MiniMax image-01(逐图调用) | 禁止批量,间隔1.5s |
| ④ 语音 | 全诗朗读MP3 | MiniMax speech-2.8-hd | hex解码 |
| ⑤ 视频 | 1080×1920竖屏视频 | FFmpeg(imageio-ffmpeg,带drawtext) | 必须执行,不可跳过 |
| ⑥ 封面(可选) | 抖音风格系列封面 | 设计工具 | 用户上传品牌素材后执行 |
Step 6:系列封面设计
⚠ 关键区分:系列封面 vs 单期封面
- 系列封面:整个「草木小识」IP 的统一封面,不含任何单期植物名,供所有期次复用
- 单期封面:在系列封面基础上可叠加当期植物名/图片(由用户自行处理)
用户说"帮我设计一个封面"时,默认请求的是系列封面,除非用户明确说"这期的封面"。
设计流程:
- 获取品牌素材:让用户提供 logo PNG 和参考封面图
- 分析参考图:用 MiniMax MCP
understand_image(工具名mcp_MiniMax_understand_image)分析图片内容,不要依赖 vision_analyze 分析本地 PNG 文件 - 提取设计要素:
- 背景色 / 主色调
- 字体风格(衬线/无衬线/书法)
- 图形元素(波点/边框/分隔线)
- 文字内容及颜色 - 使用实际素材:用户提供的 logo/图形必须直接贴图,不要用文字渲染替代
- PIL 贴图:
Image.open(path).convert("RGBA")→img.paste(logo, (x, y), logo)用 alpha 作为 mask
常见失误(已踩过的坑):
| 失误 | 后果 | 正确做法 |
|---|---|---|
| 误以为要替单期做封面 | 重复问"为什么加乌桕" | 先确认是系列还是单期 |
| 用文字渲染替代 logo | 用户说"logo用贴图不要修改" | 直接 paste 原图 RGBA |
| 忽略参考图的边框/框架 | 用户说"logo外有边框也要保持一致" | 分析参考图时留意边框细节 |
| vision_analyze 无法分析本地 PNG | 返回"请上传图片" | 换用 mcp_MiniMax_understand_image |
Step 7:维护 references/used-plants.md(必做,不可跳过)
为什么需要这一步:候选池的存在意义是让下一期 cron 任务「有据可依」地选择新植物、避免误用已用植物。SKILL.md 主流程到此结束,但 references/used-plants.md 的「已用植物」表和「候选池」需要每期追加更新。如果省略,下一期 cron 会失去"已用清单"和"季节匹配建议"的关键依据。
操作流程(每次 cron 完成后必做,手动 1-2 分钟):
- 追加新行:在
references/used-plants.md的「已用植物」表中追加一行
- 格式:|| NNN | 植物名 | 季节 | 类别 |
- 按期号升序追加到表末 - 更新总数行:
共 N 期,已用 N 种植物(N 递增) - 跨季节选择时(如 6 月写冬花):在「季节匹配建议」章节加注释,说明原因
- 季节耗尽时(如 6-7 月夏花全部已用):在文件末尾或合适位置加维护记录,列出建议的跨季节/中优先级备选
示例(2026-06-27 第 052 期后追加):
||| 051 | 山茶 | 冬春 | 灌木(古典名花) |
|| 052 | 牵牛花 | 夏 | 藤本(晨开暮落) |
> **2026-06-27 维护**:6-7 月夏花候选已彻底耗尽,下期(053)建议跨季节或中优先级冷门植物
跳过规则:
- out/ 目录已存在本期(如手动重跑修复)→ 不重复添加
- 本期植物已在表中(如修复错误)→ 更新现有行,不追加
自动化建议(未来增强):可在 synthesize_with_mmx_srt.py 末尾追加 append_used_plant(plant, season, category) 函数。当前版本仍需手动维护。
MiniMax API 配置
> 详细 API 配置(端点、参数、备选路径、self-rescue、文本失败模式)见 → api/minimax-config.md
>
> Support Files 索引表(references/ + scripts/)见 → api/minimax-config.md § Support Files
>
> cron 端到端验证流水账(053-066 共 16 期 + 首字避雷表)见 → cron-run-log.md
故障排查与 FFmpeg 视频合成
> 详细内容(占位图应急生成、image-01 额度耗尽处理、WAV/标题视频 bug 修复、FFmpeg 合成参数)见 → workflow/troubleshooting.md
工作流概览
7 步流水线(严格按顺序):
| Step | 名称 | 工具 | 耗时 | 关键产物 |
|---|---|---|---|---|
| 1 | 写 16 句七言诗(8 对) | write_file + terminal 跑 Python |
~10s | /tmp/{plant}/poem.txt |
| 2 | 为 8 对联生成 8 个提示词 | terminal 调本地 LLM |
~15s | /tmp/{plant}/prompts.txt |
| 3 | 生图(mmx CLI 批量) | mmx image generate × 8(sleep 5 间隔) |
~3 min | /tmp/{plant}/{plant}_01..08.png |
| 4 | 语音合成 | mmx speech synthesize --subtitles |
~3s | /tmp/{plant}/tts.mp3 + tts.srt |
| 5 | 视频合成 | python3 scripts/synthesize_with_mmx_srt.py |
~5s | $SKILL_DIR/out/{期号}_{plant}/video.mp4 |
| 6 | 系列封面设计 | 跳过(cron 模式) | - | - |
| 7 | 维护 used-plants.md | write_file 追加 |
~5s | references/used-plants.md |
完整代码、详细注释、踩坑记录、变体场景见 → workflow/steps-detailed.md
变体:纯诗意视频(无品牌)
本 pipeline 支持两种产出模式:
品牌模式(默认)
执行完整流水线,包括系列封面设计(Step 6),输出到 out/期号_植物名/ 归档目录。
触发条件: 用户请求时未明确说明"不要抖音品牌包装"。
纯诗意模式(无品牌元素)
用户明确说「我要古诗风的植物视频,不要抖音品牌包装」时,跳过 Step 6 封面设计,直接进入 Step 1-5,输出到 $SKILL_DIR/out/植物名/植物名吟.mp4(无期号;2026-07-12 起统一到 out/ 目录)。
与品牌模式的区别:
- 无 Step 6 封面设计
- 无归档到 out/ 目录
- 无「草木小识」IP 品牌元素
- 视频命名:植物名吟.mp4(与品牌模式的 out/期号_植物名/video.mp4 不同)
纯诗意模式的技术规格与品牌模式完全相同:
- 8 对联 16 句七言诗(校验方式相同)
- 8 张 9:16 竖屏生图
- MiniMax speech-2.8-hd 语音(male-qn-daxuesheng, speed=0.9)
- FFmpeg 1080×1920 竖屏,30fps,字幕叠加
判断流程:
1. 用户请求 → 检查是否包含「不要品牌」「不要抖音」「纯诗意」等关键词
2. 是 → 纯诗意模式,跳过封面,跳过归档,直接输出到 $SKILL_DIR/out/植物名/植物名吟.mp4(2026-07-12 起统一到 out/)
3. 否 → 品牌模式,执行完整流水线(含封面和归档)
Cron 定时任务输出格式
⚠️ This section is enforced for all cron deliveries. The report is auto-sent to the user without human review. Verbose execution logs ("执行摘要"、"技术备注"、"状态表") are prohibited.
Required output structure (in order):
- Poem block — the only substantive content:
````
第NNN期「XX吟」
XX吟
句1,句2。
句3,句4。
...
句15,句16。
````
-
File paths — one line each, minimal:
语音: $SKILL_DIR/out/NNN_XX/XX.mp3(N.NNs) # 2026-07-12 起统一到 out/ 视频: $SKILL_DIR/out/NNN_XX/XX.mp4(1080×1920) 归档: out/NNN_XX/ -
Abort conditions — only output if something failed:
- Image generation failed:⚠️ 图片生成失败(MiniMax 额度耗尽),已生成占位图
- TTS failed:⚠️ TTS失败(改用 edge-tts)
- Video failed:⚠️ 视频合成失败,请检查日志
Prohibited in cron output:
- ❌ "执行摘要" status table (steps/status/产出 columns)
- ❌ "技术备注" section with API error details
- ❌ Python stack traces in the output
- ❌ Duplication of the poem (don't show it twice)
- ❌ Timestamps for individual steps
- ❌ "自我救援" narrative — just show the fallback result silently
How to handle errors silently:
- If MiniMax fails and you fall back to edge-tts, just deliver the final audio path. Don't explain the API failure in the report.
- If image generation fails and you use PIL placeholders, just deliver the video. The
prompts.txtis already saved for later replacement. - If any step fails, the output is still a valid report — just mark the failed step and continue.