ReAct:让语言模型变聪明的“思考 + 行动”策略
在构建更智能的语言模型应用时,仅靠 LLM 自身并不足够。我们希望它不仅能“说得像人”,还要“像人一样做事”。这就引出了一个非常核心的理念——Agent(智能体) ,以及其中的一种典型策略:ReAct(Reason + Act)。
什么是 Agent?
**Agent(智能体)是一种通过结合语言模型(LLM)与外部工具/数据源,使得模型具有“观察 → 思考 → 行动 → 反馈”**能力的设计模式。
你可以把它看作是一种让大语言模型变得更加聪明、更加自主解决复杂任务的“套路”。核心思想是:
- 编写一个良好的 Prompt(提示词),定义模型行为的规则;
- 人类提问,LLM 接收到问题;
- 模型根据提示开始推理,并决定是否需要使用外部工具;
- 外部系统根据模型的请求调用函数,获得结果,再传回 LLM;
- LLM 综合多轮思考与工具反馈,得出最终答案。
整个过程就像一个闭环(loop)——模型不断地“思考 → 观察反馈 → 再思考”,直到完成任务。
###ReAct 框架:Reason + Act
ReAct 是一种特殊的 Agent 思路,它把两个关键点结合起来:
- Reasoning(思考) :模型会用自然语言解释自己的想法,比如“我需要先查一下今天的天气”。
- Acting(行动) :模型根据推理结果,主动调用工具或函数,比如调用
getWeather("Beijing")。
这个框架的核心作用就是:
把一个大问题拆分成一系列小任务,逐步推理、逐步执行,每一步都可以借助外部资源。
ReAct 智能体工具的创建
- 创建一个 agent.py 文件
# 导入所需的库
import json # 用于处理JSON数据
from llm import client # 导入LLM客户端
from prompt import REACT_PROMPT # 导入预设的提示模板
from tools import get_closing_price,tools # 导入工具函数
import re # 导入正则表达式库
# 定义发送消息到LLM的函数
def send_messages(messages):
"""
向LLM发送消息并获取响应
:param messages: 消息列表
:return: LLM的响应
"""
response = client.chat.completions.create(
model="deepseek-chat", # 使用deepseek-chat模型
messages=messages, # 传入消息列表
temperature=0.1, # 设置温度参数,控制生成文本的随机性
)
return response
if __name__ == "__main__":
# 设置助手的角色说明
instructions = "你是一个股票助手,可以回答股票相关的问题"
# 设置用户查询
query = "青岛啤酒和贵州茅台的收盘价哪个贵?"
# 使用模板构建完整的提示
prompt = REACT_PROMPT.format(instructions=instructions,tools=tools,tool_names="get_closing_price",input=query)
# 初始化消息列表
messages = [{"role": "user", "content": prompt}]
# 开始对话循环
while True:
# 发送消息并获取响应
# print("发送消息到模型...", messages)
response = send_messages(messages)
response_text = response.choices[0].message.content
# 打印模型的回复
print("大模型的回复:")
print(response_text)
# 检查是否有最终答案
# 使用正则表达式搜索回复文本中是否包含"Final Answer:",\s*匹配任意空白字符,(.*)捕获冒号后面的所有内容
final_answer_match = re.search(r'最终答案:\s*(.*)', response_text)
if final_answer_match:
# 从正则匹配结果中提取第一个捕获组(括号内匹配到的内容),即"Final Answer:"后面的文本内容
final_answer = final_answer_match.group(1)
print("最终答案:", final_answer)
# 如果有最终答案,结束对话
break
# 将模型的回复添加到消息历史
messages.append(response.choices[0].message)
# 解析模型回复中的动作和参数
action_match = re.search(r'Action:\s*(\w+)', response_text)
action_input_match = re.search(r'Action Input:\s*({.*?}|".*?")', response_text, re.DOTALL) # 非贪婪匹配,匹配到第一个"}"或"""
# 如果成功解析到动作和参数
if action_match and action_input_match:
tool_name = action_match.group(1) # 获取工具名称
params = json.loads(action_input_match.group(1)) # 解析参数
print("工具名称:", tool_name)
print("参数:", params)
if tool_name == "get_closing_price":
observation = get_closing_price(params["name"]) # 调用工具函数
print("调用第三方API结果:", observation)
# 将观察结果添加到消息历史
messages.append({'role': 'user', 'content': f"observation:{observation}"})
- 创建一个 llm.py文件
# 定义一个客户端,用于从 LLM 获取响应
from openai import OpenAI
import os
from dotenv import load_dotenv # 加载环境变量
load_dotenv('.env.local')
client = OpenAI(
api_key=os.getenv('DEEPSEEK_API_KEY'),
base_url='https://api.deepseek.com/v1'
)
- 创建一个 .env.local 文件
DEEPSEEK_API_KEY=sk-b0c7f1eddb97463fbbfdf61*********
创建一个 prompt.py 文件
# 使用三引号"""创建多行字符串,可以保留字符串中的换行和格式
REACT_PROMPT = """
{instructions}
TOOLS:
您可以使用以下工具:
{tools}
使用工具时,请使用以下格式:
思考: 我需要使用工具吗? 是
行动: 要采取的行动,必须是[{tool_names}]其中之一
行动输入: 该行动的输入内容
然后等待人类使用 Observation 回复您操作的结果。
... (这个思考/行动/行动输入/观察可以重复 N 次)
当您有回应要对人类说,或者不需要使用工具时,您必须使用以下格式:
思考: 我需要使用工具吗? 否
最终答案: [在此输入您的回应]
开始!
新输入: {input}
"""
创建文件 tools.py
# 工具列表,第三方函数的说明书
tools = [
{
"name": "get_closing_price",
"description": "使用该工具获取指定股票的收盘价格",
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "股票名称,例如:贵州茅台、青岛啤酒等"
}
},
"required": ["name"]
}
}
]
def get_closing_price(name):
# 朝第三方 API 请求获取股票收盘价
if name == "贵州茅台":
return '1488.21'
elif name == "青岛啤酒":
return '67.92'
else:
return '未搜索到该股票'
创建文件 .gitignore
.env.local
ReAct 执行示例:
text
复制代码
Question: 明天适合出门吗?
Thought: 我需要查一下明天的天气
Action: getWeather("明天", "北京")
Observation: 明天是晴天,气温26℃
Thought: 天气不错,适合出门
Final Answer: 适合
在这个流程中,模型:
- 表达了自己的思考(Thought)
- 调用了一个工具(Action)
- 接收了反馈(Observation)
- 最终形成结论(Final Answer)
背后是一个“思考-行动-反馈”的循环
这套机制背后的设计,就是一个 Agent 的典型“循环”结构:
- 模型分析人类提问
- 判断是否调用工具(用推理方式表示出来)
- 我们作为中间层接收 action 指令并实际调用工具
- 把工具结果传给模型
- 模型继续思考下一步,直到完成任务
这套机制可以用以下形式简化理解:
css
复制代码
[人类问题] → LLM(思考)→ 工具调用 → LLM(观察+思考)→ … → 最终答案
ReAct vs Function Calling:有什么不同?
虽然 ReAct 也在“调用工具”,但和现在主流的 Function Calling 有几个本质区别:
| 项目 | ReAct | Function Calling |
|---|---|---|
| 主体驱动 | LLM 自主判断是否调用 | 外部系统触发调用逻辑 |
| 表达形式 | 自然语言 Thought, Action, Observation | 结构化 JSON 格式函数调用 |
| 任务能力 | 多步推理 + 调用,适合复杂 Agent | 单步调用,适合高效 API 集成 |
| 可解释性 | 强(能看到每一步思考) | 弱(只看到结果) |
ReAct 更适合构建具有“人类思维方式”的智能体,而 Function Calling 更适合在产品中快速接入外部工具和接口。
为什么 ReAct 很重要?
-
它让 LLM 具备“自我规划”的能力
-
具备清晰的逻辑链条,可解释性强
-
能够处理复杂任务,比如:
- 多轮问答
- 数据分析
- 自动网页搜索
- 自动脚本执行
- 电商推荐、规划路径等
总结
ReAct 并不仅仅是让 LLM“会调用工具”,而是让它变成一个更像人类的问题解决者(Problem Solver) :
模型不再只是回答,而是在思考 → 行动 → 观察反馈 → 再思考中一步步逼近正确答案。