phase2-normalize.md 10 KB

阶段二 · 归一化与标注 (主 Agent fan-out 2 个子 Agent)

工作模式 (并行子 Agent 架构): 为了彻底隔离重负载上下文(含 Base64 图片和 Phase 1 漫长推理历史)并最大化提取效率,主 Agent 必须使用 Task 工具将 Phase 2 任务分流给 2 个并行的子 Agent

  • 主 Agent 绝对不要自己去跑这几十次 taxonomy-lookup 查询和 wf-patch 校验。
  • 物理切片与图片分流:主 Agent 在启动 Phase 2 时,必须先运行物理切片脚本:

    python spec/tools/prepare-subtask.py --workflow outputs/case-N/workflow.json --source input/case-N.json --out-dir outputs/case-N/_scratch
    

    该脚本会自动在 outputs/case-N/_scratch 目录下生成最小化的子任务定义文件 task_2a.jsontask_2b.json

    • 图片及数据无缝对齐:为了让子 Agent 具备完美的上下文感知能力,task_2a.jsontask_2b.json 不仅包含了精简的 IO 变量与步骤信息,还在根节点内置了当前 Case 的完整图集 image_url_list,并为每一个具体的 IO 变量对象级关联了其对应的 related_images 引用数组(如识别 图05 等关联图片)。
    • 子 Agent 采用极简 Context(2k tokens,不载入大图),只读取当前 {case_dir}/_scratch/task_2a.jsontask_2b.json,跑完查询后只返回标准的 patch_2a.jsonpatch_2b.json 补丁内容。
    • 主 Agent 只负责运行切片脚本、唤醒子 Agent 并行作业,最后收集子 Agent 返回的补丁文件(或 patch 文本段),通过 wf-patch.py --patch 一体化应用落盘。

    子 Agent 召唤机制与指令示范 (主 Agent 必看): 在 run_procedure_dsl.py 环境中,我们已通过编程式(agents 字段)在 Claude SDK 中预先注册了两个常驻子 Agent 角色:

    1. phase-2a-normalizer (作用/动作/类型归一化专家)
    2. phase-2b-matcher (实质/形式词表精确匹配专家)

    主 Agent 在运行中可通过以下两种极其优雅的方式激活并分流任务:

    🟢 方式一:显式指定调用(推荐,指向性极强)

    直接在 prompt 里命令子 Agent 工作,或在 Tool call 中指定其名称调用:

    // 1. P2A 子 Agent 显式召唤示例:
    Agent(
    subagent_type="Explore",
    description="召唤 phase-2a-normalizer 专家处理 2A 任务",
    prompt="请 phase-2a-normalizer 子 agent 立即读取 outputs/case-N/_scratch/task_2a.json 任务文件,结合 spec/ 里的 effect.json、action.json、type.json,为各步骤和 IO 变量完成作用/动作/类型归一化。完成后请在 outputs/case-N/_scratch/ 下写入标准的 patch_2a.json 并向我汇报具体修改项。"
    )
    
    // 2. P2B 子 Agent 显式召唤示例:
    Agent(
    subagent_type="Explore",
    description="召唤 phase-2b-matcher 专家处理 2B 任务",
    prompt="请 phase-2b-matcher 子 agent 立即读取 outputs/case-N/_scratch/task_2b.json 任务文件,使用 spec/tools/taxonomy-lookup.py 工具查询词表,查出各 IO 变量最精准的 substance/form 路径。完成后请在 outputs/case-N/_scratch/ 下写入标准的 patch_2b.json 并向我汇报具体修改项。"
    )
    

🔵 方式二:自动触发(编程式 description 自动匹配)

由于 SDK 在后台配置了 AgentDefinition,主 Agent 在发起一般的 prompt 对话时如果提到相关职责,Claude SDK 也会基于 descriptions 自动路由调用:

Use the phase-2b-matcher agent to run taxonomy lookup for task_2b.json and generate patch_2b.json

这样:

  • 单一真理源, 没有 phase 间复制 → 零冗余
  • 主 Agent 只出并行分发与合并决策,极速降本 90%+,速度提升 10x
  • Resume 中断时看 workflow.json 里某 step 是否有 effect 字段就知道 Phase 2A 做没做
2A 子 Agent — 作用 / 动作 / 类型 归一化

context: workflow.json + spec §A.1 (作用树, 9 叶) + §A.2 (动作树, 5 L1 + 控制) + §A.3 (类型树, 4 大类) + 现有 type_registry — 全部小, 整体进 context。

