219232de74
以讲义内容为骨架迁移到标准目录格式: - officefile/ 主内容(12章 + 附录 + CC4SI补充) - dofile/ 代码示例(11个Python脚本) - data/ 图片资源 - output/ 生成输出(忽略) - Archive/ 归档旧目录(忽略) - .claude/skills/ 保留markdown-to-docx工具链 - .pandoc/ 保留CSL和本地化配置 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
8.6 KiB
8.6 KiB
实践案例01:用LangGraph构建空间决策工作流
目标
通过本实践,你将:
- 理解状态驱动的Agent设计
- 实现一个简单的空间决策工作流
- 添加Human-in-the-Loop审查点
- 处理工作流中的错误和重试
背景知识
什么是LangGraph
LangGraph是构建有状态的多Agent应用的框架:
核心概念:
1. State(状态): 在节点间传递的数据
2. Node(节点): 处理状态的函数
3. Edge(边): 节点之间的连接
4. Graph(图): 节点和边组成的完整工作流
为什么用LangGraph
- 状态管理: 自动管理工作流状态
- 可视化: 可以绘制和查看工作流图
- 持久化: 支持中断和恢复
- 条件路由: 基于状态动态选择路径
实践步骤
步骤1:安装依赖
pip install langgraph langchain-core langchain-anthropic
步骤2:定义状态
from typing import TypedDict, Annotated, List, Optional
from operator import add
from typing_extensions import TypedDict
class EcologicalAnalysisState(TypedDict):
"""生态网络分析状态"""
# 输入
input_path: str
parameters: dict
# 处理过程
current_step: str
intermediate_results: dict
# 人机交互
review_requested: bool
human_feedback: Optional[str]
# 输出
final_result: Optional[dict]
errors: Annotated[List[str], add]
步骤3:定义节点
def load_data_node(state: EcologicalAnalysisState) -> EcologicalAnalysisState:
"""加载数据节点"""
print("执行: load_data")
# 实际实现中读取文件
return {
**state,
"current_step": "data_loaded",
"intermediate_results": {"data": "loaded"}
}
def identify_sources_node(state: EcologicalAnalysisState) -> EcologicalAnalysisState:
"""识别源地节点"""
print("执行: identify_sources")
# 实际实现中运行源地识别算法
sources = [{"id": 1, "area": 1000}, {"id": 2, "area": 800}]
return {
**state,
"current_step": "sources_identified",
"intermediate_results": {**state["intermediate_results"], "sources": sources}
}
def human_review_node(state: EcologicalAnalysisState) -> EcologicalAnalysisState:
"""人类审查节点"""
print("执行: human_review")
print(f"待审查: {state['intermediate_results']}")
# 实际实现中等待人类输入
return {
**state,
"review_requested": False,
"human_feedback": "approved"
}
def build_resistance_node(state: EcologicalAnalysisState) -> EcologicalAnalysisState:
"""构建阻力面节点"""
print("执行: build_resistance")
return {
**state,
"current_step": "resistance_built"
}
步骤4:定义路由
def should_review(state: EcologicalAnalysisState) -> str:
"""决定是否需要审查"""
sources = state["intermediate_results"].get("sources", [])
if len(sources) > 2: # 源地数量多时需要审查
return "review"
return "continue"
步骤5:构建图
from langgraph.graph import StateGraph, END
def build_workflow():
"""构建工作流图"""
# 创建图
workflow = StateGraph(EcologicalAnalysisState)
# 添加节点
workflow.add_node("load_data", load_data_node)
workflow.add_node("identify_sources", identify_sources_node)
workflow.add_node("human_review", human_review_node)
workflow.add_node("build_resistance", build_resistance_node)
# 设置入口
workflow.set_entry_point("load_data")
# 添加边
workflow.add_edge("load_data", "identify_sources")
# 添加条件边
workflow.add_conditional_edges(
"identify_sources",
should_review,
{
"review": "human_review",
"continue": "build_resistance"
}
)
workflow.add_edge("human_review", "build_resistance")
workflow.add_edge("build_resistance", END)
# 编译
return workflow.compile()
步骤6:运行工作流
def run_workflow():
"""运行工作流"""
# 初始状态
initial_state = {
"input_path": "data.geojson",
"parameters": {},
"current_step": "start",
"intermediate_results": {},
"review_requested": False,
"human_feedback": None,
"final_result": None,
"errors": []
}
# 构建并运行
app = build_workflow()
result = app.invoke(initial_state)
print("\n=== 最终结果 ===")
print(result)
扩展练习
1. 添加错误处理
def with_error_handling(node_func):
"""装饰器:添加错误处理"""
def wrapper(state):
try:
return node_func(state)
except Exception as e:
return {
**state,
"errors": [str(e)]
}
return wrapper
# 使用
@with_error_handling
def risky_node(state):
# 可能出错的节点
...
2. 添加检查点(持久化)
from langgraph.checkpoint.memory import MemorySaver
# 创建检查点保存器
memory = MemorySaver()
# 编译时添加检查点
app = workflow.compile(checkpointer=memory, interrupt_before=["human_review"])
# 运行时可以指定thread_id
config = {"configurable": {"thread_id": "conversation-1"}}
result = app.invoke(initial_state, config=config)
3. 可视化工作流
from IPython.display import Image, display
# 生成图
app = build_workflow()
display(Image(app.get_graph().draw_mermaid_png()))
完整代码示例
"""
完整的LangGraph空间决策工作流示例
"""
from typing import TypedDict, Annotated, List, Optional, Literal
from operator import add
from langgraph.graph import StateGraph, END
class State(TypedDict):
"""工作流状态"""
step: int
data: Optional[dict]
sources: Optional[list]
reviewed: bool
result: Optional[str]
errors: Annotated[List[str], add]
# 节点函数
def load_node(state: State) -> State:
"""加载数据"""
print(f"[节点: load] 步骤 {state['step']}")
return {**state, "step": state["step"] + 1, "data": {"loaded": True}}
def analyze_node(state: State) -> State:
"""分析数据"""
print(f"[节点: analyze] 步骤 {state['step']}")
return {
**state,
"step": state["step"] + 1,
"sources": [{"id": 1, "value": 100}]
}
def review_node(state: State) -> State:
"""人类审查"""
print(f"[节点: review] 步骤 {state['step']}")
print("等待人类审查...")
# 实际实现中等待输入
return {**state, "step": state["step"] + 1, "reviewed": True}
def finalize_node(state: State) -> State:
"""完成"""
print(f"[节点: finalize] 步骤 {state['step']}")
return {**state, "result": "completed"}
# 路由函数
def route_after_analyze(state: State) -> Literal["review", "finalize"]:
"""分析后的路由"""
if state.get("sources") and len(state["sources"]) > 0:
return "review"
return "finalize"
# 构建图
def build_graph():
"""构建工作流图"""
graph = StateGraph(State)
# 添加节点
graph.add_node("load", load_node)
graph.add_node("analyze", analyze_node)
graph.add_node("review", review_node)
graph.add_node("finalize", finalize_node)
# 添加边
graph.set_entry_point("load")
graph.add_edge("load", "analyze")
# 条件边
graph.add_conditional_edges(
"analyze",
route_after_analyze,
{"review": "review", "finalize": "finalize"}
)
graph.add_edge("review", "finalize")
graph.add_edge("finalize", END)
return graph.compile()
# 运行
if __name__ == "__main__":
print("=== LangGraph 空间决策工作流 ===\n")
app = build_graph()
initial_state: State = {
"step": 1,
"data": None,
"sources": None,
"reviewed": False,
"result": None,
"errors": []
}
result = app.invoke(initial_state)
print(f"\n最终状态: {result['step']}")
print(f"结果: {result['result']}")
反思问题
-
状态设计:你的状态中哪些信息是必需的?哪些可以省略?
-
节点粒度:节点应该多大?如何平衡?
-
错误处理:当节点失败时,工作流应该如何处理?
-
审查点:你的工作流中哪些地方需要人类介入?
下一步
完成这个实践后,你已经:
- ✅ 理解了状态驱动的Agent设计
- ✅ 实现了一个简单的LangGraph工作流
- ✅ 掌握了条件路由的基本方法
- ✅ 了解了如何添加HITL审查点
准备好进入下一章:02-spatial-intelligence(空间智能)