引言:从“理解 ReAct”到“真正跑起来”

上一篇我们讲了 ReAct Agent 的核心原理:

Reason(思考) + Act(行动) + Observe(观察)循环。

但只理解原理还不够。

真正开始做 Agent,一定要先跑通一个最小 Demo。

这一篇我们就从零开始,手把手搭建一个最简单的 ReAct Agent。

这个 Agent 只有两个工具:

  1. 天气查询工具
  2. 计算器工具

它可以完成:

用户:北京天气怎么样?
Agent:北京今天晴天,25度。

也可以完成:

用户:37 * 12 等于多少?
Agent:444

更重要的是,它可以自动判断:

  • 什么时候该查天气
  • 什么时候该计算
  • 什么时候不需要调用工具

这就是 ReAct Agent 的第一步。


一、我们要实现什么?

这篇文章要实现的目标很明确:

用户输入
↓
ReAct Agent 思考
↓
判断是否需要工具
↓
调用天气 / 计算器工具
↓
观察工具结果
↓
输出最终答案

最终效果类似:

用户:北京今天天气怎么样?
Agent 调用 get_weather("北京")
Agent:北京今天晴天,25度。

再比如:

用户:如果北京今天25度,比昨天高3度,那昨天多少度?
Agent 调用 calculator("25-3")
Agent:昨天是22度。

注意,这里 Agent 不是写死流程。

而是模型自己判断:

我现在应该调用哪个工具?

这就是 ReAct 的核心价值。


二、环境准备

1. 创建项目目录

mkdir react-agent-demo
cd react-agent-demo

2. 创建虚拟环境

macOS / Linux:

python3 -m venv .venv
source .venv/bin/activate

Windows PowerShell:

python -m venv .venv
.venv\Scripts\Activate.ps1

激活成功后,你会看到命令行前面出现:

(.venv)

3. 安装依赖

pip install -U langchain langgraph langchain-openai python-dotenv

这里几个包分别负责:

包名 作用
langchain 基础抽象与消息结构
langgraph Graph / Agent 执行框架
langchain-openai OpenAI 兼容模型接入
python-dotenv 加载 .env 配置

如果你使用 DeepSeek、通义千问这类 OpenAI 兼容接口,也可以继续使用 langchain-openai


三、配置 API Key

在项目根目录创建 .env 文件:

OPENAI_API_KEY=你的key

如果你使用 DeepSeek:

DEEPSEEK_API_KEY=你的key

如果你使用通义千问:

DASHSCOPE_API_KEY=你的key

建议不要把 API Key 写死在代码里。

正确做法是:

  • 本地开发放 .env
  • 生产环境放环境变量
  • .env 加入 .gitignore

.gitignore 示例:

.env
.venv/
__pycache__/

四、选择模型

这一篇我们以 OpenAI 兼容写法为主。

方式1:OpenAI

from langchain_openai import ChatOpenAI

model = ChatOpenAI(
    model="gpt-4o-mini",
    temperature=0
)

方式2:DeepSeek

import os
from langchain_openai import ChatOpenAI

model = ChatOpenAI(
    model="deepseek-chat",
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url="https://api.deepseek.com",
    temperature=0
)

方式3:通义千问

import os
from langchain_openai import ChatOpenAI

model = ChatOpenAI(
    model="qwen-plus",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    temperature=0
)

为了让教程简单,下面完整代码默认使用 OpenAI 写法。

如果你要换模型,只需要替换 model = ChatOpenAI(...) 这一段。


五、定义第一个工具:天气查询

我们先写一个假的天气工具。

真实项目里可以接天气 API,但入门阶段不建议一开始就接外部接口。

因为我们这篇的重点是理解:

Agent 如何选择并调用工具。

代码:

from langchain_core.tools import tool

