本讲目标理解工具返回的新信息怎样改变下一步行动。一、为什么有了循环还需要理解 ReAct上一讲解决的是程序如何不断接收响应、执行工具并在适当的时候停止但循环本身没有回答下一轮应该调用哪个工具还缺什么信息已经获得的信息是否足够例如用户提出查询订单 A102若已发货再查物流到哪里了。程序一开始不知道订单是否存在。是否已经发货。物流单号是什么。这些信息需要通过工具获取。因此合理的后续动作不能只依据最初的问题还要依据实际返回的结果。先查询订单 ↓ 根据订单结果判断下一步 ├─ 待发货 → 说明状态结束 ├─ 已发货且有物流单号 → 查询物流 └─ 已发货但缺少物流单号 → 说明缺失结束ReAct 关注的就是这种“获取信息后再调整下一步”的过程。二、ReAct 的三个核心部分ReAct 来自Reasoning and Acting即推理与行动相结合。经典形式常用下面三个词描述名称含义本例中的表现Thought推理根据目标和已有证据判断还缺什么、下一步做什么订单已发货需要用返回的物流单号继续查询Action行动提出具体工具调用再由程序执行调用query_shipping(T900)Observation观察工具实际返回的信息运输中当前位置为杭州转运中心它们之间的关系是已有目标和信息 ↓ 判断下一步 ↓ 提出行动 ↓ 程序执行工具取得观察 ↓ 根据新观察再次判断 ↓ 继续行动或者回答这里有两个重要边界。第一工具结果必须来自执行过程。模型写出一句“我查到了物流”不能替代实际工具返回。第二终端打印了 Thought、Action、Observation并不能证明发生了真实推理或工具调用。你在第一周已经识别过固定轨迹回放。工程实现也不要求把模型的完整内部推理展示出来。我们主要检查可验证的信息请求了什么工具、返回了什么、下一步是否合理利用了反馈。三、用一个完整过程理解信息依赖本讲的数据中ORDERS { A102: {status: 已发货, tracking_id: T900}, B203: {status: 待发货, tracking_id: None}, }物流数据是SHIPMENTS { T900: { location: 杭州转运中心, delivery_status: 运输中, }, }查询 A102 时会经历三轮轮次此时已有的信息生成的响应执行后新增的信息第1轮用户要求查询订单及物流请求query_order(A102)已发货物流单号为 T900第2轮已知物流单号但还不知道位置请求query_shipping(T900)运输中位于杭州转运中心第3轮已取得需要的信息生成最终回答循环结束这里的关键不是“执行了两个工具”而是第二个工具的参数来自第一个工具的返回结果。第一轮查询之前程序还没有从工具取得T900。不能凭空填一个物流单号。这类关系叫作数据依赖后一步需要前一步的输出作为输入。因此在本例中不能把两次查询简单地同时执行。四、如果观察变了行动也应该变化把订单换成 B203订单工具返回{ ok: True, data: { order_id: B203, status: 待发货, tracking_id: None, }, }此时用户“若已发货再查物流”的条件没有满足合理的下一步是直接说明订单B203待发货暂不查询物流。于是路径缩短为第1轮查订单 第2轮根据待发货状态回答再看第三种情况{status: 已发货, tracking_id: None}它与“待发货”不同。订单已经发货但继续查询所需的参数缺失。合理处理是订单已发货但缺少物流单号无法继续查询物流。不能因为希望完成任务就编造一个物流单号。这三种情况说明停止也可以是合理决策不是只有继续调用工具才算有效行动。五、本地规则模拟与真实 ReAct 有什么区别本讲脚本里的fake_model()使用明确的if分支if data[status] 待发货: return final_answer(...) if data[status] 已发货: tracking_id data.get(tracking_id) if not tracking_id: return final_answer(...) return tool_request( call_002, query_shipping, {tracking_id: tracking_id}, )所以它本质上是一个根据反馈选择分支的规则程序用于模拟 ReAct 的信息流。可以这样比较实现方式下一步怎样产生适合本例的程度固定轨迹回放依次输出预先写好的记录只能展示形式无法验证反馈变化条件工作流程序按明确规则读取结果并选择分支订单与物流规则清楚时很合适模型参与的 ReAct模型根据目标、上下文和观察提出下一步程序负责执行与约束目标、工具选择或信息需求较开放时更有价值当前订单场景本来就可以用规则完成。我们用它是因为数据和路径足够清楚便于你观察机制。以后替换成真实模型时控制程序仍要负责参数校验、工具执行、回填、轮次限制。模型提出的动作也仍然需要检查。六、这份新代码怎么读先按这条顺序阅读main() ↓ fake_model() ↓ execute_tool() ↓ query_order() / query_shipping()对应文件位置位置阅读重点[第115行main (line 115)](/D:/learn/ai-agent-book/chapter1/web-search-agent/lesson_react.py:115)循环、工具执行、观察回填、停止原因[第78行fake_model (line 78)](/D:/learn/ai-agent-book/chapter1/web-search-agent/lesson_react.py:78)根据最新工具结果选择下一步[第43行execute_tool (line 43)](/D:/learn/ai-agent-book/chapter1/web-search-agent/lesson_react.py:43)工具映射、参数检查、超时处理[第22行两个查询函数 (line 22)](/D:/learn/ai-agent-book/chapter1/web-search-agent/lesson_react.py:22)数据如何实际产生有两个接口变化需要先说明。工具注册表现在支持两种能力TOOL_REGISTRY { query_order: (query_order, order_id), query_shipping: (query_shipping, tracking_id), }每个键对应“函数、参数名”这一对信息。function, parameter TOOL_REGISTRY[name]表示把这一对信息分别赋给两个变量。然后function(**args)把参数字典展开为命名参数调用例如query_shipping(tracking_idT900)两个查询函数现在都直接返回统一结果。上一讲的query_order()查不到时返回None再由执行器转换成失败字典。本讲为了让两个工具使用相同接口它们直接返回{ok: True, data: ...}或者{ok: False, error: ...}execute_tool()负责分发与参数检查业务查询函数负责表达自己的查询结果。两种内部划分都可以关键是调用双方遵守同一约定。另外ORDER_ID是本地配置输入用户消息也会根据它生成。模拟器没有从自然语言中识别订单编号。七、本讲集中实践改变观察检查行动我已经验证了这些分支可以运行。现在由你运行和修改观察它们为什么不同。确认 VS Code 当前打开的是lesson_react.py保持MAX_TURNS 3实验 A查询已发货订单顶部设置ORDER_ID A102运行后重点看行动 观察 最终输出 工具调用序列预期工具序列[query_order, query_shipping]实验 B查询待发货订单只修改ORDER_ID B203预期工具序列[query_order]实验 C订单已发货但缺少物流单号先恢复ORDER_ID A102然后在ORDERS中将 A102 改为A102: {status: 已发货, tracking_id: None},运行后检查程序是否仍然调用了物流工具以及最后怎样说明缺失的信息。完成后把物流单号恢复成T900。这次不用粘贴完整历史三组分别提交工具调用序列 最终输出 决定下一步的订单结果字段再用一句话回答为什么实验 B 和 C 都没有查物流但停止的依据不同实验A工具调用序列query_orderquery_shipping最终输出订单A102的物流T900运输中当前位置杭州转运中心。决定下一步的订单结果字段tracking_id实验B工具调用序列query_order最终输出订单B203待发货暂不查询物流决定下一步的订单结果字段status实验C工具调用序列query_order最终输出 订单已发货但缺少物流单号无法继续查询物流。决定下一步的订单结果字段status tracking_id因为状态不同B是待发货没有物流状态是正常的但是C已发货就要有物流状态AI修改:C 缺少“物流查询所需的物流单号”而不是笼统的“物流状态”。