第 13 章 反思 Reflection
本章要解决的问题
模型一次生成的结果总有小错------让它自己审视自己、或让另一个模型来挑错,值得吗?成本怎么控?
章节大纲
- 13.1 自我审视与修正循环
- 13.2 双 Agent 互评机制
- 13.3 评测型反思的工程实现
- 13.4 主流框架对照与变体
- 🛠 解决方案:反思循环的成本控制与停止条件
13.1 模式原理:自我审视与修正循环
13.1.1 一句话定义
反思(Reflection)是让模型检查自己(或他人)的输出,发现错误并修正的过程。 它把一次性的"生成"变成"生成 → 检查 → 修正"的循环,用额外的推理换取更高的输出质量。
13.1.2 为什么有效:LLM 的"自我纠错"能力
一个反直觉但被广泛验证的现象:同一个模型,让它"先答再查",通常比"直接答"更准。 原因在于:
- 检查比生成简单:发现"这份代码缺了边界处理"比"直接写出无 bug 的代码"更容易。
- 注意力再分配:第二次调用时,模型把注意力集中到"找错"上,能看到第一次"顺着写"时忽略的细节。
- 有明确目标:反思提示词给出规范清单后,模型像一个拿着检查表的质检员,比"一次性完美产出"的隐性要求容易达成。
但这里必须泼一盆冷水:反思不是万能的,它有一个著名边界------"自信的幻觉"。 如果模型对某个错误事实非常自信(比如把"爱因斯坦出生年"记成 1879 年),让它自己检查,它大概率会说"没问题"------因为错误不在它的知识盲区之外,它"知道"自己是对的。反思能修的错误是"疏忽型错误"(漏了、跳步、格式错),对"幻觉型错误"(知识性错误)效果有限------但反思 + RAG + 工具校验可以间接修正部分知识型错误,因为工具和检索能提供新信息让模型纠正自身。这是设计反思系统时最重要的认知。
13.1.3 反思循环的结构
css
[生成] 模型产出初稿
↓
[反思] 按规范清单检查 → 列出问题
↓
[修正] 基于问题清单重写
↓
[再检] 是否通过?→ 通过则结束;不通过且未超次数 → 回到[反思]
↓
[终止] 达到最大轮数 → 输出当前最优稿

图 1:反思循环
三个组件缺一不可:反思提示词(规范清单)、修正提示词(带问题清单重写)、停止条件(最大轮数 + 通过标准)。停止条件是安全阀,没有它反思循环会变成无底洞(见 13.3.3)。
13.2 双 Agent 互评机制
13.2.1 自评 vs 互评:谁更可靠?
| 方式 | 做法 | 优点 | 缺点 |
|---|---|---|---|
| 自评(同一模型) | 生成后让同一模型检查自己 | 零额外模型成本、上下文连贯 | 与生成共享盲区,"自信的幻觉"风险高 |
| 互评(另一模型/实例) | 生成模型 A、检查模型 B | 独立视角、盲区不同、更客观 | 多一次调用成本、B 也可能误判 |
| 异配置互评 | 同模型但低温度/不同 seed | 降低成本版互评 | 仍共享大部分知识盲区 |

