为什么我们的AI智能体需要更好的工作流管理
想象你正在厨房里烹饪一顿复杂的饭菜。每道菜都需要多个步骤,有些需要持续监控,有些可以并行进行。现在再想象你在协调这一切的同时,还不断被门铃、电话和猫咪打断。
这正是现代AI智能体开发者所面临的挑战。随着AI智能体能力的提升,我们要求它们处理越来越复杂的工作流:跨多个来源进行主题研究、进行多轮对话、管理工具使用,并在中断后保持状态。
传统做法——编写冗长、复杂、嵌套条件的函数——很快就会变得难以维护。这就像让一个厨师顺序完成所有繁忙厨房的工作一样。
基于图的方式登场
基于图的智能体工作流就像一个组织良好的厨房,主厨(编排者)协调着各个专门岗位的厨师(节点)。每个厨师都清楚自己的任务,并知道下一步要把菜交给谁。
但为什么这种方式对AI智能体尤其有价值呢?
传统方式的问题
假设你正在构建一个AI研究助手,需要:
- 理解用户的查询
- 搜索相关信息
- 综合研究结果
- 生成报告
- 处理用户的后续提问
用传统编程方式,你可能会写出如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
def research_assistant(query):
# 解析查询
parsed_query = parse_query(query)
# 搜索信息
results = search_for_info(parsed_query)
# 综合研究结果
synthesis = synthesize_findings(results)
# 生成报告
report = generate_report(synthesis)
# 返回结果
return report
# 还有一个单独的函数处理后续问题……
def handle_followup(original_query, report, followup_question):
# 更复杂的逻辑……
pass
|
这种方式在以下情况下会失效:
- 过程耗时超过一次请求/响应周期
- 用户希望中断并修改工作流
- 需要在系统故障后从断点恢复
- 你希望可视化并理解复杂流程
AI工作流的厨房分工体系
在专业厨房中,采用“分工体系”,每位厨师都有专门的角色。沟通有结构,指挥链清晰。这让复杂的菜肴制作变得可靠高效。
类似地,基于图的AI智能体工作流:
- 将复杂流程拆分为独立节点
- 保持贯穿全流程的持久状态
- 明确定义各步骤间的转移
- 支持中断与恢复
- 让整个工作流可见、可审计
图执行是如何工作的
让我们从概念上看看基于图的方法如何运作:

