如果你已经实现了前面几篇里的Agent:有Executor有并发有长期记忆你很快会撞上一堵结构性的墙。一个必然会出现的问题:Plan→Act循环开始失控先看一个真实但常见的Agent行为:“我先查A,再查B,如果B失败就重试A,然后根据A+B决定是否
如果你已经实现了前面几篇里的 Agent:
你很快会撞上一堵结构性的墙。
先看一个真实但常见的 Agent 行为:
“我先查 A,再查 B,如果 B 失败就重试 A,然后根据 A+B 决定是否查 C, 如果 C 成功就汇总,否则回到 B 换参数再试一次。”
用 while loop + if else 写出来会变成什么?
根因是:你在用“线性模型”,解决“图结构问题”。
几乎所有“稍微复杂”的 Agent 任务,本质都是:
┌── Task A ──┐
Goal ─┤ ├── Task D ── Final
└── Task B ──┘
│
Task C
这就是一个 DAG(Directed Acyclic Graph,有向无环图)。
你可以在文章里直接点名这 5 个能力:
| 能力 | Loop 模型 | DAG 模型 |
|---|---|---|
| 并发 | 困难 | 天然支持 |
| 失败处理 | 混乱 | 节点级 |
| 重试 | 全局 | 局部 |
| 可解释性 | 很差 | 非常清晰 |
| 扩展性 | 快速腐化 | 结构稳定 |
一句话总结:
Loop 是 Demo,DAG 才是系统。
pub struct TaskNode {
pub id: TaskId,
pub tool: String,
pub input: Value,
pub depends_on: Vec<TaskId>,
}
关键点只有一个: 👉 显式依赖关系
pub struct TaskGraph {
pub nodes: HashMap<TaskId, TaskNode>,
}
你现在可以清楚回答:
这是一个非常重要的思维转变:
LLM 不负责“怎么执行” LLM 只负责“任务结构”
{
"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"]}
]
}
while let Some(ready_tasks) = graph.ready_nodes() {
run_in_parallel(ready_tasks).await;
graph.mark_done(ready_tasks);
}
一个节点:
- 所有 depends_on 都已完成
- 自己还没执行
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 模型最强的一点。
Task B
├─ success → Task C
└─ failure → Task C'
这些在 loop 模型中几乎无法优雅实现。
当你把每次执行都记录成:
{
"node":"C",
"start":"...",
"end":"...",
"status":"failed",
"error":"timeout"
}
你会第一次感觉到:
“Agent 是一个可观测系统,而不是黑盒。”
结合上一篇的 Semantic Memory,你可以做到:
这一步,就是从:
Agent = 推理机器 升级为 Agent = 经验驱动的执行系统
如果你把前四篇能力叠加起来:
你现在拥有的,其实是一个:
“LLM 驱动的分布式任务编排系统(单机版)”
这也是为什么:
大厂真正落地的 Agent,最后都会长得像 Workflow / DAG / Scheduler。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!