上一讲的 ReAct 解决了:
观察结果 ↓ 选择下一步行动Reflection 再增加一层:
行动之后得到结果 ↓ 检查这个结果是否足以支持目标 ↓ 接受、修正、重试或停止Reflection 不是“让模型再想一遍”这么简单,而是要回答:
当前结果哪里有问题?证据是否足够?修正后是否真的更接近目标?
一、Reflection 检查什么?
以订单和物流任务为例,工具返回:
{ "ok": True, "data": { "order_id": "A102", "status": "已发货", "tracking_id": "T900" } }程序或模型准备回答:
订单A102已发货,物流正在运输中,当前位置是杭州转运中心。Reflection 至少要检查:
| 检查项 | 要确认的内容 |
|---|---|
| 对象是否一致 | 回答的是不是 A102 |
| 状态是否一致 | “已发货”是否来自工具结果 |
| 物流信息是否有证据 | T900、运输中、杭州转运中心是否真的返回 |
| 任务是否完成 | 用户要求查询物流时,是否确实查过物流 |
| 是否添加了未验证内容 | 是否凭空补充预计送达时间 |
如果所有关键信息都有证据,可以接受回答。
二、Reflection 的四种结果
结果检查后,不是只有“正确”和“错误”两种状态。常见处理有四类。
1. Accept:接受
工具返回:
{ "ok": True, "data": { "order_id": "A102", "status": "已发货", "tracking_id": "T900" } }物流工具又返回:
{ "ok": True, "data": { "tracking_id": "T900", "delivery_status": "运输中", "location": "杭州转运中心" } }回答中的所有事实都有来源,可以接受:
订单A102已发货,物流正在运输中,当前位置是杭州转运中心。2. Repair:修正
假设工具结果是:
{ "order_id": "A102", "status": "已发货" }但草稿写成:
订单A102待发货。这不是缺少工具,而是回答与已有证据冲突。可以根据已有证据修正为:
订单A102已发货。这里没有必要重新调用工具,因为已有结果足以纠正错误。
3. Retry:重试
假设调用物流工具时,参数格式错误:
{ "ok": False, "error": "tracking_id必须是字符串" }程序知道:
- 工具名称是正确的。
- 物流单号来自前一步。
- 失败原因是可修正的参数类型。
这时可以修正参数后有限重试一次。
但“重试”必须有明确条件和上限:
参数格式错误 → 修正并重试一次 重复超时 → 不无限重试 权限拒绝 → 不通过重试绕过权限4. Stop:停止
如果订单已发货,但返回:
{ "status": "已发货", "tracking_id": None }这时不能生成物流查询请求,因为缺少下一工具所需的输入。
合理结果是:
订单已发货,但缺少物流单号,无法继续查询物流。这不是“程序失败”,而是基于证据得出的合理停止。
三、为什么 Reflection 不一定越多越好?
Reflection 本身也可能产生问题。
没有新证据,却不断修改
如果工具只返回:
{"status": "已发货"}模型反复检查并不能凭空得到物流单号。没有新信息时,继续反思只是重复。
为了“更完整”而添加未经验证的事实
已有证据只有:
已发货但反思后回答变成:
已发货,预计明天送达。回答看起来更具体,事实依据却变少了。这属于退化。
把正确答案改错
工具明确返回:
待发货模型却因为觉得“用户可能想知道物流”,擅自改成:
已发货,正在运输中。这是 Reflection 没有遵守证据边界。
无限修正
如果没有限制,流程可能变成:
生成答案 ↓ 反思 ↓ 修改答案 ↓ 再次反思 ↓ 再次修改因此 Reflection 也需要:
- 最大修正次数。
- 每次修正的明确原因。
- 修正前后的差异。
- 没有新证据时停止。
- 修正后必须重新检查。
四、Reflection 与 ReAct、Plan-and-Solve 的关系
三者解决的问题不同:
Plan-and-Solve:提前确定大致步骤 ReAct:根据观察选择下一步 Reflection:检查刚才的结果是否可靠、是否需要修正可以组合成:
先规划 ↓ 执行一步 ↓ 观察结果 ↓ 检查结果 ├─ 通过 → 继续计划 ├─ 可修正 → 修正并有限重试 ├─ 需要探索 → 重新决定下一步 └─ 无法继续 → 停止在当前lesson_react.py中,已经有 ReAct 的反馈分支:
if not result["ok"]: return final_answer(...)if data["status"] == "待发货": return final_answer(...)if not tracking_id: return final_answer(...)这些代码已经具备一部分“结果检查”功能,但还不是独立的 Reflection 模块,因为它们直接决定回答或下一步,没有单独记录:
检查了什么? 为什么接受? 为什么修正? 为什么停止?五、把当前代码对应起来
当前流程是:
result = json.loads(results[-1]["content"])把工具消息解析成字典。
接着:
if not result["ok"]: return final_answer(...)检查工具是否成功。
然后:
data = result["data"]读取业务数据。
再根据数据检查:
if data["status"] == "待发货":以及:
tracking_id = data.get("tracking_id") if not tracking_id:这些检查本质上是在判断:
工具结果是否支持下一步?因此,Reflection 不一定要设计成一个很大的新模块。它可以先表现为一个清楚的检查阶段:
decision = inspect_result(result, task)然后:
if decision == "accept": ... elif decision == "repair": ... elif decision == "retry": ... elif decision == "stop": ...关键是把“发现问题”和“采取动作”区分开。
六、本讲实践
先不修改现有代码,使用下面四个场景判断 Reflection 应该采取什么动作。每个场景只选择一种:
接受 / 修正 / 重试 / 停止场景 A:工具成功,证据完整
{ "ok": True, "data": { "order_id": "A102", "status": "已发货", "tracking_id": "T900" } }物流工具也返回了:
{ "ok": True, "data": { "tracking_id": "T900", "delivery_status": "运输中", "location": "杭州转运中心" } }候选回答:
订单A102已发货,物流正在运输中,当前位置是杭州转运中心。场景 B:工具成功,但候选答案与证据冲突
工具结果:
{ "ok": True, "data": { "order_id": "A102", "status": "已发货" } }候选回答:
订单A102待发货。场景 C:参数格式错误,但错误可以明确修正
工具返回:
{ "ok": False, "error": "tracking_id必须是字符串" }已知原始参数是:
{"tracking_id": 900}并且工具要求字符串。
场景 D:缺少事实,继续修改也无法补齐
工具结果:
{ "ok": True, "data": { "order_id": "A102", "status": "已发货", "tracking_id": None } }任务要求继续查询物流。
每个场景按这个格式回答:
动作: 检查依据: 修正或停止后的结果:最后再说明:
为什么场景 A 不需要继续 Reflection,而场景 D 不能靠多反思几次解决?
这组练习完成后,再把 Reflection 写成代码,避免一开始把“检查结果”和“修改程序”混在一起。
场景A:接受 场景B:修正 场景C:修正 场景D:停止 场景A:工具成功且证据完整 场景D:缺少事实,继续修正也无法不齐 没有任何意义
AI修改:A、B、D 判断正确。C 的方向对了一半:这里不是只修正最终回答,而是修正
tracking_id的输入后,再有限重试一次。它属于“修正参数 → 重试工具”,与 B 的“修正答案后接受”不同。我会记录这一区分,下一步把四种 Reflection 动作落到代码分支。A、B、D 判断正确;C 的完整分类是:
修正 tracking_id 参数 ↓ 有限重试一次工具它不是直接修正最终回答。区别如下:
场景 动作 A 接受已有结果 B 修正回答,使其符合证据 C 修正输入参数后重试工具 D 停止,缺少必要事实