任务:

  • 作用归一: 每步的"在哪个工艺位置"映射到 §A.1 叶子之一 (工艺规约 / 预准备 / 预处理 / 主体生成 / 装配 / 后期 / 配套伴生 / 检验 / 交付)。必须命中, 不命中 = 抽错回 1.3
  • 动作归一: 每步的 action 自然语言映射到 §A.2 树路径 (e.g. 提取/化学提取/反推生成/元素生成); 控制类 控制/并行 控制/遍历 自动转到 特性 字段
  • 类型归一 (对每个 input/output 的 type, 走以下 funnel 不要省步骤):

    1. 候选扩展: 基于 IO 的 name + value 描述, 显式列出 3-5 个候选 type 词 (不是只猜一个).

      • e.g. value=<图: 苏晚 25 岁年轻女性肖像, 卧室床上> → 候选: [参考图, 主角图, 人物肖像, 人物参考, 分镜图]
      • 候选要覆盖不同抽象层: 通用 (参考图) + 具体 (主角图) + 邻近 (分镜图), 让匹配有挑选余地
    2. 字典匹配 (按优先级, 命中即停):

      • a. 命中 §A.3 字典树叶子: 候选里有哪个直接命中叶子 (type.json $leaves)? 用它 (e.g. 候选含"参考图" → 命中)
      • b. extends 桥接: 都没命中, 但有候选语义贴近某叶子 → 选最近 leaf 做 extends. Edit workflow.json, 在该 procedure 顶层加 type_registry 段 (每个 procedure 独立 type_registry, 单 case 多工序时不冲突). 格式:

        // workflow.json (部分)
        {
        "procedures": [
        {
         "id": "p1-simple",
         "name": "...",
         "type_registry": {
           "主角图": {"extends": "参考图", "desc": "case-specific 主角肖像"}
         },
         "steps": [ ... ]
        }
        ]
        }
        

        不要只在 IO item 内 inline 写 extends 子字段 — renderer 走 procedure 顶层 type_registry 找, IO inline 字段它不读.

        1. 不必手工写 type_suggestions.md: Phase 3 跑 bin/lint-case.py --workflow 时, 会自动扫每个 procedure 的 type_registry 把所有 case-specific entry record 到 spec/taxonomy/type_suggestions.md (幂等, 同 (type_name, case_id) 只写一次). 你只要保证 procedure.type_registry 每个 entry 含 extends + desc 就行, suggestions 文件让工具维护.

        2. lint 自查 (轻量自检, 真正校验由 Phase 3 lint-case.py 跑):

        3. [ ] 每个 IO 的 type 要么命中 §A.3 叶子 (type.json $leaves), 要么所在 procedure 的 type_registry 段里有该 type 的 extends + desc 项

        4. [ ] 走 2b 的所有新 type 都在对应 procedure 的 type_registry 段出现

        5. [ ] 不允许 type 字段写"自由名"但所在 procedure 的 type_registry 缺对应 entry — 这种 silent gap 会让 Phase 3 渲染 HTML 时 drawer metadata 全丢, Phase 3 lint-case.py 会捕捉到

        输出: 写回 workflow.json (几十个字段批量用 wf-patch.py --patch, 见开头工作模式; type_registry 也能 --set p1.type_registry.X.extends=... 注册), 给每个 step 加 effect / action / feature / control 字段 + 给每个 IO 加 type 字段 (Phase 1.2 已有则归一, 没有则填). 不写新文件, 不写生成脚本.

        2B 子 Agent — 实质 / 形式 匹配

        context: workflow.json 中所有 input/output 的 name + value + 上下文 + 调 spec/tools/taxonomy-lookup.py tool. 实质·形式 JSON 词表本身不进 context, 完全通过 tool 查询。

        tool 接口: ```

spec/tools/taxonomy-lookup.py --dim {实质|形式} --list-l2 # 列二级路径 spec/tools/taxonomy-lookup.py --dim {实质|形式} --subtree # 返回子树 (叶子 + alias) spec/tools/taxonomy-lookup.py --dim {实质|形式} --match "" # 多 token 拆词聚合 (推荐用法) spec/tools/taxonomy-lookup.py --dim {实质|形式} --narrow "" # 层级下钻 (--match miss 兜底) spec/tools/taxonomy-lookup.py --dim {实质|形式} --validate # 校验路径存在 ```

任务 (对每个 input/output value):

  1. 从 value 抽 2-5 个 ≥2 字描述性 token, 一次调用 --match "tok1 tok2 tok3" (空格分隔, tool 自动拆词聚合)
    • 好例: value=<图: 苏晚 25 岁年轻女性肖像, 卧室床上>--match "年轻女性 卧室 床上 肖像"
    • 不要再一个个单 token 试错 — tool 内部已经做了拆词聚合 + coverage bonus
  2. --match 返 (无匹配) → 同 query 切 --narrow 走层级下钻 (按子树整体语义打分)
  3. 上面拿到 top 候选后, 用 --subtree <候选> 列叶子细节, 选最贴的
  4. --validate <chosen_leaf> 确认前再写回; 完全无法匹配 → unmatched
  5. 抽象容器/纯工具参数 → null (不标)
    • 警惕过度标注 null:诸如 产品需求结构化提示词分层提示词 等文本,虽然物理形式上是“文本”,但它们在实质(Substance)上承载了具体的业务内容(例如:提示词若描述冲锋衣,实质即为 /理念/知识/商业/产品服务/产品特征;若描述登山场景,则为 /理念/知识/商业/产品服务/使用场景)。它们决非纯工具参数,必须为其匹配对应的实质路径。
    • 仅有完全不含具体业务实体、纯粹的技术参数(如逻辑判断布尔值、循环索引 i、纯控制流指令等)才允许设为 null

避坑: 不要把整段 value 描述塞进 --match (e.g. --match "苏晚 25 岁年轻女性, 卧室床上, 湿发素颜"), 标点会被当 token 一部分干扰拆词. 提炼 2-5 个干净的描述性词组就够.

输出: 写回 workflow.json (几十个 IO 批量用 wf-patch.py --patch, 见开头工作模式; substance/form 会自动走 taxonomy-lookup 校验, 设 null 传 __null__), 给每个 IO 加 substance: /xxx/yyy 或数组 ["/xxx/yyy", "/zzz"] + form: /xxx/yyy 或数组 (或 null). 不写新文件, 不写生成脚本。对于复杂内容,推荐在 CLI 传入时用 + 连接多条路径 (如 /表象/视觉/人物 + /表象/视觉/空间),wf-patch.py 会自动校验并将其作为 JSON 数组保存至 workflow.json 中。