@tool
def get_weather(city: str) -> str:
    """查询指定城市今天的天气和温度。参数 city 是城市名称,例如 北京、上海、广州。"""
    weather_data = {
        "北京": "北京今天晴天,25度。",
        "上海": "上海今天多云,28度。",
        "广州": "广州今天小雨,30度。",
        "深圳": "深圳今天晴天,31度。"
    }

    return weather_data.get(city, f"暂时没有查询到 {city} 的天气。")

这里有一个非常关键的点:

Tool 的 docstring 很重要。

也就是这一段:

"""查询指定城市今天的天气和温度。参数 city 是城市名称,例如 北京、上海、广州。"""

模型会根据这个描述判断:

  • 这个工具是干什么的
  • 什么时候应该调用它
  • 参数应该怎么填

如果你只写:

def get_weather(city):
    """获取信息"""

模型就很可能不知道什么时候该用它。


六、定义第二个工具:计算器

再写一个计算器工具:

@tool
def calculator(expression: str) -> str:
    """计算一个数学表达式,例如 2+3、25-3、37*12。只接收简单数学表达式。"""
    try:
        result = eval(expression)
        return str(result)
    except Exception as e:
        return f"计算失败:{e}"

例如:

calculator("37*12")
→ 444

注意:

这里为了教程简单,使用了 eval()

真实生产环境不建议直接使用 eval(),因为它可能执行危险代码。

生产中更安全的做法是:

  • 使用 ast 解析表达式
  • 使用 numexpr
  • 只允许白名单运算符
  • 后端自己实现安全计算器

但作为入门 Demo,这样最容易理解。


七、使用 create_react_agent 快速创建 Agent

LangGraph 提供了预构建的 ReAct Agent:

from langgraph.prebuilt import create_react_agent

它已经帮我们封装好了:

LLM 节点
↓
Tool 节点
↓
Observation 写回
↓
继续判断是否完成

所以我们不需要手写整个循环。

只需要:

agent = create_react_agent(
    model=model,
    tools=[get_weather, calculator]
)

这就是最小 ReAct Agent。


八、完整代码

在项目根目录创建:

main.py

完整代码如下:

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langgraph.prebuilt import create_react_agent

load_dotenv()


@tool
def get_weather(city: str) -> str:
    """查询指定城市今天的天气和温度。参数 city 是城市名称,例如 北京、上海、广州。"""
    weather_data = {
        "北京": "北京今天晴天,25度。",
        "上海": "上海今天多云,28度。",
        "广州": "广州今天小雨,30度。",
        "深圳": "深圳今天晴天,31度。"
    }

    return weather_data.get(city, f"暂时没有查询到 {city} 的天气。")


@tool
def calculator(expression: str) -> str:
    """计算一个数学表达式,例如 2+3、25-3、37*12。只接收简单数学表达式。"""
    try:
        result = eval(expression)
        return str(result)
    except Exception as e:
        return f"计算失败:{e}"


model = ChatOpenAI(
    model="gpt-4o-mini",
    temperature=0
)

agent = create_react_agent(
    model=model,
    tools=[get_weather, calculator]
)


def run_agent(question: str):
    result = agent.invoke({
        "messages": [
            {"role": "user", "content": question}
        ]
    })

    final_message = result["messages"][-1]
    print("用户:", question)
    print("Agent:", final_message.content)
    print("-" * 50)


if __name__ == "__main__":
    run_agent("北京今天天气怎么样?")
    run_agent("37 * 12 等于多少?")
    run_agent("如果北京今天25度,比昨天高3度,那昨天多少度?")

运行:

python main.py

九、运行效果示例

你可能会看到类似输出:

用户:北京今天天气怎么样?
Agent:北京今天晴天,25度。
--------------------------------------------------
用户:37 * 12 等于多少?
Agent:37 * 12 等于 444。
--------------------------------------------------
用户:如果北京今天25度,比昨天高3度,那昨天多少度?
Agent:昨天是 22 度。
--------------------------------------------------

这里最重要的不是答案本身,而是:

