在实际项目中实现ReAct模式,通常需要完成两件事:

  1. 设计合适的提示词,让大语言模型按照“思考→行动→观察”的格式输出。
  2. 编写执行循环,解析模型输出,调用外部工具,并将观察结果反馈给模型,直到模型输出最终答案。

下面分别展示提示词的设计方法,以及两种代码实现方式:一种使用LangChain框架(快速开发),另一种手写简单循环(便于理解原理)。


一、提示词设计

ReAct提示词的核心是告诉模型:你应该一步一步思考,如果需要外部信息,就输出一个动作(包含工具名和输入),然后我会把工具的执行结果以“观察”的形式返回给你,你再继续思考,直到得到最终答案。

标准格式(以文本形式嵌入):

系统提示:你是一个智能助手,可以调用以下工具:{工具列表}。请按以下格式回答:

Thought: 你现在需要思考当前状态,下一步该做什么。
Action: 你要使用的工具名称(必须来自工具列表)。
Action Input: 工具的输入参数。
Observation: 工具返回的结果(由系统填充,模型不需要输出这个)。

...(重复 Thought/Action/Action Input 循环)...

当你得到足够信息后,输出最终答案:
Thought: 我现在知道最终答案了。
Final Answer: 对用户的最终回答。

请严格遵守上述格式。

为了增强稳定性,可以在提示词中加入少量示例(few-shot)。例如:

用户:今天北京天气怎么样?
Thought: 我需要查询北京的实时天气,应该使用天气查询工具。
Action: get_weather
Action Input: 北京
Observation: 北京目前晴朗,气温25℃。
Thought: 我现在知道北京的天气了,可以回答用户。
Final Answer: 今天北京天气晴朗,气温25℃。

在代码中,我们会将上述格式和工具描述一起作为系统消息,用户的输入作为用户消息。


二、Python代码样例

方法一:使用LangChain快速实现ReAct Agent

LangChain提供了内置的ReAct agent,只需定义工具和LLM,即可自动处理循环。

import os
from langchain.agents import AgentExecutor, create_react_agent
from langchain.tools import tool
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate

# 1. 定义工具
@tool
def get_weather(city: str) -> str:
    """查询指定城市的天气"""
    # 这里简化实现,实际可调用天气API
    weather_data = {"北京": "晴朗,25℃", "上海": "多云,22℃"}
    return weather_data.get(city, f"没有{city}的天气信息")

# 2. 初始化LLM
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0, api_key=os.getenv("OPENAI_API_KEY"))

# 3. 构建提示词(LangChain自带ReAct提示模板,但可以自定义)
prompt = PromptTemplate.from_template("""
你是一个智能助手,可以调用以下工具:
{tools}

工具名称格式:[工具名] - 工具描述

请按照以下格式思考并行动:

Question: 用户输入的问题
Thought: 你当前的想法
Action: 工具名称(必须从上面列表中选择)
Action Input: 工具的输入
Observation: 工具返回的结果

...(可重复多次)...

Thought: 我现在知道最终答案了
Final Answer: 对用户的最终回答

开始!
Question: {input}
Thought: {agent_scratchpad}
""")

# 4. 创建Agent
tools = [get_weather]
agent = create_react_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, handle_parsing_errors=True)

# 5. 运行
response = agent_executor.invoke({"input": "今天北京天气怎么样?"})
print(response["output"])

说明

  • verbose=True会打印出整个思考-行动链条,便于观察ReAct过程。
  • LangChain自动处理了输出解析、循环调用、观察填充等,非常适合生产环境。

方法二:手写简单ReAct循环(原理演示)

如果不依赖框架,可以自己实现核心逻辑,更清楚地理解ReAct的运作。

import os
import json
from openai import OpenAI

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

# 定义工具函数
def get_weather(city):
    # 模拟天气查询
    data = {"北京": "晴朗,25℃", "上海": "多云,22℃"}
    return data.get(city, f"没有{city}的天气信息")

# 工具映射
tools = {
    "get_weather": get_weather,
}

