AI Agent开发:ReAct模式实际应用
·
在实际项目中实现ReAct模式,通常需要完成两件事:
- 设计合适的提示词,让大语言模型按照“思考→行动→观察”的格式输出。
- 编写执行循环,解析模型输出,调用外部工具,并将观察结果反馈给模型,直到模型输出最终答案。
下面分别展示提示词的设计方法,以及两种代码实现方式:一种使用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等成熟框架,它们已经处理了各种边缘情况(如模型输出格式错误、多步推理、工具异常等)。
四、关键点总结
- 提示词设计:必须明确告诉模型输出格式,并提供few-shot示例,否则模型容易自由发挥。
- 解析与循环:需要正确解析模型输出的
Action和Action Input,并将Observation反馈给模型。 - 工具定义:每个工具应有清晰的描述和输入输出格式,便于模型理解。
- 终止条件:当模型输出
Final Answer或达到最大迭代次数时停止。
通过ReAct模式,智能体可以在复杂任务中动态决策,调用外部工具获取实时信息,从而表现得更加“智能”。希望以上代码和提示词示例能帮助你快速上手!
更多推荐
所有评论(0)