我们将从零开始,一步步实现一个基于 LangGraph 的 ReAct 智能体。这个智能体将扮演一位“研究员”,能够利用工具(比如网络搜索)来回答复杂问题。
LangGraph 从0到1:手把手教你实现 ReAct 研究员智能体
目标
完成本教程后,你将:
- 理解什么是 ReAct 模式,以及它为什么这么强大。
- 掌握 LangGraph 的核心概念:状态(State)、节点(Node)和边(Edge)。
- 亲手编码实现一个会思考、行动、观察的研究员智能体。
- 能够让你的智能体连接互联网,进行深入研究。
第一部分:核心概念,先“画个图”
在写代码之前,先用大白话聊聊几个核心概念。
1. 什么是智能体(Agent)?
想象一下,你不是直接跟一个聊天机器人说话,而是跟一个拥有“大脑”和“双手”的助理沟通。
- 大脑 (LLM):就是像 GPT-4.1 这样的大语言模型,负责思考和决策。
- 双手 (Tools):就是它能使用的工具,比如“网络搜索引擎”、“计算器”、“代码执行器”等。
智能体就是大脑 + 双手的结合体。你给它一个任务,它会自己思考“我该用哪个工具?”,然后使用工具,看到结果后,再继续思考,直到任务完成。
2. 什么是 ReAct 模式? (智能体的“工作流程”)
ReAct 是 “Reason” (推理) + “Act” (行动) 的缩写。这是目前最经典、最有效的智能体工作模式。
把它想象成一个侦探破案的过程:
- 思考 (Reason):侦探拿到案子(你的问题),首先会分析线索,心想:“嗯…我需要先去查一下受害者的背景资料。”
- 行动 (Act):他决定使用一个工具——“档案查询系统”(调用 Tool)。
- 观察 (Observe):他从系统中拿到了资料(工具返回结果),然后观察这份新资料。
- 重复循环: 基于新的资料,他再次思考:“哦,原来受害者和某某公司有联系,我下一步要去搜一下这家公司的新闻。” -> 行动(上网搜索) -> 观察…
这个 “思考 -> 行动 -> 观察” 的循环,就是 ReAct 的精髓。它让 AI 不再是一问一答的“黑盒子”,而是有了一个透明、可追溯的思考过程。
3. 什么是 LangGraph? (“流程图搭建工具”)
如果 ReAct 是工作流程,那 LangGraph 就是帮你把这个流程固化和可视化的工具。
LangGraph 让我们用代码来定义一个“流程图”(在技术上称为“图”或“Graph”)。
这个图由三个核心部分组成:
- 状态 (State):一个共享的“记事本”。图里的每个步骤都可以读取和更新这个记事本。在我们的例子里,记事本上记录了所有的对话历史。
- 节点 (Nodes):流程图中的一个个“工作站”。每个节点都是一个函数,负责执行一项具体任务。比如有:
- 一个 “思考” 节点(调用大模型进行推理)。
- 一个 “行动” 节点(使用工具)。
- 边 (Edges):连接节点的“箭头”。它决定了完成一个节点后,下一步该去哪个节点。有些箭头是固定的,有些则是“有条件的”(比如,如果思考结果是“需要用工具”,就走向“行动”节点;如果是“任务已完成”,就直接结束)。
总结一下:要用 LangGraph 这个工具,搭建一个 ReAct 流程图,图中的“记事本”来做状态管理,“工作站”是对应的节点,连接线是对应的边。
第二部分:准备工作,磨刀不误砍柴工
在开始编码前,确保已经准备好了以下环境。
1. 安装必要的库
打开你的终端(命令行工具),执行以下命令:
pip install langgraph langchain langchain_openai tavily-python
langgraph
: 核心的图构建库。langchain
: LangGraph 的基础,提供了很多便捷的组件。langchain_openai
: 用于连接 OpenAI 的模型。tavily-python
: 使用 Tavily 作为网络搜索工具,它非常适合研究任务,并且提供免费的 API。
2. 获取 API 密钥
智能体需要两个“通行证”才能工作:
- OpenAI API Key: 用于访问 GPT 模型(大脑)。前往 OpenAI 平台 创建。
- Tavily API Key: 用于访问 Tavily 搜索引擎(双手)。前往 Tavily AI 官网 注册获取。
获取密钥后,为了安全和方便,需将其设置为环境变量。
import os
os.environ["OPENAI_API_KEY"] = "sk-..." # 替换成你的 OpenAI Key
os.environ["TAVILY_API_KEY"] = "tvly-..." # 替换成你的 Tavily Key
提示: 在实际项目中,最好使用
.env
文件来管理密钥,这里为了教程简化,直接在代码中设置。
第三部分:动手编码,一步步搭建你的智能体
好了,理论和准备工作都已就绪,开始写代码!
步骤 1: 定义状态 (State) - 共享记事本
这里需要一个地方来存储对话过程中的所有信息。LangGraph 中,通常用一个 TypedDict
来定义状态。
from typing import TypedDict, Annotated
from langchain_core.messages import BaseMessage
import operator
# Annotated[operator.add, []] 的意思是
# 这个字段的更新方式是“追加”,而不是“覆盖”
# 这样就能保存所有历史消息
class AgentState(TypedDict):
messages: Annotated[list[BaseMessage], operator.add]
AgentState
就是“记事本”模板。它里面只有一个字段 messages
,用来存放一个消息列表。Annotated[..., operator.add]
是一个 LangGraph 的小魔法,它告诉图,每次更新 messages
时,应该把新消息添加到列表末尾,而不是替换整个列表。
步骤 2: 定义工具 (Tools) - 赋予智能体“双手”
研究员需要一个强大的搜索引擎。这里使用 Tavily。
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain.agents import Tool
# 实例化工具
# max_results=3 表示每次搜索最多返回3个结果
tavily_tool = TavilySearchResults(max_results=3)
# 将工具包装成 LangChain 的 Tool 对象
# 这是 LangGraph 和 LangChain 的标准做法
# name: 工具的名称,LLM会根据这个名字来决定是否调用
# func: 工具的实际执行函数
# description: 工具的描述,LLM会根据这个描述来理解工具的功能,非常重要!
tools = [
Tool(
name="tavily_search",
func=tavily_tool.invoke,
description="一个强大的搜索引擎,可以查找关于人物、事件、公司、最新新闻等各类信息。"
)
]
# 创建一个 ToolExecutor,它像一个工具箱,负责管理和执行所有的工具
from langgraph.prebuilt import ToolExecutor
tool_executor = ToolExecutor(tools)
步骤 3: 定义模型 (Model) - 选择智能体的“大脑”
选择 OpenAI 的模型,并使用一个特殊的方法 .bind_tools()
,将刚刚定义的工具“告诉”模型。这样,模型在思考时,就知道自己有哪些工具可以用。
from langchain_openai import ChatOpenAI
# 选择 gpt-4o-mini,性价比高,效果好
# temperature=0 表示让模型尽可能输出稳定、确定的结果
model = ChatOpenAI(model="gpt-4o-mini", temperature=0, streaming=True)
# 将工具信息绑定到模型上,让模型知道它能使用哪些工具
model = model.bind_tools(tools)
步骤 4: 定义节点 (Nodes) - 搭建流程图的“工作站”
现在,要定义 ReAct 流程中的两个核心节点:agent
(思考) 和 action
(行动)。
A. “思考”节点: agent
这个节点负责调用大模型,让它根据当前对话历史进行思考,并决定下一步是直接回答还是使用工具。
from langgraph.graph import StateGraph, END
from langchain_core.messages import ToolMessage, AIMessage
# 定义 "agent" 节点
def agent(state: AgentState):
"""
调用大模型进行思考和推理
:param state: 当前的状态
:return: 一个包含模型响应的字典,用于更新状态
"""
print("---思考中...---")
# 从状态中获取所有消息
messages = state["messages"]
# 调用大模型
response = model.invoke(messages)
# response 就是一个 AIMessage 对象,我们将其返回
# operator.add 会自动把它追加到 messages 列表中
return {"messages": [response]}
# 定义 "action" 节点
def action(state: AgentState):
"""
执行工具
:param state: 当前的状态
:return: 一个包含工具执行结果的字典,用于更新状态
"""
print("---执行工具...---")
# 从状态中获取最新的AI消息,这个消息里包含了工具调用的请求
ai_message = state["messages"][-1]
# 从AI消息中解析出工具调用信息
tool_calls = ai_message.tool_calls
# 调用 ToolExecutor 执行工具
tool_outputs = tool_executor.batch(tool_calls)
# 将工具的输出结果包装成 ToolMessage
# 以便下一步模型可以理解
tool_messages = [
ToolMessage(content=str(output), tool_call_id=call["id"])
for call, output in zip(tool_calls, tool_outputs)
]
# 返回工具消息,更新状态
return {"messages": tool_messages}
B. "决策"函数: should_continue
除了工作站,还需要一个“调度员”。这个函数会检查大模型最新的一步思考结果,然后决定流程是继续(调用工具),还是结束(已经得到最终答案)。
def should_continue(state: AgentState):
"""
决策下一步走向
:param state: 当前状态
:return: "action" 或 "end"
"""
print("---决策中...---")
last_message = state["messages"][-1]
# 如果最新的消息是 AI 消息,并且包含了 tool_calls,说明需要执行工具
if isinstance(last_message, AIMessage) and last_message.tool_calls:
print(">>> 决策:执行工具")
return "action"
else:
# 否则,说明流程可以结束了
print(">>> 决策:结束")
return "end"
步骤 5: 构建图 (Graph) - 连接所有节点和边
万事俱备,只欠东风!现在用 LangGraph 把上面定义的所有部分组装起来。
# 创建一个 StateGraph 对象,并绑定对应的状态类型
workflow = StateGraph(AgentState)
# 1. 添加节点
workflow.add_node("agent", agent) # 添加“思考”节点
workflow.add_node("action", action) # 添加“行动”节点
# 2. 设置入口点
# 流程从 "agent" 节点开始
workflow.set_entry_point("agent")
# 3. 添加条件边
# 从 "agent" 节点出来后,调用 should_continue 函数进行决策
workflow.add_conditional_edges(
"agent",
should_continue,
{
# 如果决策结果是 "action", 就流向 "action" 节点
"action": "action",
# 如果决策结果是 "end", 就流向终点
"end": END
}
)
# 4. 添加普通边
# "action" 节点执行完后,无条件地流回 "agent" 节点,形成循环
workflow.add_edge("action", "agent")
# 5. 编译图
# 将定义的图编译成一个可执行的应用
app = workflow.compile()
ReAct 流程图已经画好了!它的逻辑是:
入口
-> agent(思考)
-> should_continue(决策)
- 如果需要工具 ->
action(行动)
->agent(思考)
… (形成循环) - 如果不需要工具 ->
END(结束)
第四部分:运行与测试,见证奇迹的时刻
到此已经成功构建了研究员智能体,现在来和它聊聊天,让它为我们做个深入研究吧!
from langchain_core.messages import HumanMessage
# 定义一个输入,这是一个需要多步研究才能回答的问题
inputs = {"messages": [HumanMessage(content="埃隆·马斯克最近在人工智能领域有什么新的大动作?他旗下的 xAI 公司和他的其他公司(比如特斯拉、SpaceX)之间有什么协同效应?")]}
# 使用 .stream() 方法来运行,可以清晰地看到每一步的输出
for event in app.stream(inputs, stream_mode="values"):
# event 是一个 AgentState 对象
# 我们可以打印出最新的消息,观察智能体的思考过程
event["messages"][-1].pretty_print()
print("\n" + "="*80 + "\n")
当你运行这段代码时,你会在终端看到类似这样的输出,完美展示了 ReAct 的过程:
---思考中...---
================================================================================
AIMessage(
content="",
tool_calls=[
{
"name": "tavily_search",
"args": {
"query": "Elon Musk xAI latest developments and synergy with Tesla SpaceX"
},
"id": "call_..."
}
]
)
================================================================================
---决策中...---
>>> 决策:执行工具
================================================================================
---执行工具...---
================================================================================
ToolMessage(
content='[{"url": "...", "content": "xAI 宣布了其先进大模型 Grok-1.5 的细节..."}, {"url": "...", "content": "马斯克表示,特斯拉的股东将对是否将 AI 和机器人技术融入公司进行投票..."}, ...]'
tool_call_id="call_..."
)
================================================================================
---思考中...---
================================================================================
AIMessage(
content="根据最新的信息,埃隆·马斯克在人工智能领域有几个重要动向:\n\n1. **xAI发布新模型**: 他的 xAI 公司最近发布了名为 Grok-1.5 的先进大语言模型,该模型在多个基准测试中表现出色,尤其是在长上下文理解和编码能力方面。\n\n2. **与特斯拉和SpaceX的协同**: 马斯克正在推动特斯拉、SpaceX 和 xAI 之间的深度整合。\n * **特斯拉**: 他计划利用特斯拉的计算资源和在自动驾驶领域积累的数据来训练 xAI 的模型。同时,xAI 的技术也可能反过来赋能特斯拉的完全自动驾驶(FSD)和擎天柱(Optimus)机器人项目。\n * **SpaceX**: SpaceX 的星链(Starlink)网络可以为 xAI 提供全球化的数据连接和计算基础设施支持。\n\n总的来说,马斯克的策略是创建一个强大的AI生态系统,其中 xAI 负责核心AI模型的研发,而特斯拉和SpaceX则提供数据、硬件和应用场景,形成一个互相促进的闭环。"
)
================================================================================
---决策中...---
>>> 决策:结束
================================================================================
看!智能体首先思考,决定使用搜索工具;然后行动,调用了 Tavily;接着观察到搜索结果后,再次思考,最后整合信息,给出了一个全面而深入的回答。
总结与展望
回顾一下,我们完成了:
- 用
TypedDict
定义了状态。 - 用
Tool
和ToolExecutor
定义了工具。 - 用普通的 Python 函数定义了节点。
- 用
StateGraph
将所有部分组装成了一个强大的应用。
接下来你可以尝试什么?
- 添加更多工具:比如添加一个计算器工具,或者一个可以执行 Python 代码的工具,让你的智能体能力更强。
- 处理更复杂的流程:比如在图中加入一个“错误处理”节点,当工具调用失败时,可以自动重试或向用户求助。
- 长期记忆: 探索如何将对话状态保存到数据库中,让你的智能体拥有长期记忆。