Appearance
2. 核心抽象:只有两个概念
PocketFlow 的全部设计可以用一句话概括:
Node(节点)负责"做事",Flow(流程)负责"调度"。
就这两个概念,没有更多了。
2.1 Node —— 执行的最小单元
每个 Node 都遵循三阶段执行模型:
prep(shared) | → | exec(prep_res) | → | post(shared, prep_res, exec_res) |
|---|---|---|---|---|
| 准备数据 | → | 执行逻辑 | → | 后处理 & 决策 |
- prep:从共享存储
shared中读取所需数据 - exec:执行核心业务逻辑(如调用 LLM API)
- post:将结果写回
shared,并返回一个action字符串

Node:单步推理的最小执行单元
🔄Node 三阶段生命周期
1
prep()
从 shared 中读取所需数据
输入
shared↓
输出
...→
2
exec()
执行核心业务逻辑
输入
...↓
输出
...→
3
post()
将结果写回 shared,返回 action
输入
...↓
输出
...关键理解:prep 从 shared 读数据 → exec 执行核心逻辑(如调用 LLM)→ post 将结果写回 shared 并返回 action。 三个阶段职责分明,互不耦合。
为什么要分三个阶段?
分三阶段的好处在于解耦:
prep和post负责和外部世界(shared)交互exec是纯粹的业务逻辑,完全不知道 shared 的存在- 这让
exec可以被独立测试、独立重试、独立替换
node.run() vs flow.run()
直接调用 node.run(shared) 只会执行这一个节点,不会沿着 >> 连接运行后继节点。如果你连接了后继,PocketFlow 会发出警告:"Node won't run successors. Use Flow." 要让整条链路跑起来,必须用 Flow(start=node).run(shared)。
2.2 Flow —— 图编排引擎
Flow 做的事情非常简单:
- 从
start_node开始 - 执行当前节点的
_run(shared) - 根据返回的
action找到下一个节点 - 重复,直到没有后继节点
python
# Flow 的核心循环(伪代码)
curr = start_node
while curr:
action = curr._run(shared) # 执行节点
curr = curr.successors[action] # 跳转到下一个
Flow:串联多个 Node,实现多步推理
🗺️Flow 图执行可视化
最基本的模式:Node 依次执行,每个 post() 返回 "default"
NodeA
NodeB
NodeC
a, b, c = NodeA(), NodeB(), NodeC()
a >> b >> c # 链式连接
flow = Flow(start=a)
flow.run(shared) # A → B → C2.3 连接节点的两种方式
PocketFlow 通过 Python 操作符重载,让节点连接变得极其优雅:
python
# 方式 1:默认连接(>> 操作符)
node_a >> node_b >> node_c
# 等价于:node_a.next(node_b, "default")
# 方式 2:条件连接(- 操作符)
check_node - "approve" >> approve_node
check_node - "reject" >> reject_node
# 等价于:check_node.next(approve_node, "approve")
条件分支:根据 action 字符串走不同路径
这里发生了什么?
>>重载了__rshift__方法,调用self.next(other, "default")-重载了__sub__方法,返回一个_ConditionalTransition对象_ConditionalTransition的>>再调用src.next(tgt, action)- 两层操作符重载,实现了直观的图构建语法
2.4 形式化视角:PocketFlow 即有限状态自动机
可选阅读 —— 本节从理论角度解析 PocketFlow,不影响后续学习。如果你只想快速上手,可以跳过直接进入 §3 通信机制。
如果你学过编译原理或形式语言,会发现 PocketFlow 的执行模型和有限状态自动机(Finite State Automaton, FSA)几乎同构。这不是巧合 —— PocketFlow 的设计就是在 LLM 场景下实现了一台 FSA。
五元组映射
一台 FSA 由五元组 (Q, Σ, δ, q₀, F) 定义,它们和 PocketFlow 的对应关系如下:
| FSA 形式定义 | PocketFlow 对应 | 示例 |
|---|---|---|
| 状态集 Q | 所有 Node 实例 | {think, search, synthesize} |
| 字母表 Σ | Action 字符串的集合 | {"need_more", "enough", "default"} |
| 转移函数 δ(q, a) | node - "action" >> next_node 建立的后继映射 | think - "need_more" >> search |
| 初始状态 q₀ | Flow(start=node) 的起始节点 | Flow(start=think) |
| 终止条件 | post() 返回的 action 在后继映射中找不到 → 流程结束 | post() 返回 None |
应用模式 = 自动机拓扑
不同 LLM 应用模式,实际上对应不同形态的自动机。下面是三个典型例子:
1. 聊天机器人 —— 单状态 + 自环,最简单的循环结构
ChatNode 的
post()返回"continue"时跳回自身,形成对话循环;返回None时无后继节点,Flow 结束。
2. 搜索智能体 —— 分支 + 环,LLM 决定转移方向
Think 通过
"need_more"跳转 Search;Search 完成后"default"回到 Think 继续判断;Think 认为信息足够时"enough"进入 Synthesize 输出结果。
3. 结构化输出 —— 回退边,校验失败回退重试
Generate 生成 → Validate 校验 → Check 判断:校验不通过时
"retry"回到 Generate 重新生成;校验通过时"done"进入 Output 输出结果。
更多模式一览:
| 应用模式 | 自动机形态 | 状态数 | 特征 |
|---|---|---|---|
| 聊天机器人 | 单状态 + 自环 | 1 | 最简单的循环 |
| 写作工作流 | 线性链 | 3 | 无分支、无环 |
| 搜索智能体 | 分支 + 环 | 3 | LLM 决定转移方向 |
| 结构化输出 | 回退边 | 3-4 | 校验失败回退重试 |
| 多智能体 | 并发自动机组 | N×M | 多台自动机通过队列通信 |
与 LangGraph"状态机"的区别
LangGraph 也自称"状态机",但含义不同:
- LangGraph 的"状态" = 运行时数据(TypedDict),框架管理状态的持久化、快照和回放
- PocketFlow 的"状态" = 当前所在的 Node,数据由你自己在
shared中管理
PocketFlow 更接近经典 FSA:状态转移由 action 字符串驱动,状态数据在自动机之外管理。这让框架保持在 100 行,同时你可以自由选择任何持久化方案。
