智能体设计范式:ReAct
1 核心思想
ReAct(Reason + Act) 将推理(Reasoning)与行动(Action)交替进行。模型在每一步不仅进行思考(产生推理轨迹),还可以选择调用外部工具(如搜索引擎、计算器),然后根据工具返回的观察结果继续推理,如此循环,直到得出最终答案。
2 工作原理

- 系统提示词告诉模型可用的工具及输出格式(Thought/Action/Action Input)。
- 模型输出一个推理步骤(Thought),如果需要外部信息,则输出 Action(工具名)和 Action Input(工具参数)。
- 程序解析并执行该工具,获取 Observation(观察结果)。
- 把 Thought、Action、Observation 拼接回对话历史,继续下一次生成。
- 当模型输出 Final Answer 时结束循环,返回最终答案。
3 使用场景
- 需要多步推理并结合实时信息的问题(如"2022年世界杯冠军的教练现在执教哪支球队?")。
- 需要计算、搜索、数据库查询等的复杂问答。
- 任何 LLM 单次推理无法可靠完成的、需要与环境交互的任务。
4 优缺点
优点
- 推理过程透明、可解释。
- 灵活调用外部工具,突破模型知识截止限制。
- 通过中间推理降低幻觉,提高最终答案可靠性。
缺点
- 每一步都要调用 LLM,延迟较高、成本较大。
- 格式解析可能失败,导致行动执行错误。
- 缺乏全局规划,容易在局部绕圈。
5 Python 实现
python
import re
import requests
from typing import List, Dict
class AliYunLLM: ... # 定义与前文相同
# ---------- 简单工具集 ----------
def search(query: str) -> str:
"""真实搜索:使用 DuckDuckGo 免费 API"""
try:
url = f"https://api.duckduckgo.com/?q={requests.utils.quote(query)}&format=json"
resp = requests.get(url, timeout=5)
data = resp.json()
# 优先返回"AbstractText"摘要
abstract = data.get("AbstractText", "")
if abstract:
return abstract
# 如果没有摘要,尝试取第一个关联主题的文本
related = data.get("RelatedTopics", [])
if related and "Text" in related[0]:
return related[0]["Text"]
return f"未找到关于'{query}'的信息。"
except Exception as e:
return f"搜索出错:{e}"
def calculator(expression: str) -> str:
"""安全计算数学表达式"""
try:
# 仅允许数字、运算符、括号和小数点
if re.match(r'^[\d\+\-\*/\(\)\.\s]+$', expression):
result = eval(expression)
return str(result)
else:
return "表达式包含非法字符"
except Exception as e:
return f"计算错误:{e}"
TOOLS = {
"search": search,
"calculator": calculator
}
# ---------- ReAct Agent ----------
class ReActAgent:
def __init__(self, llm: AliYunLLM, max_steps: int = 5):
self.llm = llm
self.max_steps = max_steps
self.history: List[Dict[str, str]] = []
def _build_system_prompt(self) -> str:
tool_descriptions = (
"- search(query: str): 搜索互联网,返回相关信息。\n"
"- calculator(expression: str): 计算数学表达式,返回结果字符串。"
)
return (
"你是一个具备行动能力的智能助手。你可以使用以下工具:\n"
f"{tool_descriptions}\n\n"
"请严格按照以下格式输出(每行以 'Thought:', 'Action:', 'Action Input:', 'Observation:' 开头):\n"
"Thought: 对当前情况的推理\n"
"Action: 工具名称(search 或 calculator)\n"
"Action Input: 工具的输入\n"
"然后你会收到一个 Observation(观察结果),接着继续推理。\n"
"当你获得最终答案时,请用以下格式结束:\n"
"Thought: 我现在知道最终答案了\n"
"Final Answer: 最终答案内容\n\n"
"开始!"
)
def run(self, question: str) -> str:
self.history = []
prompt = f"问题:{question}\n"
for step in range(self.max_steps):
# 调用模型
response = self.llm.generate(prompt, system_prompt=self._build_system_prompt())
print(f"\n--- Step {step+1} 模型输出 ---\n{response}")
self.history.append({"role": "assistant", "content": response})
# 检查是否包含 Final Answer
final_match = re.search(r"Final Answer:\s*(.*)", response, re.DOTALL)
if final_match:
return final_match.group(1).strip()
# 解析 Action 和 Action Input
action_match = re.search(r"Action:\s*(.*)", response)
action_input_match = re.search(r"Action Input:\s*(.*)", response)
if not action_match or not action_input_match:
# 格式解析失败,提示重新输出
prompt += f"{response}\nObservation: 输出格式错误,请按指定格式回复。\n"
continue
action = action_match.group(1).strip()
action_input = action_input_match.group(1).strip()
# 执行工具
if action in TOOLS:
observation = TOOLS[action](action_input)
else:
observation = f"未知工具:{action}"
print(f"Observation: {observation}")
# 将助手输出和观察结果追加到 prompt
prompt += f"{response}\nObservation: {observation}\n"
return "未能得出最终答案,请增大步骤数或调整问题。"
# ---------- 示例运行 ----------
if __name__ == "__main__":
# 配置你自己的 API 信息
llm = AliYunLLM(api_key="your-api-key", base_url="https://dashscope.aliyuncs.com/api/v1", model="qwen-plus")
agent = ReActAgent(llm, max_steps=5)
question = "2022年世界杯冠军的现任主教练是谁?"
answer = agent.run(question)
print(f"\n最终答案:{answer}")