在这个模型中:
- 有向图定义了智能体可能采取的所有路径
- 编排者管理执行流程并维护状态
- 节点执行器负责各个具体动作
- 状态和依赖由每个节点使用,但统一管理
现实案例:餐厅订单处理
看看餐厅如何处理订单:
- TakeOrder节点:服务员记下你的订单
- VerifyOrder节点:服务员确认订单无误
- RouteOrder节点:订单根据内容分发到不同厨房岗位
- PrepareFood节点:不同厨师准备不同部分的餐食
- QualityCheck节点:主厨检查成品
- DeliverFood节点:服务员上菜
- ProcessPayment节点:你结账
这个工作流:
- 有清晰的顺序和决策点
- 在整个流程中维护状态(你的订单详情)
- 能处理中断(你修改了订单)
- 各节点专责特定任务
- 某些步骤可并行(多位厨师同时工作)
状态如何在节点间流转
神奇之处在于状态的流转。在餐厅里:
- 订单小票就是“状态”
- 每个岗位都可在上面添加信息
- 小票在各岗位间流转
- 任何人都能通过小票了解当前进度
在基于图的AI系统中:
- 状态对象包含整个工作流所需的全部上下文
- 每个节点都可读取和写入状态
- 编排者确保状态在节点间持续有效
- 状态可保存,实现暂停与恢复
从概念到代码:PydanticAI的基于图方式
理解了概念后,我们来看如何用PydanticAI的pydantic_graph
框架实现。我们选择PydanticAI的原因:
- 生产级,企业级稳定性
- 全流程严格类型约束
- 与AI智能体和LLM无缝集成
- 内置状态持久化能力
- 支持工作流可视化
与许多牺牲类型安全换取灵活性的图工作流库不同,PydanticAI确保你的工作流既灵活又类型安全。这意味着许多潜在的bug会在开发时被发现,而不是运行时——对于复杂AI系统来说,这至关重要。
我们来构建一个简单的对话智能体,它能:
- 问候用户
- 询问用户姓名
- 询问兴趣
- 根据兴趣推荐话题
- 告别
状态的定义
首先,定义工作流需要维护的信息:
1
2
3
4
5
6
7
8
9
|
from dataclasses import dataclass, field
from typing import List, Optional
@dataclass
class ConversationState:
"""对话过程中维护的状态。"""
user_name: Optional[str] = None
interests: List[str] = field(default_factory=list)
recommendations: List[str] = field(default_factory=list)
|
定义各节点
每个节点代表对话中的一个独立步骤:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
from dataclasses import dataclass
from pydantic_graph import BaseNode, End, GraphRunContext
@dataclass
class GreetUser(BaseNode[ConversationState]):
"""问候用户并开启对话。"""
async def run(self, ctx: GraphRunContext[ConversationState]) -> 'AskName':
print("你好!欢迎使用我们的推荐系统。")
return AskName()
@dataclass
class AskName(BaseNode[ConversationState]):
"""询问用户姓名。"""
async def run(self, ctx: GraphRunContext[ConversationState]) -> 'AskInterests':
name = input("请问你的名字是?")
ctx.state.user_name = name
return AskInterests()
@dataclass
class AskInterests(BaseNode[ConversationState]):
"""询问用户兴趣。"""
async def run(self, ctx: GraphRunContext[ConversationState]) -> 'GenerateRecommendations':
interests = input(f"很高兴认识你,{ctx.state.user_name}!你有哪些兴趣?")
ctx.state.interests = [i.strip() for i in interests.split(',')]
return GenerateRecommendations()
@dataclass
class GenerateRecommendations(BaseNode[ConversationState, None, str]):
"""根据兴趣生成推荐。"""
async def run(self, ctx: GraphRunContext[ConversationState]) -> 'SayGoodbye' | End[str]:
# 这里可以调用LLM生成推荐
for interest in ctx.state.interests:
ctx.state.recommendations.append(f"关于{interest}的书籍")
print("根据你的兴趣,我推荐:")
for rec in ctx.state.recommendations:
print(f"- {rec}")
continue_chat = input("还需要更多推荐吗?(yes/no)")
if continue_chat.lower() == 'yes':
return SayGoodbye()
else:
return End(f"感谢你的交流,{ctx.state.user_name}!")
@dataclass
class SayGoodbye(BaseNode[ConversationState, None, str]):
"""向用户道别。"""
async def run(self, ctx: GraphRunContext[ConversationState]) -> End[str]:
return End(f"再见,{ctx.state.user_name}!希望你喜欢这些推荐。")
|
注意,PydanticAI的类型注解让流程一目了然。每个节点的run
方法的返回类型明确声明了下一个可能的节点。这创造了类型安全的工作流,非法转移会在开发时被发现。
创建并运行图
现在我们创建图结构并运行:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
from pydantic_graph import Graph
import asyncio
# 定义工作流图
conversation_graph = Graph(
nodes=[GreetUser, AskName, AskInterests, GenerateRecommendations, SayGoodbye],
state_type=ConversationState
)
# 运行图
async def main():
# 初始化状态
state = ConversationState()
# 从GreetUser节点开始运行
result = await conversation_graph.run(GreetUser(), state=state)
# 输出最终结果
print(f"最终结果:{result.output}")
if __name__ == "__main__":
asyncio.run(main())
|
持久化的力量
PydanticAI基于图的工作流最强大的特性之一就是持久化。让我们为对话增加暂停和恢复的能力:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
from pydantic_graph.persistence.file import FileStatePersistence
from pathlib import Path
async def run_with_persistence():
persistence = FileStatePersistence(Path("conversation.json"))
# 检查是否有已保存的对话
if await persistence.load_next():
# 从保存的状态恢复
async with conversation_graph.iter_from_persistence(persistence) as run:
print("恢复上次对话……")
node = await run.next()
print(f"恢复到节点:{node.__class__.__name__}")
else:
# 开始新对话
state = ConversationState()
await conversation_graph.initialize(GreetUser(), state=state, persistence=persistence)
async with conversation_graph.iter(GreetUser(), state=state, persistence=persistence) as run:
# 运行到AskInterests节点后暂停
while True:
node = await run.next()
if isinstance(node, AskInterests):
print("对话已暂停……稍后继续。")
break
elif isinstance(node, End):
print(f"对话完成:{node.data}")
break
|
PydanticAI的持久化能力对于需要:
- 处理长时间任务
- 异步与用户交互
- 需应对系统重启或故障
- 跨多会话运行
的AI智能体尤为重要。
类型安全:为何对AI工作流至关重要
你可能会疑惑,我们为何如此强调PydanticAI的类型完整性。在AI智能体工作流中,类型安全不仅是锦上添花——而是可靠性的基础。
想象在无类型系统中,意外返回了错误的节点类型或在节点间传递了错误数据。最好的情况是运行时报错,最坏的情况是工作流悄悄出错,导致结果不正确却难以察觉。
有了PydanticAI的类型约束:
- 编译器在运行前就能发现结构性错误
- IDE可为节点转移提供智能补全
- 数据校验确保状态一致
- 可视化工具能准确展示工作流可能性
随着AI智能体承担越来越关键的任务,这种可靠性变得不可或缺。
不止是组织结构的好处
使用PydanticAI的基于图方式带来多项实际优势:
- 可视化工作流:自动生成流程图,直观理解复杂行为
- 可恢复执行:中断后可继续长时间运行的流程
- 可测试组件:单独测试每个节点
- 灵活编排:根据新需求动态调整流程
- 持久化状态:在长流程中持续保持上下文
- 类型安全转移:强类型保障流程完整性
- 可并行执行:独立分支可并发运行(需框架支持)
何时使用基于图的方法
虽然强大,基于图的方法也带来一定复杂度。适合以下场景:
- 智能体工作流可能被中断且需恢复
- 需要可视化复杂决策逻辑
- 流程包含众多决策点和分支
- 需在多步骤间维护复杂状态
- 不同专家负责流程不同部分
- 构建对类型安全有严格要求的关键系统
对于简单智能体,直接方式可能更合适。
总结:用图思考
基于图的方法从根本上改变了我们对复杂AI工作流的思考方式。我们不再将其视为单一的整体过程,而是由一系列明确步骤、清晰转移和持久状态组成。
正如厨房分工体系革新了餐饮业,PydanticAI的基于图方法让我们能够构建更复杂、更可靠、更易维护的AI智能体。随着这些智能体承担越来越复杂的任务,这种结构化方式将不仅是有益的,而是必不可少的。
选择像PydanticAI这样强调类型安全的生产级框架,为我们的智能体系统日益复杂做好了准备。“只在我的电脑上能跑”的AI智能体时代正在过去——未来属于健壮、可恢复、类型安全的工作流。