前言

        在人工智能快速发展的今天,AI Agent 已成为备受关注的前沿领域。从简单的对话机器人到复杂的自动驾驶系统,Agent 技术正逐步渗透到我们生活的方方面面。而在众多 Agent 范式中,ReAct 以其独特的推理与行动结合机制,成为了构建强大 AI Agent 的基石。

本文将深入解析 ReAct 范式的核心原理,带您手动构建 Prompt 实现 ReAct 循环,并从零开始打造一个能回答复杂问题的 ReAct Agent。无论您是 AI 领域的研究者、开发者,还是对 Agent 技术感兴趣的爱好者,相信都能从本文中获得有价值的 insights。

一、ReAct 范式核心解析:什么是 Thought -> Action -> Observation 循环

1.1 ReAct 范式的定义与起源

        ReAct(Reasoning and Acting)是一种让 AI 模型通过 "思考 - 行动 - 观察" 循环来解决问题的范式。它最早由 Google Research 在 2022 年的论文《ReAct: Synergizing Reasoning and Acting in Language Models》中提出,旨在解决传统大型语言模型(LLM)在复杂推理任务中容易产生幻觉(Hallucination)和缺乏实时信息的问题。

        与单纯依赖模型内部知识的方法不同,ReAct 赋予了模型 "行动" 能力 —— 通过调用外部工具(如搜索引擎、计算器等)获取信息,从而更准确、更灵活地解决问题。

1.2 Thought -> Action -> Observation 循环机制

        ReAct 的核心在于一个不断迭代的循环过程:Thought(思考)→ Action(行动)→ Observation(观察)。

  • Thought(思考):这是 Agent 的推理过程,负责理解问题、规划下一步行动。例如,当被问及 "上海今天的天气如何?" 时,Thought 过程可能是:"我需要知道上海今天的天气,但我的知识截止到 2023 年 10 月,所以需要查询最新信息。"

  • Action(行动):基于思考结果,Agent 决定采取的具体行动,通常是调用外部工具。在天气查询的例子中,Action 可能是调用天气 API 或搜索引擎查询上海的天气。

  • Observation(观察):行动的结果,即工具返回的信息。例如,调用天气 API 后得到的 "上海今天晴,气温 18-25℃" 就是 Observation。

这个循环会不断重复,直到 Agent 认为已经获得了足够的信息来回答原始问题。

1.3 ReAct 相比其他范式的优势

  1. 减少幻觉:通过调用外部工具获取真实信息,避免了模型仅凭内部知识回答可能产生的错误。

  2. 处理实时信息:能够获取最新数据,解决了模型知识更新不及时的问题。

  3. 提高复杂推理能力:将复杂问题分解为多个步骤,逐步解决,提高了处理复杂任务的能力。

  4. 可解释性更强:通过显式的 Thought 过程,我们可以更好地理解 Agent 的决策逻辑。

二、手动构造 ReAct Prompt:让 LLM 自主决策

2.1 ReAct Prompt 的核心组成部分

        要实现 ReAct 循环,关键在于设计一个合适的 Prompt。一个完整的 ReAct Prompt 通常包含以下几个部分:

  1. 角色定义:明确告知 LLM 它需要扮演的角色(即一个可以思考、行动的 Agent)。

  2. 任务描述:说明需要解决的问题或完成的任务。

  3. 可用工具:列出 Agent 可以使用的工具及其使用方法。

  4. 格式要求:规定 Agent 输出的格式,特别是 Thought 和 Action 的表达方式。

  5. 示例:提供 1-2 个完整的 ReAct 循环示例,帮助 LLM 理解预期行为。

  6. 当前问题:提出用户的具体问题。

  7. 对话历史:记录之前的 Thought、Action 和 Observation,供 Agent 进行多轮推理。

2.2 设计一个通用的 ReAct Prompt 模板

下面是一个通用的 ReAct Prompt 模板,您可以根据具体需求进行调整:

你是一个可以使用工具解决问题的智能助手。

## 可用工具
1. 搜索引擎:当你需要获取最新信息、时事新闻、天气情况、特定数据等无法凭记忆回答的问题时使用。
   使用格式:<search>查询关键词</search>

