这道 skill 做一件事:读一篇 AI 创作教程/案例,把它背后的"做法"还原成一张工序表,存成
workflow.json,再渲染成一个网页。本文是总览。具体操作和字段规则在 extraction/ 子目录的三个阶段文件里,按阶段读即可。
本目录是一个自包含的 skill:跑提取流程时需要的所有说明都在这里。外部的案例原文(input/case-N-raw.json)和你产出的成果(outputs/case-N/)不算 skill 的一部分。
输入:任意一篇创作案例——公众号 / 小红书 / 推文 / 博客(正文 + 配图)、视频教程(带转写)、或你自己的工作复盘。
输出:一个网页 outputs/case-{N}/case-{N}-<slug>.html。
读懂这几条,就能看懂工序表在表达什么。
工序表把"一步操作"拆成几个层面(对应 workflow.json 里的字段):
| 层面 | 通俗讲 | 在 workflow.json 里 |
|---|---|---|
| 数据类型 | 这份数据算什么角色(参考图/提示词…) | 输入输出的 type |
| 用的工具 | 哪个具体产品(manus/nano_banana…) | 步骤的 via |
| 做的动作 | 干了什么(生成/反推…) | 步骤的 action |
| 工序位置 | 处在流水线哪个环节(预处理/主体生成…) | 步骤的 effect |
| 内容实质 | 这步本质上涉及什么(人物/场景/观念…) | 步骤的 substance |
| 呈现形式 | 内容怎么组织(光影/构图/叙事…) | 步骤的 form |
| 工序模板 | 一整套可复用的做法 | 一个 procedure |
| 实际内容 | 这次具体填进去的真实值 | 输入输出的 value |
| 目的 | 一句话概括这步在干嘛 | 步骤的 intent |
一篇案例可以有多个工序:比如一篇文章同时讲"简单做法"和"进阶做法",那就是两个工序,放在顶层 procedures: [] 数组里(只有一个时也用长度 1 的数组)。
自造的类型要"挂靠":类型词表里没有的词,要在这个工序的 type_registry 里写明它"算作"哪个标准词,例如 主角图 挂靠 参考图。这样既能自由起名,又不弄乱标准词表。
命名约定:类型名、动作名用中文;工具品牌名用英文(nano_banana_pro);每个输出都要有不重复的编号(如 s2o1)供后面引用。其中"作用 / 实质 / 形式 / 动作 / 目的"是整步一个,"类型 / 值 / 来源 / 编号"是每个输入输出各一个。
三个分类词表(作用/动作/类型 要对到它们):作用 = effect.json、动作 = action.json、类型 = type.json。实质/形式 没有词表——读懂步骤后直接提炼,不查词表。
| 文件 | 内容 | 什么时候读 |
|---|---|---|
| extraction/phase1-skeleton.md | 第一阶段:搭骨架(判断有几个工序 / 切步骤 / 连数据流);含第一阶段字段说明 | 第一阶段 |
| extraction/phase2-normalize.md | 第二阶段:归类标注 + 填目的列;含第二阶段字段说明 | 第二阶段 |
| extraction/phase3-finalize.md | 第三阶段:检查 + 渲染出网页 | 第三阶段 |
| 文件 | 内容 |
|---|---|
| case-data.schema.json | 给机器看的字段清单(必填项/可选值的最终裁判) |
| 文件 | 维度 | 规模 | 怎么用 |
|---|---|---|---|
| effect.json | 作用 | 9 个词 | 第二阶段读,把每步的作用对上 |
| action.json | 动作 | 30+ 个词 | 同上 |
| type.json | 类型 | 50 个词 | 同上;自造类型写进 workflow.json 的 type_registry |
| type_suggestions.md | 新类型登记 | 只增不改 | 第三阶段跑 lint-case.py 时工具自动登记,不用手写 |
实质/形式 没有词表:第二阶段直接提炼,不查词表。
tools.md 是各脚本(建骨架/批量改 wf-patch · 原文捞引 quote-source · 渲染 render-case · 检查 lint-case)的接口手册。脚本本身不用读源码,会用就行;渲染网页的样式(renderer.py/styles.css/script.js)也不用碰。
wf-patch.py,绝不手写 / edit 改它(手写嵌套 JSON 易漏逗号崩、edit 在 p1/p2 重复结构上会撞「多个匹配」):
[{"path":...,"value":...}]),proc/step 头 + 每个 step 的 inputs[i]/outputs[i] 的 type、value、anchor 都写进同一份,跑 wf-patch.py --patch <清单>——缺的 procedure/step/IO 默认自动建(upsert)、output 的 id 自动补、文件不存在也从空建。路径统一用 id 式 p1.s1.inputs[0].type(下标式 procedures[0].steps[0].inputs[0].type 工具也接受)。--set 别用单引号包参数(cmd.exe 不剥 → 路径会带 ';值含空格用双引号,或写进 --patch 文件)。wf-patch.py --set 'path=值' 或 --patch <清单>。缺路径默认自动建,所以填 IO 不会撞「越界」;想严格抓路径 typo,加 --no-create(纯填已存在结构时用)。@quote|<起锚>|<止锚>(或 @quote|<关键词>),patch 时带 --resolve-quotes --source <原文json> --ocr <case_dir>/_scratch/ocr.txt,工具自动把标记换成原文 / 配图 OCR 里的真实内容。别自己读原文、粘长内容。
@quote|<起锚>|<止锚> 框整段, 工具逐字回填)。⚠️ 第三阶段 render-case.py 见到文本类 <占位> value 会拒绝出 HTML。原文确实没有 → value 写成 <占位>(原文未提供)(或标 inferred:true)即可放行;媒体类(图/视频/音频)用 <具体描述> 不受此限。Read→read_file · Write→write_file · Edit→edit_file · Bash→bash_command · Glob→glob_files · Grep→grep_content · 看本地图 read_images。bash_command 是 cmd.exe:一条命令一次调用;别用 ; 串(要串用 &&);别加 | cat / ; type ... / $? 这类尾巴(返回已含 stdout+stderr+exit code);路径用正斜杠、含空格的值用双引号。goal 规划工具:focus 只认计划里的纯数字序号(goal(focus="4"),不是标题);goal(done="总结") 完成当前焦点(须先 focus 成功)。报错只改成纯数字重试一次,别反复换写法(会死循环烧回合)。procedure-dsl/(不是 spec/),spec 文档里的相对链接(extraction/x.md)读取时补 spec/ 前缀。read_file 有 2000 字/行、50KB/次 上限会静默截断长内容,长文本用 quote-source.py(读全文件)别 read_file 通读。整个流程围绕一个文件 workflow.json 滚动演化——从第一阶段搭好骨架,后面每阶段都在它上面就地补字段,不另存新文件。
第一阶段 · 搭骨架
1.0 读懂案例 → 调 plan_procedures 工具交计划(工序/每工序步骤展开/章节认领)
(Cyber 引擎: 工具校验完整性后自动生成 workflow.json 骨架; 其他引擎: 写 understanding.md 再建)
1.1 在骨架上填 value/directive(别增删工序/步骤)
1.3 把步骤间的数据流连起来 → 改 workflow.json(补"来源/去处")
第二阶段 · 归类标注
· 作用/动作/类型 归到分类词表
· 实质/形式 直接提炼元素点(不查词表)
· 自造类型登记 → 写进 type_registry
· 每步填目的列(intent)
第三阶段 · 检查收尾
· 跑 lint-case.py 检查
· 跑 render-case.py 渲染出网页
把本文读一遍,之后别再回头读它——需要某阶段细节时直接读对应的阶段文件。再把 tools.md 读一遍(后面要调的脚本接口都在那),也别重读。
上下文是累积的,你读过的文件内容会一直留在记忆里。不要因为某处写了"详见 tools.md"就又去读一遍;不要进了第二阶段又去重读阶段文件。需要时从记忆里翻,别重复 Read——那会白白浪费预算。
要做的事:通读案例 → 想清楚有几个工序、每个工序怎么走 → 切步骤 → 填骨架 → 把数据流连起来。
| 该读(还没读过的) | 干嘛用 |
|---|---|
| extraction/phase1-skeleton.md | 第一阶段具体怎么做 + 字段填写规则 |
input/case-{N}-raw.json |
案例原文(你的素材) |
产物(写到 outputs/case-{N}/):workflow.json——先调 plan_procedures 工具交计划(几个工序 / 每工序步骤展开 / 每工序认领哪些 0N 章节),Cyber 引擎据此自动生成骨架;再用 wf-patch.py 在骨架上填 value/directive、补"来源/去处"(别增删工序/步骤)。
过关条件:workflow.json 自查通过(数据流都连上了、类型一致)→ 进第二阶段。
要做的事:把"作用/动作/类型"归到分类词表;把"实质/形式"直接提炼成元素点;填每步的目的列。
| 新读 | 干嘛用 |
|---|---|
| extraction/phase2-normalize.md | 第二阶段怎么做 |
| taxonomy/effect.json / action.json / type.json | 三张分类词表 |
产物:用 wf-patch.py(--set/--patch)在 workflow.json 上补 作用/动作/类型 + 实质/形式 + 自造类型登记 + 目的列。别 edit/手写改它,也不要另存新文件。
过关条件:每步的作用/动作都命中标准词、每个类型要么命中词表要么登记了挂靠、每步该有的实质/形式都提炼好、每步都有目的列 → 进第三阶段。
要做的事:跑检查 → 渲染出网页(脚本自动组装,不另存中间文件)。
| 新读 | 干嘛用 |
|---|---|
| extraction/phase3-finalize.md | 检查清单 + 脚本命令 |
| format/case-data.schema.json | 字段的机器清单(最终裁判) |
脚本命令:
# 1. 检查 + 自动登记新类型(轻量, 不卡流程); 带 --source 才查「章节覆盖」+「value 逐字」
python spec/tools/lint-case.py --workflow outputs/case-{N}/workflow.json --case-id {N} \
--source input/case-{N}.json --ocr outputs/case-{N}/_scratch/ocr.txt
# 2. 渲染网页(脚本在内存里组装好数据直接出 HTML)
python spec/tools/render-case.py \
--workflow outputs/case-{N}/workflow.json \
--source-input input/case-{N}-raw.json \
--page-title "Case {N} · <主题>" \
--case-id {N} \
--out outputs/case-{N}/case-{N}-<slug>.html
产物:outputs/case-{N}/case-{N}-<slug>.html(唯一产物)。
workflow.json(及 understanding.md 草稿,若有)要重读;spec 没变,不用重读。某阶段的过关条件过不去 → 别硬闯下一阶段,回当前阶段修。修两次还过不去 → 在产物里挂个 inferred: true, inferred_reason: "反复过不去, 需人工看" 标记,再往下走。