taskflow

验证

taskflow 如何在花 token 之前验证一张 DAG。

你正要跑一个 fan-out 五百个文件的 flow。按下回车之前,一个合理的问题是:你怎么知道它不会死循环、引用一个不存在的阶段、或者花一个小时才发现最后一步里有个笔误?

taskflow 在花任何一个 token 之前回答这个问题。verify 动作对你的 flow 定义跑一遍静态分析——不启动任何 agent、不跑任何模型、不碰网络。它是"我写了点 JSON"和"我要为一百次子代理调用付钱"之间的安全网。

这张安全网

随时都能跑,对已保存的 flow 或内联定义都行:

验证一个已保存的 flow
/tf verify review-changes
验证一个内联 flow
/tf verify define='{ "name": "hello", "phases": [{ "id": "greet", "type": "agent", "task": "Say hi." }] }'

验证分两个阶段。第一个验证你定义的schema 和结构——每个字段的形状、每个引用是否存在、有没有循环。如果通过了,第二个对 DAG 本身跑一组图分析 pass:死胡同、不可达阶段、能把整次运行困住的 gate。

两个阶段都是纯函数。它们只花一点 CPU。

那个经典错误

下面这个 flow 一眼看去是对的。仔细读 report 阶段:

review-broken.json
{
  "name": "review-broken",
  "phases": [
    {
      "id": "audit",
      "type": "agent",
      "task": "Audit src/ for security risks."
    },
    {
      "id": "report",
      "type": "agent",
      "task": "Summarize the audit:\n{steps.audit.output}",
      "final": true
    }
  ]
}

report 插值了 {steps.audit.output},但它从没声明 "dependsOn": ["audit"]。没有这条边,运行时把两个阶段当作独立的:reportaudit 并行跑,看到的是字面字符串 {steps.audit.output},把一个占位符而不是结果交给模型。这是最常见的编写错误,而且运行时是静默的——模型对着垃圾输入尽力而为。

验证在任何东西跑之前抓住它:

验证输出
Schema validation failed:
Phase 'report': task references {steps.audit.*} but 'audit' is not
reachable via dependsOn. The phase will run in parallel with 'audit'
and see the literal placeholder. Add "dependsOn": ["audit"] (or include
'audit' transitively).

修法就是报错建议的那一行:

review-fixed.json
{
  "id": "report",
  "type": "agent",
  "task": "Summarize the audit:\n{steps.audit.output}",
  "dependsOn": ["audit"],
  "final": true
}

可达性检查是传递的。如果 report 依赖 summarize,而 summarize 依赖 audit,那么 report 可以自由引用 {steps.audit.output}——验证会顺着链条走。

例外是 join: "any"。一个只需要其依赖中一个完成的阶段,可以把其他阶段作为信息性上下文引用,所以它豁免于可达性规则。

schema 阶段抓住什么

第一阶段是绝大多数日常错误浮现的地方。它在图 pass 之前检查定义是否良构:

  • 循环——一个阶段不能传递依赖自身。用拓扑排序检测;报错会点名这条环。
  • 重复 id——两个阶段共用一个 id 会在 step map 里静默互相覆盖。
  • 未知阶段类型——把 "type": "map" 写错(其实你想写 "map")会被拒绝。
  • 缺失引用——dependsOnfrom 里的每个 id 都必须存在。
  • 插值损坏——{steps.X.*} 引用必须能通过 dependsOn 到达,如上所示。
  • 按类型的要求——map 需要 overtaskparallel 需要 branchesreduce 需要 fromtaskflow 需要 usedef 之一;loop 需要 taskuntil
  • 多个 final——至多一个阶段可带 final: true
  • 契约和缓存形状——expect 要求 output: "json";缓存 fingerprint 必须用已知前缀;retry 和 timeout 值必须在范围内。
  • 命名——阶段 id 和 agent 名用连字符而非下划线,这样 {steps.audit-each.output} 才能保持一致。

这些里任何一个失败,验证就停在这里。你会拿到所有问题的列表,而不是只有第一个。

图阶段抓住什么

当 schema 干净时,验证转向 DAG 本身。这些是更微妙的问题——一个 flow 可以合法但仍然出乎意料

死胡同

一个没有依赖者、又没标记 final 的阶段,算出一个没人读的结果:

死胡同警告
Phase 'draft' is a terminal phase (no dependents) but not marked as
'final'. Its output will be discarded. Add "final": true or a
downstream phase that depends on it.

这是警告,不是错误——运行仍能工作——但它几乎总是意味着你忘了 final: true 或一条 dependsOn 边。

不可达阶段

如果一个阶段被接进图里却和主 DAG 断开,它永远跑不了:

不可达错误
Phase 'cleanup' is disconnected from the main DAG. Add a 'dependsOn'
edge to connect it, or remove it.

一个完全没有边的独立阶段是没问题的——它是一个合法的独立入口。只有那些边却待在自己断开连通分量里的阶段才会被标记。

能困住你的 gate

一个 gateapproval 是到达 final 阶段的唯一路径时是危险的。一旦它 block,整次运行就停住,没有替代路径:

gate 耗尽警告
Gate 'verify' is the sole path to final phase(s) 'ship'. A block here
halts the entire flow with no alternative route. Consider adding a
bypass or marking the flow's structure as intentional.

验证还会标记守卫矛盾:两个阶段有相同依赖却带对立的 when 条件(==!=),保证其中一条分支永远不跑。有时这是故意的;验证只是想让你确认。

预算现实检查

如果你设了 budget,验证估算跑每个阶段的最低成本,并在上限低于它时警告:

预算溢出警告
Budget cap (50000 tokens) is below the estimated minimum of ~120 tokens
for 120 phase(s). The flow will likely be truncated before completion.

loop 和 tournament 按其预期迭代/变体数计算,所以一个 maxIterations: 6loop 不会被当作单次调用。

验证抓住结构性问题。它无法告诉你一个子代理会不会产出正确内容、一个 JSON schema 是否过宽、或一个 gate agent 会不会意外 block。这些运行时关注点由 retryoptionalonBlock 处理——见控制流参考。

当 flow 由 LLM 生成时

有些 flow 是手写并经人审查的。另一些由 flow { def } 阶段在运行时生成——一个 LLM 撰写子流程,使它的内容不可信。

对那些生成的子流程,验证施加了第二层加固,是手写 flow 永远看不到的。一个模型发出失控图的爆炸半径是有界的:

  • 每个子流程至多 100 个阶段
  • 每次 fan-out 至多 200 个 map 条目
  • 至多 16 个并发子代理。
  • 至多 5 层嵌套子流程。
  • cwd 必须留在运行目录内——不能逃到兄弟或父目录。
  • 不允许 script 阶段(shell 执行对模型来说太强)。
  • 不允许 code-compilesregex 评分 gate 类型(编译器执行和 ReDoS 风险)。

这些都不适用于你自己写的 flow。它存在 solely 是为了让一个生成的计划不会——不管有意还是恶意——DoS 你的机器。

把它纳入你的循环

验证便宜到可以在每次保存时跑。很多团队把 /tf verify 接进 pre-commit 钩子或 CI 步骤,让一个坏 flow 永远到不了运行。零 token 保证意味着不跑它没有理由。

下一步

Last updated on

这页内容对你有帮助吗?

帮助我们改进文档,或在社区中提问。

On this page