Rust Agent + DAG:为什么真正复杂的任务,必须用“任务图”
350 字·3 分钟阅读
RustAI Agent
如果你已经实现了前面几篇里的 Agent:
- 有 Executor
- 有并发
- 有长期记忆
你很快会撞上一堵结构性的墙。
一个必然会出现的问题:Plan → Act 循环开始失控
先看一个真实但常见的 Agent 行为:
“我先查 A,再查 B,如果 B 失败就重试 A,然后根据 A+B 决定是否查 C, 如果 C 成功就汇总,否则回到 B 换参数再试一次。”
用 while loop + if else 写出来会变成什么?
- 状态散落在各处
- 很难判断“现在在第几步”
- Debug 基本靠猜
- 想并发?几乎不可能
根因是:你在用“线性模型”,解决“图结构问题”。
Agent 的真实执行模型,其实是 DAG
几乎所有“稍微复杂”的 Agent 任务,本质都是:
┌── Task A ──┐
Goal ─┤ ├── Task D ── Final
└── Task B ──┘
│
Task C这就是一个 DAG(Directed Acyclic Graph,有向无环图)。
为什么 DAG 对 Agent 是质变
你可以在文章里直接点名这 5 个能力:
| 能力 | Loop 模型 | DAG 模型 |
|---|---|---|
| 并发 | 困难 | 天然支持 |
| 失败处理 | 混乱 | 节点级 |
| 重试 | 全局 | 局部 |
| 可解释性 | 很差 | 非常清晰 |
| 扩展性 | 快速腐化 | 结构稳定 |
一句话总结:
Loop 是 Demo,DAG 才是系统。
用 Rust 建模 Task Graph(核心抽象)
Step 1:定义 Task Node
pub struct TaskNode {
pub id: TaskId,
pub tool: String,
pub input: Value,
pub depends_on: Vec<TaskId>,
}关键点只有一个: 👉 显式依赖关系
Step 2:Task Graph 本身
pub struct TaskGraph {
pub nodes: HashMap<TaskId, TaskNode>,
}你现在可以清楚回答:
- 哪些任务可以并发?
- 哪些任务必须等?
Agent 的新职责:不再“直接行动”,而是“生成任务图”
这是一个非常重要的思维转变:
LLM 不负责“怎么执行” LLM 只负责“任务结构”
LLM 输出示例(JSON)
{
"type": "TaskGraph",
"nodes": [
{"id":"A","tool":"read_file","input":{"path":"log.txt"}},
{"id":"B","tool":"http_get","input":{"url":"..."}},
{"id":"C","tool":"analyze","depends_on":["A","B"]},
{"id":"D","tool":"summarize","depends_on":["C"]}
]
}Executor 升级:从“执行任务”到“调度任务图”
核心算法:拓扑执行(Topological Execution)
while let Some(ready_tasks) = graph.ready_nodes() {
run_in_parallel(ready_tasks).await;
graph.mark_done(ready_tasks);
}ready_nodes 的定义
一个节点:
- 所有 depends_on 都已完成
- 自己还没执行DAG + Tokio:并发调度的自然结合
use futures::stream::FuturesUnordered;
let mut running = FuturesUnordered::new();
for task in ready_tasks {
running.push(executor.run_task(task));
}
while let Some(result) = running.next().await {
graph.record_result(result);
}📌 Tokio 在这里不是“加速器”,而是调度引擎的一部分。
失败不是异常,而是图的一部分
这是 DAG 模型最强的一点。
三种失败策略(非常适合写成小节)
① Fail Fast
- 任一关键节点失败 → 整体失败
② Retry Subgraph
- 失败节点 + 下游节点全部重跑
③ Fallback Branch
Task B
├─ success → Task C
└─ failure → Task C'这些在 loop 模型中几乎无法优雅实现。
Debug 体验的质变:任务图 = 执行记录
当你把每次执行都记录成:
{
"node":"C",
"start":"...",
"end":"...",
"status":"failed",
"error":"timeout"
}你会第一次感觉到:
“Agent 是一个可观测系统,而不是黑盒。”
DAG + Memory:让 Agent 真正“学会做复杂事”
结合上一篇的 Semantic Memory,你可以做到:
- 记住哪些子图经常失败
- 记住某类任务的最优结构
- 下次让 LLM 复用成熟 DAG 模板
这一步,就是从:
Agent = 推理机器 升级为 Agent = 经验驱动的执行系统
到这里,Agent 已经变成什么?
如果你把前四篇能力叠加起来:
- 并发 Executor
- 成本控制
- 长期记忆
- DAG 调度
你现在拥有的,其实是一个:
“LLM 驱动的分布式任务编排系统(单机版)”
这也是为什么:
大厂真正落地的 Agent,最后都会长得像 Workflow / DAG / Scheduler。