2. 计算器:当你需要进行数学计算时使用。
   使用格式:<calculator>计算表达式</calculator>

## 思考与行动规则
1. 你需要通过思考(Thought)、行动(Action)、观察(Observation)的循环来解决问题。
2. 首先,分析问题并进行思考(Thought),决定是否需要使用工具。
3. 如果需要使用工具,输出对应的Action;如果不需要,可以直接给出答案。
4. 你会收到工具返回的结果(Observation),然后基于此进行新一轮思考。
5. 重复上述过程,直到你可以给出最终答案。
6. 最终答案请用<answer>和</answer>包裹。

## 示例
问题:今天是星期几?北京现在的温度是多少?

Thought:我需要知道今天的日期和北京的当前温度,这些信息具有时效性,所以应该使用搜索引擎。
Action:<search>今天星期几 北京当前温度</search>
Observation:今天是2023年10月15日,星期日。北京现在的温度是12℃,晴。
Thought:我已经获取了所需的信息,可以直接回答问题了。
<answer>今天是星期日,北京现在的温度是12℃,晴。</answer>

## 开始解决问题
问题:[用户的具体问题]

2.3 针对天气查询问题的 Prompt 优化

        对于本文开头提到的 "上海今天的天气如何?它和北京的气温差多少?" 这个问题,我们可以对 Prompt 进行针对性优化:

你是一个可以使用工具解决问题的智能助手,特别擅长处理天气相关的查询。

## 可用工具
1. 搜索引擎:用于获取上海和北京的最新天气信息,包括温度、天气状况等。
   使用格式:<search>城市 今天天气</search> 或 <search>城市 今日气温</search>

2. 计算器:用于计算两个城市的气温差值。
   使用格式:<calculator>数值1 - 数值2</calculator>

## 思考与行动规则
1. 你需要通过思考(Thought)、行动(Action)、观察(Observation)的循环来解决问题。
2. 首先分析问题,确定需要哪些信息,然后决定使用什么工具获取。
3. 对于天气查询,确保获取的是当天的最新数据。
4. 计算气温差时,注意使用正确的温度值(建议使用摄氏度)。
5. 最终答案请用<answer>和</answer>包裹,用自然语言清晰表述。

## 示例
问题:广州今天的天气怎么样?和深圳的气温相差多少?

Thought:我需要知道广州今天的天气情况,以及广州和深圳的气温,才能计算差值。这些都是实时信息,需要用搜索引擎获取。
Action:<search>广州 今天天气</search>
Observation:广州今天多云转晴,气温22-30℃,南风3级。
Thought:我已经知道了广州的天气和气温范围,现在需要获取深圳今天的气温。
Action:<search>深圳 今日气温</search>
Observation:深圳今天晴,气温24-32℃,东风2级。
Thought:现在我有了两个城市的气温数据。广州的气温范围是22-30℃,深圳是24-32℃。我需要计算它们的温差,应该用计算器。
Action:<calculator>22-24, 30-32</calculator>
Observation:计算结果:-2, -2
Thought:计算结果显示,广州和深圳的最低气温相差-2℃(即深圳比广州高2℃),最高气温也相差-2℃。现在我可以整理这些信息,给出最终答案了。
<answer>广州今天多云转晴,气温22-30℃。与深圳相比,深圳今天晴,气温24-32℃,两地的气温差为-2℃(即深圳比广州高2℃)。</answer>

## 开始解决问题
问题:上海今天的天气如何?它和北京的气温差多少?

        这个优化后的 Prompt 更专注于天气查询任务,提供的示例也与目标问题更相似,有助于 LLM 更好地理解并完成任务。

三、从零构建 ReAct Agent:不依赖重型框架

3.1 ReAct Agent 的核心组件设计

要从零构建一个 ReAct Agent,我们需要实现以下核心组件:

  1. Prompt 构建器:根据模板、历史记录和当前问题生成完整的 Prompt。

  2. LLM 接口:与大语言模型(如 GPT 系列、LLaMA 等)交互的模块。

  3. 工具调用器:解析 LLM 输出的 Action,调用相应工具并获取结果。

  4. 循环控制器:管理整个 Thought→Action→Observation 循环的流程,决定何时终止循环。

  5. 结果处理器:从最终的 LLM 输出中提取答案并格式化。

