1. 我一开始应该怎么想 Agent?

不是先想:

1
怎么接 LLM?

而是先想:

1
2
如果没有 LLM,
这个业务本身应该怎么跑?

比如查订单:

1
2
3
4
5
6
7
8
9
用户要查订单

有没有订单号?

有 → 查订单

成功 / 失败

返回结果

所以我的理解是:

先把确定性的业务流程设计出来,再看哪些地方真的需要 LLM。

可以先记:

1
2
3
4
确定性的事情 → 程序
自然语言不确定性 → LLM
真实业务操作 → Tool
流程执行 → Runtime / Graph

2. State 到底是什么?

我的理解:

State 就是“这个任务现在进行到什么情况了”。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
taskId: "T001",
taskType: "order_query",

status: "running",
step: "check_order",

candidate: {
orderId: "A100"
},

verified: {
order: null
},

error: null
}

它里面保存:

1
2
3
4
5
6
我是谁?
我在做什么任务?
现在做到哪里?
用户提供了什么?
哪些东西已经被业务系统确认?
有没有错误?

3. candidate 和 verified 为什么要分开?

这个我现在理解成:

1
2
3
4
5
6
7
用户说的 / LLM 提取的

candidate

业务系统真正确认的

verified

比如用户说:

1
订单号 A100

只能先放:

1
candidate.orderId = "A100"

不能直接认为:

1
A100 一定是真的订单

必须 Tool 查过之后,才能进入:

1
verified.order

所以:

LLM 给我的只是“候选信息”,Tool 验证之后才是“业务事实”。


4. status 和 step 有什么区别?

我现在理解:

1
2
3
4
5
status
= 现在还能不能继续跑

step
= 如果继续跑,现在该干什么

比如:

1
2
3
4
{
status: "waiting_user",
step: "need_order_id"
}

意思就是:

现在不能继续,因为缺订单号;当前位置是在等订单号。


5. State 是谁改的?

不是让 LLM 随便改。

应该是:

1
2
3
4
5
State + Event

程序定义好的 Transition

New State

比如:

1
用户补充订单号

程序决定:

1
2
3
4
5
waiting_user
→ running

need_order_id
→ check_order

所以:

LLM 可以帮助理解用户,但是最终怎么改 State 是程序决定的。


6. Tool 是什么?

我的理解:

Tool 就是一个真实业务能力。

比如:

1
2
3
getOrder({
orderId: "A100"
})

Tool 不应该拿整个 State:

1
getOrder(state)

而应该只拿它需要的数据:

1
2
3
getOrder({
orderId: state.candidate.orderId
})

所以:

Tool 不需要知道整个 Agent,它只需要完成一个具体业务操作。


7. Runner 是什么?

以前手写的时候:

1
2
3
4
5
6
7
while (state.status === "running") {

if (state.step === "check_order") {
...
}

}

我的理解:

Runner 就是自动推进任务的人。

它一直做:

1
2
3
4
5
6
7
看现在 step 是什么

执行

更新 State

再看下一步

直到:

1
2
3
4
5
waiting_user
completed
failed
paused
...

8. applyMessage() 和 run() 有什么区别?

这个可以这样理解:

1
2
3
4
5
applyMessage()
= 用户推动任务

run()
= Agent 自己推动任务

例如:

1
2
3
4
5
6
7
8
9
10
11
Agent:请提供订单号

用户:A100

applyMessage()

State 变成 running

run()

自动查订单

9. handleMessage() 到底是干嘛的?

我现在理解它不是具体业务逻辑。

它更像:

一次用户请求进来之后的总协调入口。

大概:

1
2
3
4
5
6
7
8
9
10
11
12
13
用户 Message

找到 Session / Task

判断用户想干什么

更新任务

如果能自动执行就执行

保存

生成 Response

所以:

1
2
3
4
5
handleMessage
= 外层协调

Task
= 业务本身

10. 为什么需要 Task Registry?

因为以后不可能只有查订单。

可能有:

1
2
3
4
5
order_query
refund
payment
booking
...

所以可以注册:

1
2
3
4
taskRegistry = {
order_query: {...},
refund: {...}
}

我的理解:

顶层只负责找到对应业务,具体业务自己知道怎么运行。


11. Session 和 Task State 是一回事吗?

不是。

我的理解:

1
2
3
4
5
Session
= 用户当前在操作哪个任务

Task State
= 某一个具体任务现在做到哪里

例如:

1
2
3
4
5
{
sessionId: "S001",
activeTaskId: "T002",
resumableTaskIds: ["T001"]
}

意思:

