← 全部文章

改了一个输入,哪些工作需要重来?

用一个五步流程,拆开输入变化、结果变化与强制重跑,解释 taskflow 的增量重算及其边界。

本地教学模型 不调用模型

增量重算Recompute

改一次摘要,看看哪些步骤重做,哪些结果保留。

示例节点 / 5已完成 5重算 0复用 0截断 0

基线已完成。选择一个情景,从这份基线观察哪些节点需要重新计算。

选中节点

01 · 读取资料

已完成

固定资料已读取,作为摘要与链接两条分支共同的基线输入。

当前输出资料:增量计算只处理受影响的部分;参考链接 /notes/incremental。

高亮连线:变化传播 · 虚线:提前截断点击节点查看原因

一个多步 Agent 任务已经完成。现在,你改了其中一个要求。

全部重跑容易理解,却会重复已经完成的工作。直接复用旧结果又可能把旧判断带进新输出。需要先回答两个问题:谁依赖了这次变化?它收到的输入是否真的不同了?

taskflow 的增量重算把这两件事分开:先找出可能受影响的范围,再按依赖顺序检查实际输入。上面的交互图把这个过程缩成了五步,两个按钮分别表示“输出发生变化”和“重新执行后输出相同”。

这是固定情景的本地教学模型,不调用模型。图中的节点数量只说明这个示例的行为,不代表真实运行的成本、耗时或性能成绩。单独打开实验。

五步流程,两个分支

假设任务是阅读一份资料,生成摘要,检查链接,再汇总结果:

阶段工作读取什么
source读取资料资料内容
draft生成摘要source 的输出、摘要要求
links检查链接source 的输出
review审查摘要draft 的输出
report汇总结果review 与 links 的输出

这张图有两条分支。摘要经过审查进入报告;链接检查独立进行,最后也进入报告。模型假设这些依赖已被完整记录,缓存可用,而且所有阶段都没有需要重复执行的外部副作用。

这里刻意选择一个小流程。只有把每一步读什么写清楚,才能讨论“少做几步”是否合理。

摘要要求改了

点击“摘要改了”,模型会改变 draft 的摘要要求,并假定重新执行得到不同的摘要。

source 读取的资料没有变化,links 使用的资料也没变。这两步可以复用。draft 要重新执行;review 收到了新的摘要,要重新审查。模型同时假定审查结果变化,因此 report 也要重新生成。

结果是三个阶段重做、两个阶段复用。这只是这个具体情景的结论。实际运行中,如果审查输出最终相同,报告阶段仍可能复用,不能提前把整个下游都当成必然重新执行。

重新执行,输出却没变

“结果没变”表达另一种情况:要求 draft 再执行一次,但它产出的摘要与上次相同。

这时 draft 确实执行了,review 的有效输入却没有改变。在缓存键的其他部分也不变、旧结果可用的前提下,review 可以直接沿用原结果。report 随后检查自己的输入,也可以保留旧输出。这两个节点都在传递影响范围内,因此在 taskflow 报告中都标记为提前截断。

taskflow 把这种停止向下重算的行为称为 early cutoff。它比较的是实际输入身份,不是人认为两段文字“意思差不多”。如果输出多了一句解释,或者参与输入指纹的其他内容发生变化,是否还能复用就需要重新判断。

这个区别也影响接口设计。把可供下游消费的结构化结果单独输出,通常比逐层转交整段自由文本更容易解释依赖。但真实模型仍可能产生不同的结果,系统不能预先承诺一定命中缓存。

强制重跑和检查范围

用户有时会明确要求重新检查某一步,即便输入没变。重算允许把这个阶段指定为种子,强制它重新执行。下游仍需逐个判断,不能因为种子重跑过就一律失效。

在 Pi 宿主里,可以先检查范围:

/tf recompute <runId> draft

不带 --apply 时,这一步生成报告,不调用模型。它能给出可能受影响的范围,却不能提前知道 draft 下一次会输出什么。

确定要执行时再运行:

/tf recompute <runId> draft --apply

报告中的 rerun 表示重新执行;reused 表示影响范围外直接复用;cutoff 表示在影响范围内,但实际输入未变,提前截断并保留结果。后两者都没有重新执行,报告仍会区分它们的原因。在“结果没变”的教学情景中,计数是重算 1、复用 2、截断 2。

检查范围与真正执行是两种操作,干跑里可能受影响的节点不等于最后必然重跑的节点。

图上的边还不够

一个 Agent 可能读取额外文件、共享上下文或外部状态。如果这些依赖没有进入记录,缓存命中只能说明“记录过的输入没变”。

taskflow 的文档区分声明的引用和执行时观测到的读取。声明可以覆盖尚未触发的依赖,观测记录则补充实际读取的上游及版本。过期检测保守地考虑这些关系,再按依赖顺序重新检查输入。

这套记录也有边界。当前文档列出了共享上下文、子 flow、上下文文件预读和特定非 steps 插值等无法充分追踪的情况。对这样的运行,--apply 会被阻止;仍可干跑查看范围,或者调整流程后重新运行。

文件、提交等外部变化也需要正确的指纹声明。缓存不会自动感知整个外部世界,更不能把“有一份输出”当成某次写文件、发消息等副作用已经安全完成的证明。

这些保守处理会牺牲一部分复用机会。我认为这个取舍值得保留:如果系统解释不了结果为何仍然有效,跳过执行就缺少依据。

三种操作,三个问题

操作回答的问题是否产生新的模型输出
Resume:续跑暂停或失败之后,从哪里继续?需要执行的阶段可能会
Recompute:重算条件变化后,哪些旧结果需要更新?应用重算时可能会;干跑不会
Replay:离线重放如果当时门槛或预算不同,会怎样裁决?不会,只重新检查已记录事件

taskflow 的 replay 不会生成新输出,也不会修改已保存的运行。缺少事件日志时会失败;需要新模型输出的变化,会要求实际重跑。

这几个名字容易混淆。把它们区分开,才能知道自己是在继续工作、更新结果,还是检查过去的一次决定。

继续看实现

本文依据 2026 年 10 月 2 日可见的项目文档整理,具体能力与限制以后可能改变。教学模型刻意省略了真实执行器、缓存存储、并发和副作用处理。

如果你有一个“看起来能复用,实际上会失效”的流程,欢迎带着可复现例子到 Issues 讨论。这样的反例能直接帮助检查依赖边界。