3.2 实现代码:一个简易但功能完整的 ReAct Agent

        下面我们用 Python 实现一个完整的 ReAct Agent,它能够回答需要查询天气并进行温度比较的问题。我们将使用 OpenAI 的 API 作为 LLM 接口,使用 requests 库调用搜索引擎 API(这里使用一个模拟的天气 API)。

import re
import requests
import openai
from typing import List, Dict, Tuple, Optional

# 配置API密钥
openai.api_key = "your_openai_api_key"
WEATHER_API_KEY = "your_weather_api_key"

class ReActAgent:
    def __init__(self):
        # 初始化提示模板
        self.prompt_template = """你是一个可以使用工具解决问题的智能助手,特别擅长处理天气相关的查询。

## 可用工具
1. 搜索引擎:用于获取城市的最新天气信息,包括温度、天气状况等。
   使用格式:<search>城市</search>

2. 计算器:用于计算两个城市的气温差值。
   使用格式:<calculator>数值1 - 数值2</calculator>

## 思考与行动规则
1. 你需要通过思考(Thought)、行动(Action)、观察(Observation)的循环来解决问题。
2. 首先分析问题,确定需要哪些信息,然后决定使用什么工具获取。
3. 对于天气查询,确保获取的是当天的最新数据。
4. 计算气温差时,注意使用正确的温度值(使用摄氏度)。
5. 最终答案请用<answer>和</answer>包裹,用自然语言清晰表述。

## 示例
问题:广州今天的天气怎么样?和深圳的气温相差多少?

Thought:我需要知道广州今天的天气情况,以及广州和深圳的气温,才能计算差值。这些都是实时信息,需要用搜索引擎获取。
Action:<search>广州</search>
Observation:广州今天多云转晴,气温22-30℃,南风3级。
Thought:我已经知道了广州的天气和气温范围,现在需要获取深圳今天的气温。
Action:<search>深圳</search>
Observation:深圳今天晴,气温24-32℃,东风2级。
Thought:现在我有了两个城市的气温数据。广州的气温范围是22-30℃,深圳是24-32℃。我需要计算它们的温差,应该用计算器。
Action:<calculator>22-24, 30-32</calculator>
Observation:计算结果:-2, -2
Thought:计算结果显示,广州和深圳的最低气温相差-2℃(即深圳比广州高2℃),最高气温也相差-2℃。现在我可以整理这些信息,给出最终答案了。
<answer>广州今天多云转晴,气温22-30℃。与深圳相比,深圳今天晴,气温24-32℃,两地的气温差为-2℃(即深圳比广州高2℃)。</answer>

## 开始解决问题
问题:{question}

{history}
"""
        
        # 初始化历史记录
        self.history = []
        # 最大循环次数,防止无限循环
        self.max_iterations = 10
    
    def get_weather(self, city: str) -> str:
        """调用天气API获取城市天气信息"""
        # 这里使用模拟的天气API,实际应用中应替换为真实API
        # 例如:https://api.openweathermap.org/data/2.5/weather?q={city}&appid={WEATHER_API_KEY}&units=metric
        mock_weather_data = {
            "上海": "上海今天晴转多云,气温18-25℃,东北风2级。",
            "北京": "北京今天晴,气温10-18℃,西北风3级。",
            "广州": "广州今天多云转晴,气温22-30℃,南风3级。",
            "深圳": "深圳今天晴,气温24-32℃,东风2级。"
        }
        
        if city in mock_weather_data:
            return mock_weather_data[city]
        else:
            return f"抱歉,暂时无法获取{city}的天气信息。"
    
    def calculate(self, expression: str) -> str:
        """简单的计算器功能"""
        try:
            # 仅允许减法运算,确保安全性
            if '-' in expression:
                parts = expression.split(',')
                results = []
                for part in parts:
                    nums = part.strip().split('-')
                    if len(nums) == 2:
                        num1 = float(nums[0].strip())
                        num2 = float(nums[1].strip())
                        results.append(str(num1 - num2))
                return "计算结果:" + ", ".join(results)
            else:
                return "计算器仅支持减法运算,请使用格式:数值1 - 数值2"
        except Exception as e:
            return f"计算错误:{str(e)}"
    
    def parse_action(self, llm_response: str) -> Tuple[Optional[str], Optional[str]]:
        """解析LLM输出,提取动作类型和参数"""
        # 检查是否有搜索动作
        search_match = re.search(r'<search>(.*?)</search>', llm_response, re.DOTALL)
        if search_match:
            return "search", search_match.group(1).strip()
        
        # 检查是否有计算动作
        calc_match = re.search(r'<calculator>(.*?)</calculator>', llm_response, re.DOTALL)
        if calc_match:
            return "calculator", calc_match.group(1).strip()
        
        # 检查是否有最终答案
        answer_match = re.search(r'<answer>(.*?)</answer>', llm_response, re.DOTALL)
        if answer_match:
            return "answer", answer_match.group(1).strip()
        
        # 未识别的动作
        return None, None
    
    def run(self, question: str) -> str:
        """运行ReAct循环,处理用户问题"""
        self.history = []  # 重置历史记录
        
        for _ in range(self.max_iterations):
            # 构建完整提示
            prompt = self.prompt_template.format(
                question=question,
                history="\n".join(self.history)
            )
            
            # 调用LLM获取响应
            response = openai.ChatCompletion.create(
                model="gpt-3.5-turbo",
                messages=[{"role": "user", "content": prompt}]
            )
            llm_response = response.choices[0].message['content'].strip()
            
            # 将LLM的思考添加到历史
            self.history.append(f"Thought: {llm_response.split('Action:')[0].replace('Thought:', '').strip()}")
            
            # 解析动作
            action_type, action_param = self.parse_action(llm_response)
            
            if not action_type:
                # 没有识别到动作,可能是LLM直接回答了
                return f"无法识别的响应:{llm_response}"
            
            if action_type == "answer":
                # 获得最终答案,结束循环
                return action_param
            
            # 执行动作并获取观察结果
            self.history.append(f"Action: <{action_type}>{action_param}</{action_type}>")
            
            if action_type == "search":
                observation = self.get_weather(action_param)
            elif action_type == "calculator":
                observation = self.calculate(action_param)
            else:
                observation = f"未知的动作类型:{action_type}"
            
            # 将观察结果添加到历史
            self.history.append(f"Observation: {observation}")
        
        # 达到最大循环次数
        return "抱歉,我无法在规定步骤内完成查询。请尝试简化问题或稍后再试。"

