Agent-study项目教程(03):手写 Mini-ReAct Agent(不依赖框架)
·
一、理论部分
1. Agent 的核心闭环:思考 → 选工具 → 执行 → 反馈 → 再思考
与“聊天机器人”不同,Agent 的关键能力在于:遇到需要外部信息或计算时,不是直接凭空回答,而是能够主动调用工具(Tool)完成任务,并将工具结果纳入后续推理。
一个最小可用的 Agent,通常具备以下流程:
- Reasoning(推理):判断下一步该做什么
- Acting(行动):选择一个工具并给出参数
- Observation(观察):获取工具执行结果
- Loop(循环):把观察结果反馈给模型,直到得到最终答案
这就是经典的 ReAct(Reason + Act) 思路。
2. 工具(Tool)的本质:可被模型调用的函数
在工程实现上,工具就是一组可执行函数,例如:
get_weather(city):查询天气(本项目用模拟数据)calculate(expression):计算表达式(本项目用eval演示)
为了让模型“可选”,我们通常维护一个 工具注册表(如 tools_map),实现“工具名 → 函数”的映射。模型输出工具名后,程序才能准确路由到对应函数执行。
3. 约束模型输出:固定格式 + Stop Sequence + 解析
要让模型稳定触发工具调用,必须对输出格式做约束。本项目采用如下组合策略:
- 固定输出格式:要求模型按
思考/行动/行动输入/观察/最终答案的格式输出 - Stop Sequence:调用模型时设置
stop=["观察:"],让模型在准备输出“观察”之前停下来,等待程序填入真实工具结果 - 正则解析:从模型输出中解析
行动与行动输入,从而驱动工具执行
这三点共同解决一个问题:让模型的“行动意图”可被程序可靠识别与执行。
4. 结果回灌:Observation 是 Agent 能多步完成任务的关键
当工具返回结果后,需要把结果以 观察: ... 的形式写回对话上下文,模型才可能进行下一步推理(例如:先查天气,再从温度提取数值,再做计算)。
因此,Observation 回灌是多步推理的必要条件。
二、实战部分
1. 项目目标
实现一个不依赖任何 Agent 框架的 Mini-ReAct Agent,支持:
- 基于固定格式的 ReAct Loop
- 工具选择与执行(天气查询、计算器)
- 多步任务:能“先查再算”,直到输出最终答案
2. 运行准备
请确保本地已安装依赖并配置环境变量:
依赖安装(示例):
pip install -r requirements.txt
环境变量(示例):
DEEPSEEK_API_KEYDEEPSEEK_BASE_URL
说明:
get_weather为模拟数据,仅支持少数城市(如 北京/上海/Beijing/Shanghai 等)calculate使用eval()仅用于教学演示,不可用于生产环境
3. 主要代码(核心实现)
以下为项目核心代码(包含工具定义、Prompt、循环、解析与回灌逻辑;可直接运行):
import re
import os
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
client = OpenAI(
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url=os.getenv("DEEPSEEK_BASE_URL"),
)
# --- 1) Tools ---
def get_weather(city_name):
"""查询天气工具。实际项目中应调用真实 API,这里模拟返回。"""
print(f" [系统日志] 正在查询 {city_name} 的天气...")
mock_data = {
"Beijing": "25°C",
"Shanghai": "22°C",
"New York": "15°C",
"北京": "25°C",
"上海": "22°C",
}
return mock_data.get(city_name, "未知城市,无法查询天气")
def calculate(expression):
"""计算器工具。"""
print(f" [系统日志] 正在计算: {expression}")
try:
return str(eval(expression))
except Exception as e:
return f"计算错误: {str(e)}"
tools_map = {
"get_weather": get_weather,
"calculate": calculate,
}
# --- 2) ReAct Prompt ---
REACT_PROMPT = """
请尽你所能回答以下问题。你可以使用以下工具:
{tool_descs}
请使用以下格式:
问题: 你需要回答的输入问题
思考: 你应该总是思考接下来该做什么
行动: 你采取的行动,必须是 [{tool_names}] 之一
行动输入: 行动的输入参数
观察: 行动的结果
... (这个 思考/行动/行动输入/观察 的循环可以重复 N 次)
思考: 我现在知道最终答案了
最终答案: 针对原始问题的最终答案
开始!
问题: {query}
""".strip()
def build_tool_desc():
desc = []
for name, func in tools_map.items():
desc.append(f"{name}: {func.__doc__}")
return "\n".join(desc)
# --- 3) Agent Core ---
class MiniReActAgent:
def __init__(self, model="deepseek-chat"):
self.model = model
def chat(self, query: str) -> str:
tool_descs = build_tool_desc()
tool_names = ", ".join(tools_map.keys())
prompt = REACT_PROMPT.format(tool_descs=tool_descs, tool_names=tool_names, query=query)
messages = [{"role": "user", "content": prompt}]
step_count = 0
max_steps = 10
while step_count < max_steps:
print(f"\n--- Step {step_count + 1} ---")
response = client.chat.completions.create(
model=self.model,
messages=messages,
stop=["观察:"],
)
llm_output = response.choices[0].message.content
print(f"🤖 Agent: {llm_output}")
messages.append({"role": "assistant", "content": llm_output})
if "最终答案:" in llm_output:
return llm_output.split("最终答案:")[-1].strip()
action_match = re.search(r"行动:\s*(.*?)\n行动输入:\s*(.*)", llm_output, re.DOTALL)
if action_match:
action_name = action_match.group(1).strip()
action_input = action_match.group(2).strip()
if action_name in tools_map:
observation = tools_map[action_name](action_input)
else:
observation = f"Error: Tool '{action_name}' not found."
print(f"🔍 观察: {observation}")
messages.append({"role": "user", "content": f"观察: {observation}"})
else:
messages.append({"role": "user", "content": "请继续。如果你有了答案,请输出 '最终答案:'。"})
step_count += 1
return "Task limit reached without final answer."
# --- 4) CLI Entrance ---
if __name__ == "__main__":
agent = MiniReActAgent()
print("🧠 Mini-ReAct Agent 已启动!")
print("支持工具: get_weather (模拟), calculate (计算器)")
print("示例: 查询北京的天气,并将温度数值乘以 10")
while True:
query = input("\n👤 请输入问题 (输入 exit 退出): ")
if query.lower() in ["exit", "quit"]:
break
final_answer = agent.chat(query)
print(f"\n✅ 最终答案: {final_answer}")
4. 如何运行
在项目根目录执行:
python stage_02_agent_principles/project_03_mini_react/main.py
可测试输入示例:
15 * 15 + 400 等于多少?查询北京的天气查询北京的天气,并将温度数值乘以 10
5. 运行结果示例
下面展示一个“多步推理”的典型输出(示例为:先查北京天气,再把温度数值乘以 10):
🧠 Mini-ReAct Agent 已启动!
支持工具: get_weather (模拟), calculate (计算器)
示例: 查询北京的天气,并将温度数值乘以 10
👤 请输入问题 (输入 exit 退出): 查询北京的天气,并将温度数值乘以 10
--- Step 1 ---
🤖 Agent: 问题: 查询北京的天气,并将温度数值乘以 10
思考: 我需要先获取北京的温度,然后提取数值并进行计算。
行动: get_weather
行动输入: 北京
[系统日志] 正在查询 北京 的天气...
🔍 观察: 25°C
--- Step 2 ---
🤖 Agent: 思考: 已获得温度 25°C,需要计算 25 * 10。
行动: calculate
行动输入: 25*10
[系统日志] 正在计算: 25*10
🔍 观察: 250
--- Step 3 ---
🤖 Agent: 思考: 我现在知道最终答案了
最终答案: 北京当前温度约为 25°C,温度数值乘以 10 的结果为 250。
✅ 最终答案: 北京当前温度约为 25°C,温度数值乘以 10 的结果为 250。
6. 项目总结
- 本项目用最小实现复现了 ReAct 的核心闭环:模型输出行动 → 程序执行工具 → 结果回灌 → 多轮推理直至最终答案。
- “Stop Sequence + 格式约束 + 正则解析”是让模型可控调用工具的关键工程组合。
- 该实现清晰展示了 Agent 的底层机制,为后续引入框架(LangChain/LangGraph)与更复杂的工具系统打下基础。
- 安全提示:示例中的
eval()仅用于教学演示,生产环境必须替换为安全计算方案(否则存在严重注入风险)。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)