跳到主要内容

04 · 设计模式、巧妙之处与边界

前三章讲透了 100 行内核。本章把视角拉高:怎么用这套原语拼出常见 AI 模式PocketFlow 最值得借鉴的几个决定、以及它刻意不做什么、会在哪崩


4.1 用原语拼模式

PocketFlow 的卖点是「一套图,搭一切」。核心就一句:不同的连线方式 = 不同的模式

Agent 循环 = 带回边的图

「Agent」不神秘——就是一个能自己决定下一步、且能循环的图。决策节点的 post 返回模型选的动作名,图据此分支;干完活的边指回决策节点,形成循环。

┌──────────────────────────────┐
│ │ "decide"(搜完回来)
▼ │
DecideAction ──"search"──▶ SearchWeb

└──"answer"──▶ AnswerQuestion(出口,无后继 → Flow 结束)

对应 cookbook/pocketflow-agent/flow.pycreate_agent_flow(第 01 章已引)。图的走向由运行时模型输出驱动——这正是 agent 与固定 workflow 的分界。

Workflow = 一条(基本)线性的链

把节点用 >> 串成一条线,就是传统 workflow(如「拟大纲 → 写正文 → 套格式」)。没有回边、少分支。见 README 的 pocketflow-workflow 示例。

RAG = 两个 Flow 接力

检索增强通常拆成离线(切块 → 嵌入 → 建索引)和在线(检索 → 拼上下文 → 生成)两条 Flow。每条都是几个节点串起来,再靠 shared 把索引/上下文传下去。见 README 的 pocketflow-rag

其它

Map-Reduce → BatchNode/BatchFlow(第 03 章);多智能体 → 两个 AsyncFlowasyncio.Queue 通信(cookbook/pocketflow-multi-agent);人在环 → 在 prepinput()(chat 示例)。没有一个模式需要改框架代码。


4.2 巧妙之处(可借鉴的技术)

妙一:公开方法 vs 内部钩子的分层

你覆写 exec;框架覆写 _exec。重试、批、异步全插在 _exec/_run 这层,你的业务逻辑毫不知情。这让「能力叠加」变成纯粹的类组合。见 Node._exec 包住 BaseNode.exec(pocketflow/__init__.py:29-34)。

妙二:用运算符重载把「代码」变成「流程图」

a - "x" >> b 读起来就是流程图上的一条带标签的箭头。__sub__ 返回一个中间对象 _ConditionalTransition 承接动作名,再由它的 __rshift__ 落地(pocketflow/__init__.py:17-24)。三行代码换来极高的可读性。

妙三:节点无状态、copy.copy 保平安

图里的节点是定义不是实例;真正跑的是每步的浅拷贝(Flow._orch,pocketflow/__init__.py:47-48)。于是同一节点能在循环里被反复经过而不串味,successors/shared 又因浅拷贝仍被共享。**「拷执行者、共享图与数据」**是这套设计的精髓。

妙四:None"default"

post 返回 None(什么都不写就是这样)被当作走 "default" 边(get_next_nodeaction or "default",pocketflow/__init__.py:43)。于是「线性流程」零心智负担:节点不管返回值,>> 自然接上。

妙五:附带 .pyi 类型存根

源码本体为了「100 行」写得极度紧凑(分号连写),但配了一份完整的 pocketflow/__init__.pyi,用 Generic[_PrepResult, _ExecResult, _PostResult] 把三段之间的类型关系标出来。紧凑的实现 + 清晰的类型契约分离,兼顾了「短」和「可读/可类型检查」。


4.3 边界与局限(诚实)

PocketFlow 刻意只做「图编排」这一件事。以下都不在框架里,得你自己写或抄 cookbook:

  • 没有 LLM 调用。 框架零依赖、不含任何模型 SDK。call_llm 全在 cookbook/*/utils.py 里由用户实现(如 cookbook/pocketflow-chat/utils.py)。
  • 没有工具 / 函数调用抽象、没有内置记忆 / 向量库、没有 prompt 模板。 这些都是你在节点里自己拼(agent 示例的 prompt 是手写字符串)。
  • 没有可观测性 / 追踪 / 持久化。 shared 是内存里的 dict,进程结束即失;要落盘自己在节点里存。
  • 没有并发安全。 并行分支共享同一 shared,写同键会竞争,框架不加锁(第 03 章 3.4)。

会在哪「崩」或让人困惑

  • 动作名拼错 = 静默结束。 post 返回一个没连线的动作名,Flow 只 warnings.warn 然后停下(get_next_node,pocketflow/__init__.py:44),不报错。忘看告警就会以为「流程没跑完」。
  • 忘了用 Flow、直接 node.run() 单节点 run 不跑后继(只告警,pocketflow/__init__.py:15)。
  • 异步节点用错入口。AsyncNode 调同步 _runRuntimeError("Use run_async.")(pocketflow/__init__.py:74)。
  • 无限循环。 图里有回边又没退出条件(比如决策节点永远返回 "search"),_orchwhile 不会自己停——退出得靠你的 post 逻辑最终返回一个通向出口的动作。

4.4 横向对比(同 shelf 的取舍)

PocketFlow 在 agent-frameworks 货架上是极简主义的极端。它与 LangGraph 最像(都把程序建模成图),但取舍相反:

维度PocketFlow典型重框架(LangChain/LangGraph 等)
代码量100 行、零依赖数万~数十万行、大量依赖(README 对比表:LangChain ~405K 行)
抽象只有「图」Agent/Chain/Tool/Memory/Retriever… 成套
LLM/工具/记忆不含,自己写内置大量 wrapper
学到的东西逼你理解「Agent 本质就是带回边的图」拿来即用,但底层被封装
适合想完全掌控、轻量嵌入、教学想快速搭、要现成集成

核心区别:PocketFlow 卖的不是功能,是「一个足够小、能一眼看穿的核心抽象」。 它甚至主张让 AI(Cursor 等)照着这套原语帮你生成 agent 代码(README 的「Agentic Coding」)——框架小到模型能完整装进上下文,是这个主张成立的前提。


代码地图(导航索引)

主题文件符号
Agent 循环模式cookbook/pocketflow-agent/flow.pynodes.pycreate_agent_flowDecideAction.post
用户侧 LLM 调用(框架不含)cookbook/pocketflow-chat/utils.pycall_llm
None→default 路由pocketflow/__init__.pyFlow.get_next_node
单节点误用告警pocketflow/__init__.pyBaseNode.run
异步入口保护pocketflow/__init__.pyAsyncNode._run
类型契约pocketflow/__init__.pyiBaseNodeNodeFlow 的泛型签名