Agent 会自动决定是否调用工具。

当你问天气时,它会调用 get_weather

当你问数学时,它会调用 calculator

这就是 ReAct Agent 的基础形态。


十、如何查看 Agent 的中间过程?

如果你想看到 Agent 每一步执行过程,可以用 stream

例如:

inputs = {
    "messages": [
        {"role": "user", "content": "北京今天天气怎么样?"}
    ]
}

for chunk in agent.stream(inputs, stream_mode="updates"):
    print(chunk)

你会看到类似:

模型决定调用 get_weather
工具返回 北京今天晴天,25度
模型生成最终回答

也可以使用:

stream_mode="values"

查看每一步完整状态。

这对调试非常有用。


十一、如果你想使用 DeepSeek 或通义千问

只需要替换模型初始化部分。

DeepSeek 版本

model = ChatOpenAI(
    model="deepseek-chat",
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url="https://api.deepseek.com",
    temperature=0
)

通义千问版本

model = ChatOpenAI(
    model="qwen-plus",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    temperature=0
)

其他代码不用改。

这也是为什么我们前面一直强调:

不要把模型能力和业务逻辑耦合在一起。


十二、常见错误排查

1. ModuleNotFoundError

说明依赖没装好。

重新安装:

pip install -U langchain langgraph langchain-openai python-dotenv

2. API Key 没读取到

检查 .env 是否在项目根目录。

可以临时打印:

print(os.getenv("OPENAI_API_KEY"))

如果是 None,说明没有加载成功。

3. Agent 不调用工具

优先检查:

  • Tool 名字是否清楚
  • Tool docstring 是否清楚
  • 用户问题是否确实需要工具
  • 模型是否支持工具调用

4. 计算器报错

可能是模型传入了不安全或不合法表达式。

例如:

25 度 - 3 度

计算器只适合接收:

25-3

如果要更稳定,可以在 Tool 里做表达式清洗。

5. create_react_agent 导入失败

尝试升级:

pip install -U langgraph

如果仍然失败,说明你当前环境里 LangGraph 版本较旧或依赖不完整。

建议重新创建虚拟环境后安装。


十三、这个 Demo 的核心意义

这个 Demo 看起来很简单,但它已经包含了 ReAct Agent 最核心的结构:

用户输入
↓
模型思考
↓
选择工具
↓
执行工具
↓
观察结果
↓
最终回答

换句话说:

你已经跑通了一个真正的 Agent 最小闭环。

后面无论你做:

  • 搜索 Agent
  • 数据库 Agent
  • RAG Agent
  • 编程 Agent
  • 企业办公 Agent

本质都是在这个基础上继续扩展工具和状态管理。


十四、下一步可以怎么扩展?

你可以继续给它加:

1. 更多工具

例如:

  • 搜索工具
  • 查询订单工具
  • 发邮件工具
  • 数据库查询工具

2. Memory

让它记住上一轮问题。

例如:

用户:北京天气怎么样?
Agent:北京今天25度。
用户:那昨天呢?

3. LangSmith

查看完整 Trace:

  • 模型调用
  • 工具调用
  • Token 消耗
  • Latency

4. 自定义 Graph

当预构建 Agent 不够用时,就可以手写 LangGraph:

  • 自定义 State
  • 自定义节点
  • 自定义边
  • 自定义停止条件

结语

一句话总结:

create_react_agent 让我们用最少代码跑通了一个 ReAct Agent。

在这篇文章里,我们完成了:

  • 环境准备
  • 安装 LangChain / LangGraph
  • 定义天气工具
  • 定义计算器工具
  • 使用 create_react_agent 构建 Agent
  • 运行并查看结果
  • 初步调试中间过程

这就是单 Agent 开发的第一个真正实战 Demo。

下一篇,我们可以继续深入:

手写 ReAct 循环:不用预构建 Agent,自己实现 Thought → Action → Observation。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