AI Agent工作流编排的核心问题
构建AI智能体时,单次LLM调用无法处理复杂业务逻辑。Agent需要根据中间结果决定下一步动作——调用工具、切换分支还是终止流程。LangGraph通过有向图状态机模型解决这个问题,每个节点是一个处理单元,边定义转移条件和数据流向。
LangGraph状态图设计原理
LangGraph的核心抽象是StateGraph。定义一个TypedDict作为全局状态容器,所有节点共享读写这个状态对象。图的构建分三步:初始化StateGraph、添加节点函数、添加条件边或固定边。
状态定义示例:
from typing import TypedDict, Annotated, List
from langgraph.graph import StateGraph, END
import operator
class AgentState(TypedDict):
messages: Annotated[list, operator.add]
next_action: str
tool_results: dict
iteration: int
max_iterations: int
每个节点的函数签名统一为def node_fn(state: AgentState) -> dict,返回部分状态更新。LangGraph自动合并返回值到全局状态。
多节点条件分支实现
实际业务中Agent需要根据推理结果选择不同路径。条件边通过add_conditional_edges实现,接收一个映射函数和路径字典。
def router(state: AgentState) -> str:
if state['iteration'] >= state['max_iterations']:
return 'end'
if state['next_action'] == 'tool_call':
return 'tool_node'
elif state['next_action'] == 'reflect':
return 'reflect_node'
else:
return 'end'
graph = StateGraph(AgentState)
graph.add_node('planner', planner_fn)
graph.add_node('tool_node', tool_fn)
graph.add_node('reflect_node', reflect_fn)
graph.add_node('finalizer', finalizer_fn)
graph.set_entry_point('planner')
graph.add_conditional_edges('planner', router, {
'tool_node': 'tool_node',
'reflect_node': 'reflect_node',
'end': 'finalizer'
})
graph.add_edge('tool_node', 'planner')
graph.add_edge('reflect_node', 'planner')
graph.add_edge('finalizer', END)
app = graph.compile()
执行时调用app.invoke(initial_state),图引擎自动完成状态传递和节点调度。
工具调用节点的生产级写法
工具节点不应直接执行外部API调用,而是封装为异步函数,加入超时控制和重试逻辑:
import httpx
import asyncio
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(stop=stop_after_attempt(3), wait=wait_exponential(min=1, max=10))
async def call_search_api(query: str, timeout: float = 15.0) -> dict:
async with httpx.AsyncClient(timeout=timeout) as client:
resp = await client.get(
'https://api.search.example/v1/search',
params={'q': query, 'limit': 5}
)
resp.raise_for_status()
return resp.json()
在节点函数中调用异步工具时,使用asyncio.run或直接将图编译为异步模式。
循环与迭代终止策略
Agent容易陷入无限循环——规划节点持续调用工具节点,工具节点返回结果又触发新一轮规划。三种终止策略必须同时配置:
1. 硬性迭代次数上限(max_iterations)
2. 状态收敛检测:连续两次状态差异小于阈值时终止
3. 超时兆底:整个图执行超过限定时间强制退出
def convergence_check(state: AgentState) -> bool:
msgs = state.get('messages', [])
if len(msgs) < 2:
return False
last_two = msgs[-2:]
similarity = compute_similarity(last_two[0], last_two[1])
return similarity > 0.95
持久化与断点恢复
长时间运行的Agent需要支持断点恢复。LangGraph集成checkpoint机制,使用SQLite或PostgreSQL作为后端:
from langgraph.checkpoint.sqlite import SqliteSaver
memory = SqliteSaver.from_conn_string('./agent_checkpoints.db')
app = graph.compile(checkpointer=memory)
config = {'configurable': {'thread_id': 'session_001'}}
result = app.invoke(initial_state, config)
# 恢复执行:用相同thread_id重新调用
resumed = app.invoke(None, config)
每次节点执行完毕,状态自动写入数据库。进程崩溃后用相同thread_id恢复,从最后一个完成的节点继续执行。
生产环境部署要点
部署LangGraph Agent到生产环境需要关注三个维度:
并发控制:图引擎本身无并发限制,需要在外层加入信号量或工作队列。推荐使用Redis队列加Worker模式,每个Worker运行独立图实例。
可观测性:通过langchain.callbacks集成LangSmith或自建OpenTelemetry追踪。关键指标包括单节点执行耗时、整体图执行时间、工具调用成功率和状态体积变化。
from langchain.callbacks import LangChainTracer
tracer = LangChainTracer(project_name='agent-prod')
result = app.invoke(
initial_state,
config={'callbacks': [tracer], 'configurable': {'thread_id': tid}}
)
成本控制:LLM调用是主要成本来源。在节点函数中缓存重复查询结果,对工具返回值做摘要压缩再写回状态,减少下游节点的token消耗。
实际生产中一个中等复杂度的Agent(5-8节点、3轮平均迭代)单次执行成本在0.02-0.15美元之间,日均处理1000次请求的月token成本约600-4500美元。合理的缓存和摘要策略可以将成本压缩到原始的30%-50%。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/aiagent-gong-zuo-liu-bian-pai-shi-zhan-langgraph-duo-jie/