LangGraph 从0到1:手把手教你实现 ReAct 研究员智能体

我们将从零开始,一步步实现一个基于 LangGraph 的 ReAct 智能体。这个智能体将扮演一位“研究员”,能够利用工具(比如网络搜索)来回答复杂问题。


LangGraph 从0到1:手把手教你实现 ReAct 研究员智能体

目标

完成本教程后,你将:

  1. 理解什么是 ReAct 模式,以及它为什么这么强大。
  2. 掌握 LangGraph 的核心概念:状态(State)、节点(Node)和边(Edge)。
  3. 亲手编码实现一个会思考、行动、观察的研究员智能体。
  4. 能够让你的智能体连接互联网,进行深入研究。

第一部分:核心概念,先“画个图”

在写代码之前,先用大白话聊聊几个核心概念。

1. 什么是智能体(Agent)?

想象一下,你不是直接跟一个聊天机器人说话,而是跟一个拥有“大脑”和“双手”的助理沟通。

  • 大脑 (LLM):就是像 GPT-4.1 这样的大语言模型,负责思考和决策。
  • 双手 (Tools):就是它能使用的工具,比如“网络搜索引擎”、“计算器”、“代码执行器”等。

智能体就是大脑 + 双手的结合体。你给它一个任务,它会自己思考“我该用哪个工具?”,然后使用工具,看到结果后,再继续思考,直到任务完成。

2. 什么是 ReAct 模式? (智能体的“工作流程”)

ReAct 是 “Reason” (推理) + “Act” (行动) 的缩写。这是目前最经典、最有效的智能体工作模式。

把它想象成一个侦探破案的过程:

  1. 思考 (Reason):侦探拿到案子(你的问题),首先会分析线索,心想:“嗯…我需要先去查一下受害者的背景资料。”
  2. 行动 (Act):他决定使用一个工具——“档案查询系统”(调用 Tool)。
  3. 观察 (Observe):他从系统中拿到了资料(工具返回结果),然后观察这份新资料。
  4. 重复循环: 基于新的资料,他再次思考:“哦,原来受害者和某某公司有联系,我下一步要去搜一下这家公司的新闻。” -> 行动(上网搜索) -> 观察

这个 “思考 -> 行动 -> 观察” 的循环,就是 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 定义了状态
  • ToolToolExecutor 定义了工具
  • 用普通的 Python 函数定义了节点
  • StateGraph 将所有部分组装成了一个强大的应用。

接下来你可以尝试什么?

  1. 添加更多工具:比如添加一个计算器工具,或者一个可以执行 Python 代码的工具,让你的智能体能力更强。
  2. 处理更复杂的流程:比如在图中加入一个“错误处理”节点,当工具调用失败时,可以自动重试或向用户求助。
  3. 长期记忆: 探索如何将对话状态保存到数据库中,让你的智能体拥有长期记忆。
1、内容概要: (1)数据结构设计:定义了包含头像、昵称、消息类型、时间、内容、未读计数的消息对象模型 (2)界面展示: a.支持文本、图片、视频三种消息类型的差异化显示 b.未读消息数字气泡提示 c.时间显示(精确到分钟 / 天前) d.交互功能:点击消息行弹出包含消息内容的操作菜单 e.空状态处理:无消息时显示占位提示 2、适用人群 (1)微信小程序开发初学者 (2)希望学习小程序 UI 组件实现的前端开发者 (3)需要快速搭建消息中心模块的开发者 (4)对微信小程序数据绑定、条件渲染机制不熟悉的人员 3、使用场景及目标 (1)社交类小程序消息中心 (2)应用通知系统 (3)订阅消息展示 (4)多平台账号聚合消息流 (5)提供可复用的消息列表 UI 组件代码 (6)展示微信小程序基础开发技术的实际应用 (7)演示如何处理列表数据渲染、条件判断、事件绑定 (8)帮助开发者快速实现具有现代感的消息界面 4、其他说明 (1)代码特点: 采用 MVVM 模式,数据与视图分离 使用微信官方推荐的组件化开发方式 完整实现了从数据定义到视图渲染的全流程 (2)扩展建议: 增加消息分类标签(如 "已读 / 未读"、"重要 / 普通") 实现消息滑动删除功能 添加消息搜索筛选能力 增加消息分组折叠展示 (3)注意事项: 图片资源路径需根据实际项目结构调整 实际项目中建议使用真实接口数据替代静态数据 未读计数应与消息状态管理系统联动 建议添加消息点击后的已读状态更新逻辑 (4)技术价值: 清晰展示了 WXML 模板语法的使用 演示了微信小程序事件处理机制 提供了列表渲染性能优化的基础框架 可作为微信小程序 UI 组件库的基础组件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值