# 使用示例
if __name__ == "__main__":
    agent = ReActAgent()
    question = "上海今天的天气如何?它和北京的气温差多少?"
    answer = agent.run(question)
    print(answer)

3.3 代码解析与工作流程

上面的代码实现了一个完整的 ReAct Agent,其工作流程如下:

  1. 初始化:创建 Agent 实例时,会加载预设的 Prompt 模板,并初始化一些参数如最大循环次数。

  2. 接收问题:当调用run方法并传入用户问题时,Agent 开始工作。

  3. 构建 Prompt:根据模板、用户问题和历史记录(初始为空)构建完整的 Prompt。

  4. 调用 LLM:将构建好的 Prompt 发送给 LLM(这里使用 GPT-3.5-turbo),获取响应。

  5. 解析响应:从 LLM 的响应中提取 Thought 和 Action。

  6. 执行 Action

    • 如果是搜索动作(search),调用天气 API 获取指定城市的天气信息。
    • 如果是计算动作(calculator),执行温度差值计算。
    • 如果是回答动作(answer),返回结果并结束循环。
  7. 记录历史:将 Thought、Action 和 Observation 记录到历史中,用于下一轮循环。

  8. 循环迭代:重复步骤 3-7,直到获得最终答案或达到最大循环次数。

        这个实现虽然简单,但完整地体现了 ReAct 范式的核心思想。在实际应用中,您可以根据需要扩展它的功能,例如添加更多工具、优化 Prompt 模板、增强错误处理等。

四、ReAct Agent 的应用与扩展