# 工具描述(用于提示词)
tools_description = """
- get_weather: 查询城市天气,输入城市名称(字符串)。
"""

# 系统提示词
system_prompt = f"""你是一个智能助手,可以调用以下工具:
{tools_description}

请严格按以下格式输出:
Thought: 你的思考
Action: 工具名称(必须来自上述列表,如果不需要工具则输出"None")
Action Input: 工具的输入(必须是合法JSON格式)
Observation: 工具返回的结果(由系统填充,你不要输出这个)

如果你已经有最终答案,请输出:
Thought: 我现在知道最终答案了
Final Answer: 你的最终回答

注意:每次只能输出一个 Thought 和 Action/Action Input 对,不要输出 Observation。
"""

def call_llm(messages):
    """调用LLM并返回响应文本"""
    response = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=messages,
        temperature=0
    )
    return response.choices[0].message.content

def run_react(user_input, max_iter=5):
    messages = [
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": user_input}
    ]
    
    for i in range(max_iter):
        print(f"\n=== 第 {i+1} 轮 ===")
        # 1. 调用LLM
        assistant_output = call_llm(messages)
        print(f"LLM输出:\n{assistant_output}")
        
        # 2. 检查是否输出最终答案
        if "Final Answer:" in assistant_output:
            # 提取最终答案
            final_answer = assistant_output.split("Final Answer:")[-1].strip()
            return final_answer
        
        # 3. 解析 Thought 和 Action
        lines = assistant_output.strip().split("\n")
        thought = ""
        action = ""
        action_input = ""
        for line in lines:
            if line.startswith("Thought:"):
                thought = line.replace("Thought:", "").strip()
            elif line.startswith("Action:"):
                action = line.replace("Action:", "").strip()
            elif line.startswith("Action Input:"):
                action_input = line.replace("Action Input:", "").strip()
        
        # 如果Action是None或空,说明模型认为不需要工具,直接退出(但这里我们期望Final Answer)
        if not action or action == "None":
            return "模型没有输出最终答案,且没有指定工具,退出。"
        
        # 4. 执行工具
        if action in tools:
            try:
                # 简单处理输入,假设是字符串
                observation = tools[action](action_input.strip('"'))
            except Exception as e:
                observation = f"工具执行出错:{e}"
        else:
            observation = f"未知工具:{action}"
        
        # 5. 将Assistant的回复和Observation追加到消息列表
        messages.append({"role": "assistant", "content": assistant_output})
        messages.append({"role": "user", "content": f"Observation: {observation}"})
    
    return "达到最大迭代次数,未能得到最终答案。"

# 运行示例
result = run_react("今天北京天气怎么样?")
print(f"\n最终答案:{result}")

说明

  • 该代码模拟了ReAct的核心循环:LLM输出Thought和Action → 系统执行工具 → 将Observation作为用户消息返回 → 继续。
  • 使用了OpenAI的Python客户端,需要安装openai库并设置API Key。
  • 工具输入假设为简单字符串,实际可根据需要解析JSON。

三、两种方式的对比

维度 LangChain Agent 手写循环
开发效率 高,内置解析、工具调用、错误处理 低,需自己处理解析、格式检查
灵活性 中等,可定制但受框架约束 高,可完全自定义提示和流程
适合场景 快速原型、生产项目 教学、特殊格式要求、轻量集成

在实际项目中,建议使用LangChain等成熟框架,它们已经处理了各种边缘情况(如模型输出格式错误、多步推理、工具异常等)。


四、关键点总结

  1. 提示词设计:必须明确告诉模型输出格式,并提供few-shot示例,否则模型容易自由发挥。
  2. 解析与循环:需要正确解析模型输出的ActionAction Input,并将Observation反馈给模型。
  3. 工具定义:每个工具应有清晰的描述和输入输出格式,便于模型理解。
  4. 终止条件:当模型输出Final Answer或达到最大迭代次数时停止。

通过ReAct模式,智能体可以在复杂任务中动态决策,调用外部工具获取实时信息,从而表现得更加“智能”。希望以上代码和提示词示例能帮助你快速上手!

Logo

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

更多推荐