流水线与任务依赖图
流水线把任务按顺序交给不同执行者,上一步的产物成为下一步的输入。任务依赖图进一步表达哪些步骤可以并行、哪些步骤必须等待。如果图中的依赖有方向且不形成环,就称为 DAG(Directed Acyclic Graph,有向无环图)。
例如,一份产品对比报告需要先取得资料,再形成对比表,最后核对结论。资料查询可以分支执行,写报告必须等所需资料齐全。这一结构规定的是步骤间的关系,每个 Agent 在自己的步骤中仍可以选择查询工具、补充信息或调整写作方式。
从一条流水线开始
最简单的协作可以写成:
调研 Agent → 写作 Agent → 审查 Agent → 交付
资料记录 报告草稿 审查结果三个节点各自有输入和交付物。调研节点返回来源与要点;写作节点基于这些资料形成草稿;审查节点对照资料检查结论,给出是否通过及具体问题。
顺序确定后,还需要确定交接时传什么。如果写作 Agent 只收到“资料查完了”,它仍然需要重新查询。若收到所有搜索页面和尝试历史,则要从大量无关信息中重建结论。合适的输入应包括资料条目、来源定位、未确认事项,以及报告范围;草稿再通过引用关联回这些资料。
节点返回不代表产物已可用于下游。调研结果缺少型号版本,写作节点可能把不同版本的参数合并;审查结果格式正确,也可能没有检查引用。因此,调度器要区分“节点执行结束”和“产物满足交接条件”,通过后再解锁后续步骤。
这种线性结构容易定位问题,但所有节点都会等待前一个节点。多个独立查询也被塞进一个调研节点时,外部图看不到可以并行的部分;要利用这些独立性,可以进一步展开依赖图。
把调研拆成有分支的 DAG
沿用 A、B、C 三款手机的比较任务,先确定型号、地区和存储版本,再分别查询资料:
┌→ 查询 A ─┐
确定比较范围 ─────┼→ 查询 B ─┼→ 生成对比表 → 写报告 → 核对与交付
└→ 查询 C ─┘箭头表示后一个节点需要前一个节点的产物。三项查询共同依赖“确定比较范围”,彼此之间没有依赖;“生成对比表”需要三项查询的结果。
应用可以把节点与依赖保存为如下记录:
{
"plan_version": 1,
"nodes": [
{"id": "scope", "depends_on": []},
{"id": "research-A", "depends_on": ["scope"]},
{"id": "research-B", "depends_on": ["scope"]},
{"id": "research-C", "depends_on": ["scope"]},
{
"id": "compare",
"depends_on": ["research-A", "research-B", "research-C"]
},
{"id": "write", "depends_on": ["compare"]},
{"id": "verify", "depends_on": ["write"]}
]
}depends_on 决定就绪条件,产物引用决定下游读取哪些资料。写作节点虽然直接等待对比表,但对比表还应引用原始来源,审查节点才能追溯论据。完整任务描述还需要目标、输入、输出要求和验收规则,见任务契约与上下文交接。
调度之前要检查节点 ID 是否唯一、依赖是否存在、图中是否有环。若 compare 等待 write,而 write 又等待 compare,两个节点都无法开始。模型生成的计划也必须通过这些检查。
节点什么时候可以开始
在上面的全部依赖都必需的图中,一个节点就绪,需要满足:它尚未运行,所属分支已启用,所有前置节点都已交付可接受的产物。就绪以后,还要等待可用的并发名额。
scope 产物通过验收
→ research-A、research-B、research-C 进入就绪队列
research-A、research-C 通过,research-B 仍在查询
→ compare 继续等待
research-B 通过
→ compare 进入就绪队列这与“每个前置节点都结束了”不同。B 查询失败也是一种结束状态,但没有提供对比所需的资料。可以重试 B、停止本次比较,或在交付要求允许时明确改为部分比较;不能自动把失败状态当成依赖已满足。
查询成功也不代表查到有利结果。确认 B 没有某项售后服务,仍然可以成为有效产物。查询节点的验收目标是准确记录结果和来源,不要求所有产品都满足相同条件。
只要图固定,调度器就能按状态更新重复执行“找就绪节点、安排运行、验收产物、解锁后继”。并行协作讨论其中的并发和汇合规则,本篇的依赖关系则决定了哪些任务有资格进入运行队列。
固定图、条件分支与运行时生成任务
固定工作流把节点、可能路径和分支规则预先写好。节点由 Agent 执行时,内部仍可以自主决定查询动作。例如,“查询 B”这一节点无论查几次资料,成功后都交给比较节点;内部自主性不会改变外部的依赖关系。
固定图也可以根据结果选择分支。例如,用户要求对比某个地区的保修政策,但查询结果只找到其他地区的资料:
查询 B
├─ 地区已核实 ─────────────┐
└─ 地区未核实 → 补查地区 ─┴→ 接收 B 的资料两条可能路径预先存在,运行时只是决定启用哪条。直接路径被选中后,“补查地区”没有执行,也不应该导致汇合节点永久等待。调度器需要记录分支选择,只等待本次选中路径的必需前置产物。这里的分支互斥,汇合语义与前面等待 A、B、C 全部结果的情况不同。
运行时规划还可以创建原先没有枚举的任务。主管发现 B 有多个地区版本,需要新增若干版本核查任务,再将它们接到比较节点之前。新增任务后应发布新的计划版本,重新校验依赖,并检查已完成产物是否还能使用。
例如,比较范围从 256 GB 改成 512 GB,旧的价格对比和推荐就可能失效。仅把任务描述里的数字改掉,无法修正下游已经使用旧资料形成的结论。计划更新、产物版本和依赖失效要一起处理,相关过程见任务规划与分解。
审查回路为什么不属于 DAG
审查节点只执行一次,通过或不通过都结束,可以保留在 DAG 中。若不通过后返回写作节点继续修改,就形成了环:
写作 → 审查 ──通过──→ 交付
↑ │
└─修改──┘这属于有环工作流,不能继续称为 DAG。它可以由状态图执行,但需要额外记录修改轮次、反馈、当前草稿版本和停止条件。审查与修改若使用不同 Agent,还要保证反馈对应当前草稿,避免拿第一版的问题反复要求第三版修改。
也可以把有限的修改轮次展开成无环结构:写作 v1 → 审查 v1 → 写作 v2 → 审查 v2。这种表示将最多允许的轮次写进图中,但节点更多。直接保留循环或按轮次展开,都不能省略“何时停止修改”的规则。
这一判断也与图的层次有关:外层任务图可以是 DAG,其中的某个 Agent 节点却在内部运行多轮工具循环。外层没有回到此前的任务节点,就仍然是外层 DAG。具体的审查流程见审查、辩论与结果裁决,图框架的节点和条件边示例见LangGraph 实践。
哪些节点需要独立 Agent
图中的每个节点都可以由程序、工具或 Agent 执行。单位换算、字段校验、根据状态选分支,通常直接用代码;查询来源、依据反馈修改报告等需要持续选择动作的任务,才可能适合 Agent。
给每个阶段设置独立 Agent,可以分别组织上下文和工具范围,但也会产生交接成本。上游遗漏的来源、下游误解的限定条件都会沿着依赖传播。拆分前应先确定中间产物能否表达下一步需要的信息;如果两个步骤必须反复共享几乎全部上下文,把它们放在同一个 Agent 节点内可能更合适。
流程成熟、交付步骤固定时,可以由程序安排节点,让 Agent 专注执行。任务范围经常随资料变化时,再让主管参与生成与调整依赖。两种方式可以组合:固定的“调研、写作、核对”外层流程中,调研节点内部动态创建多个子任务。
参考:LangGraph:Workflows and agents;Anthropic:Building effective agents,Prompt chaining 与 Orchestrator-workers。