| name | zhifa-upload |
| description | 知发上传:把已合成的笔记图(PNG/JPG)导入知发系统,排期定时发布到小红书或抖音账号。当用户想把一批图片/笔记安排到某个账号在某些日期发布,就用这个 Skill。触发词:上传到知发、上传到飞书、上传笔记、发布笔记、导入发布、准备发布、填封面发布、笔记上传发布、排期发布、安排发布、导入知发、笔记排期、发布排期、按日期发布、批量发布、发到小红书、发到抖音、发布到抖音、发布到小红书、定时发布笔记、知发排期、排期到抖音、排期到小红书、发布到抖音号、安排到抖音。 |
融景合成图 → 知发上传 Skill
把融景已合成的笔记图,通过知发(zhifa,运行在 localhost:3210)上传到飞书多维表格,定时发布小红书 / 抖音。
CLI 路径
python3 ~/zhifa/scripts/skill_upload.py
输入目录结构(融景标准输出)
合成图/
课件A/ ← 主题文件夹(topic group)
1/ ← 模板1(一篇笔记)
1.jpg 2.jpg ...
2/ ← 模板2(一篇笔记)
1.jpg 2.jpg ...
课件B/
1/
1.jpg 2.jpg ...
工作流程
Step 0:确认参数
用户在一条消息里提供所有必要信息,之后只需在 Step 4 确认内容预览即可。
标准输入格式:
合成图文件夹:~/笔记制作输出/合成图
账号:小红书-账号A、小红书-账号B、小红书-账号C、小红书-账号D、小红书-账号E、小红书-账号F
发布日期:5.1–5.2
时间段:11:10 / 13:00 / 20:00
组1:~/Desktop/草船借箭封面.jpg
主题:草船借箭的历史背景与战争智慧
组2:~/Desktop/赤壁之战封面.jpg
主题:赤壁之战:以少胜多的经典战例
参数说明:
- 合成图文件夹:融景输出的
合成图/ 目录路径
- 封面图:每个主题组一张,请提供完整磁盘路径;文件名无所谓,Skill 统一存为
0.jpg
- 主题:每组一句话,Claude 用于写小红书文案
- 账号:一个或多个小红书/抖音账号,多个用顿号、逗号或换行分隔
- 发布日期:支持「5.1–5.4」「后天到大后天」等自然语言;先执行
date "+%Y-%m-%d" 拿今天日期推算绝对日期;单天也可以直接写「后天」
- 时间段:一个或多个发布时间(如「11:10 / 13:00 / 20:00」),多个时间段按规则二独立轮转;若用户指定了某账号的固定时间,以指定为准
- 发布渠道(xiaohongshuChannel):可选,默认
蚁小二;如果用其他渠道,用户需显式说明
关于封面图的附件(@ 拖入):如果用户通过 @ 或拖拽发送封面图,先运行:
ls -la <用户说的路径或附件路径>
确认文件确实存在后再 cp。如果路径不确定,直接问用户"封面图的完整路径是什么?",不要猜。
Step 1:检查知发服务
curl -sf http://localhost:3210/api/import/preflight
失败 → 提醒用户打开知发 App(macOS 菜单栏可见),等用户确认后继续。
Step 2:扫描合成图文件夹
python3 ~/zhifa/scripts/skill_upload.py scan <合成图文件夹>
脚本会:
- 打印人类可读摘要(每个主题组 + 包含的笔记 + 是否已有封面)
- 把原始 JSON 写入
/tmp/zhifa_scan_result.json(Step 3、5 会用到)
扫描后必须列出识别结果让用户确认:打印扫描到的主题组列表,让用户核对数量和名称是否符合预期,再继续。
常见干扰项:合成图文件夹下可能存在非主题文件夹(如 逐字稿/、封面/、备份/ 等),scan 会把它们也识别为主题组。列出扫描结果时,若发现疑似非笔记文件夹(没有数字编号子目录、名称明显不像课题),主动提示用户确认是否排除,不要自动纳入上传。
数量不一致时停下来问用户:
「扫描发现 N 个主题组:[列出名称],但你提供了 M 张封面。请告诉我哪张封面对应哪组,或者确认是否有组不需要封面。」
等用户明确确认后再继续 Step 3。
Step 3:放置封面图
读取 /tmp/zhifa_scan_result.json,取出每个 topic group 下所有笔记的 folderPath 字段(已是绝对路径)。
单张封面(该组只提供一张图):
cp "<封面图路径>" "<folderPath>/0.jpg"
多张封面(该组提供了多张图):
按用户发送的文件名顺序排列(不要按文件系统排序,以用户列出的顺序为准),依次重命名为:
第1张 → 0.jpg
第2张 → 0(1).jpg
第3张 → 0(2).jpg
……以此类推
cp "<第1张路径>" "<folderPath>/0.jpg"
cp "<第2张路径>" "<folderPath>/0(1).jpg"
cp "<第3张路径>" "<folderPath>/0(2).jpg"
排序规则保证 0 < 0(1) < 0(2) < 1 < 2…,所有封面图始终排在内容图前面。
- 同一主题组的多个模板子文件夹(
1/、2/、3/)放完全相同的封面组合
- 放完后再运行一次
scan,确认所有笔记都显示"含 0.jpg ✓"
Step 4:Claude 生成文案
每篇笔记单独生成文案,即使同一主题下有多篇,也必须各写各的——标题角度不同、切入点不同。 不允许任何情况下复用同一套文案。
完整写作规范:执行前读取 ~/zhifa/src/ai-writer.js 开头的 SYSTEM_PROMPT,严格按其中的标题公式、标签规则、禁用词执行。
正文(description)固定留空 "",不生成任何正文内容。
生成流程
- 读取
~/zhifa/src/ai-writer.js 的 SYSTEM_PROMPT
- 每篇笔记单独生成:依据内容类型选最贴切的标题公式,写 2–3 个候选标题
- description 固定填
""
- 按 SYSTEM_PROMPT 标签规范组装标签;飞书「标签」字段提取 3–5 个关键词(不带
#)
生成后展示预览,等用户说「确认」后才继续:
📋 内容预览(请确认后说「确认」继续)
── 组1:草船借箭的历史背景与战争智慧 ──
标题候选:
A. ……
B. ……
正文:""(留空)
标签:历史故事 / 三国 / 战争智慧
── 组2:赤壁之战:以少胜多的经典战例 ──
…
Step 5:分配账号与时间,构建 records JSON 并上传
从 /tmp/zhifa_scan_result.json 中读取扫描结果,结合用户输入和 Claude 生成的文案,按以下原则分配后写入 /tmp/zhifa_records.json 并上传。
分配规则
规则一:主题双轮转
N 个主题、M 个账号,账号按顺序分成 N 组(每组 M÷N 个,余数均匀分散到前几组)。
- 第 1 天:组1→主题1,组2→主题2,…,组N→主题N
- 第 k 天:整体向后轮转 k−1 格(循环)
效果:同一天各组发不同主题;单个账号跨天覆盖全部主题,无重复。
规则二:时间段独立轮转
T 个时间段,账号按与主题分组交叉的方式重新分成 T 组(每组 M÷T 个)。
- 第 1 天:时间组A→时间1,时间组B→时间2,…
- 第 k 天:整体向后轮转 k−1 格(循环)
关键:时间分组与主题分组独立交叉,使同一账号的主题和时间段都在变,不产生固定搭配。若用户为某账号指定了固定时间,以指定为准,不参与轮转。
规则三:拉丁方模板分配(CRITICAL — 违反 = 全部废品)
公式:template = (topic_idx + account_idx) % K + 1,K = min(可用模板数, 账号数)。
效果:同一账号跨主题必须用不同模板,同一主题跨账号也必须用不同模板。绝不允许一个账号的所有笔记用同一个模板。
构建 records JSON 后、上传前,必须跑校验:每个账号使用的模板种类数 ≥ min(主题数, K),不通过则停下报告。
天序号推算:用户给发布日期范围(如「5.1–5.4」),起始日=第1天,依次递推;执行 date "+%Y-%m-%d" 辅助推算绝对日期。单天发布则所有账号在同一天按规则一、二的第1天逻辑处理。
python3 ~/zhifa/scripts/skill_upload.py create /tmp/zhifa_records.json
字段来源说明(构建每条 record 时参照):
| 字段 | 来源 |
|---|
topic | scan JSON 里的 topic 字段 |
topicOverride | 用户提供的「主题」(写文案用的那句话) |
noteKey | scan JSON 里每条 note 的 noteKey 字段 |
folderPath | scan JSON 里每条 note 的 folderPath 字段(绝对路径) |
images | scan JSON 里每条 note 的 images 数组(已含 name/path/size,直接复用,不要重新构建) |
xiaohongshuAccount | 按分配原则确定的该条笔记对应账号,没有小红书账号则留空 "" |
douyinAccount | 用户提供的抖音账号名,没有则留空 "" |
publishTime | 按分配原则确定的该条笔记对应时间槽,格式 YYYY-MM-DD HH:mm |
xiaohongshuChannel | 用户指定的渠道,未指定时固定填 "蚁小二" |
title | Claude 生成的标题 |
description | Claude 生成的正文 |
tags | Claude 生成的标签数组(不带 # 的关键词) |
records JSON 结构示例(写入 /tmp/zhifa_records.json):
{
"records": [
{
"topic": "课件A",
"topicOverride": "草船借箭的历史背景与战争智慧",
"noteKey": "课件A/1",
"folderPath": "~/笔记制作输出/合成图/课件A/1",
"images": [
{"name": "0.jpg", "path": "~/笔记制作输出/合成图/课件A/1/0.jpg", "size": 98765},
{"name": "1.jpg", "path": "~/笔记制作输出/合成图/课件A/1/1.jpg", "size": 123456}
],
"xiaohongshuAccount": "小红书-测试账号",
"douyinAccount": "",
"publishTime": "2026-04-30 15:00",
"xiaohongshuChannel": "蚁小二",
"title": "草船借箭,诸葛亮凭什么敢这么赌?",
"description": "完整正文内容……",
"tags": ["历史故事", "三国", "战争智慧"]
}
]
}
注意:title 字段预填时,知发跳过自身 AI 生成,直接使用 Claude 写的文案。
Step 6:归档未安排的笔记到备用文件夹
上传完成后,对照本次发布计划检查:有没有制作了但没排进发布的笔记?或用户在备注里单独标记"不发"的笔记? 有的话复制(不是移动)到独立的备用文件夹,便于以后补发。
默认归档路径:<合成图根目录>/_未安排备用/(在合成图同级建子文件夹)
命名规则:{主题简称}-{原编号}-模板{融景模板号}/ —— 例:屏-2-模板3/、致-9-模板17/
操作:
mkdir -p "<合成图根目录>/_未安排备用"
cp -R "<合成图根目录>/<主题全称>/<原编号>" \
"<合成图根目录>/_未安排备用/<主题简称>-<原编号>-模板<融景模板号>"
写 README:在备用文件夹根目录写一份 README.md:
- 本期日期 + 项目名
- 表格列出每个文件夹的来源主题、原编号、模板号、为什么没排到
- 命名规则说明、文件结构示例、怎么用(如何拷回主目录走补发)
用户单独备注的"不发"笔记:用户如果在对话里说"这几篇先不发" / "X 留着以后用",也按同样规则放到 _未安排备用/,README 里另起一段说明"用户备注不发的"。
不触发归档:
- 已经全部排进发布计划的(无未安排)→ 跳过本步,不建空文件夹
- 用户明确说"删掉不要"的笔记 → 直接
rm -rf 不留备用,省硬盘
Step 7:报告结果
✅ 上传完成
处理 2 组,共 4 篇笔记:
✓ 成功 4 篇
- 跳过 0 篇(已存在,指纹查重命中)
✗ 失败 0 篇
飞书表格已更新。**发布状态字段保持为空**,需要人工在飞书里手动改成"待发布"才会进入调度发布。
未安排笔记已归档:<合成图根目录>/_未安排备用/(共 N 篇,含 README)
与 zhifa-pipeline 的区分(入口判断标准)
判断口诀:磁盘上能 ls 看到合成图(JPG/PNG)→ 用这个 Skill(zhifa-upload);只有 PPT 文件还没合成 → 用 zhifa-pipeline。
| 情况 | 正确入口 |
|---|
合成图文件夹已存在(ls 能看到 .jpg/.png) | zhifa-upload(本 Skill) |
| 起点是 PPT,还没跑融景合成 | zhifa-pipeline |
AI 强制自检:接到"上传/发布/排期"类任务时,必须先执行 ls <目标文件夹> 确认图片文件是否存在,不能凭用户口头描述或目录名推断。看到图片文件 → 选本 Skill;看不到图片(只有 PPT)→ 选 zhifa-pipeline。
注意事项
- 知发服务必须运行(macOS 菜单栏可见),否则 Step 1 会失败
- 查重:相同内容二次上传知发自动跳过(指纹查重),不会重复创建记录
- 封面图文件名无所谓,Skill 统一重命名为
0.jpg
- 多个模板共用同一封面:同一主题的
1/、2/、3/ 文件夹都放相同的 0.jpg
images 数组直接从 scan JSON 复用(含 size 字段),不要手动构建
- 发布状态默认留空(2026-05-06 调整):上传只建档不触发自动发布。
小红书发布状态 和 抖音发布状态 字段在飞书里默认为空,要人工改成"待发布"调度才会扫到。这是为了避免上传完误自动发,也方便用户分批审核后再开闸。
- 立即发布按钮也受人工闸门保护(设计意图,不是 bug):UI 发布页的「立即发布」按钮内部仍要求状态
=== '待发布' 才执行。上传后状态为空时点该按钮会被静默跳过——这是有意为之,避免文案/封面还没审核完就被人误触发。要立即发布某条,先去飞书把状态改成"待发布"再点按钮。