这篇重点学什么
本篇重点:画出没有循环依赖的任务图(DAG),找出耗时最长的依赖路径(关键路径),再规定角色、预算和失败后受影响的任务。 安排固定角色的权限、依赖、通信与结果合并,判断是否需要多个 Agent。
开始读正文 ↓学过相近内容?查看哪些可以跳过
两条路线有些基础相同。已经学会的部分可以跳过,另一条路线只用来补你当前需要的内容。
工作流与决策搜索
共同基础任务、状态、依赖与预算是共同语言,固定调度与搜索未知动作分别学习。
另一条路线补充什么?
比较 Beam Search 与 MCTS / UCT 怎样选择候选,推演尝试新选择(探索)、利用已有好选择与搜索预算之间的关系。
多 Agent 协作
共同基础角色之间会交换信息并共享结果;工程编排不等于联合策略训练。
另一条路线补充什么?
先观察队友改变策略后,旧经验为什么可能失效;再学集中训练、分散执行(CTDE)和 COMA,检查各成员的动作对策略更新与合作收益分别有什么影响。
读完这篇,你能做什么
能比较单代理与受控协作的同组任务结果,报告质量、耗时、调用量与冲突处理。
学习理由、准备要求与知识点
为什么学它
多个 Agent 是否有帮助,要与单个 Agent 做同组任务比较,并限制总预算;任务拆分不当可能增加成本和错误。
先读这些内容
知识点清单
- 单代理基线
- 任务独立性和依赖
- 结构化计划
- 依赖无环检查
- 有限并发和全局预算
- 子任务最小上下文
- 证据合并与冲突保留
- 共享写入和重复副作用
运行前先确认完整算法使用 Java 17 标准库;JDK 源码基线为 OpenJDK 17 GA。示例是内存中的机制模型,外部服务接入需要另外配置和验证。
01先弄懂它是什么
有些工作可以交给多个 Agent 同时做,例如分别查找互不依赖的两类资料,再汇总结果。前提是每项工作能独立推进,最后也知道怎样合并。把一个含糊问题复制给更多模型,可能只会增加重复、分歧和成本。先让单个 Agent 跑通并测出问题,再判断哪些部分确实适合拆开。
能执行的计划,需要写明每个任务用什么输入、交什么结果、依赖谁、最多花多少预算。例如 B 必须先读 A 找到的证据,就不能假装它们能同时独立完成。每个子任务只获得自身需要的上下文和权限,最后由专门的汇总步骤保留证据,并说明冲突和失败,而不是简单拼接所有回答。
控制器要统一管住同时跑几个任务、总共能花多少预算、怎样取消,以及谁负责写共享资源。例如每个子任务都没超过各自十次调用的上限,十个子任务合起来仍可能超出父任务预算,因此父任务要维护总账。若多个 Agent 都想改同一条记录或同一文件,要先指定写入负责人,或要求写入时核对版本;不能各自改完再指望模型自动消除冲突。
文中这几个词,先认一认
- 依赖DAG
- 表示任务先后关系的有向无环图。沿箭头往后走的任务,必须等前面的依赖完成,图中不能绕回自己。
- 关键路径
- 按依赖关系必须串行完成、耗时最长的那条路线,它决定了任务总时间能缩短到什么程度。
- 全局预算
- 所有子任务合起来共同遵守的时间、调用次数和用量上限。
02看看它怎么工作
本题假设任务耗时已知、依赖固定且工作者无限,计算最早完成时间。没有前驱的任务从零开始,其他任务开始时间等于所有前驱完成时间的最大值,完成时间再加自身耗时。依赖最长路径决定这个理想条件下的总耗时下界。
任务A耗时三、B耗时五,C耗时二且依赖A和B,A与B并行后C在五开始,总耗时七,不是三加五加二。若实际只有一个工作者,总时间可能十;算法的七不能被宣传为任意机器资源下的可实现最优调度。
把每个任务还有几个前置任务没完成记下来,这个数叫入度。处理完一个任务,就把它的完成时间告诉后面的任务,并把相应计数减一;降到零,才说明所有依赖都处理完,可以加入待执行队列。这个过程就是拓扑排序。若最后仍有任务没有被处理,说明依赖绕成了环,应在执行前拒绝。另记下哪个前驱最晚完成,就能回溯一条关键路径,判断缩短哪项任务可能影响总时间。
正在加载流程图…
查看流程图源码(Mermaid)
flowchart TD
P["结构化计划与全局预算"] --> A["独立资料任务 A"]
P --> B["独立资料任务 B"]
A --> M["证据与冲突合并"]
B --> M
M --> V["统一验证"]
V --> W["受控提交或部分失败"]按顺序看,每一步做了什么
- 用单Agent基线确定真正可拆分的独立任务。
- 定义各子任务输入、产出、依赖与权限范围。
- 检查计划无环并计算理想关键路径。
- 在有限工作者和共享预算下调度,传播失败与取消。
- 合并证据、保留冲突,再做整体质量与成本比较。
03对照实现代码
OpenJDK 17 GA,jdk-17+35 · CompletableFuture.allOf
下方为原创 Java 教学片段,调用已核验的 JDK API 说明局部机制;不是该类原样源码,也不实现神经网络。完整程序见算法模块。
CompletableFuture<Void> joined = CompletableFuture.allOf(first, second);
joined.thenRun(() -> mergeValidatedResults(first.join(), second.join()));
// allOf协调已经创建的任务完成,不自动限制并发或合并业务证据。
// 一个子任务失败会影响组合完成状态;取消和资源清理仍需显式设计。CompletableFuture.allOf创建等待所有给定Future完成的组合阶段,返回类型是Void;业务结果仍需从各自Future读取并按契约合并。
allOf不会自动启动任意业务工作,也不为每个任务分配独立权限和预算。任务如何提交、使用哪个执行器以及并发上限,都在应用控制范围内。
异常完成与业务冲突不同。任务成功返回两份互相矛盾证据时,Future状态可能完全正常,合并器仍要保留冲突并核对来源。
本篇Java算法计算DAG关键路径,没有调用线程池或模型。它提供依赖结构的理论基线,真实并发延迟还包括资源等待、网络和调度开销。
代码先调用谁,再调用谁
- 按依赖创建或提交受限任务
- 前驱Future完成
allOf聚合依赖完成状态- 读取并验证各结果
- 按证据与冲突规则合并
04动手写一个例子
DAG最早完成时间与关键路径
输入非负任务耗时和依赖边,假设无限工作者且无调度开销,输出最早总完成时间及一条关键路径;有环拒绝。Java17 Main.java运行自测。
- 输入 1
耗时[3,5,2],0和1都指向2- 输出 1
总时间7,路径[1,2]
- 输入 2
两个独立任务3和5- 输出 2
总时间5
拓扑入度确保任务只在全部前驱都已传播信息后处理。start[v]保存目前已处理前驱完成时间最大值,最后一个前驱完成传播时它就成为正确最早开始时间。
finish[v]=start[v]+duration[v],再用它更新各后继。时间相同采用较小前驱编号稳定选择路径,不影响最早时间本身。
最终选择完成时间最大的终点并回溯前驱。若图有环,部分节点永远不会入度归零,processed<n时拒绝,不输出一个遗漏环内任务的伪完成时间。
为什么这样写是对的任务出队时所有前驱已处理,start等于其前驱完成时间最大值;每条依赖被传播一次,保存的前驱形成一条达到该时间的有效路径。
邻接表、入度、开始时间与前驱数组按图规模存储,拓扑队列每节点一次,边每条一次;不含实际任务执行。
保存为 Main.java,运行 javac --release 17 -encoding UTF-8 Main.java,再运行 java Main。程序包含固定输入自测,断言失败时会抛出错误。
import java.util.*;
public class Main {
record Plan(long finish,List<Integer> path){}
static Plan critical(long[] duration,int[][] edges){
int n=duration.length;for(long d:duration)if(d<0)throw new IllegalArgumentException("duration");
List<List<Integer>> g=new ArrayList<>();for(int i=0;i<n;i++)g.add(new ArrayList<>());
int[] indegree=new int[n],previous=new int[n];Arrays.fill(previous,-1);long[] start=new long[n],finish=new long[n];
for(int[] e:edges){if(e.length!=2||e[0]<0||e[1]<0||e[0]>=n||e[1]>=n)throw new IllegalArgumentException("edge");g.get(e[0]).add(e[1]);indegree[e[1]]++;}
Deque<Integer> q=new ArrayDeque<>();for(int i=0;i<n;i++)if(indegree[i]==0)q.add(i);int processed=0;
while(!q.isEmpty()){
int u=q.removeFirst();processed++;finish[u]=Math.addExact(start[u],duration[u]); // 时间溢出明确失败。
for(int v:g.get(u)){
if(finish[u]>start[v]||finish[u]==start[v]&&(previous[v]<0||u<previous[v])){start[v]=finish[u];previous[v]=u;} // 保存最大前驱。
if(--indegree[v]==0)q.addLast(v); // 全部依赖处理后才允许开始。
}
}
if(processed!=n)throw new IllegalArgumentException("cycle");if(n==0)return new Plan(0,List.of());
int end=0;for(int i=1;i<n;i++)if(finish[i]>finish[end])end=i;
List<Integer> path=new ArrayList<>();for(int p=end;p>=0;p=previous[p])path.add(p);Collections.reverse(path);return new Plan(finish[end],path);
}
static void eq(Plan e,Plan a){if(!e.equals(a))throw new AssertionError(a);}
public static void main(String[] args){
eq(new Plan(7,List.of(1,2)),critical(new long[]{3,5,2},new int[][]{{0,2},{1,2}}));
eq(new Plan(5,List.of(1)),critical(new long[]{3,5},new int[][]{}));
eq(new Plan(0,List.of()),critical(new long[]{},new int[][]{}));
eq(new Plan(3,List.of(0,1)),critical(new long[]{0,3},new int[][]{{0,1}}));
try{critical(new long[]{1,1},new int[][]{{0,1},{1,0}});throw new AssertionError();}catch(IllegalArgumentException expected){}
System.out.println("PASS 5 tests");
}
}这些特殊情况也要试一试
- 无限工作者是假设,有限并发可能更慢。
- 有环计划必须拒绝,不只忽略无法排序的任务。
- 零耗时任务仍可能承担依赖边界。
进阶选读 · 后续再读:资源约束项目调度
场景只有少量工作者,工具还共享API限额或写锁。
做法先保留原有任务依赖,再加上现实限制:例如只有两名工作者、某个 API 每次只能承受一个调用,或写同一资源时必须排队。可以先尝试优先安排关键路径上的任务等经验规则,但它们不保证总是最优。对小例子,可用分支定界逐步排除不可能更好的排程,求一个精确结果作对照;同时记录每项任务到底等了多久资源。
复杂度一般资源约束调度可为NP-hard,不能把DAG最长路径的线性时间结论推广到有限资源最优排程。
好处与代价并发提升速度可能增加峰值成本和冲突,更多Agent也增加合并工作,需以端到端质量和延迟衡量。
05项目里怎么用
只分派具体且边界清楚的子任务,提供完成标准、允许资料和返回结构。每个子任务返回证据与不确定点,父任务合并时核对引用和冲突,不能把多数意见当作事实投票。多个模型共享相同错误来源时,一致性并不增加真实性。
共享写入应串行化或基于版本合并,同一文件、数据库记录或操作键指定所有者。子任务失败时决定整体失败、部分结果或受限重试,并传播取消。不要让父任务已结束而后台子任务继续消耗预算或修改资源。
比较单Agent与多Agent时使用相同资料和验收集,报告总调用量、关键路径、等待时间和合并质量。速度改善可能来自更多并行资源,必须同时展示资源代价,避免只用首个子任务完成时间代表整件事完成。
容易踩的坑
- 含糊任务复制给多个Agent而没有独立产出。
- 把无限资源关键路径当成实际有限资源最优时长。
- 子任务预算独立设置,合计超出全局上限。
- 多个Agent一致就认定事实已经核实。
几种做法,怎么选
表格有四列,窄屏可左右滑动查看代价和适用条件。
| 方案 | 优势 | 代价与限制 | 适用条件 |
|---|---|---|---|
| 单Agent | 控制简单,上下文集中 | 部分独立工作无法并行 | 小任务与基线 |
| 受控并行子任务 | 可缩短独立步骤等待 | 增加资源、协调和合并成本 | 清晰可分任务 |
| 集中合并与写入 | 减少冲突,便于审核 | 合并节点可能成为瓶颈 | 多来源研究与修改计划 |
06检验是否读懂了
先自己想、自己写,再展开答案核对。场景题是假设的练习情境;遇到还没学过的内容,可以做完基础后再回来。
先用自己的话解释
01关键路径说明什么?
展开参考答案与推导
在固定耗时和依赖下无法并行压缩的最长路径,给无限资源最早完成时间;实际有限资源可能更慢。
02allOf会自动合并证据吗?
展开参考答案与推导
不会,它聚合Future完成状态,业务结果仍要按来源、权限和冲突规则处理。
03为何保留单Agent基线?
展开参考答案与推导
用于判断拆分是否真的改善质量或延迟,并揭示并发收益是否只是更多资源换来的。
换个情况,还会处理吗
01两个子任务都改同一记录,怎么办?
展开参考答案与推导
指定写入所有者或采用版本条件更新和明确合并,不能靠自然语言约定后任意并发覆盖。
02多个答案一致但都引用同一错文档,如何判定?
展开参考答案与推导
一致不代表独立证据,核对原始来源和版本;保留来源相关性,避免把重复信息当多重验证。
03父任务取消后子任务仍跑,哪里缺设计?
展开参考答案与推导
缺少任务树取消与资源清理,需传播停止并核对已经发出的副作用,不能仅隐藏UI。
写代码试一试
01并行例题为何是七不是十?
- 输入 / 条件
A3,B5并行,C2依赖两者- 输出 / 预期
7
提示 C等待最大前驱时间。
展开参考答案与推导
A在三完成、B在五完成,C从五开始到七;相加十是假定A和B串行。
02有环怎样发现?
- 输入 / 条件
A依赖B,B依赖A- 输出 / 预期
processed小于节点数
提示 入度无法全部归零。
展开参考答案与推导
拓扑队列无法处理环内节点,最终数量检查拒绝计划,而不是返回已处理部分的耗时。
03如何验证实际并发限制?
- 输入 / 条件
相同DAG仅一个工作者- 输出 / 预期
实际时间可能10
提示 增加资源模型。
展开参考答案与推导
本算法输出理想下界,另写受限调度模拟或测量真实执行,不能把七作为必须达到的运行断言。
读完之后,独立完成
课后作业:自己做一遍
本篇共 8 道作业,先完成 6 道基础与综合任务,再按前置知识挑战进阶题。其中包含 1 道思维拓展,试着换个条件,看看原来的办法是否还管用。不附参考答案。
能比较单代理与受控协作的同组任务结果,报告质量、耗时、调用量与冲突处理。
本篇重点加练
计划合法性与共享预算是多代理调度的基本约束,按需深入组合后的失效边界。
- 依赖无环检查
- 有限并发和全局预算
重点内容会多练几次。先说清楚怎么做,再试试空数据、出错或条件改变时会发生什么。
先自己做,卡住时记下问题,再回来查。问答题写清“为什么、什么时候适用”;动手题留下输入、输出和运行步骤,方便下次重做。
基础必做
- 作业 01原理问答
选择一个Java笔记整理任务,写出单代理基线与拆分方案;逐个判断子任务是否独立,并列出拆分新增的协调成本。
- 提交内容
- 一份独立书面回答或标注图,包含自造例子、概念边界及判断依据。
- 怎样算完成
- 逐项回应题目要求,区分事实、假设与个人判断。
- 能用自己的例子解释适用条件,记录尚未弄懂的点。
练到的知识点单代理基线 · 任务独立性和依赖
- 作业 02代码推演
输入任务依赖A→C、B→C、C→D,再加入D→B。定义计划结构,手工推演校验器处理过程,并指出应保留哪些错误定位信息。
- 提交内容
- 手工推演记录,写明输入约定、中间状态、最终判断;可运行时附程序对照记录。
- 怎样算完成
- 推演包含中间过程,数据形状、单位、顺序或状态前后一致。
- 主动检查至少一个边界或异常条件,能解释手算与运行差异。
练到的知识点结构化计划 · 依赖无环检查
综合应用
- 作业 03面试场景
三个子任务共享100单位预算且最多并发2个,另有一份只允许主任务读取的资料。制定分配规则和最小上下文清单。
- 提交内容
- 场景分析与决策记录,包含信息缺口、约束、备选方案和验证计划。
- 怎样算完成
- 方案回应给定约束,列出至少一种替代方案及代价。
- 每项关键结论说明所需证据,不能核实的部分明确标注。
练到的知识点有限并发和全局预算 · 子任务最小上下文
- 作业 04编程实践
实现本地假代理调度器,两个子任务给出冲突证据并重复提出同一写入动作;验证合并结果、冲突保留和副作用次数。
- 提交内容
- 可复现的最小程序或实验材料、自写测试、环境说明与运行记录;题目允许纸面实验时提交完整案例表。
- 怎样算完成
- 提交物按说明可以重现;测试覆盖正常、边界与失败场景。
- 运行证据与断言能核验声明的行为,注明模拟与真实执行范围。
练到的知识点证据合并与冲突保留 · 共享写入和重复副作用
- 作业 05排错实验
注入一个永不结束的子任务与一个已失败的依赖,观察整体是否能停止;记录排队、运行、取消和预算释放的事件顺序。
- 提交内容
- 最小故障输入、复现步骤、观测记录、定位依据及回归用例。
- 怎样算完成
- 其他人可按记录复现故障,并区分现象与已验证原因。
- 定位过程有观测依据,回归用例可以再次暴露原问题。
练到的知识点任务独立性和依赖 · 依赖无环检查 · 有限并发和全局预算
- 作业 06编程实践
实现允许提交“计划修订版”的小调度模拟器,初版无环且预算可行,修订版增加依赖与额外工作量;检验修订在不同运行阶段的接收规则。
- 提交内容
- 可复现的小程序或题目允许的离线实验、自写测试、输入和运行记录。
- 怎样算完成
- 输入、环境和运行过程可重现,测试覆盖声明的重点条件。
- 验证目标由合同确定,模拟结果不冒充真实服务证据。
练到的知识点依赖无环检查 · 有限并发和全局预算
进阶挑战 · 掌握前置后选做
- 作业 07设计评审
用固定任务集对照单代理与多代理,预先定义质量、总成本与耗时指标,给出控制随机性和解释失败样本的方法。
- 提交内容
- 设计评审文档或对照实验报告,包含前置条件、方案取舍、验收证据与尚未验证的范围。
- 怎样算完成
- 比较使用一致条件与事先声明的标准,记录失败与未知结果。
- 说明方案的适用前提、未覆盖风险及后续验证方法。
练到的知识点单代理基线 · 有限并发和全局预算 · 证据合并与冲突保留
思维拓展
先写出你的猜想,再用例子或实验验证。答案可以不同,但要说清依据、适用情况,以及还有什么没弄懂。
- 作业 08思维拓展 · 反例构造
为“任务图无环且每个子任务有独立Token预算,整体执行必然按期结束”设计反例,要求说明等待条件与共享资源,给出可复现事件序列。
- 提交内容
- 具体反例、原主张的适用前提、复现或逻辑证明及结论边界。
- 怎样算完成
- 反例满足原主张所声称的前提,确实针对其结论。
- 说明一个反例能推翻什么、不能进一步证明什么。
练到的知识点依赖无环检查 · 有限并发和全局预算
完成后,再检验一次
给每题标记“独立完成 / 查阅后完成 / 尚未完成”。查过资料或没做出的题,记下卡在哪里。隔一天换一组输入再试,能解释结果和出错原因,再把这个知识点标为掌握。
继续阅读
先看能帮你完成当前练习的部分。例子跑通后,再读更深入的解释。
- 必读实践指南英文
LangGraph:Workflows and agents (新标签页)↗
阅读串联、路由、并行和编排者工作者模式,对比预设工作流与模型动态选择的适用范围。
- 实践参考文档英文
LangGraph:Graph API overview (新标签页)↗
重点阅读 State、Nodes、Edges 和更新规则,将业务转换表映射到图结构并控制循环。
- 深入参考文档中文
Python 3.12:协程与任务 (新标签页)↗
先阅读 coroutine、
TaskGroup、超时与取消,再用本地模拟任务理解并发异常如何传播。 - 深入参考文档英文
OpenJDK 17 CompletableFuture 源码 (新标签页)↗
按指定版本定位
CompletableFuture.allOf;对照源码分析理解实际调用边界。