一、理论部分
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. 运行准备
请确保本地已安装依赖并配置环境变量:
依赖安装(示例):
pipinstall-rrequirements.txt环境变量(示例):
DEEPSEEK_API_KEYDEEPSEEK_BASE_URL
说明:
get_weather为模拟数据,仅支持少数城市(如 北京/上海/Beijing/Shanghai 等)calculate使用eval()仅用于教学演示,不可用于生产环境
3. 主要代码(核心实现)
以下为项目核心代码(包含工具定义、Prompt、循环、解析与回灌逻辑;可直接运行):
importreimportosfromdotenvimportload_dotenvfromopenaiimportOpenAI load_dotenv()client=OpenAI(api_key=os.getenv("DEEPSEEK_API_KEY"),base_url=os.getenv("DEEPSEEK_BASE_URL"),)# --- 1) Tools ---defget_weather(city_name):"""查询天气工具。实际项目中应调用真实 API,这里模拟返回。"""print(f" [系统日志] 正在查询{city_name}的天气...")mock_data={"Beijing":"25°C","Shanghai":"22°C","New York":"15°C","北京":"25°C","上海":"22°C",}returnmock_data.get(city_name,"未知城市,无法查询天气")defcalculate(expression):"""计算器工具。"""print(f" [系统日志] 正在计算:{expression}")try:returnstr(eval(expression))exceptExceptionase:returnf"计算错误:{str(e)}"tools_map={"get_weather":get_weather,"calculate":calculate,}# --- 2) ReAct Prompt ---REACT_PROMPT=""" 请尽你所能回答以下问题。你可以使用以下工具: {tool_descs} 请使用以下格式: 问题: 你需要回答的输入问题 思考: 你应该总是思考接下来该做什么 行动: 你采取的行动,必须是 [{tool_names}] 之一 行动输入: 行动的输入参数 观察: 行动的结果 ... (这个 思考/行动/行动输入/观察 的循环可以重复 N 次) 思考: 我现在知道最终答案了 最终答案: 针对原始问题的最终答案 开始! 问题: {query} """.strip()defbuild_tool_desc():desc=[]forname,funcintools_map.items():desc.append(f"{name}:{func.__doc__}")return"\n".join(desc)# --- 3) Agent Core ---classMiniReActAgent:def__init__(self,model="deepseek-chat"):self.model=modeldefchat(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=0max_steps=10whilestep_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.contentprint(f"🤖 Agent:{llm_output}")messages.append({"role":"assistant","content":llm_output})if"最终答案:"inllm_output:returnllm_output.split("最终答案:")[-1].strip()action_match=re.search(r"行动:\s*(.*?)\n行动输入:\s*(.*)",llm_output,re.DOTALL)ifaction_match:action_name=action_match.group(1).strip()action_input=action_match.group(2).strip()ifaction_nameintools_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+=1return"Task limit reached without final answer."# --- 4) CLI Entrance ---if__name__=="__main__":agent=MiniReActAgent()print("🧠 Mini-ReAct Agent 已启动!")print("支持工具: get_weather (模拟), calculate (计算器)")print("示例: 查询北京的天气,并将温度数值乘以 10")whileTrue:query=input("\n👤 请输入问题 (输入 exit 退出): ")ifquery.lower()in["exit","quit"]:breakfinal_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()仅用于教学演示,生产环境必须替换为安全计算方案(否则存在严重注入风险)。