首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >从零构建一个基于大模型的 ReAct 智能体:原理、实现与调优

从零构建一个基于大模型的 ReAct 智能体:原理、实现与调优

原创
作者头像
IT大佬 jzit-top
发布2026-07-23 13:41:51
发布2026-07-23 13:41:51
1180
举报

从零构建一个基于大模型的 ReAct 智能体:原理、实现与调优

当 LLM 不再只是“聊天机器人”,而是能调用工具、自主决策的“智能体”,我们离通用人工智能又近了一步。本文将带你手搓一个最小可用的 ReAct Agent,并深入探讨其背后的设计哲学与工程陷阱。

1. 为什么需要 Agent?

大语言模型(LLM)展示了惊人的推理能力,但其本质仍是“静态知识库”——它无法主动获取实时信息,也无法执行外部操作。例如,当你问“今天石家庄天气如何?”模型只能根据训练数据(可能截止到 2026 年初)给出过时回答。

AI 智能体(Agent) 的出现弥补了这一鸿沟。它赋予 LLM 两个关键能力:

  • 工具调用(Tool Use):访问外部 API、数据库或执行代码。
  • 自主决策(Planning):根据观察结果动态调整行动步骤。

在众多 Agent 范式中,ReAct(Reasoning + Acting) 因其简洁、可解释性强而成为入门首选。

2. ReAct 模式速览

ReAct 由 Yao 等人在 2022 年提出,其核心循环为:

代码语言:javascript
复制
Thought -> Action -> Observation -> (Repeat) -> Final Answer
  • Thought:模型推理当前状态,思考下一步该做什么。
  • Action:选择并执行一个工具(如搜索、计算)。
  • Observation:工具返回的结果。
  • Final Answer:当模型认为信息足够时,输出最终回答。

这种“思考-行动-观察”的循环让模型能像人类一样,边做边想,逐步解决问题。

3. 环境准备与工具定义

我们将使用 Python 3.10+,并借助 OpenAI API(或兼容接口)来驱动 LLM。为方便演示,所有工具均为本地模拟。

3.1 安装依赖

代码语言:javascript
复制
pip install openai python-dotenv

3.2 定义工具集

工具以字典形式注册,包含名称、描述和调用函数。这里实现三个示例工具:

  • get_current_time:获取当前时间(模拟实时信息)。
  • search_web:模拟搜索引擎(实际可替换为 SerpAPI)。
  • calculate:执行数学表达式。
代码语言:javascript
复制
import json
import math
from datetime import datetime

def get_current_time():
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

def search_web(query: str):
    # 模拟搜索,实际可接入真实 API
    mock_db = {
        "石家庄天气": "2026-07-23 石家庄:多云,25°C ~ 32°C,东南风 3 级。",
        "Python 3.11 新特性": "包括异常组、性能优化等。"
    }
    return mock_db.get(query, f"未找到关于 '{query}' 的结果。")

def calculate(expression: str):
    try:
        # 安全起见,仅允许简单算术
        allowed = set("0123456789+-*/(). ")
        if not all(c in allowed for c in expression):
            return "错误:表达式包含非法字符。"
        result = eval(expression, {"__builtins__": {}}, {})
        return f"计算结果:{result}"
    except Exception as e:
        return f"计算错误:{e}"

TOOLS = {
    "get_current_time": {
        "name": "get_current_time",
        "description": "获取当前的精确时间(年-月-日 时:分:秒)。无需参数。",
        "func": get_current_time,
        "parameters": {}
    },
    "search_web": {
        "name": "search_web",
        "description": "模拟网络搜索,输入查询字符串,返回相关信息。",
        "func": search_web,
        "parameters": {"query": "str"}
    },
    "calculate": {
        "name": "calculate",
        "description": "执行数学计算,输入算式,如 '3 * (5 + 2)'。",
        "func": calculate,
        "parameters": {"expression": "str"}
    }
}

4. 提示词工程:让模型学会 ReAct

