什么是LangGraph?
LangGraph(🦜🕸️)是LangChain团队开发的一个强大工具,它让我们能够以图的方式构建语言代理。简单来说,它就像是给AI搭建的一个"思维导图",让AI能够按照我们设计的路径进行思考和决策。
与传统的线性工作流相比,LangGraph最大的特点是支持循环和分支,这意味着AI可以:
- 反复思考同一个问题直到解决它
- 根据不同情况选择不同的行动路径
- 在需要时回到之前的步骤重新考虑
官方文档:langchain-ai.github.io/langgraph/
LangGraph的核心优势
LangGraph相比其他LLM框架有三大核心优势:
- 循环能力:大多数代理架构都需要循环(比如思考-行动-观察的循环),LangGraph让这变得简单
- 精细控制:作为底层框架,它提供了对应用流程和状态的精确控制权
- 内置持久性:可以在任何时候保存和恢复执行状态,实现高级的"人机交互"和记忆功能
LangGraph的核心概念
1. 状态(State)
状态是LangGraph的核心概念,它就像是代理的"记忆"或"工作区":
- 作用:保存计算过程中的所有上下文信息
- 特点:随着代理思考和行动不断更新
- 示例:对话历史、收集到的信息、中间计算结果等
2. 节点(Node)
节点代表计算过程中的一个步骤或行动:
- 作用:执行特定任务,如思考、搜索信息、调用工具等
- 特点:接收当前状态,执行操作,然后更新状态
- 示例:代理思考节点、工具调用节点、人类反馈节点等
3. 边(Edge)
边连接不同的节点,定义计算流程:
- 作用:决定下一步该执行哪个节点
- 类型:普通边(固定路径)和条件边(根据状态动态选择路径)
- 特点:支持循环和分支逻辑,实现复杂决策流程
LangGraph的主要功能
-
循环和分支:在应用中实现循环和条件语句,让AI能够反复思考或根据情况选择不同路径
-
持久性:在图中的每个步骤后自动保存状态,支持:
- 错误恢复:出错时可以从上一个正确状态恢复
- 暂停和恢复:可以随时暂停执行,稍后再继续
- 时间旅行:可以回到之前的任何状态,尝试不同的决策路径
-
人机交互:可以在关键决策点暂停执行,让人类审核或修改AI的计划
-
流式输出:支持在每个节点产生输出时进行流式传输,包括令牌级的流式输出
-
与LangChain集成:与LangChain和LangSmith无缝集成,但不强制依赖它们
安装LangGraph
pip install langgraph
如果你想使用LangSmith进行可视化和调试(推荐但不是必须的):
pip install langsmith
LangGraph实战:构建一个具有搜索能力的代理
下面我们将构建一个简单但功能完整的代理,它能够:
- 理解用户问题
- 决定是直接回答还是使用搜索工具
- 调用搜索工具获取信息
- 基于搜索结果生成最终答案
步骤1:设置环境和导入依赖
import os
from typing import List, Tuple, Dict, Any
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
from langchain_openai import ChatOpenAI
from langchain_community.tools.tavily_search import TavilySearchResults
from langgraph.graph import StateGraph, END
from langgraph.checkpoint import MemorySaver
from langgraph.prebuilt import MessagesState
# 设置API密钥
os.environ["OPENAI_API_KEY"] = "你的OpenAI API密钥"
os.environ["TAVILY_API_KEY"] = "你的Tavily API密钥" # 用于搜索工具
步骤2:定义搜索工具
# 创建搜索工具
search_tool = TavilySearchResults(max_results=3)
步骤3:创建语言模型
# 创建支持工具调用的语言模型
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
llm_with_tools = llm.bind_tools([search_tool])
步骤4:定义代理节点函数
# 代理节点 - 决定下一步行动
def agent(state: MessagesState) -> MessagesState:
"""代理决策节点,决定是直接回答还是使用工具"""
# 获取消息历史
messages = state.messages
# 添加系统提示,指导代理如何工作
if not any(isinstance(msg, SystemMessage) for msg in messages):
messages = [
SystemMessage(content="你是一个有用的AI助手。当用户提出问题时,如果你知道答案,就直接回答。"
"如果你不确定或需要最新信息,使用搜索工具查找相关信息。")
] + messages
# 调用语言模型获取回复
response = llm_with_tools.invoke(messages)
# 返回更新后的状态
return MessagesState(messages=messages + [response])
步骤5:定义工具调用节点函数
# 工具调用节点 - 执行工具调用并返回结果
def tools(state: MessagesState) -> MessagesState:
"""执行工具调用并将结果添加到消息历史"""
# 获取最后一条AI消息
messages = state.messages
last_message = messages[-1]
if not isinstance(last_message, AIMessage) or not last_message.tool_calls:
# 如果没有工具调用,直接返回原状态
return state
# 处理所有工具调用
new_messages = []
for tool_call in last_message.tool_calls:
# 获取工具调用信息
tool_name = tool_call.name
tool_args = tool_call.args
# 执行工具调用
if tool_name == "tavily_search_results_json":
# 调用搜索工具
search_results = search_tool.invoke(tool_args.get("query", ""))
# 创建工具响应消息
new_messages.append(
AIMessage(
content="",
tool_call_id=tool_call.id,
tool_calls=[],
additional_kwargs={"tool_responses": [{"content": str(search_results)}]}
)
)
# 返回更新后的状态
return MessagesState(messages=messages + new_messages)
步骤6:定义条件路由函数
# 条件路由函数 - 决定下一步去向
def should_continue(state: MessagesState) -> str:
"""决定是继续使用工具还是结束执行"""
# 获取最后一条AI消息
messages = state.messages
last_message = messages[-1]
# 检查是否有未完成的工具调用
if isinstance(last_message, AIMessage) and last_message.tool_calls:
return "tools" # 继续执行工具
else:
return END # 结束执行
步骤7:构建和编译图
# 创建状态图
graph = StateGraph(MessagesState)
# 添加节点
graph.add_node("agent", agent)
graph.add_node("tools", tools)
# 设置入口点
graph.set_entry_point("agent")
# 添加边 - 定义执行流程
graph.add_conditional_edges("agent", should_continue) # 条件边:根据agent输出决定下一步
graph.add_edge("tools", "agent") # 工具执行后总是返回到代理
# 编译图
memory_saver = MemorySaver() # 创建内存保存器
chain = graph.compile(checkpointer=memory_saver) # 编译图为可执行链
步骤8:执行图并与代理交互
# 创建一个新的对话线程
thread_id = "thread_1"
# 发送第一个问题
response = chain.invoke(
{"messages": [HumanMessage(content="太阳能电池板是什么?它们如何工作?")]},
{"configurable": {"thread_id": thread_id}}
)
# 输出代理回复
print("代理回复:")
print(response.messages[-1].content)
# 继续对话 - 发送后续问题
response = chain.invoke(
{"messages": [HumanMessage(content="太阳能电池板的安装成本大约是多少?")]},
{"configurable": {"thread_id": thread_id}}
)
# 输出代理回复
print("\n代理回复:")
print(response.messages[-1].content)
LangGraph执行流程详解
当我们运行上面的代码时,LangGraph会按照以下流程执行:
-
初始化状态:
- 创建一个新的MessagesState对象,包含用户的初始消息
-
进入入口点节点:
- 执行"agent"节点
- 语言模型分析用户问题并决定下一步行动
-
条件路由:
- 如果语言模型决定使用工具(生成了tool_calls):
- 执行"tools"节点
- 调用相应的工具(如搜索)
- 将工具结果添加到状态
- 返回到"agent"节点
- 如果语言模型直接回答(没有tool_calls):
- 执行结束,返回最终状态
- 如果语言模型决定使用工具(生成了tool_calls):
-
循环执行:
- 代理可能会多次决定使用工具
- 每次工具执行后都会返回到代理进行下一步决策
- 直到代理决定不再需要工具,生成最终回答
-
返回结果:
- 返回包含完整对话历史的最终状态
LangGraph的高级功能
1. 状态持久化
LangGraph允许在执行过程中保存状态,这对于长时间运行的代理特别有用:
# 使用文件系统保存状态
from langgraph.checkpoint import FileSystemCheckpointer
fs_checkpointer = FileSystemCheckpointer(path="./checkpoints")
chain = graph.compile(checkpointer=fs_checkpointer)
2. 人机协作工作流
可以在关键决策点暂停执行,让人类审核或修改代理的计划:
# 定义人类反馈节点
def human_feedback(state: MessagesState) -> MessagesState:
"""获取人类反馈"""
messages = state.messages
last_message = messages[-1]
# 显示AI的计划并获取人类反馈
print("AI计划:", last_message.content)
feedback = input("请提供反馈或按Enter继续: ")
if feedback:
# 添加人类反馈到消息历史
return MessagesState(messages=messages + [HumanMessage(content=feedback)])
return state
# 将人类反馈节点添加到图中
graph.add_node("human_feedback", human_feedback)
graph.add_edge("agent", "human_feedback")
graph.add_edge("human_feedback", "agent")
3. 多代理协作
LangGraph支持构建多个代理之间的协作系统:
# 定义不同角色的代理
def researcher(state: MessagesState) -> MessagesState:
"""研究者代理 - 负责收集信息"""
# 实现研究者逻辑
...
def critic(state: MessagesState) -> MessagesState:
"""批评者代理 - 负责评估信息质量"""
# 实现批评者逻辑
...
def writer(state: MessagesState) -> MessagesState:
"""撰写者代理 - 负责生成最终内容"""
# 实现撰写者逻辑
...
# 将多个代理添加到图中并定义它们之间的协作流程
graph.add_node("researcher", researcher)
graph.add_node("critic", critic)
graph.add_node("writer", writer)
# 定义协作流程
graph.add_edge("researcher", "critic")
graph.add_edge("critic", "writer")
进阶:自定义状态类型
除了使用预构建的MessagesState,我们还可以定义自己的状态类型:
from pydantic import BaseModel, Field
from typing import List, Dict, Any, Optional
# 定义自定义状态类
class CustomAgentState(BaseModel):
"""自定义代理状态"""
messages: List[Any] = Field(default_factory=list)
collected_data: Dict[str, Any] = Field(default_factory=dict)
current_step: str = "initial"
class Config:
arbitrary_types_allowed = True
# 使用自定义状态创建图
custom_graph = StateGraph(CustomAgentState)
总结
LangGraph是一个强大的工具,它通过图的方式让我们能够构建复杂的AI代理系统。它的核心优势在于:
- 支持循环和分支:让AI能够反复思考和根据情况选择不同路径
- 状态管理:维护代理的"记忆",实现复杂的推理过程
- 灵活的控制流:通过节点和边定义精确的执行流程
- 内置持久性:支持状态保存和恢复,实现高级交互功能
通过LangGraph,我们可以构建比简单问答更复杂的AI应用,如:
- 多步骤推理代理
- 自主研究助手
- 多代理协作系统
- 人机协作工作流
LangGraph不仅扩展了LangChain的能力,还为构建下一代AI应用提供了坚实的基础。