4.1 ReAct 范式的适用场景

ReAct 范式特别适合以下场景:

  1. 需要实时信息的任务:如天气查询、新闻获取、股票价格查询等。

  2. 需要外部知识的任务:如特定领域知识问答、技术文档查询等。

  3. 需要多步推理的任务:如旅行规划、复杂问题解决等。

  4. 需要计算能力的任务:如数据分析、数学问题求解等。

  5. 需要与外部系统交互的任务:如邮件发送、日历安排、智能家居控制等。

4.2 扩展 ReAct Agent 的功能

上面实现的 ReAct Agent 可以通过以下方式进行扩展:

  1. 添加更多工具:如地图服务、翻译工具、数据库查询等。

  2. 优化工具调用方式:实现更复杂的工具调用参数解析,支持多参数调用。

  3. 增强错误处理:添加更完善的异常处理机制,提高 Agent 的鲁棒性。

  4. 实现记忆功能:让 Agent 能够记住过去的对话和结果,支持上下文连贯的对话。

  5. 添加反思机制:让 Agent 能够评估自己的回答质量,并进行修正。

  6. 多 Agent 协作:实现多个 ReAct Agent 协同工作,解决更复杂的任务。

4.3 ReAct 与其他 Agent 范式的结合

ReAct 范式可以与其他 Agent 范式结合,形成更强大的 AI 系统:

  1. ReAct + Reflexion:在 ReAct 的基础上添加反思机制,让 Agent 能够从错误中学习。

  2. ReAct + Plan-and-Execute:先进行全局规划,再用 ReAct 执行具体步骤。

  3. ReAct + Memory:结合长期记忆和短期记忆,增强 Agent 的上下文理解能力。

  4. ReAct + Tool Selection:添加工具选择机制,让 Agent 在众多工具中选择最适合的。

五、总结与展望

5.1 本文要点总结

本文深入探讨了 ReAct 范式,这是构建 AI Agent 的重要基石。我们了解到:

  1. ReAct 范式的核心是 Thought→Action→Observation 的循环机制,通过推理与行动的结合,使 AI 能够更灵活、更准确地解决问题。

  2. 手动构造 ReAct Prompt 需要包含角色定义、任务描述、可用工具、格式要求、示例等关键部分。

  3. 我们从零开始实现了一个 ReAct Agent,它能够回答需要查询天气并进行温度比较的问题,展示了 ReAct 范式的实际应用。

  4. ReAct 范式适用于需要实时信息、外部知识、多步推理或计算能力的任务,具有广泛的应用前景。

5.2 ReAct 范式的未来发展

ReAct 范式作为 AI Agent 领域的重要突破,未来还有很大的发展空间:

  1. 更高效的推理机制:通过优化 LLM 的推理过程,减少不必要的步骤,提高 Agent 的效率。

  2. 更智能的工具选择:让 Agent 能够根据任务特点自动选择最合适的工具,甚至创造新的工具组合。

  3. 更强的环境交互能力:扩展 Agent 与物理世界和数字世界的交互方式,实现更复杂的任务。

  4. 更好的可解释性:通过改进 Thought 过程的表达方式,提高 Agent 行为的可解释性和可信度。

  5. 更广泛的应用场景:将 ReAct 范式应用到更多领域,如教育、医疗、工业等,解决实际问题。

        随着大语言模型和 Agent 技术的不断发展,我们有理由相信,基于 ReAct 范式的 AI Agent 将会变得越来越强大,为我们的生活和工作带来更多便利。

结语

        ReAct 范式为我们提供了一种将推理与行动相结合的有效方式,使 AI 能够像人类一样思考和行动。通过本文的介绍,希望您对 ReAct 有了更深入的理解,并能够动手实践,构建自己的 ReAct Agent。

        AI Agent 领域仍在快速发展,新的方法和技术不断涌现。作为开发者和研究者,我们需要不断学习和探索,才能跟上这一领域的发展步伐。让我们一起期待 AI Agent 带来的更美好的未来!


欢迎在评论区留言讨论,分享您对 ReAct 范式的看法和实践经验!如果觉得本文对您有帮助,别忘了点赞和收藏哦!

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