LLM 本身不会自动按 ReAct 格式输出,我们需要通过精心设计的系统提示来“指导”它。提示必须包含:

  • 可用工具列表及其描述。
  • 输出格式规范(Thought: / Action: / Action Input: / Final Answer:)。
  • 循环终止条件。

4.1 系统提示模板

代码语言:javascript
复制
SYSTEM_PROMPT = """你是一个能够使用工具的智能助手。请严格按照以下格式回复:

Thought: 你当前的推理过程(中文)。
Action: 你要调用的工具名称,必须从以下列表中选择:{tool_names}
Action Input: 调用该工具所需的输入参数(JSON 格式)。

如果你已经有足够信息回答用户问题,则输出:
Final Answer: 最终回答内容(中文)。

注意:每次只调用一个工具,等待观察结果后再继续。
"""

def build_system_prompt():
    tool_names = ", ".join(TOOLS.keys())
    return SYSTEM_PROMPT.format(tool_names=tool_names)

4.2 用户消息与历史管理

维护一个消息列表,包含系统提示、用户问题以及每次循环产生的 assistantuser(工具结果)消息。

5. 核心循环实现

主循环负责:

  1. 调用 LLM 获取响应。
  2. 解析响应,提取 ThoughtAction
  3. 执行工具,获得 Observation
  4. 将观察结果追加到对话历史。
  5. 重复直至产出 Final Answer

5.1 解析函数

代码语言:javascript
复制
import re

def parse_response(text: str):
    """解析 LLM 输出,返回 (thought, action, action_input, final_answer)"""
    thought_match = re.search(r"Thought:\s*(.*?)(?=\nAction:|\nFinal Answer:|$)", text, re.DOTALL)
    action_match = re.search(r"Action:\s*(\w+)", text)
    action_input_match = re.search(r"Action Input:\s*(\{.*\})", text, re.DOTALL)
    final_match = re.search(r"Final Answer:\s*(.*)", text, re.DOTALL)

    thought = thought_match.group(1).strip() if thought_match else None
    action = action_match.group(1).strip() if action_match else None
    action_input = action_input_match.group(1).strip() if action_input_match else None
    final_answer = final_match.group(1).strip() if final_match else None

    return thought, action, action_input, final_answer

5.2 Agent 类

代码语言:javascript
复制
import openai

class ReActAgent:
    def __init__(self, model="gpt-4o-mini", max_iterations=5):
        self.model = model
        self.max_iterations = max_iterations
        self.messages = [
            {"role": "system", "content": build_system_prompt()}
        ]

    def run(self, user_query: str):
        self.messages.append({"role": "user", "content": user_query})
        iteration = 0

        while iteration < self.max_iterations:
            iteration += 1
            print(f"\n--- 迭代 {iteration} ---")

            # 调用 LLM
            response = openai.chat.completions.create(
                model=self.model,
                messages=self.messages,
                temperature=0.0,  # 保持确定性
            )
            reply = response.choices[0].message.content
            self.messages.append({"role": "assistant", "content": reply})
            print(f"LLM 回复:\n{reply}")

            thought, action, action_input, final_answer = parse_response(reply)

            if final_answer:
                print(f"\n✅ 最终回答:{final_answer}")
                return final_answer

            if not action:
                print("⚠️ 未解析到 Action,强制终止。")
                return "抱歉,我无法继续推理。"

            # 执行工具
            tool = TOOLS.get(action)
            if not tool:
                observation = f"错误:未知工具 '{action}'。"
            else:
                try:
                    # 解析参数
                    if action_input:
                        params = json.loads(action_input)
                    else:
                        params = {}
                    func = tool["func"]
                    observation = func(**params)
                except Exception as e:
                    observation = f"工具执行异常:{e}"

            print(f"🔧 执行 {action} -> 观察:{observation}")
            # 将观察结果作为 user 消息追加
            self.messages.append({
                "role": "user",
                "content": f"Observation: {observation}\n请继续思考并给出下一步 Action 或 Final Answer。"
            })

        return "达到最大迭代次数,未得出最终答案。"

6. 运行演示

