上期讲 reference 该拆到哪一层。这期讲那个 scripts/ 目录里到底该放什么——答案是验证器。官方 pdf / docx / xlsx / pptx 四个 skill 的校验脚本逐个打开看,包括它们互相矛盾的退出码。
批量改 50 个表单域、给一份合同加红线、往财务模型里铺一排公式——这类活的共同点是:模型一边想一边改,错了你还不知道错在哪一步。官方给这类活起了个名字,叫 plan-validate-execute:先让模型把打算做的事写成一份结构化文件,用脚本把这份文件骂一遍,过了才允许动原件。Skill authoring best practices 里有一整节叫 Create verifiable intermediate outputs 专门讲它。本期不停在文档上——把 anthropics/skills 里真实存在的八九个校验脚本一个个打开,看它们怎么写报错、什么时候退 1、什么时候明明报错却退 0。
best-practices 那一节把场景写死了四项:Batch operations, destructive changes, complex validation rules, high-stakes operations. 举的例子也很具体——按一张表格去更新 PDF 里 50 个表单域,没有校验的话模型可能「引用不存在的域、造出互相冲突的值、漏掉必填项、或者把更新打错位置」。流程官方写成五步:analyze → create plan file → validate plan → execute → verify。
# pdf / docx / xlsx / pptx 四件套 —— 本期主角全在这里
/plugin marketplace add anthropics/skills
/plugin install document-skills@anthropic-agent-skills
# skill-creator / mcp-builder 等(03 节会用到)
/plugin install example-skills@anthropic-agent-skills
不装 plugin 也行——这四个 skill 目录可以直接拷进 ~/.claude/skills/ 或 .claude/skills/,scripts/ 里的 Python 文件本来就是独立可跑的。什么时候别装:你根本不碰 Office / PDF 文件,那这四个装了纯交 metadata 税;但 scripts/ 里的写法值得读,本期就是干这个。
skills/pdf/forms.md 开头第一句就是 CRITICAL: You MUST complete these steps in order. Do not skip ahead to writing code. 四条命令,在 skills/pdf/ 目录下跑:
# ① 探:有没有可填写域(注意:文档里这行漏了 .py,见 04 节)
python scripts/check_fillable_fields.py form.pdf
# ② 析:把真实域抽成 JSON —— field_id / page / rect / type
python scripts/extract_form_field_info.py form.pdf field_info.json
# ③ 计划:你自己写 field_values.json,field_id 必须与上一步一致
# [{"field_id":"last_name","description":"...","page":1,"value":"Simpson"}]
# ④ 执行:脚本先校验 ID 与值域,不过就 exit 1,不写输出
python scripts/fill_fillable_fields.py form.pdf field_values.json out.pdf
第 ④ 步是整套模式的缩影:fill_fillable_fields.py 在写任何字节之前先对着 field_info.json 把你的计划核一遍,报错长这样——ERROR: Invalid value "X" for radio group field "citizenship". Valid values are: [...]。它把合法取值全列出来,正是官方那条 implementation tip 的实现:报错要 verbose 到模型能照着改。
field_id 能,「把标题改得更专业些」不能。差别是「错误在哪一刻被发现」。A 类的代价是你得先有一份可枚举的真实清单(PDF 的域表、表格的行边界);没有这份清单,计划里的键就没法比对,模式退化成一场仪式。B 类不需要事先枚举,代价是原件已经被改过了——docx 的做法是用 --original 兜底:python scripts/office/validate.py out.docx --original doc.docx 会把 XSD 检查基线对到原件上,原件自带的错就不算你的,还有 --auto-repair 修常见问题。
fill_fillable_fields.py 做的三件事就是:ID 在不在真实域表里、page 对不对、value 落不落在 checked_value / radio_options 这些枚举里。三件事全是集合运算,一行散文都做不到。check_bounding_boxes.py 只做两种检查——框相交、框高小于字号——但报错里带上了你自己写的 description 字段:FAILURE: entry bounding box height (12) for Last name entry field is too short for the text content (font size: 14). Increase the box height or decrease the font size. 既点名又给修法。它超过 20 条就停:Aborting further checks; fix bounding boxes and try again。check_bounding_boxes.py 的相交检查同时用到 entry_bounding_box 和 label_bounding_box。只写 entry 不写 label,它就查不出「文字压到标签上」这类错——而这恰好是最常犯的一类。计划的 schema 少一个字段,验证器就瞎一只眼,而且不会告诉你它瞎了。python scripts/extract_form_structure.py in.pdf form_structure.json # 标签/横线/复选框坐标
# 你写 fields.json;pdf_width/pdf_height 表示 PDF 坐标,image_* 表示像素坐标
python scripts/check_bounding_boxes.py fields.json # 先校验,再填
python scripts/fill_pdf_form_with_annotations.py in.pdf fields.json out.pdf
python scripts/convert_pdf_to_images.py out.pdf verify/ # 最后看图
同目录还有一个 create_validation_image.py,把 entry 框画红、label 框画蓝叠在页面图上——forms.md 里没提它,但它是「让模型用眼睛验计划」最直接的一个工具。什么时候别用这条链路:PDF 本来就有可填写域(先跑 check_fillable_fields.py 判一下),手算坐标纯属自找麻烦。
unzip -q doc.docx -d unpacked/ && find unpacked -type l -delete
python scripts/merge_runs.py unpacked/ # 先合并碎片 run,否则文字在 XML 里不是连续串
# 编辑 unpacked/word/document.xml —— 不要重排、不要 pretty-print
(cd unpacked && zip -Xr ../out.docx .)
python scripts/office/validate.py out.docx --original doc.docx --author "Reviewer"
merge_runs.py 替你干掉的那件事,是 Word 把一句话切成一堆 <w:r> 导致「你看得见的短语在 XML 里并不存在」。它是 plan 的前置条件:不先归一化,计划里写的定位字符串根本匹配不上。--author 则是一个很妙的 plan 校验——它报出「你改了文字却没包 <w:ins>/<w:del>」的地方,官方原话说这种错 is easy to do by accident and invisible in the accepted view。出清洁版用 accept_changes.py,但它和 pandoc --track-changes=accept 都有同一个已知缺陷(删掉段落标记时留下空段落),官方自己写在 SKILL.md 里。
skills/pptx/scripts 里 clean.py 的说明只有一句硬约束:Run after <p:sldIdLst> is final——它删的是「已经没人引用」的幻灯片和媒体,结构还在变就跑,删的可能是你下一步要用的。thumbnail.py 是选版式阶段的「看图做计划」,但第二个参数必须传:默认前缀 thumbnails 会让同目录第二份 deck 静默覆盖第一份的网格图。
recalc.py 是本期唯一被写成 mandatory 的验证器(openpyxl 写公式不带缓存值,不重算的话 pandas 读回来全是 None)。但 SKILL.md 自己按了刹车:A green recalc proves your formulas evaluate, not that they are right. 差一行的区间会产出一个干净无错、数字全错的文件。它给的对策是行为上的:先写 2–3 个公式核对结果,再铺整张网格。
skill-creator 的 SKILL.md 给了两条命令:python -m scripts.run_loop --eval-set <trigger-eval.json> --skill-path <skill> 和 python -m scripts.package_skill <path/to/skill-folder>;scripts/ 下还有 quick_validate.py / run_eval.py / aggregate_benchmark.py。它强调的顺序是 GENERATE THE EVAL VIEWER BEFORE evaluating inputs yourself——先把判据固化成可跑的东西,再动手判。什么时候别上:只有一个 SKILL.md、你自己一个人用,写 eval 的成本大于收益。
以上全部依赖模型肯按顺序跑。真要钉死,就把它挪出模型的判断范围:Claude Code hooks 的 PreToolUse 在工具调用前触发、exit code 2 直接拦掉(官方原话:On events that can block, exit 2 blocks whether or not you print JSON),拦截理由可以用 permissionDecision: "deny" + permissionDecisionReason 精确回传给模型。PostToolUse 拦不住(工具已经跑了),exit 2 只是把 stderr 作为警告递给模型。什么时候别上 hooks:一次性任务、或者校验本身需要判断力——hooks 是确定性闸门,不是审稿人(详见 Skill 27)。
field_id、单元格地址、slideN.xml)→ 走 A 类,先校验计划。只能「看起来对」(文案、版式美感)→ 走 B 类或直接看图,别硬造 JSON。docx / xlsx 全系都是 B 类。后果很具体:你在 CI 或 hook 里写 python scripts/recalc.py out.xlsx && echo OK,一份满是 #NAME? 的工作簿会一路绿灯过去。xlsx 的 SKILL.md 明确警告过这件事——errors_found exits 0, so never treat a clean exit as a clean workbook——但 check_bounding_boxes.py 的同类行为没有任何文档提醒,是读源码才看到的。把这两个接进自动化时,必须解析它们的 stdout,不能只看 $?。
forms.md 第一条命令漏了扩展名。原文写 python scripts/check_fillable_fields <file.pdf>,仓库里的真实文件名是 check_fillable_fields.py(同目录其余七个脚本都带 .py)。照抄即 No such file or directory。analyze_form.py / validate_fields.py / fill_form.py / verify_output.py,真实 skills/pdf/scripts/ 里是 extract_form_field_info.py / check_bounding_boxes.py / fill_fillable_fields.py / convert_pdf_to_images.py。文档教的是模式,不是命令——命令永远从 ls 的输出里抄。check_bounding_boxes.py 只查相交和高度——不查你有没有漏填必填项、不查值对不对、不查字段语义。过了校验的计划仍然可以是一份完全错的计划。pdf_width vs image_width、entry_bounding_box)是验证器和执行器之间的私有契约。上游脚本改一次输出格式,你手写的那份 fields.json 就悄悄失配,而验证器只会说「没问题」。docx / xlsx / pptx 三个官方 skill 全是这么干的——它们连 plan 文件都没有。可重跑的单文件操作,这就够了。PreToolUse exit 2,不给模型跳过的机会(Skill 27)。--check 的,就别自己造中间格式——少一份要维护的 schema。convert_pdf_to_images.py / thumbnail.py / soffice.py --convert-to pdf + pdftoppm 这条路线,官方四件套全都保留了——因为有些东西只有眼睛能验。fields.json 照样能把客户的名字填进出生日期栏——因为没有任何脚本知道那一栏该填什么。校验器守的是格式,不是意图;意图仍然只能你自己看一眼。先读这三份:① skills/pdf/forms.md——全仓库唯一一份把 plan-validate-execute 从头到尾写完的文件,两条链路(可填写域 / 坐标标注)都在里面。② fill_fillable_fields.py——98 行,把「校验后再写」和「报错要列出合法取值」两件事写成了最短范例。③ best-practices 的 Create verifiable intermediate outputs 一节——模式的定名处与四条适用场景。
按需再看:check_bounding_boxes.py(独立 plan 校验器的写法,注意它退 0)· xlsx/scripts/recalc.py(产物校验 + JSON 契约 + 退出码陷阱)· docx/scripts/office/validate.py(--original 基线、--auto-repair、--author 红线检查)· merge_runs.py(写计划之前的归一化)· pptx/scripts(clean.py 的时序约束、thumbnail.py 的前缀坑)· skill-creator(run_loop.py / package_skill.py,把这套用在 skill 自己身上)· Claude Code hooks(PreToolUse exit 2 硬门)。
明确别做:给没有 oracle 的任务硬造 plan 文件 · 把验证器的退出码当真而不读 stdout · 从文档里抄脚本名(两处已证实与仓库不符)· 在 <p:sldIdLst> 定稿前跑 clean.py · 在 merge_runs.py 之前写 docx 的定位计划 · 把「recalc 绿了」当成「数字对了」。
docx / xlsx / pptx 全系都是这一类,靠 --original 或重算兜底。merge_runs.py 合并碎片 run 就是典型:不做这一步,计划里的定位字符串在 XML 里根本不存在。field_info.json / field_values.json / fields.json 三种格式的字段定义;pdf_width 与 image_width 的坐标系信号;第 4 行的 check_fillable_fields 缺 .pycheck_fillable_fields.py、extract_form_field_info.py、extract_form_structure.py、check_bounding_boxes.py、fill_fillable_fields.py、fill_pdf_form_with_annotations.py、convert_pdf_to_images.py、create_validation_image.py(最后一个 forms.md 未提及,作用是把 entry/label 框画在页面图上)recalc.py 为何强制;errors_found 退 0 的明文警告;「绿灯只证明公式能算,不证明算对」;LibreOffice 不支持 XLOOKUP / FILTER 等而必须用 INDEX/MATCH 的清单merge_runs.py → 编辑 → validate.py --original 的完整循环;--author 红线漏包检查;accept_changes.py 与 pandoc --track-changes=accept 的同一个已知缺陷clean.py 必须在 <p:sldIdLst> 定稿后跑;thumbnail.py 默认前缀会静默覆盖;validate.py --original 对模板派生 deck 的基线作用PreToolUse / PostToolUse 的可拦截性差异;exit 2 的语义;permissionDecision + permissionDecisionReason 的 JSON 回传格式(2026-09 访问)python -m scripts.run_loop --eval-set ... --skill-path ... 与 python -m scripts.package_skill ...;scripts/ 下另有 quick_validate.py / run_eval.py / aggregate_benchmark.py / improve_description.pydocument-skills 含 xlsx / docx / pptx / pdf;example-skills 含 skill-creator / mcp-builder 等 12 个;claude-api 单独一个 pluginscripts/ 那一格,本期是它的展开