理论部分
在第 9 篇我们已经跑通了 Function Calling:模型能决定“调用哪个工具 + 传什么参数”,我们负责执行并把结果回传。
但在更复杂的任务里,往往不是“调用一次工具就结束”,而是需要一个多步流程:
- 先查询信息(搜索)
- 再做计算(计算器)
- 再根据结果补充查询或修正
- 最后综合输出
Agent(智能体)要解决的就是这种“多步决策与执行”的问题。ReAct(Reasoning + Acting)是非常经典的一种 Agent 结构:
- Thought(推理):模型先思考下一步要做什么
- Action(行动):选择一个工具并给出输入
- Observation(观察):我们执行工具,把结果反馈给模型
- 重复以上过程,直到产出最终答案
我们可以把它理解成一个“带工具的循环”:模型负责规划与决策,我们负责执行与反馈。只要工具足够、循环结构清晰,Agent 就能完成比单次工具调用更复杂的任务。
实践部分
本案例做什么
实现一个最小 ReAct Agent,内置两个工具:
search:模拟搜索引擎(返回实时信息的“假数据”)calculate:简单计算器(演示用)
Agent 将严格按照 ReAct 格式输出并循环:
- 模型给出 Thought / Action / Action Input
- 我们解析 Action 并执行对应工具
- 把 Observation 追加回上下文
- 重复直到模型输出 Final Answer 或达到最大步数
主要代码
本篇使用项目脚本:src/4.2_react_agent.py
importosimportrefromdotenvimportload_dotenvfromopenaiimportOpenAI load_dotenv()client=OpenAI(api_key=os.getenv("ZHIPUAI_API_KEY"),base_url=os.getenv("ZHIPUAI_BASE_URL"))# === 1. 定义工具 ===defsearch(query):"""模拟搜索引擎"""print(f" [System] 正在搜索:{query}")if"美元"inqueryor"USD"inquery:return"当前美元兑人民币汇率约为 7.25"elif"天气"inquery:return"北京今天晴转多云,气温 20-28 度"else:return"未找到相关信息"defcalculate(expression):"""简单的数学计算器"""print(f" [System] 正在计算:{expression}")try:# ⚠️ 注意:eval 在生产环境中非常危险,仅用于演示returnstr(eval(expression))exceptExceptionase:returnf"计算错误:{e}"# 工具映射表tools={"search":search,"calculate":calculate}# === 2. 构建 ReAct Prompt ===# 核心:教会 LLM 按照 Thought -> Action -> Observation 的格式思考REACT_SYSTEM_PROMPT=""" 你是一个智能助手,可以使用以下工具来回答问题: 1. search: 搜索引擎,用于查询实时信息。输入参数:搜索关键词。 2. calculate: 计算器,用于数学计算。输入参数:数学表达式(如 1+1)。 回答问题时,请遵循以下格式(可以循环多次): Question: 用户的问题 Thought: 我应该怎么做?思考下一步。 Action: 工具名称 Action Input: 工具参数 Observation: 工具返回的结果 ... (重复 Thought/Action/Observation 直到得到答案) Thought: 我已经有了最终答案 Final Answer: 最终回复给用户的答案 一定要严格遵守上述格式。不要在 Observation 之前输出 Final Answer。 """defparse_action(text):"""解析 LLM 返回的文本,提取 Action 和 Action Input"""action_match=re.search(r"Action:\s*(.*?)\n",text)input_match=re.search(r"Action Input:\s*(.*?)\n",text)ifaction_matchandinput_match:returnaction_match.group(1).strip(),input_match.group(1).strip()returnNone,Nonedefagent_run(question,max_steps=5):print(f"\n🚀 开始执行任务:{question}")# 初始化对话历史history=REACT_SYSTEM_PROMPT+f"\nQuestion:{question}\n"foriinrange(max_steps):# 1. 让 LLM 思考并决定行动response=client.chat.completions.create(model="glm-4-flash",messages=[{"role":"user","content":history}],temperature=0.1,stop=["Observation:"]# 关键:让模型在生成 Observation 前停止,等待我们填入结果)llm_output=response.choices[0].message.contentprint(f"\n🤖 Step{i+1}LLM Output:\n{llm_output}")# 将 LLM 的输出拼接到历史中history+=llm_output+"\n"# 2. 检查是否已经得出最终答案if"Final Answer:"inllm_output:final_answer=llm_output.split("Final Answer:")[-1].strip()returnfinal_answer# 3. 解析并执行动作action,action_input=parse_action(llm_output)ifactionandactionintools:# 执行工具observation=tools[action](action_input)print(f"👀 Observation:{observation}")# 将执行结果拼接到历史中,供 LLM 下一步参考history+=f"Observation:{observation}\n"else:print("⚠️ 未能解析出有效的 Action,尝试让 LLM 继续...")history+="Observation: 格式错误,请确保使用 Action: 和 Action Input: 格式\n"return"任务执行超时,未能找到答案。"if__name__=="__main__":# 测试案例 1:需要搜索 + 计算q1="如果有 100 美元,兑换成人民币是多少?"result=agent_run(q1)print(f"\n✅ 最终结果:{result}")# 测试案例 2:仅需搜索# q2 = "北京今天适合穿短袖吗?"# agent_run(q2)运行方式
在项目根目录执行:
python3 src/4.2_react_agent.py运行结果示例
运行时我们会看到模型按 Step 输出 Thought/Action,并看到工具执行的 Observation,最后输出 Final Answer:
🚀 开始执行任务: 如果有 100 美元,兑换成人民币是多少? 🤖 Step 1 LLM Output: Question: 如果有 100 美元,兑换成人民币是多少? Thought: 我需要先知道当前美元兑人民币汇率,然后再计算 100 美元对应多少人民币。 Action: search Action Input: 美元兑人民币 汇率 [System] 正在搜索: 美元兑人民币 汇率 👀 Observation: 当前美元兑人民币汇率约为 7.25 🤖 Step 2 LLM Output: Thought: 现在用计算器算 100 * 7.25。 Action: calculate Action Input: 100*7.25 [System] 正在计算: 100*7.25 👀 Observation: 725.0 🤖 Step 3 LLM Output: Thought: 我已经有了最终答案 Final Answer: 100 美元大约可以兑换 725 元人民币(按 1 USD≈7.25 估算)。 ✅ 最终结果: 100 美元大约可以兑换 725 元人民币(按 1 USD≈7.25 估算)。总结
这一篇我们用最小实现跑通了 ReAct Agent:模型负责分步规划(Thought)、选择工具(Action),我们负责执行并把结果反馈(Observation),循环直到产出最终答案(Final Answer)。这类结构可以自然扩展到更多工具与更复杂的任务。