1
2
当前正在做 T002
T001 暂停了,以后还能继续

12. Router / Resolver 是干嘛的?

它解决的是:

用户这句话,到底想干什么?

比如得到:

1
2
3
4
{
action: "create",
taskType: "refund"
}

或者:

1
2
3
4
{
action: "continue",
taskId: "T001"
}

关键点:

Resolver 只负责判断,不负责真正修改业务 State。


13. 为什么 LLM 判断完不能直接执行?

因为 LLM 输出只能算:

1
Candidate Action

还要经过:

1
2
3
4
5
Schema Validation

Semantic Validation

Trusted Action

例如 LLM 说:

1
resume T999

程序还要检查:

1
T999 是不是当前真的可以 resume?

所以:

LLM 提建议,程序做最终裁决。


14. 为什么给 LLM 的 Context 要尽量少?

因为 Resolver 只是判断:

1
这个用户现在想干什么?

它可能只需要:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
message,

currentTask: {
taskId,
taskType,
status,
step
},

resumableTaskIds,
supportedTasks
}

没必要把整个数据库给它。

所以:

每一层只拿完成自己工作需要的数据。


15. 多任务怎么切换?

例如:

1
2
3
正在查订单 T001

用户突然说我要退款

可以:

1
2
3
4
5
6
7
T001 → paused

保存

创建 T002 refund

activeTaskId = T002

以后:

1
resume T001

继续之前的位置。

关键:

pause 不能把原来的执行位置丢掉。


16. 参数验证是不是只有一次?

不是。

我现在把它分成三层:

1
2
3
1. 有没有?
2. 格式对不对?
3. 业务上是不是真的?

例如:

1
2
3
4
5
6
7
8
9
10
11
null
→ 没提供

abc
→ 格式错误

A999
→ 格式正确,但可能根本没有这个订单

A100
→ Tool 查到以后才是真实订单

17. error 不等于 failed

这个很重要。

例如:

1
订单号格式错了

有 error,但是:

1
用户还能重新输入

所以应该可能是:

1
waiting_user

而不是:

1
failed

我现在区分:

1
2
3
Validation Error
Business Error
Execution Error

18. Retry 应该放在哪里?

Tool 做一次操作。

比如:

1
getOrder()

至于:

1
2
3
超时多久
失败重试几次
什么错误能重试

应该是 Tool 的执行策略。

所以:

1
2
3
4
5
Tool
= 做什么

Tool Policy
= 怎么安全执行

19. ToolResult 为什么要统一?

因为第三方 API 格式可能全都不同。

Runner 不应该懂:

1
2
3
Shopify 返回什么
Stripe 返回什么
内部订单服务返回什么

统一变成:

1
2
3
4
{
ok: true,
data: ...
}

或者:

1
2
3
4
5
{
ok: false,
errorType: "business",
error: {...}
}

或者:

1
2
3
4
5
{
ok: false,
errorType: "execution",
error: {...}
}

所以:

ToolResult 是 Tool 和 Runner 之间的统一协议。


20. Response 为什么和 State 分开?

因为:

1
2
3
4
5
State
= 内部运行数据

Response
= 给外部看的结果

不能把整个内部 State 都直接返回用户。

所以每个业务可以:

1
buildResponse(state)

现在我已经有三个协议:

1
2
3
4
5
6
7
8
Resolver → Executor
= Action

Tool → Runner
= ToolResult

Agent → Client
= Response

21. Checkpoint 是什么?

我的理解非常简单:

State 是现场,Checkpoint 是存档。

Agent 不是一直跑着。

而是:

1
2
3
4
5
6
7
8
9
10
11
请求进来

加载

跑一段

保存

返回

结束

下次用户再来:

1
2
3
加载上次存档

继续

22. 为什么有 Checkpoint 还需要 Idempotency?

因为可能发生:

1
2
3
4
5
createRefund 已经成功

程序突然挂了

还没保存 checkpoint

恢复以后又执行:

1
createRefund

可能退款两次。

所以写操作要有稳定的:

1
idempotencyKey

可以理解:

1
2
3
4
5
Checkpoint
= 不丢进度

Idempotency
= 不重复做业务动作

23. version 是解决什么?

解决并发。

两个请求同时:

1
load version 3

都修改,再保存,就可能互相覆盖。

所以保存的时候检查:

1
数据库现在还是不是 version 3?

如果不是:

1
说明别人已经改过了

所以:

1
2
version
= 防止并发覆盖

24. 到这里,为什么开始学 Graph?

因为我们手写 Runtime 已经变成:

