# 脚本怎么用(工具手册) > 跑这道 skill 时会用命令行调用几个脚本,这里讲它们**怎么用**。脚本都在 `spec/tools/` 下,**你不用读脚本源码,会调就行**。 ## 1. `spec/tools/wf-patch.py` — workflow.json 安全批量字段设置器 **做什么**:批量给 `workflow.json` 的步骤/输入输出填字段(第一阶段连"来源",第二阶段填作用/动作/类型/实质/形式)。**凡是"给一批字段赋值"都用它,别写 Python 脚本去拼/改 workflow.json**(脚本拼 JSON 容易把文件弄坏)。分工:**你出判断(哪个字段填什么),工具负责安全写入 + 当场检查对错**。 **两个保证**: - **不会写坏文件**:由工具读取→修改→写回,你从不直接碰 JSON 文本。 - **当场检查,有错全不写**:每条赋值都立刻对照词表/类型登记/格式校验;**只要有一条不合法,就报出是哪条、整批都不写**(不会产出悄悄出错的文件)。检查规则和最后的 lint 一致——**这里通过了,lint 一定过**。 **用法**: ```bash # 单条 / 多条 --set (path=value, 只在第一个 = 处切; value 可含 = 和空格, 整体加引号) python spec/tools/wf-patch.py --workflow outputs/case-{N}/workflow.json \ --set 'p1.s1.inputs[0].anchor=← s0.主角图' \ --set 'p1.s2.effect=主体生成' \ --set 'p1.s2.action=生成/图像生成/文生图' # 批量: 几十处 anchor / 字段一次过 — 写一份 patch 清单 (你仍逐条显式决策每个值), 一条命令应用 python spec/tools/wf-patch.py --workflow outputs/case-{N}/workflow.json --patch _scratch/anchors.json # anchors.json = [{"path":"p1.s1.inputs[0].anchor","value":"← s0.x"}, ...] # 只校验不写 python spec/tools/wf-patch.py --workflow ... --set '...' --dry-run # 透传回填: anchor 设好后, 自动把"原样透传"的 value/directive 从源逐字抄过来 python spec/tools/wf-patch.py --workflow outputs/case-{N}/workflow.json --resolve-passthrough # 建骨架: 缺失的 procedure/step/IO 元素**默认自动创建 (upsert)**、文件不存在也从空建 —— 不必 write_file 手写嵌套 JSON python spec/tools/wf-patch.py --workflow outputs/case-{N}/workflow.json --patch _scratch/skeleton.json # skeleton.json = 扁平 [{"path":"p1.s1.inputs[0].type","value":"参考图"}, ...]: 列全所有 step/IO 路径, 一条命令建出整个骨架 # (你只写扁平清单——工具负责拼成合法嵌套 JSON; output 的 id 会自动补 sNo1) # ⚠️ 默认自动建; 只有「纯填已存在结构、想抓路径 typo」时才加 --no-create (路径不存在就报错) # @quote 回填 --resolve-quotes: value/directive 写短标记 @quote|起锚|止锚 (或 @quote|关键词), # 工具顺标记从 --source 原文 / --ocr 配图文本匹配真实内容【批量替换】(省得手动粘长内容; 跟 anchor patch 一起跑) python spec/tools/wf-patch.py --workflow outputs/case-{N}/workflow.json --patch _scratch/anchors.json \ --resolve-quotes --source input/case-{N}.json --ocr outputs/case-{N}/_scratch/ocr.txt ``` > **`@quote` 回填(强烈推荐填真实 value/directive 的方式)**: 别手动 quote-source 再粘长内容——直接把 value/directive 的值设成 `@quote|<起锚>|<止锚>`(两个独特短串, 框出原文那段)或 `@quote|<关键词>`(命中落 JSON 块返回整块, 否则返回所在行/段), 然后跑 `--resolve-quotes --source <原文> [--ocr ]`, 工具按空白无关匹配从原文/配图把真实内容**一次性批量**替换进去。匹配不到会 `⚠` 提示(标记原样留着, 回去改锚点)。 **`--resolve-passthrough`(省得重复抄内容)**:规矩是"值"要写真实内容,不能写"(同上)""见 s1o1"这类引用(那是"来源"该干的,lint 会报)。但同一段内容被后面好几步原样复用时,手抄好几遍很烦。用这个:**只在源头那个输出里填一次真内容**,其余引用处把"来源"设成 `← 源的编号`,工具就顺着编号把真内容**逐字抄到**每个"值"还空着的输入(链式引用也会一路抄到底)。源头找不到的会用 `⚠` 提示。可单独跑,也可跟在 `--set/--patch` 后面。 **路径语法** (**文档统一用 id 式** `pN.sM.字段`: proc 用 id `p1`、step 用 id `s1`、`inputs/outputs` 才用 `[i]` 下标、嵌套步 id 带点如 `s2.1`。**下标式 `procedures[N].steps[M].inputs[i].字段` 工具也接受**——镜像 workflow 结构时不会挂): | 路径 | 设的字段 | |---|---| | `p1.s2.effect` | step 标量 (effect / substance / form / via / action / directive / kind / intent / group) | | `p1.s1.inputs[0].anchor` | IO 字段 (anchor / type / value / id) | | `p1.s2.1.outputs[0].type` | 嵌套步的 IO | | `p1.s2.focus` | step 的 focus 数组 (逗号分隔: `focus=via,action,out-type-0`) | | `p1.purpose` | procedure 头部 (name / purpose / category / platform / author) | | `p1.declarations.inputs[0].desc` | declarations 内任意字段 (通用下钻) | | `source.url` | case-level 原帖信息 (platform / author / date / url / title / excerpt) | | `p1.type_registry.场景图.extends` | 注册 case-specific 类型 (自动建 type_registry 段) | ⚠️ **`--set` 别用单引号包参数**(Cyber 引擎跑 cmd.exe,**不剥单引号** → 路径会带个 `'` → 报「找不到 procedure id='p1」)。工具已兜底自动剥成对引号,但仍建议:值含空格/特殊字符用**双引号**,或干脆把一批改动写进 `--patch` 清单文件(JSON,无引号坑)。 **`--unset PATH`** (删字段, 可重复): 删掉某字段, 取代手 Edit 删. e.g. `--unset p1.declarations.inputs[0].inferred` (declarations 不收 inferred, schema 会拒). 字段本就不存在 → 跳过 (幂等, 不报错). **各字段校验规则**: - `effect` → effect.json 叶子 (给全路径会自动归一到叶名) - `action` → action.json 叶子 / 叶路径 (给叶名自动展开成全路径) - `type` → **自由文本, 不校验**(Phase 1 随便起描述标签;归一到标准叶子 / 注册 type_registry 是 Phase 2 的事,最终由 lint Check 1 + render schema 兜底) - `extends` → 必须桥到 type.json 叶子 - `substance` / `form` → 自由提炼的元素点 (字符串或数组), **不查词表、不校验**; `url` → 自由文本; 三者均可传 `__null__` 设 JSON null - `anchor` → 必须 `←` (输入引用, 指向某 output id) 或 `→` (输出去向) 开头 - `kind` → {step / block / nested} - `focus` → 逗号分隔 → 数组 (空串 → `[]`) - 其余 (name / value / intent / via / desc / purpose / ...) → 自由文本, 不校验 **骨架创建推荐用 `--create`**(免手写嵌套 JSON, 见上)。**仍可用 Write 从 template 写骨架, 但弱模型易漏逗号崩 JSON, 不推荐**。 改字段 / 删字段 / 改 source 现在都走 wf-patch (`directive` 也是普通标量字段, 直接 `--set`), **不要再 Read→Edit 改 workflow.json** (那会反复重读、烧 token). **退出码**: `0` 全通过并写入 (或 `--dry-run` 通过) / `1` 有校验失败 (整批未写) / `2` CLI 错 / 文件不存在 / JSON 损坏. --- ## 1.5 `spec/tools/quote-source.py` — 原文片段捞取 (填 value / directive 用) **做什么**: value / directive 要填原文真实内容(那段 JSON 风格分析、那句完整提示词)时, 用它按字符匹配从原文 case json 把那段**逐字捞出来**再粘进 workflow.json。别凭记忆缩写、别写 `` 这种空壳。 **匹配空白无关**: 原文 body_text 常有乱换行 / 前导空格, 工具匹配时忽略所有空白, 但返回的是**原文逐字片段**。 **用法**: ```bash # 普通: 按关键词捞周边 (默认前后各 300 字) python spec/tools/quote-source.py --source input/case-{N}.json --query "视觉风格分析" # 捞整段 JSON: 命中点落在 {...} 内时返回整块 (适合捞结构化分析) python spec/tools/quote-source.py --source input/case-{N}.json --query "视觉风格分析" --json-block # 捞一句完整提示词当 directive python spec/tools/quote-source.py --source input/case-{N}.json --query "结构化数据的形式" # 范围引用: 用首尾两个短锚点引出之间的整段长原文 (比 --window 猜长度精确, 适合整段 JSON / 多段提示词) python spec/tools/quote-source.py --source input/case-{N}.json --from "请以 JSON" --to "500 字以内" python spec/tools/quote-source.py --source input/case-{N}.json --from "视觉风格分析" --to "电影感人像" # 放宽窗口 (关键词模式) python spec/tools/quote-source.py --source input/case-{N}.json --query "主色调" --window 800 # 连配图 OCR 文本一起搜 (prompt/JSON/参数常只在截图里, 不在 body_text) python spec/tools/quote-source.py --source input/case-{N}.json --query "主色调" --ocr outputs/case-{N}/_scratch/ocr.txt ``` > **关于 `--ocr`**: runner 执行前会把每张配图 OCR 成文本落到 `outputs/case-{N}/_scratch/ocr.txt`。带上 `--ocr <该文件>` 就能在原文 body_text **和图片文字**里一起搜(命中分别标 `[原文]` / `[配图OCR]`)。 **小贴士**: query 用**一段独特的短语**(4-10 字)最稳; 整句带标点时可能因全/半角差异落到 `~approx`(仍返回最接近的一段, 核对即可)。 **退出码**: `0` 有命中(含 approx) / `1` 找不到 / `2` CLI/IO 错。 --- ## 2. `spec/tools/render-case.py` — 阶段三 workflow.json → HTML 渲染 + schema 校验 **用途**: 阶段三. 接收 `workflow.json` (多工序 `procedures:[]`), 在内存组装成 case_data (merge `--source-input` 原帖 + `--page-title` + `--case-id`), 跑 schema 校验, 渲染输出 HTML. **不落盘 case_data.json**. **Agent 用法** (推荐): workflow.json 已含全部 procedures + 标注后: ```bash # 1. 只校验, 看 schema 错 (建议带 --source-input, 让校验看到的就是 merge 后的最终版) 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} \ --validate # 2. 校验通过后渲染输出 (--source-input 必带, 否则 HTML 折叠原文区只剩 60 字 excerpt) 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}-.html ``` **关于 `--source-input`** (2026-05-22 新增): renderer 直接从原帖 raw json 抽 `body_text` + 封面 + 图集兜底, in-place 填到 `case_data.source` —— Agent 不必手工复制原文内容. 行为: - `body_text` / `cover_image` 直接覆盖 case_data 同字段 - `title` / `url` 仅在 case_data 缺时填 - `excerpt` / `author` / `date` / `platform` **不动** (那些是 Agent 推断的友好版本) - **图集兜底**: 检查 raw.body_text 已 inline 的 `[image:URL]` 标记, 把 raw.image_url_list 里没 inline 也不是封面的图 append 到 body 末尾 (加 `--- 附图 ---` 分隔符). 适配"小红书短文 + 多图独立列" 和 "微信公众号长文 + inline 图" 两种平台 **输入契约**: 见 [`spec/format/case-data.schema.json`](format/case-data.schema.json) (canonical JSON Schema Draft 2020-12, 受控字段 + enum + 条件约束 `if/then`). **模板**: 见 [`format/workflow-format.md`](format/workflow-format.md) 末尾「附:骨架模板」, 复制后替换 `<填:...>` 占位符. ⚠ **绝对不要参考其他 `outputs/case-*/` 下的产物文件当模板** — 那些是 case-specific 产物. 唯一 canonical 骨架在 `format/workflow-format.md` 末尾. **输出码**: - `0` — 成功 (渲染或校验通过) - `1` — IO / schema / 渲染异常 - `2` — CLI 参数错误 **依赖**: - 必需: `spec/tools/renderer.py` (跟本脚本同目录, 自动 import) - 可选: `jsonschema` (装了用 Draft 2020-12 完整校验; 没装走 minimal check fallback) --- ## 3. `spec/tools/lint-case.py` — 轻量 lint + 自动 record 新 type **用途**: 阶段三. 跑完 `render-case.py` 后跑一次. 干六件事: 1. **type 完整性 hint**: 扫 workflow.json 各 procedure 的 IO type 字段, 找出"用了 case-specific type 但该 procedure 的 `type_registry` 漏注册 / 缺 extends / 缺 desc"的情况, 打 stdout 给 Agent / 用户看 2. **value 自包含 hint**: 扫每个 IO 的 value + 每个 directive, 揪出「(同 sN 输出)」「见 sN」「← sN」这类**引用占位** —— spec 要求 value 逐字回填数据本身 (引用归 anchor), 这种占位 schema/type 检查抓不到. 命中会提示跑 `wf-patch.py --resolve-passthrough` 自动回填 3. **value/directive 真实性 hint**: 揪出 `<…>` 占位 value、以及工具步骤(`via` 是具体工具)**缺失的 directive**, 提示用 `quote-source.py` 从原文/配图 OCR 捞真内容回填(弱模型常把 value 全填 `<…>` 占位、漏 directive)。 4. **章节覆盖 hint (结构强制, 需 `--source`)**: 按原文 `0N` 章节标号切段, 逐章节算骨架覆盖率, 把整段漏抽的章节(<40%)报出来 —— 弱模型"按成品图扫"常漏掉**无独立成品图的框架/案例章节**. 缺 `--source` 则跳过. 5. **value 逐字 hint (值强制, 需 `--source`)**: 文本类 value 应是原文里**一整段连续文本**; 算"最长连续命中原文"比例, <80% 判**缩写/改写/截断**(典型: 抄了开头第一句、后面用原文小标题拼盘)并报出, 提示用 `@quote` 重填. 缺 `--source` 则跳过. 6. **副作用: auto-record**: 把各 procedure.type_registry 里的 case-specific entry 自动 append 到 `spec/taxonomy/type_suggestions.md` 累积条目段 (Agent **不必手工 Write** suggestions, 工具代劳) **幂等**: dedup key = `(type_name, case_id)` 二元组. 同 case 重跑不重复; 不同 case 同名允许 (跨 case 频次是升级信号). **用法**: ```bash # 推荐: 带 --source 原文(+ --ocr 配图文本)才会跑「章节覆盖」+「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 # 不带 --source: 只跑前 3 项 + record (结构/值强制跳过) python spec/tools/lint-case.py --workflow outputs/case-{N}/workflow.json --case-id {N} # 只检测不写 python spec/tools/lint-case.py --workflow outputs/case-{N}/workflow.json --case-id {N} --no-record ``` **输出范例** (case 含 silent gap 的情况): ``` [lint] case-1 (workflow.json) · type 完整性: 2 个提示 - [p1] step[1].outputs[0].type='主角图' 是 case-specific 但该 procedure 的 type_registry 没注册 - [p1] step[8].inputs[1].type='主角图' 是 case-specific 但该 procedure 的 type_registry 没注册 · 无新 type 可 record (type_registry 为空 — 全部 type 命中字典叶子) ``` **退出码**: 始终 0 (不阻塞流程). 解析 2 = CLI 参数错 / 文件不存在. **设计哲学**: 不严格. 检测项是 hint 不是 fail; record 是副作用不是核心契约. Agent 看 stdout 决定回不回去补 case_data.type_registry. --- ## 4. `spec/tools/renderer.py` — HTML 渲染主模板 (Python 模块) **做什么**:把数据渲染成网页的主程序,被 `render-case.py` 在内部调用。 **你不用读它**——通过 `render-case.py` 调用即可。要了解格式,看 [`format/case-data.schema.json`](format/case-data.schema.json)(机器清单)和 [`format/workflow-format.md`](format/workflow-format.md)(格式说明)就够了。 --- ## 5. `run_procedure_dsl.py` — runner 本身 (不在 skill 内) 跑 Agent 的入口脚本. **Agent 不读它** — 它在跑 Agent. 但 Agent 可能想知道**自己被怎么起的**: - OAuth Max 模式 (走 ~/.claude 凭证, 不计 API 费) - `allowed_tools = ["Read", "Write", "Edit", "Bash", "Glob", "Grep"]` # 单 Agent 全程, 不开 Task/Agent (第二阶段不再用子助手, 见 §7) - `permission_mode = "bypassPermissions"` (Agent 全自动, 不停下来问) - `cwd = procedure-dsl/` (Agent 的工作目录) - `--resume` 支持 (中断后续跑) - runner 实时把每 turn 写到 `outputs/case-{N}/_trace.md` --- ## 6. 文件路径约定 (per case) | 路径 | 内容 | 谁负责 | |---|---|---| | `input/case-{N}-raw.json` | 原 case 素材 (title / link / body_text / image_url_list / ...) | 人 (Agent 读, 不写) | | `outputs/case-{N}/` | Agent 工作目录 (一 case 一目录) | Agent (写产物) | | `outputs/case-{N}/.session_id` | SDK session UUID, 用于 `--resume` | runner 写, Agent 不动 | | `outputs/case-{N}/_trace.md` | runner 写的实时执行流水 | runner (Agent 别 Read) | | `outputs/case-{N}/_scratch/` | **sanctioned scratch 区** — 只用于 dump 大 Bash 输出 (taxonomy --subtree 长结果 / find 结果) 之后 Read 切片, 或一次性 smoke test. runner 预创建. **不要用项目根的 `scratch/`**. ⚠ **不是写 build/normalize 脚本生成 workflow.json 的地方** —— 那个用 §2 `wf-patch.py` | Agent (随便 dump, 跟着 case 一起清理) | | `outputs/case-{N}/understanding.md` | 阶段一 1.1 心智模型 (含多工序判断) | Agent | | `outputs/case-{N}/workflow.json` | **唯一中间产物** — Phase 1.2 用 `wf-patch.py --create` 建骨架 (扁平清单, 免手写嵌套 JSON; 也可 Write 模板但易错), 之后**逐字段演化**: 批量结构化字段 (1.3 anchor / 2 effect·action·type + step 级 substance·form) 用 `wf-patch.py`, 单处零星用 Edit. **绝不写 Python 脚本生成 / 批改它**. 符合 case-data.schema.json (`procedures:[]`) | Agent | | `outputs/case-{N}/case-{N}-.html` | 阶段三 .html 输出 (跑 render-case.py 生成; **唯一产物, .md 已取消**) | render-case.py | --- ## 7. 第二阶段不再用子助手 第二阶段(作用/动作/类型 归类 + 实质/形式 提炼)现在**由主流程自己一趟做完**——不再切任务、不再召唤子助手、不再查"实质/形式"大词表(实质/形式 改为自由提炼元素点)。具体见 [phase2-normalize.md](extraction/phase2-normalize.md)。