图 2:自评 vs 互评
经验结论:自评修格式错、漏步等"低级疏忽"够用;高风险任务(代码、财务、法律文本)用互评;知识性错误两种都难修,要靠 RAG 或外部校验(见 13.3.2)。
13.2.2 双 Agent 互评的完整示例:代码审查
python
import json
from openai import OpenAI
client = OpenAI(base_url="https://api.deepseek.com", api_key="<你的Key>")
CODE = "def calc(a, b): return a / b"
# Agent A:代码生成(或初稿)
def generate_impl(spec: str) -> str:
resp = client.chat.completions.create(
model="deepseek-coder",
messages=[{"role": "user", "content":
f"根据需求写 Python 实现:{spec}"}],
temperature=0.2,
)
return resp.choices[0].message.content
# Agent B:独立审查(互评)
def review_code(code: str) -> dict:
resp = client.chat.completions.create(
model="deepseek-coder",
messages=[{"role": "user", "content": f"""你是资深代码审查员。
审查以下代码,只输出 JSON:
{{"bugs": ["问题列表"], "severity": "low|medium|high",
"suggestions": ["修改建议"]}}
代码:
{code}
"""}],
temperature=0.1, # 审查任务低温度(第24章表二)
response_format={"type": "json_object"},
)
try:
return json.loads(resp.choices[0].message.content)
except json.JSONDecodeError:
return {"bugs": [], "note": "审查输出解析失败,跳过本轮"}
# 修正循环
def reflect_loop(spec: str, max_rounds: int = 3):
code = generate_impl(spec)
for r in range(max_rounds):
review = review_code(code)
if not review["bugs"]:
print(f"第{r+1}轮通过")
return code
print(f"第{r+1}轮发现 {len(review['bugs'])} 个问题,修正中...")
resp = client.chat.completions.create(
model="deepseek-coder",
messages=[{"role": "user", "content":
f"根据审查意见修正代码。审查意见:{json.dumps(review, ensure_ascii=False)}\n"
f"原代码:\n{code}"}],
temperature=0.2,
)
code = resp.choices[0].message.content
print(f"达到最大轮数 {max_rounds},返回当前版本")
return code
fixed = reflect_loop("实现除法函数,需处理除零")
这个示例展示了反思循环的完整骨架:生成 → 互评(结构化问题清单)→ 带意见修正 → 循环 → 最大轮数终止。
13.2.3 让互评更有效的三个技巧
- 审查清单显式化:别写"请审查代码",写"检查:除零/边界/类型错误/资源泄漏/可读性"。清单越具体,审查越有效(呼应第 17 章评估自检的"可判定硬规则")。
- 问题要可执行:审查输出别只说"有问题",要求给出"问题 + 位置 + 修改建议"三段式,修正模型才知道改哪。
- 互评用低温度:审查是判定任务,temperature 0.1 左右,别让审查员"发挥"。
13.3 评测型反思的工程实现
13.3.1 三种反思触发方式的对比
| 触发方式 | 机制 | 成本 | 适用 |
|---|---|---|---|
| 固定反思 | 每次生成都反思 | 高(每轮多 1 次调用) | 质量敏感的短输出 |
| 评测触发 | 先跑规则/评测,不通过才反思 | 中 | 有可判定的硬规则时 |
| 采样触发 | 抽样反思(10% 流量) | 低 | 大规模、成本敏感 |
评测触发是工程上最推荐的:先用零成本的规则校验(JSON 格式、必填字段、正则)筛一遍,规则不过才进反思循环------用第 12 章的话说,这是"用规则层拦掉 80% 的错误,模型只处理长尾"。
python
def evaluate_and_maybe_reflect(output, rules):
# 第一层:零成本规则校验
violations = [r for r in rules if not r(output)]
if not violations:
return output, "pass" # 规则通过,不反思
# 第二层:规则不过,才进模型反思
review = model_review(output, violations)
return model_rewrite(output, review), "reflected"
13.3.2 反思的边界:什么是反思修不了的
再强调一次 13.1.2 的结论,并给出对策:

图 3:反思能力边界
| 错误类型 | 例子 | 反思能修吗 | 正确对策 |
|---|---|---|---|
| 疏忽型 | 漏了异常处理、JSON 少个括号 | ✅ 能 | 反思循环 |
| 边界型 | 没考虑输入为空 | ✅ 大部分能 | 反思 + 清单 |
| 知识型 | 事实错误、数据算错 | ❌ 基本不能 | RAG 检索 / 外部工具校验(第 8、14 章) |
| 偏好型 | 语气不合适、风格不符 | ⚠️ 有限 | 加用户反馈(第 18 章) |
设计原则:反思只用于"它本来就能答对、只是粗心"的任务;"它根本不知道"的任务,别指望反思,去接工具/检索。
13.3.3 停止条件:反思循环的生死线
反思循环必须回答"什么时候停",否则就是无限烧钱。三个停止条件组合使用:

图 4:反思三停止条件
- 通过即停:评测/审查通过就返回(最高优先级)。
- 最大轮数:默认 2~3 轮,超过强制返回当前最优稿(保证延迟有上界)。
- 改进检测:本轮修正后,若评测分数不升反降,回退到上一版(防"越修越烂")。
python
# score() 可用以下方式实现:
# - 规则通过率:JSON Schema 校验 + 必填字段检查 + 正则匹配
# - 业务校验分数:特定业务规则的通过比例
# - LLM-as-Judge 分数:让模型按评分标准打分(第 17 章)
best, best_score = initial, score(initial)
for r in range(max_rounds):
revised = revise(best, review(best))
s = score(revised)
if s <= best_score and r > 0:
return best # 改进停止:回退
if s >= threshold:
return revised # 通过停止
best, best_score = revised, s
return best # 轮数停止
13.4 主流框架对照与变体
13.4.1 框架对照
| 实现方式 | 特点 | 适用 |
|---|---|---|
| 自研循环(本章主线) | 生成/审查/修正三函数 + 停止条件,完全可控 | 教学、中小规模 |
| LangGraph 反思边 | 条件边实现"不过→回生成节点"的循环 | 复杂图内嵌反思 |
| ReAct / Reflexion 框架 | 把反思作为 Agent 循环的一环(行动后反思) | Agent 场景(第 6 章) |
| LLM-as-Judge 评测工具 | 用模型当评测器(Prometheus 等开源 judge) | 第 20 章评测体系 |
13.4.2 变体一:Reflexion(带记忆的反思)
Reflexion 是反思模式的重要演进:不仅修当前输出,还把"本次教训"存入记忆,下次生成时直接避开同样的坑。
python
# 把教训写入历史,供下次生成参考
lessons.append(f"上次在场景 X 犯了错误 Y,因为 Z")
它和第 9 章记忆系统、第 18 章学习与适应直接衔接------反思是"单次自我修正",带记忆的反思才是"跨次持续进化"。
13.4.3 变体二:树状反思(Tree of Thoughts)
不满足于"一条路反思",而是生成多个候选分支、对每个分支反思、剪掉差分支再展开(呼应第 12 章方案并行 + 本反思)。ToT 在数学、规划类任务上效果显著,但成本很高,是"重武器"。
13.4.4 变体三:外部校验式反思
让反思不是"模型看模型",而是"模型跑工具验证":生成 SQL 后真的执行一次看报错,生成代码后跑单测,生成数据后对账。工具是最诚实的审查员(呼应第 14 章工具增强)------能跑通就是最好的反思结果。
🛠 解决方案:反思循环的成本控制与停止条件
常见问题
- "反思了还是错":大概率是知识型错误,反思修不了。对策:换 RAG/工具校验(13.3.2),别在反思上死磕。
- "成本翻了三倍":每轮反思都多 1~2 次调用。对策:改用评测触发(13.3.1),规则先筛;只对高风险路径开反思。
- "越修越烂":反思意见本身是错的,或修正模型被带偏。对策:加改进检测(分数不升回退),反思次数设硬上限。
- "自评形同虚设":自评和生成共享盲区。对策:高风险任务换互评(另一模型/实例),审查清单显式化。
- "反思循环不结束":没有停止条件。对策:三条件组合(通过即停/最大轮数/改进检测),缺一不可。
解决方案速查表
| 现象 | 根因 | 解决方案 |
|---|---|---|
| 反思无效 | 知识型错误 | 接 RAG/工具校验 |
| 成本翻倍 | 无差别反思 | 评测触发 + 抽样 |
| 越修越烂 | 无改进检测 | 分数回退 + 轮数上限 |
| 自评盲区 | 同模型共享盲区 | 互评 + 显式清单 |
| 循环不停 | 缺停止条件 | 三条件组合 |
实战提示
- 先想清楚"反思能修什么"再动手:疏忽型用反思,知识型用工具,别用反思解决检索问题。
- 审查输出务必结构化:问题清单三段式(问题+位置+建议),修正模型才改得准。
- 预算封顶:反思的 token 消耗要单独计量、单独设上限(呼应第 24 章 G5),防止"质量优化"变成"成本事故"。
- 反思是兜底手段:优先级通常是"规则校验 → 工具校验 → 反思",反思是最后一道闸,不应作为首选。