代码语言:javascript
复制
# 设置 OpenAI API Key(或兼容端点)
import os
openai.api_key = os.getenv("OPENAI_API_KEY")
openai.base_url = os.getenv("OPENAI_BASE_URL")  # 可选

agent = ReActAgent()
question = "现在石家庄天气如何?另外,计算 123 * 456 等于多少?"
answer = agent.run(question)
print(answer)

预期输出片段(实际可能略有不同):

代码语言:javascript
复制
--- 迭代 1 ---
LLM 回复:
Thought: 用户想知道石家庄的天气,并计算一个乘法。我需要先搜索天气信息。
Action: search_web
Action Input: {"query": "石家庄天气"}
...
🔧 执行 search_web -> 观察:2026-07-23 石家庄:多云,25°C ~ 32°C,东南风 3 级。
...
--- 迭代 2 ---
LLM 回复:
Thought: 已获得天气信息。现在需要计算 123*456。
Action: calculate
Action Input: {"expression": "123 * 456"}
...
🔧 执行 calculate -> 观察:计算结果:56088
...
--- 迭代 3 ---
LLM 回复:
Final Answer: 当前石家庄天气为多云,气温 25°C ~ 32°C,东南风 3 级。123 乘以 456 等于 56088。

7. 关键设计与调优指南

7.1 工具描述的清晰度

工具描述必须精准,因为 LLM 依赖它来选择正确工具。例如,get_current_time 描述中注明“无需参数”,可避免模型传入空 JSON 时出错。

7.2 参数解析鲁棒性

实际应用中,模型可能输出畸形的 JSON,甚至不包含 Action Input。建议:

  • 使用正则或 json5 库宽容解析。
  • 在提示中强调“必须使用合法 JSON”。

7.3 防止无限循环

设置最大迭代次数(max_iterations),并在提示中加入“如果重复相同 Thought 超过两次,请直接给出 Final Answer”。

7.4 工具执行的安全性

  • eval 等危险操作需限制上下文(如上例中清空 __builtins__)。
  • 对网络请求加入超时和异常捕获。

7.5 模型选择

  • GPT-4o-mini:性价比高,适合简单任务。
  • GPT-4o:复杂推理更稳定,但成本更高。
  • 开源模型如 Qwen2.5-72BLlama-3 配合适当微调也能胜任。

8. 进阶扩展思路

  • 多工具并行:在一步中同时调用多个工具(需修改提示格式)。
  • 工具回调:工具可返回错误码或要求用户确认。
  • 记忆机制:引入短期记忆(向量数据库)让 Agent 记住历史对话。
  • Human-in-the-loop:当不确定性高时,请求人工介入。

9. 总结

本文从零构建了一个基于 ReAct 模式的 AI 智能体,核心代码仅百行左右,却展示了 LLM 自主规划、工具调用和推理的全过程。ReAct 的可解释性使其成为生产级 Agent 的可靠起点。

但请记住,Agent 不是“万能药”——它受限于基座模型的能力和工具质量。在实际落地中,需结合错误处理、监控和评估体系,才能让 Agent 真正可靠。

希望这篇文章能帮助你快速上手 AI Agent 开发,并在思否社区激发更多技术讨论。欢迎在评论区留下你的实践心得或问题!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 从零构建一个基于大模型的 ReAct 智能体:原理、实现与调优
    • 1. 为什么需要 Agent?
    • 2. ReAct 模式速览
    • 3. 环境准备与工具定义
      • 3.1 安装依赖
      • 3.2 定义工具集
    • 4. 提示词工程:让模型学会 ReAct
      • 4.1 系统提示模板
      • 4.2 用户消息与历史管理
    • 5. 核心循环实现
      • 5.1 解析函数
      • 5.2 Agent 类
    • 6. 运行演示
    • 7. 关键设计与调优指南
      • 7.1 工具描述的清晰度
      • 7.2 参数解析鲁棒性
      • 7.3 防止无限循环
      • 7.4 工具执行的安全性
      • 7.5 模型选择
    • 8. 进阶扩展思路
    • 9. 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档