DAG 任务调度:Flowness 如何找出能开工的任务,优先推进最长依赖链

先检查前置工作是否完成,再在可运行任务中安排优先级,避免并发数量增加了,项目仍卡在同一条链上。
Flowness 会把一项较大的工作拆成多个任务,交给不同 AI Agent。例如开发上传功能时,可以拆出接口约定、后端实现、联调和帮助文档。任务之间存在先后关系:联调需要后端完成,后端需要接口约定明确,帮助文档则可能在接口确定后就开始。
这些关系组成一张有向无环图,通常简称 DAG。箭头从前置任务指向依赖它的后续任务。本文的调度机制接收这张图、已发生的任务结果和可用执行名额,返回本轮可以派发的任务。它要同时回答两个问题:谁已经具备开工条件,以及名额不够时先做谁。
前置任务成功,后续任务才进入可运行集合
把所有尚未完成的任务都交给 Agent,会产生大量无效等待。后端接口还没确定,联调 Agent 就只能猜接口,或者反复询问前置工作进度。任务表看起来都在运行,真正的依赖却没有被消除。
Flowness 先计算可运行集合。一个任务的前置条件必须得到满足,才能成为派发候选。这里需要区分成功完成与仅仅结束运行:一次失败的执行退出,不会自动释放后续依赖。经核实已经在别处完成的任务,也可以通过相应状态满足前置条件,但不能仅凭某个进程不见了就算完成。
这一步与常见的拓扑调度相通。Python 的 graphlib 文档[1] 也把获取可运行节点和登记节点完成分开:调度器需要持续接收完成信息,才能放出下一批节点。图提供顺序约束,运行事实决定约束何时被满足。

名额有限时,先派整张计划里最长依赖链上的任务
现在看四项工作:A 是接口约定,B 是后端实现,C 是联调,D 是帮助文档。依赖关系为 A→B→C,以及 A→D。A 完成后,B 和 D 都能开始,但如果只剩一个执行名额,先启动哪一个会影响后续工作何时有机会开始。
当前实现计算从各节点出发的最长依赖链。它先把任务排成前置在前的顺序,再从末端往回推:没有后继的任务,链长是 1;其余任务的链长,等于它后继里最长的那条再加 1。每个任务只算一次,结果留给前面的任务用。A→B→C 包含三个节点,A→D 包含两个节点,前一条因此成为最长链。常驻编排进程批量派发时,在已经合格的候选中,最长链上的任务排在前面;这个例子中就是 B。这条最长链按整张计划算,已经完成的任务也计在内,不随进度重算还剩多少工作。假如最长链上只剩最后一项没做,另一条较短的链三项都还没开始,派发时仍先考虑最长链上那一项。
这里的优先级不能越过开工条件。C 虽在最长链上,只要 B 尚未成功,C 仍不能启动。实际批次还要接受并发上限、模型档位容量和冲突检查。最长链告诉调度器值得优先推进哪里,并不授予任务绕过其他限制的资格。
如果存在两条同样长的路径,两条路径上的任务都算在最长链上,派发时同样优先;同一级的任务再按任务编号排序。相同输入因此总得到同样的派发顺序,便于追查调度为什么这样安排。
这里的“最长”按任务数量计算,不是预测真实工期
理解这项实现最需要守住的区别,是链长与耗时。当前算法把每项任务视作一个单位,按节点数量比较路径。代码中虽然使用了 makespan 这个字段名,其数值并不是预计几分钟完成。
如果 B 只需一分钟,D 却要两小时,上面的数量排序就可能低估 D 的影响。真实工期还与任务持续时间、资源争用、失败概率和可并行程度有关。若要据此预测交付时间,需要额外收集并验证这些信息,不能把无权重图中的最长链直接宣传成最优排程。
这种简化仍有用:当没有可靠耗时估计时,它提供了一个可解释的优先规则,比任意顺序更明确。但它是一项启发式选择,具体项目是否因此更快,需要相同任务条件下的对照。
计划撤掉一条依赖,调度也必须读到这个变化
图算法之外,还有一处很容易影响真实执行的细节。工作计划会变化:某项前置关系后来被确认不再需要。若调度器只读取最早新增的箭头,没有扣除已撤销的关系,后续任务就会一直等一项已经不存在的要求。
当前代码让依赖图读取统一的“当前有效依赖边”。最长链分析与开工判断都应围绕这张实际生效的图展开。源码记录过撤边未进入可运行集合的修复背景,这说明调度正确性同时依赖算法和状态来源。
图中的环需要由另一个计划检查拒绝:计划冻结时检查一次,冻结后再加依赖时也检查。有环的图里没有哪条链称得上最长,最长链函数遇到环直接报错,不给出半截结果;派发这一步接住错误后,这张计划的任务都不再按最长链优先,只按编号排。等长路径很多时,给人看的列表最多展示 16 条,同时记下总数;派发用的是所有最长链经过的任务,不受这个上限影响。
可解释的调度,需要同时展示约束与选择理由
这篇核对了最长链计算、派发消费方与已有测试,没有将它们换算成线上工期改善。这套排序写在常驻编排进程的批量派发里;按节拍逐个派发的那条路径不按最长链排。9 月 20 日起,执行任务的自动派发处于暂停。我们能够具体解释一次选择:B 的前置工作已经成功,它处在更长的依赖链上,并且通过了本轮资源与冲突检查,所以获得一个执行名额。
这样的解释对人和 Agent 都有价值。任务没有启动时,可以追到尚未满足的依赖;任务被优先派发时,可以看到它会解锁哪些后续工作。并行执行因此有了明确的秩序,后续再引入耗时估计,也有一套能够比较和修正的基础。
更正(2026年10月26日):此前的小标题写“优先考虑仍有较长后续工作的路径”,读起来像调度比较的是每条路径上还剩多少活。实际派发用的最长链按整张计划算,已经完成的任务也计在内,不随进度重算。另外,正文原来写的算法(沿后继任务向下搜索)、等长路径(保留全部备选)和遇环处理(把回边当作终点,仍返回一个结果),是 9 月 15 日代码的写法;9 月 21 日这段代码已重写,现在先排好前置在前的顺序再从末端往回推,遇环直接报错,等长路径最多展示 16 条,派发用的是所有最长链经过的任务。“派发时最长链优先”也只发生在常驻编排进程批量派发时,按节拍逐个派发的路径不看最长链,执行任务的自动派发 9 月 20 日起暂停。小标题和相关段落已改,并补了这些说明。
引用链接
[1] graphlib 文档: https://docs.python.org/3/library/graphlib.html