| name | task-decomposition |
| tier | meta-meta |
| description | Decompose each verification rule into independent sub-tasks and assign the optimal method (rule, code, LLM, manual) to each. Use when converting extracted rules into implementation plans, when a rule skill is too expensive or inaccurate and needs restructuring, or when designing a multi-step verification pipeline. Covers MECE decomposition, method selection via the four-dimension decision matrix, cost-benefit analysis, and source tagging. Also use when auditing an existing workflow for cost optimization opportunities. |
任务分解与方法分配——柳叶刀方法
为什么叫「柳叶刀」
外科手术用柳叶刀,不用斧头。核查任务的分解也是如此——每一刀都要精准,切在方法论的边界上,而不是随意劈开。
每条核查规则看起来是一个动作,实际上是一条操作链。以「核查发票日期是否在合同有效期内」为例,实际包含:定位发票日期字段 → 提取日期值 → 标准化日期格式 → 与合同日期比对 → 生成批注。这五个步骤各自需要不同的处理方法。
把整条链丢给 LLM 处理当然能跑通。但成本是精细分解方案的 100 倍,出了问题也无法定位是哪个环节错了。柳叶刀方法的核心:把每条规则切到方法论上同质的最小单元,然后为每个单元分配最便宜且能胜任的方法。
MECE 分解原则
将核查规则分解为子任务时,必须满足 MECE(互斥穷尽)原则:
互斥——任意两个子任务不做重复的事。如果子任务A提取了发票日期,子任务B就不应再提取发票日期。重复意味着浪费和潜在的不一致。
穷尽——所有子任务合在一起覆盖整条规则。不能有遗漏。如果依次执行所有子任务,规则应当被完整核查。
每个子任务有且仅有一个输入和一个输出。上一个子任务的输出是下一个子任务的输入。这形成了一条接口清晰的流水线。
何时停止分解
当子任务在方法论上是同质的——即只需一种方法就能完成——就停止分解。如果一个子任务仍然需要两种不同的方法(比如先用正则提取,再用 LLM 判断),说明它还不是原子级别,需要继续切分。
标准分解链
大多数文档核查规则的分解遵循这条链路:
定位 → 提取 → 标准化 → 判定 → 批注
不是每条规则都有全部五个阶段。有些规则不需要标准化(提取值已经是目标格式),有些通过时不需要批注。但这条链路是可靠的起始框架。
流水线拓扑
根据规则类型不同,流水线有三种典型拓扑:
- 线性:单文档、单字段。
定位 → 提取 → 标准化 → 判定 → 批注。大多数阈值检查属于此类,如资本充足率核查。
- 汇聚:来自不同文档或不同章节的两个字段。两条平行的定位-提取链在判定步骤汇合。发票金额与合同金额的交叉验证属于此类。
- 扇出:一条规则应用于文档内的多个条目(如验证发票中的每一行项目)。定位步骤产出 N 个条目,每个条目独立流过后续链路。此时规模维度至关重要——如果 N 很大,方法分配必须考虑单条目成本。
分解示例:发票核查
以「核查发票金额是否与合同约定一致」为例,完整分解如下:
| 序号 | 子任务 | 输入 | 输出 | 分配方法 | 依据 |
|---|
| 1 | 定位发票金额字段 | 发票全文 | 字段位置 | 正则 | 增值税发票金额字段位置固定,标记明确 |
| 2 | 提取发票金额 | 定位到的区域 | 数值(浮点数) | 正则 | 格式可预测:¥xxx,xxx.xx |
| 3 | 大写金额交叉校验 | 小写金额 + 大写金额文本 | 一致/不一致 | Python | 中文大写转数字后比对 |
| 4 | 定位合同金额条款 | 合同全文 | 段落文本 | LLM (Tier 3) | 合同格式多样,金额条款位置不固定 |
| 5 | 提取合同金额 | 定位到的段落 | 数值(浮点数) | 正则 + Python | 正则提取数字,Python 处理万/亿单位换算 |
| 6 | 金额比对 | 发票金额, 合同金额 | 通过/不通过 | Python | 纯算术比较(允许配置容差范围) |
| 7 | 生成批注 | 所有提取值 | 批注字符串 | 模板 | 「发票金额 {X} 元与合同约定 {Y} 元不一致,差异 {Z} 元」 |
七个子任务中只有一个需要 LLM 调用。其余全部由正则或 Python 代码完成,成本为零。
决策矩阵
分解完成后,为每个子任务分配处理方法。分配依据四个维度的综合评估。完整的决策矩阵及详细用例参见 references/decision-matrix.md。
四维度定义
| 维度 | 含义 | 低(1-2) | 中(3) | 高(4-5) |
|---|
| 确定性 | 输入格式的可预测程度 | 自由文本,无固定格式 | 半结构化,已知章节但格式多变 | 固定模板,字段位置精确 |
| 规模 | 每份文档需处理的条目数量 | 1-5 个 | 10-100 个 | 1,000 个以上 |
| 语义深度 | 需要的语言理解程度 | 零——纯模式或数值 | 中等——实体识别、简单上下文 | 深度——判断、充分性评估、意图推理 |
| 成本敏感度 | 单文档预算约束 | 不限(一次性审计) | 中等(每月数百份) | 紧张(每日数千份) |
方法优先级
始终优先选择最便宜的能达到准确率要求的方法:
规则/正则 → Python 代码 → LLM 调用 → 人工审核
不允许跳级。先试正则,不行再试代码,代码不行再试 LLM,LLM 不行才上人工。每次升级必须有下级方法失败的证据。
注意四个维度之间存在交互作用。高规模加高成本敏感度会强力推动选择代码方案,即使中等语义深度在正常情况下指向 LLM。相反,低规模可以放松成本压力,使得 LLM 在理论上可以用复杂正则解决的任务上也成为可行选项。让维度的组合引导你,不要被任何单一维度绑架。
决策速查表
| 确定性 | 规模 | 语义深度 | 成本敏感度 | 推荐方法 |
|---|
| 高 | 任意 | 低 | 任意 | 正则 / 规则 |
| 高 | 任意 | 低 | 任意 | Python 代码 |
| 中 | 高 | 低 | 高 | 代码 + 正则 |
| 中 | 低 | 中 | 低 | LLM |
| 低 | 任意 | 高 | 任意 | LLM |
| 低 | 高 | 高 | 高 | 低层级 LLM + 抽样校验 |
| 任意 | 任意 | 任意 | — | 人工(兜底) |
成本效益意识
方法分配不是纸上谈兵,它直接决定了生产环境的单文档成本。
实战案例:发票匹配合同
某企业需要将 31,800 张发票与 15,940 份合同进行匹配。暴力方案:对 5.07 亿个配对全部调用 LLM 比对。
按 SiliconFlow API 定价(以 Qwen2.5-7B 为例,输入 ¥0.35/百万 token,输出 ¥0.35/百万 token),每次比对按 500 token 输入 + 100 token 输出计算:
- 单次调用成本:约 ¥0.00021
- 5.07 亿次调用:约 ¥106,470
十万元人民币只为了做配对匹配,这在任何业务场景下都不可接受。
柳叶刀方法的分层方案:
| 层级 | 方法 | 输入规模 | 输出规模 | 消减率 | 成本 |
|---|
| 1. 供应商名称 + 合同编号精确匹配 | 正则 | 5.07 亿对 | 25,200 匹配 | 99.5% | ≈ ¥0 |
| 2. 金额区间(±5%)+ 日期重叠 | Python | 剩余未匹配 | 12,400 候选 | 97.6% | ≈ ¥0 |
| 3. 行项目描述语义比对 | LLM (Tier 3) | 12,400 候选 | 7,652 确认 | 精准过滤 | ≈ ¥170 |
| 4. 低置信度匹配人工审核 | 人工 | ~200 不确定 | ~200 解决 | 兜底 | ≈ ¥700 |
总成本:约 ¥870。是暴力方案的 1/122。准确率相同,可调试性更强。
成本计算模板
在分解阶段就估算单文档成本:
| 子任务 | 方法 | 单次成本 | 每文档调用次数 | 小计 |
|---|
| 定位章节 | LLM Tier 3 | ¥0.001 | 2 | ¥0.002 |
| 提取字段 | 正则 | ¥0.000 | 5 | ¥0.000 |
| 标准化 | Python | ¥0.000 | 5 | ¥0.000 |
| 交叉比对 | Python | ¥0.000 | 1 | ¥0.000 |
| 语义判定 | LLM Tier 2 | ¥0.003 | 1 | ¥0.003 |
| 批注生成 | 模板 | ¥0.000 | 1 | ¥0.000 |
| 单文档合计 | | | | ¥0.005 |
将单文档成本乘以预期文档量,与开发者用户确认预算是否可接受。如果超预算,优先优化成本最高的子任务——通常是单次调用最贵或调用次数最多的 LLM 步骤。
核心原则:先廉后贵
永远把便宜的过滤器放在前面。让正则和代码先消减 90%+ 的工作量,只把剩余的疑难部分交给 LLM。这不仅降低成本,还提升了调试能力——因为到达 LLM 步骤的数据已经被前序步骤严格筛选过。
来源标记
每个子任务的输出必须携带 extraction_method 字段。这不是可选的元数据——这是核查系统的承重结构。
来源标记支撑三项不可或缺的能力:
调试定位——当核查结论出错时,标记告诉你是哪个子任务、哪种方法产生了错误。没有标记,你面对的是一个黑盒。例如:金额比对失败,标记显示 extraction_method: regex,说明是正则提取阶段出了问题,而非判定逻辑有误。
成本归因——标记让你精确计算每种方法的实际成本贡献。哪些 LLM 调用在消耗预算?哪些正则步骤在免费贡献准确率?这些数据驱动优化决策。
置信度校准——不同方法有不同的可靠性特征。正则提取的结果非对即错,没有中间地带。LLM 提取的结果有置信度分布。来源标记直接输入 confidence-system 的方法先验(method prior),使置信度分数具备校准基础。
标记规范
在项目范围内保持一致的标记值:
| 标记值 | 含义 |
|---|
regex | 正则表达式提取 |
python_calc | Python 计算或转换 |
llm_tier1 ~ llm_tier4 | 对应层级的 LLM 调用 |
template | 模板填充(批注生成等) |
manual_review | 人工审核 |
多智能体协同 —— 不要用锁
如果一个任务大到你打算用 agent_tool 起多个子智能体并行做,按独立单元分片(一个规则一个子智能体、一份文档一个子智能体),让子智能体之间不需要通过共享可变文件来协同。
来自一个友邻团队的失败教训:他们让所有子智能体平等,通过共享协同文件领任务,并加锁防抢占。两类失败必然出现:
- 锁被持有太久,或者干脆忘了释放。即便锁机制工作,二十个智能体的吞吐会下降到只有两三个的水平 —— 大部分时间都在等。
- 系统脆弱:智能体可能在持有锁时崩溃,或重复获取自己已经持有的锁,或干脆不获取锁就更新协同文件。
KC 偏好的两种模式:
- 单调度器 ——
TaskManager 一次发一个任务给主 conductor。无锁,无 peer 协同。这是 ralph-loop 的默认架构。
- 按单元分片 —— 用
agent_tool 起子智能体时,每个子智能体只负责一个不重叠的切片(一规则、一文档)。子智能体的状态写到自己的 sub_agents/<taskId>/ 下,共享产物(rule_skills/<id>/、workflows/<id>/)按规则路径分开。Block 11 的 git 自动提交把共享路径的写入序列化,按规则分片让"后写覆盖前写"不再是问题。
如果两个本应并行的子智能体非要相互通信才能推进,那它们其实应该是一个任务(顺序跑)或一条流水线(父智能体发完 A 再发 B),而不是平行的 peer。
反模式
LLM 万能论
把整份文档丢给 LLM,提示词写「请检查是否符合规则X」。演示时效果漂亮,生产中成本灾难。
典型案例:某项目将发票号码格式校验交给 LLM 处理。发票号码是固定的 20 位数字,一条正则 ^\d{20}$ 就能搞定。LLM 每次调用消耗 2000 token,准确率反而低于正则(LLM 偶尔会「理解」出不存在的格式问题)。成本差异:正则 ¥0 vs LLM ¥0.001/次 × 10 万张 = ¥100。
诊断信号:如果一个子任务的输入格式完全可预测,且不需要语义理解,它不应该用 LLM。
正则过度工程
为了覆盖所有可能的日期格式写了 500 行正则,结果维护成本极高,遇到新格式就崩溃。
诊断信号:如果正则需要频繁修补或超过 3 行,考虑这个子任务是否应该升级到 LLM 处理。
黑盒流水线
子任务之间没有中间输出,只有最终结论。出错时无法定位是哪个环节的问题。
典型案例:资本充足率核查流水线输出「不通过」,但无法区分是提取的资本充足率数值有误、还是比较逻辑有 bug、还是文档中根本没有这个字段。
诊断信号:如果调试一条规则需要从头到尾重新跑一遍,说明缺少中间检查点。
巨石端到端
不分层,每个子任务都对每份文档执行,即使前序步骤已经可以短路。
典型案例:贷款申请交叉验证流水线对每份申请执行完整的七步核查,即使第一步「定位」就发现文档中缺少相关章节。合理做法:定位失败 → 直接输出「字段缺失」→ 跳过后续步骤。
诊断信号:如果流水线对明显不适用的文档也消耗完整的处理资源,说明缺少短路逻辑。
过早优化
在核查逻辑还没验证正确之前就花大量时间优化方法分配。
正确顺序:先全部用 LLM 跑通 → 在 Samples/ 上证明核查逻辑正确 → 再逐个子任务尝试推向更便宜的方法,每次替换后验证准确率保持不变。先对再便宜,不能反过来。分解本身是最难的部分,方法分配随时可以调整。
与其他技能的衔接
任务分解在 KC 生命周期中处于规则提取和技能编写之间。
输入:来自 rule-extraction 的规则目录。每条规则是一个原子级、可测试的核查要求。如果规则尚未达到原子级别,先退回给规则提取环节做进一步分解,再进入任务分解。
输出:每条规则的子任务分解清单——每个子任务包含定义好的输入、输出和分配方法。这份分解清单直接输入 skill-authoring,成为技能文件夹的实现蓝图。分解清单同时也是测试契约:每个子任务的输出都可以独立测试和验证。
方法分配同时指导 skill-to-workflow 中的层级选择。当技能被蒸馏为工作流时:
- 正则/代码子任务 →
scripts/ 中的代码
- LLM 子任务 →
prompts/ 中的提示词
- 人工子任务 → 质量控制层的升级路径
分解方案不是一成不变的。通过 evolution-loop 的测试迭代,你会发现某些方法分配需要调整——以为是确定性的子任务出现了需要 LLM 处理的边界情况,以为需要 LLM 的子任务其实一条正则就能解决。更新分解方案,用 version-control 记录变更。
如果你发现一条规则很难分解为干净的子任务,这通常意味着你还没有充分理解这条规则。回到开发者用户那里去。问他们人工核查这条规则时的实际操作步骤。他们的人工流程往往就是最佳的分解蓝图——它揭示了再多抽象分析也无法发现的自然子任务边界。