Day 19第一阶段测验准备 —— 手动模拟 ReAct 循环欢迎来到第十九天在前面的学习中我们已经掌握了 Function CallingDay 11理解了模型如何请求调用工具。但 Function Calling 是 API 层面的机制模型只是“给出参数”而推理过程是隐藏的。今天我们要深入 Agent 的核心范式——ReActReasoning Acting并手动模拟一个 ReAct 循环让模型在每一步都显式地输出“思考Thought”和“行动Action”然后我们手动执行行动并将“观察Observation”反馈给模型。这将帮助你真正理解 Agent 是如何工作的并为明天使用 LangChain 的现成 Agent 打下坚实基础。一、今日学习目标理解 ReAct 范式的核心思想模型交替进行推理Thought、行动Action、观察Observation直到得出最终答案。掌握如何通过精心设计的 Prompt 让模型按照 ReAct 格式输出。动手实现一个手动模拟 ReAct 循环模型输出 Thought/Action程序执行工具将 Observation 返回给模型循环直到模型给出 Final Answer。通过实验观察模型如何决定使用哪个工具、如何分析观察结果、如何修正错误。对比 ReAct 与 Function Calling 的异同为后续使用 LangChain 的 Agent 做准备。二、详细实现步骤步骤 1理解 ReAct 范式ReAct 是一种让大模型进行推理和行动的提示范式。它的核心思想是模型不直接回答而是先生成一个“Thought”说明它下一步要做什么然后生成一个“Action”表示调用某个工具并给出输入外部程序执行该工具返回结果作为“Observation”模型再根据 Observation 继续思考如此循环直到模型认为已经得到足够信息输出“Final Answer”。一个典型的 ReAct 交互如下Thought: 我需要知道北京今天的天气所以应该调用天气查询工具。 Action: get_weather[北京] Observation: {city: 北京, temperature: 26°C, condition: 晴} Thought: 我已经得到了天气信息可以回答用户了。 Final Answer: 北京今天晴气温 26°C。与 Function Calling 相比ReAct 将推理过程显式化更容易调试和理解并且可以灵活地控制循环。步骤 2设计 ReAct Prompt 模板我们需要一个 System Prompt指导模型按照规定的格式输出。这个 Prompt 需要包含可用工具列表及其描述。输出格式说明必须包含 Thought 和 Action或者 Final Answer。示例Few-shot帮助模型理解格式。强调在不确定时要继续调用工具不能凭空猜测。我们以两个简单工具为例calculator(expression)计算数学表达式返回结果。search(query)模拟搜索返回一条预设的答案。由于是手动模拟我们不调用真实 API而是用 Python 函数模拟。步骤 3准备模拟工具新建manual_react.py定义两个模拟工具函数defcalculator(expression:str)-str:模拟计算器实际可以使用 eval 但为了安全这里只处理简单表达式try:# 禁止危险操作ifany(cinexpressionforcinabcdefghijklmnopqrstuvwxyz):return错误表达式包含字母resulteval(expression)returnstr(result)exceptExceptionase:returnf错误{e}defsearch(query:str)-str:模拟搜索引擎返回预设的搜索结果knowledge{北京天气:北京今天晴26°C湿度 40%。,中国首都:中国的首都是北京。,人工智能:人工智能是计算机科学的一个分支研究如何让机器模拟人类智能。}# 简单匹配forkey,valueinknowledge.items():ifkeyinquery:returnvaluereturn未找到相关信息。步骤 4编写 ReAct System Prompt这个 Prompt 至关重要我们要让模型严格按照格式输出。提供一两个示例展示如何使用工具以及最终回答。react_system_prompt 你是一个智能助手可以通过调用工具来回答用户问题。你可以使用以下工具 1. calculator计算数学表达式输入一个数学表达式字符串返回计算结果。 2. search搜索信息输入关键词或问题返回相关答案。 你必须按照以下格式输出每行以 Thought:、Action:、Observation: 或 Final Answer: 开头 - 如果你想使用工具输出 Thought: 你的思考 Action: 工具名[输入] - 当工具返回结果后你会看到一行以 Observation: 开头的内容那是工具的结果。 - 当你认为已经得到足够信息输出 Thought: 我现在可以回答了 Final Answer: 最终答案 示例1 用户北京天气怎么样 Thought: 我需要查询北京的天气。 Action: search[北京天气] Observation: 北京今天晴26°C湿度 40%。 Thought: 我已经得到天气信息。 Final Answer: 北京今天晴气温26°C湿度40%。 示例2 用户计算 (12 7) * 3 的结果。 Thought: 这是一个数学计算我应该使用计算器。 Action: calculator[(12 7) * 3] Observation: 57 Thought: 计算结果是57。 Final Answer: 结果是57。 现在开始回答用户的问题。记住不要猜测如果信息不足就使用工具。当工具返回结果后继续根据 Observation 思考。最终必须输出 Final Answer。 步骤 5实现手动 ReAct 循环我们需要一个循环反复调用模型解析输出中的 Action如果有执行工具将 Observation 追加到对话历史中直到模型输出 Final Answer 或达到最大步数。importrefromopenaiimportOpenAIimportosfromdotenvimportload_dotenv load_dotenv()clientOpenAI(api_keyos.getenv(DEEPSEEK_API_KEY),base_urlhttps://api.deepseek.com)defparse_action(text:str):从模型输出中提取 Action 和输入action_patternrAction:\s*(\w)\[(.*?)\]final_patternrFinal Answer:\s*(.*)action_matchre.search(action_pattern,text)final_matchre.search(final_pattern,text)ifaction_match:tool_nameaction_match.group(1)tool_inputaction_match.group(2)return(action,tool_name,tool_input)eliffinal_match:return(final,final_match.group(1))else:return(unknown,None,None)defrun_react_agent(user_input:str,max_steps:int5):messages[{role:system,content:react_system_prompt},{role:user,content:user_input}]step0whilestepmax_steps:step1# 调用模型responseclient.chat.completions.create(modeldeepseek-chat,messagesmessages,temperature0.1,# 低温度保证格式稳定max_tokens200)model_outputresponse.choices[0].message.contentprint(f\n--- 第{step}步模型输出 ---)print(model_output)# 解析输出parsedparse_action(model_output)ifparsed[0]final:print(\n 最终答案 )print(parsed[1])returnparsed[1]elifparsed[0]action:tool_name,tool_inputparsed[1],parsed[2]print(f\n执行工具{tool_name}输入{tool_input})# 执行工具iftool_namecalculator:observationcalculator(tool_input)eliftool_namesearch:observationsearch(tool_input)else:observationf错误未知工具{tool_name}print(fObservation:{observation})# 将模型输出和 Observation 追加到消息历史# 注意我们应将模型输出作为 assistant 消息Observation 作为 user 消息或 system 消息messages.append({role:assistant,content:model_output})messages.append({role:user,content:fObservation:{observation}})else:# 格式错误提醒模型print(输出格式错误提醒模型。)messages.append({role:assistant,content:model_output})messages.append({role:user,content:你的输出格式不正确请遵循格式要么输出 Action: 工具[输入]要么输出 Final Answer: 答案。})continueprint(达到最大步数停止。)returnNone# 测试if__name____main__:query请帮我查一下北京天气然后计算 25 * 4 的结果。run_react_agent(query)步骤 6运行并观察运行脚本你应该能看到类似以下的输出实际可能有所不同--- 第 1 步模型输出 --- Thought: 用户需要两个信息北京天气和计算结果。我需要先查天气再计算。 Action: search[北京天气] 执行工具search输入北京天气 Observation: 北京今天晴26°C湿度 40%。 --- 第 2 步模型输出 --- Thought: 我已经得到了天气信息现在需要计算 25 * 4。 Action: calculator[25 * 4] 执行工具calculator输入25 * 4 Observation: 100 --- 第 3 步模型输出 --- Thought: 我现在有了天气和计算结果。 Final Answer: 北京今天晴26°C湿度 40%25 * 4 100。这个过程中模型展现出了清晰的“思考-行动-观察”循环甚至能处理多步骤任务。步骤 7实验不同任务你可以尝试其他问题比如“人工智能是什么用搜索工具查一下。”“计算 123 456 再乘以 2。”故意让模型调用不存在的工具观察它如何反应你可以在执行时返回“未知工具”。三、常见问题与调试Q1模型输出的格式不稳定有时缺少 Thought 或 Action 标签。→ 可以加强 System Prompt 中的格式要求增加更多示例降低 temperature如果格式错误可以像代码中那样提醒模型重新输出。Q2模型总是直接给出 Final Answer不调用工具。→ 在 System Prompt 中强调“如果信息不足必须使用工具不要猜测”。也可以在用户消息中加上“请使用工具查询”。如果仍然不调用可能需要提供更明确的 Few-shot 示例展示工具调用。Q3循环无法终止模型一直调用工具。→ 设置最大步数如 5并在 System Prompt 中要求模型在获得足够信息后尽快输出 Final Answer。如果模型陷入循环可以在 Observation 中加入提示例如“你已经有了足够的信息请给出最终答案”。Q4如何将手动 ReAct 与 Function Calling 结合→ 实际上 LangChain 的 Agent 底层就是结合了 ReAct 和 Function Calling。手动模拟的目的是理解原理实际开发中建议使用框架。Q5工具执行出错怎么办→ 在 Observation 中返回错误信息模型会根据错误调整下一步行动例如修正表达式后重试。四、今日总结与作业今天你完成了✅ 理解了 ReAct 范式的核心循环Thought → Action → Observation。✅ 设计了一个 ReAct 格式的 System Prompt并实现了手动循环。✅ 通过实验观察了模型进行多步骤推理和工具调用的过程。✅ 学会了如何处理格式错误和工具执行错误。今日作业必做修改run_react_agent函数增加一个名为get_time的工具模拟返回当前时间并测试用户提问“现在几点了用工具查一下。”。让模型解决一个需要多次工具调用的复杂问题例如“先搜索中国的人口再用计算器乘以 2”观察 ReAct 循环的步骤记录模型是如何安排行动的。思考ReAct 循环中Observation 应该以什么角色user/assistant/system添加到消息历史为什么请查阅 LangChain 的 ReAct 实现明天我们将使用它。明日预告我们将正式使用 LangChain 的create_react_agent构建一个真正的 Agent并给它配备计算器和搜索工具体验框架带来的便利。有任何问题欢迎随时提问