1
2
3
4
5
6
State
step
while
if / else
Runner
Transition

会发现:

本质上我已经在自己实现一个 Graph Runtime。

所以框架只是把这些东西正式抽象出来。


25. 手写 Runtime 和 Graph 怎么对应?

我现在这样记:

1
2
3
4
5
6
7
8
9
10
11
12
13
以前                     Graph

State State

step 里面执行的代码 Node

step → 下一个 step Edge

if / else 下一步 Conditional Edge

while + step 调度 Graph Runtime

整个业务流程 Graph

26. Node 到底是什么?

Node 就是:

流程里的一个执行站点。

以前:

1
2
3
if (step === "check_order") {
...
}

现在:

1
checkOrderNode(state)

所以我可以理解成:

Node ≈ 原来某个 step 里面真正执行的那段代码。


27. Node 和 Tool 一样吗?

不一样。

1
2
3
4
5
Tool
= 一个真实能力

Node
= 流程中的一步

例如:

1
2
3
4
checkOrderNode

内部调用
getOrder Tool

Node 还负责:

1
2
解释 ToolResult
更新 State

28. Edge 是什么?

很简单:

一个 Node 做完以后,下一个 Node 去哪。

固定下一步:

1
A → B

就是普通 Edge。


29. Conditional Edge 是什么?

如果下一步需要判断:

1
2
3
4
5
check_order

订单存在?
├─ yes → show_order
└─ no → ask_again

就是 Conditional Edge。

所以:

1
2
3
固定下一步 → Edge

要判断下一步 → Conditional Edge

30. Graph 是什么?

Graph 可以理解成:

把整个业务流程显式画出来。

以前流程藏在:

1
while + if + step

现在直接:

1
2
3
4
5
6
7
8
9
START

check_order

Conditional Edge
├─ show_order
└─ ask_again

END

31. 每个 State 都有自己的 Graph 吗?

不是。

我的理解:

1
2
3
4
5
Graph
= 模板

State
= 某一次具体任务的数据

比如:

1
2
3
4
5
OrderQueryGraph

T001 在跑
T002 也在跑
T003 也在跑

三个任务可以共用同一张 Graph。


32. 有 Graph 以后还需要 step 吗?

如果 step 只是告诉 Runner:

1
下一段代码执行什么

通常不需要了。

因为 Graph Runtime 已经知道:

1
现在在哪个 Node

但是如果:

1
approvalStatus = pending

这是业务事实,就还是应该存在 State。

所以:

1
2
3
4
5
技术执行位置
→ Graph Runtime

业务状态
→ State

33. Node 为什么可以只 return 一部分 State?

Node 可以:

1
2
3
return {
order: result.data
}

不用:

1
2
3
4
return {
...state,
...
}

因为 Graph Runtime 会根据 State 的 merge rule 合并。

所以执行过程:

1
2
3
4
5
6
7
8
9
State

Node

Partial State Update

Reducer / Merge

New State

34. Reducer 是什么?

Reducer 就是:

新值和旧值到底怎么合并?

例如:

1
2
3
4
5
6
7
8
order
→ 替换

error
→ 替换

messages
→ 追加

以前我自己写:

1
2
3
4
candidate: {
...state.candidate,
orderId
}

其实就是在手写 merge rule。


35. START / END 是什么?

1
2
3
4
5
START
= Graph 入口

END
= Graph 真正结束

它们通常不是实际业务 Node。


36. Interrupt 和 END 一样吗?

不一样。

1
2
3
4
5
END
= 任务完成了

Interrupt
= 暂时跑不下去

比如:

1
2
3
缺退款原因

Interrupt

用户之后补充:

1
商品坏了

再:

1
Resume

继续。

所以:

1
2
3
4
5
waiting_user
≈ Interrupt

继续任务
≈ Resume

37. Graph 的 Checkpoint 多保存了什么?

以前主要保存:

1
业务 State

Graph 还需要知道:

1
2
3
4
5
业务 State
+
当前 Graph 执行到哪里
+
恢复运行需要的信息

所以恢复以后不需要重新:

1
START

而是从暂停的位置继续。


38. Graph Run / Task ID 是什么?

同一张 Graph 可以跑很多实例:

1
2
3
4
5
refundGraph

T001
T002
T003

所以必须知道:

我现在恢复的是哪一次运行?

不同框架可能叫:

1
2
3
4
taskId
threadId
runId
executionId

本质一样:

一次具体 Graph 运行实例的 ID。


39. Session 和 Graph Run ID 又是什么关系?

我现在这样理解:

1
2
3
4
5
Session
= 用户当前任务导航

