前情回顾
在人工智能代理开发的旅程中,LangGraph 作为一个强大的图编排框架,正在改变我们构建复杂 AI 系统的方式。本文将深入探讨 LangGraph 的核心概念,帮助你从零开始掌握这个强大的工具。
一、编译的重要性:只有编译后的图才能被执行
在 LangGraph 中,一个最重要的原则是:只有编译后的图才能被执行。这个概念看似简单,却蕴含着深刻的设计思想。
python
# 未编译:只是一个蓝图
workflow = StateGraph(AgentState)
# ... 添加节点和边 ...
# 编译:LangGraph 会进行以下检查
app = workflow.compile()
当我们调用 compile() 方法时,LangGraph 会进行一系列重要的检查:
- 检查是否有孤立的节点:确保所有节点都被正确连接
- 检查路径完整性:确保至少有一条从 START 到 END 的路径
- 验证条件边的路由:确保路由函数的返回值都在映射表中
- 优化执行计划:识别可并行执行的节点
如果编译失败,LangGraph 会抛出清晰的错误信息,明确指出问题所在。这种编译期检查机制是 LangGraph 相比手写状态机的一大优势。
二、三种执行模式
LangGraph 提供了灵活的执行模式,以适应不同的使用场景:
1. 阻塞执行(invoke)
最简单的执行方式,等待整个图执行完毕返回最终结果:
python
result = app.invoke(input_data)
2. 流式输出(stream)
逐节点或逐 token 产出结果,适合实时监控和调试:
python
for event in app.stream(input_data):
for node_name, output in event.items():
print(f"[{node_name}] 输出了: {output}")
3. 异步版本(ainvoke / astream)
适用于异步编程环境,提高并发性能:
python
result = await app.ainvoke(input_data)
async for event in app.astream(input_data):
# 处理事件
pass
三、可视化你的图
LangGraph 内置了强大的可视化能力,这是调试复杂 Agent 流程的利器。
ASCII 终端预览
在开发阶段,最快捷的查看方式是打印 ASCII 图:
python
print(app.get_graph().print_ascii())
这种方式虽然简陋,但能在终端中一眼看出图的整体结构,特别适合快速验证。
Mermaid 图表生成
LangGraph 支持导出为 Mermaid 语法,并可渲染为 PNG 图片:
python
mermaid_code = app.get_graph().draw_mermaid()
png_bytes = app.get_graph().draw_mermaid_png()
通过 xray=True 参数还可以展示更详细的内部结构。
可视化的价值
- 调试利器:快速定位问题节点和边
- 沟通工具:向团队解释工作流程
- 文档沉淀:保存为项目文档的一部分
常见问题与避坑指南
新手最常见的 5 个错误
错误1:忘记使用 Reducer,导致消息被覆盖
python
# ❌ 错误:每次返回都会覆盖 messages
return {"messages": [new_message]} # 旧消息消失了!
# ✅ 正确:使用 add_messages Reducer
# 在 State 定义中:messages: Annotated[List, add_messages]
return {"messages": [new_message]} # 追加到已有列表
错误2:条件边的路由函数返回了不存在的节点名
python
# ❌ 错误:返回了未注册的节点名
def router(state):
return "non_existent_node" # 编译时会报错
# ✅ 正确:确保返回值在 add_conditional_edges 的映射中
def router(state):
return "existing_node" # 必须事先 add_node
错误3:没有从 START 出发的边
python
# ❌ 错误:图没有入口
workflow.add_node("node_a", func_a)
workflow.add_edge("node_a", END) # 但没有从 START 到 node_a 的边
# ✅ 正确:必须有入口边
workflow.add_edge(START, "node_a")
错误4:节点函数返回了非字典类型
python
# ❌ 错误:返回字符串
def bad_node(state):
return "hello" # 必须是 dict
# ✅ 正确:返回字典
def good_node(state):
return {"messages": [AIMessage(content="hello")]}
错误5:在节点内部修改了 State 的可变对象而没有返回
python
# ❌ 错误:直接修改了传入的 state
def bad_node(state):
state["messages"].append(new_msg) # 虽然有效,但不推荐
return {} # 返回空字典,LangGraph 无法追踪这次修改
# ✅ 正确:通过返回值声明修改
def good_node(state):
return {"messages": [new_msg]} # 让 LangGraph 通过 Reducer 处理