Task / Run ID
= 一个具体业务任务实例

比如:

1
2
3
4
Session S001

active → T002
paused → T001

所以:

1
2
3
4
5
6
7
8
9
10
11
Session
= 桌面

Task
= 窗口

Checkpoint
= 窗口里保存的现场

Graph
= 窗口运行的流程模板

40. Subgraph 是什么?

就是:

Graph 里面还能调用 Graph。

例如:

1
2
3
4
5
6
Agent Graph

Router
├─ OrderQueryGraph
├─ RefundGraph
└─ Chat

这里:

1
2
OrderQueryGraph
RefundGraph

都是 Subgraph。


41. Supervisor Graph 是什么?

可以理解:

顶层总调度。

它负责:

1
2
3
用户想干什么?

应该送到哪个业务 Graph?

例如:

1
2
3
4
Supervisor
├─ 查订单 → OrderQueryGraph
├─ 退款 → RefundGraph
└─ 聊天 → Chat

这其实就是以前:

1
2
3
4
5
handleMessage
+
Resolver
+
Task Registry

的 Graph 版本。


42. Parent Graph 和 Subgraph 要共用全部 State 吗?

不一定,而且通常不应该。

顶层可能只需要:

1
2
3
userId
message
activeTaskType

退款 Graph 自己需要:

1
2
3
4
orderId
refundReason
verified
error

所以:

每个 Graph 只维护自己关心的数据。


43. Subgraph 怎么把结果返回父 Graph?

像函数一样:

1
2
3
4
5
6
7
8
9
Parent

给 Subgraph Input

Subgraph 自己执行

返回 Output

Parent 继续

父 Graph 不需要知道 Subgraph 所有内部细节。


44. Subgraph 中途 Interrupt 怎么办?

如果 Parent 正在等 Subgraph:

1
2
3
4
5
Parent

RefundGraph

Interrupt

那么整个这次运行也只能先停。

Checkpoint 要记:

1
2
3
Parent 在哪
Subgraph 在哪
Subgraph State 是什么

用户回来以后:

1
2
3
4
5
6
7
8
9
Resume

恢复 Subgraph

Subgraph 跑完

结果回 Parent

Parent 再继续

45. Node 里面一定是 Tool 吗?

不是。

Node 可以分成:

1
2
3
普通代码 Node
LLM Node
Tool Node

例如:

1
2
3
4
5
6
7
8
格式验证
→ 普通代码 Node

理解用户意图
→ LLM Node

查订单
→ Tool Node

所以:

Node 是执行位置,里面用什么能力是另外一件事。


46. Agent Loop 是什么?

它的结构:

1
2
3
4
5
6
7
8
9
10
11
Agent Node

LLM 判断下一步
├─ 回答 → END
└─ Tool

Tool Node

ToolResult

Agent Node

所以:

1
2
3
4
5
Agent
→ Tool
→ Agent
→ Tool
...

可以理解:

1
2
3
4
5
Agent Node
= 大脑

Tool Node
= 手脚

47. Fixed Graph 和 Agent Loop 怎么选?

我的理解:

流程确定

例如退款:

1
2
3
4
5
查订单

检查退款资格

创建退款

用:

1
Fixed Graph

因为程序已经知道流程。


下一步不确定

例如:

1
帮我分析这个客户最近为什么一直投诉

可能需要:

1
2
3
先查订单?
还是退款?
还是客服记录?

可以用:

1
Agent Loop

48. 最后我现在形成的整体理解

Agent 不是:

1
LLM + 一堆 Tools

这么简单。

一个比较完整的 Agent,其实是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
用户消息

Session / Context

Router / Resolver

Graph / Task

State

Node

Tool / LLM / 普通代码

State Update

Edge

Checkpoint

Response

而最重要的设计原则一直没有变:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
能让程序确定的
就不要交给 LLM

需要自然语言理解的
再交给 LLM

真实业务事实
必须让 Tool / 业务系统确认

流程能提前确定
就画成 Graph

流程真的开放
才让 Agent 动态决定下一步

我目前已经从:

1
“Agent 就是 LLM 调工具”

走到了:

1
2
3
4
5
6
“Agent 本质上是一个
有 State、
有流程、
有边界、
有执行规则、
有持久化能力的 Runtime”

然后 Graph Framework 做的事情,就是把我之前手写的:

1
2
3
4
5
6
State
step
while
if / else
Transition
Runner

变成更正式的:

1
2
3
4
5
6
7
State
Node
Edge
Conditional Edge
Reducer
Checkpoint
Graph Runtime