Chapter 04 · 失败模式

Reflexion 的失败模式与适用边界

03 章把 Algorithm 1 跑通了:Actor 写代码、Evaluator 跑测试、Self-Reflection 产出第一人称教训、episodic memory 滑动窗口存储。一个能跑通的循环掩盖了一个事实——同一套机制在错误的任务、错误的 Evaluator、错误的初始正确率上会净降准确率。本章拆解六个被论文与复现实验量化过的失败模式,每个回指 02 章的机制根因。

本章你将建立的 schema

  • Reflexion 的收益不是无条件的:Evaluator 信号质量 + 初始正确率 + 任务多样性需求,三者共同决定它是净赚还是净亏。
  • 失败模式按受损组件归类:Evaluator 失效(信号不可靠)、Actor 失效(反思幻觉、弱模型)、Memory 失效(上下文膨胀)、整体经济性(成本 ≈ self-consistency)。
  • 最危险的失败是隐性的:在没有 oracle、初始正确率已高的场景,反思会把对的答案改错——而表面流程一切正常。
  • "什么时候不用 Reflexion"是一个可执行的判据清单,不是态度问题。

03 章的 worked example 之所以收敛,是因为三个前提同时成立:Evaluator(单元测试)给的是可信信号、初始代码确实错了、任务有唯一正确解。把任意一个前提抽掉,同一套循环就从"自我改进"滑向"自我损害"。本章按受损组件组织六个失败模式,每个都附论文或复现实验的具体数字,并把根因链回 02 章的机制小节。先看一张全景:这些失败模式分别打在循环的哪个组件上、严重度如何。

Reflexion 失败模式 2D 分类图 严重度 致命 中等 较轻 受损组件 Evaluator Actor Memory / 经济性 无 oracle 崩溃 §5.1 evaluator-dependency 反思幻觉翻转 §5.2 hallucination 高置信反改错 §5.6 task-fit 平台期 / 弱模型 §5.5 convergence 成本≈采样 §5.4 cost 上下文膨胀 §5.3 memory-bloat
图 4-1六个失败模式按「受损组件 × 严重度」分布。注意:左上两格(红框)——Evaluator 无 oracle、Actor 反思幻觉——是致命的,因为它们都让错误答案被当成正确答案提交,且发生时表面流程毫无异常。

致命的失败模式集中在左上:它们破坏的是循环的正确性判据本身,而不只是效率。下面逐个拆解,顺序按 02 章的机制依赖从根上排起——先是 Evaluator 失效,因为它是整个循环的地基。

5.1失败模式一:没有 oracle,改进就消失

症状:论文里漂亮的提升数字,换到生产环境就蒸发,甚至变成净下降。

症状

团队照搬论文 setup 接一个 reasoning 任务(数学题、常识问答),线下用带标准答案的数据集验证时收益明显;一旦上线、失去标准答案,Reflexion 不再带来提升,内在自我纠错的准确率反而低于不纠错的单遍生成。

Huang et al.(《LLMs Cannot Self-Correct Reasoning Yet》,ICLR 2024,arXiv 2310.01798)复现了 Reflexion 式 setup,得出一个尖锐结论:headline 收益依赖一个 oracle label 来决定何时停止反思。把 oracle 拿掉、让模型自己判断对错并据此反思,内在自我纠错净降准确率:

Huang et al. ICLR 2024:去掉 oracle 后的内在自我纠错(单位:准确率 %)
任务 / 模型初始(单遍)自我纠错后净变化
GSM8K75.974.7−1.2
Llama-2 (GSM8K)62.036.5−25.5
CommonSenseQA75.841.8−34.0

根因

这直接命中 02 章 #reward-source 的机制:Reflexion 的"学习"由 Evaluator 的 reward 驱动,而反思的方向由"判定为错"这个信号触发。当 Evaluator 就是被反思的同一个 LLM、又没有外部 ground-truth 锚定时,它的"对错判断"本身就带着和 Actor 一样的偏差。模型既判不准对错,反思自然指向错误的方向——它在用一个不可靠的信号去"纠正"一个本来正确的答案。论文设计里 reasoning 任务用的是 exact-match 对标准答案(HotPotQA +20%),决策任务用手写启发式或环境信号(AlfWorld 130/134)——这些都是外部、可信的判据,不是模型自评。

修复:错误写法 / 正确写法

evaluator_self_judge.pypython
# ❌ 错误:让同一个 LLM 既生成答案又判断对错,没有外部锚
class SelfJudgeEvaluator:
    def __init__(self, llm):
        self.llm = llm
    def evaluate(self, answer, task):
        verdict = self.llm.complete(
            f"问题:{task.spec}\n答案:{answer}\n这个答案对吗?只回答 对/错。"
        )
        passed = verdict.strip().startswith("对")
        return passed, ""        # 没有 ground-truth,verdict 和答案同源偏差
evaluator_grounded.pypython
# ✅ 正确:Evaluator 锚定到外部可验证信号;无信号时拒绝启用反思循环
class GroundedEvaluator:
    def __init__(self, oracle=None, tests=None):
        if oracle is None and tests is None:
            raise ValueError(
                "Reflexion 需要可信的对错判据(ground-truth / 单元测试 / 环境 reward);"
                "缺失时应退化为单遍生成,而非自评反思。"
            )
        self.oracle, self.tests = oracle, tests
    def evaluate(self, answer, task):
        if self.tests is not None:          # 编程:跑测试(外部、确定)
            return run_tests(answer, self.tests)
        return answer == self.oracle[task.id], ""   # 推理:对标准答案

如何避免再次触发

把"是否存在外部可验证信号"列为接入 Reflexion 的准入门槛,而不是事后检查项:有单元测试、编译器、环境 reward、或线下标准答案,才上反思循环;只有模型自评时,默认退化为单遍生成或 self-consistency。线上无 oracle 的场景,可用线下带标签的数据评估反思是否真的提升,再决定是否部署——绝不能用"线上看起来在自我改进"作为依据。

5.2失败模式二:反思幻觉——把对的答案改错

症状:反思这一步本身产出了错误的"教训",把一个已经正确的答案翻转成错误答案。

症状

Actor 第一次就答对了,但循环没有可靠信号确认"已通过",于是照常进入反思;Self-Reflection 凭空捏造一条"问题",Actor 据此"修正",结果把对的改成错的。这种 correct→incorrect 翻转在 Huang 的实验里被量化:

correct→incorrect 翻转率与净准确率(无 oracle 的内在自我纠错)
设置correct→incorrect 翻转净准确率影响
GSM8K · GPT-3.58.8%翻转多于纠正 → 净降
GSM8K · Llama-231%62.0 → 36.5(净降 25.5)

Llama-2 上 31% 的正确答案被反思改错,是 62.0→36.5 这个崩塌的直接来源。翻转率越高、模型越弱,这个失败模式越致命。

根因

回指 02 章 #why-language:Reflexion 起作用的前提是 Self-Reflection 能做出有效的 credit assignment——准确定位"哪一步错了、该怎么改"。但这个能力依赖一个真实存在的错误。当答案其实是对的、却被要求"找出问题并改进"时,语言模型会顺从指令编造一个并不存在的缺陷(语言反思的灵活性在这里成了双刃:它能表达任何"教训",包括错误的)。这也是 02 章 #ablation 那条结论的反面——反思之所以比 blind retry 强(+8% 绝对值),是因为 credit assignment 指向了真实错误;一旦指向虚构错误,同一机制反向放大损害。

预测一下

若把反思的触发条件从"每轮都反思"改成"仅当 Evaluator 明确判失败才反思",上面 31% 的翻转率会怎样变化?为什么这恰好对应 Huang 所说的"oracle 决定何时停"?

展开答案

翻转率会大幅下降:只要 Evaluator 是可信的,正确答案根本不会进入反思分支,自然不会被改错。Huang 的"oracle 决定何时停"正是这个意思——oracle 的真正作用不是指导反思内容,而是门控反思是否发生。论文的收益里有相当一部分来自"oracle 让正确答案及时停止、不被破坏",而非反思本身的纠错力。把这个门控误当成模型自评,翻转就回来了。

修复:错误写法 / 正确写法

loop_always_reflect.pypython
# ❌ 错误:无条件每轮反思,正确答案也被拖进反思 → 可能被改错
def loop(task, actor, reflector, memory, max_trials=3):
    code = ""
    for t in range(max_trials):
        code = actor.act(task, memory.reflections)
        sr = reflector.reflect(code, "请找出可改进之处", task)  # 不管对错都反思
        memory.add(sr)
    return code
loop_gated_reflect.pypython
# ✅ 正确:反思由可信 Evaluator 门控;判通过立即停,绝不反思正确答案
def loop(task, actor, evaluator, reflector, memory, max_trials=3):
    code = ""
    for t in range(max_trials):
        code = actor.act(task, memory.reflections)
        passed, error = evaluator.evaluate(code, task)   # 外部可信判据
        if passed:
            return code                                  # 停止条件:正确即停
        sr = reflector.reflect(code, error, task)        # 仅在确认失败时反思
        memory.add(sr)
    return code

如何避免再次触发

反思永远由"已确认的失败"触发,不是由"惯例每轮反思"触发。停止条件必须是循环里的硬约束(if passed: return),而非"跑满 max_trials"。模型越弱,这个门控越要严——下一个失败模式会量化弱模型的处境。

5.3失败模式三:记忆膨胀——上下文溢出窗口

症状:trial 数一多,prompt 越拼越长,先是变慢变贵,最后溢出上下文窗口直接报错或截断丢信息。

症状

到 trial 3 时,prompt 已经等于「任务描述 + 3 次完整尝试 + 3 条反思文本」;长任务(长代码、长轨迹)累积几轮后超出模型上下文窗口,要么报错,要么静默截断——把最关键的早期反思或任务描述挤掉,循环开始"失忆"。

根因

回指 01 章 #episodic-memory:episodic memory 是一个 Ω=1-3 的滑动窗口,这正是论文为适配上下文长度限制而设的容量上限——它不是向量库(论文把向量检索/SQL 列为 future work)。设计上 AlfWorld 用 Ω=3、programming 用 Ω=1。忽略这个上限、把所有历史反思无限拼接,就是把一个有意为之的"有界窗口"当成"无界缓冲"用,直接撞上 02 章 #memory-injection 描述的 context 拼接边界。

修复:错误写法 / 正确写法

memory_unbounded.pypython
# ❌ 错误:无限累积反思 + 把每轮完整代码也塞进 prompt
class UnboundedMemory:
    def __init__(self):
        self.reflections = []
        self.full_trajectories = []          # 还把整段代码也存下来
    def add(self, sr, code):
        self.reflections.append(sr)          # 永不截断 → 线性膨胀
        self.full_trajectories.append(code)  # trial 越多,prompt 越长
memory_sliding.pypython
# ✅ 正确:Ω 滑动窗口(论文设计),只保留最近 capacity 条反思
from dataclasses import dataclass, field

@dataclass
class EpisodicMemory:
    capacity: int = 3                        # 论文 Ω=1-3;programming 用 1
    reflections: list[str] = field(default_factory=list)
    def add(self, sr: str) -> None:
        self.reflections.append(sr)
        self.reflections = self.reflections[-self.capacity:]  # 截断到窗口
    # 注意:长期记忆只存"反思文本",不存完整 trajectory;
    # 当前 trajectory 是短期记忆,每个 trial 重置,不进 memory。

如何避免再次触发

把 capacity 作为显式参数,按任务轨迹长度调:短轨迹任务可放宽到 3,长代码任务收紧到 1。只持久化反思文本(长期记忆),当前 trajectory 属于短期记忆、随 trial 重置、不进 memory(这条区分在 01 章已建立)。上线前用最长的预期输入压测一遍,确认拼好的 prompt 在窗口内。

5.4失败模式四:成本——相同预算下并不比采样划算

症状:上了 Reflexion,延迟和 token 成本翻几倍,但在相同采样预算下,准确率并不优于简单多采几次。

症状

Reflexion 一轮(生成 → 评估 → 反思)耗时约 10-30 秒,而单次生成约 0.8 秒。多个 trial 叠加,延迟和成本成倍上升。关键的反直觉点:在相同采样预算下,Reflexion 的多次尝试 ≈ self-consistency(多采样投票)——也就是说,把同样的 token 预算花在"采 N 次取多数"上,常常拿到相当甚至更好的结果,而 Reflexion 不一定赚回它的复杂度和延迟。

根因

回指 02 章 #why-language:Reflexion 用"推理期算力"换"可靠性"——每个 trial 都是一次完整的生成+评估+反思。当任务本身可以靠并行多采样解决(答案分布里正确答案占多数),反思那一步带来的 credit assignment 增益,抵不过它串行、多轮、长 prompt 的开销。Reflexion 的优势区是"单纯多采样救不了"的任务(初始正确率低、需要定向纠错),不是所有需要多次尝试的场景。

修复:错误写法 / 正确写法

cost_blind.pypython
# ❌ 错误:默认上 Reflexion,不和等预算的 self-consistency 比
answer = reflexion(task, max_trials=5)   # 5 轮串行,5×(生成+评估+反思)
# 没有对照:同样的预算如果改成多采样投票会怎样?
cost_baselined.pypython
# ✅ 正确:在相同采样预算下,先和 self-consistency 对照再决策
from collections import Counter

def self_consistency(task, actor, n=5):
    samples = [actor.act(task, reflections=[]) for _ in range(n)]
    return Counter(samples).most_common(1)[0][0]   # 多数投票,可并行

# 选型:仅当 Reflexion 在等预算下显著优于 self-consistency,
# 且任务确实"多采样救不了"(初始正确率低),才付它的串行+延迟代价。
def choose(task, actor, evaluator, budget=5):
    baseline = self_consistency(task, actor, n=budget)
    if not needs_directed_correction(task):   # 多采样能解 → 用更便宜的
        return baseline
    return reflexion(task, max_trials=budget)

如何避免再次触发

把 self-consistency 当作默认对照基线:任何上 Reflexion 的决策,都要回答"同样的 token 预算花在多采样投票上,结果如何"。只有当反思的定向纠错确实赢过等预算的并行采样时,串行多轮的延迟和成本才值得。

5.5失败模式五:不收敛——平台期、局部最优、弱模型

症状:trial 加了,曲线却拍平不动;或陷在同一类错误里反复反思;或换个弱模型直接毫无效果。

症状

三种相关表现:

  • 平台期:WebShop 任务上,Reflexion 在 trial 4 即触顶,且始终不超过 ReAct(论文 Fig 6)。需要大量探索/多样性的任务,反思无法靠"复盘上次"突破。
  • 局部最优:反思反复指向同一类小修补,Actor 在一个错误区域里打转,不做结构性的换路线尝试。
  • 弱模型无效:starchat-beta 上,Reflexion 得分 0.26 = baseline 0.26——完全没有提升。

根因

回指 02 章 #why-language 与 #credit-assignment:Reflexion 探索的是"时间"维度(跨 trial 复盘),而非"宽度"维度(一次尝试内的分支搜索,那是 Tree of Thoughts 的领域)。当任务的瓶颈是探索不足而非纠错不到位时,反思——再精准的 credit assignment——也只能在已走过的路径附近微调,跳不出去。而弱模型无效的根因更深:自我纠错是更强模型的涌现能力。credit assignment 要求模型有能力推断"a_i 导致后续出错、应改成 a'_i";starchat 这一级的模型根本产不出有效的反思文本,语言强化无从发生。

修复:错误写法 / 正确写法

converge_naive.pypython
# ❌ 错误:盲目加大 max_trials,期待更多轮就能突破平台期
answer = reflexion(task, model="starchat-beta", max_trials=10)
# 弱模型产不出有效反思;探索型任务再多轮也拍平 → 纯烧钱
converge_guarded.pypython
# ✅ 正确:能力门槛 + 平台期早停 + 探索型任务改用宽度搜索
def run_with_guards(task, model, max_trials=4):
    if model_capability(model) < CORRECTION_THRESHOLD:
        return single_pass(task, model)          # 弱模型:别上反思
    if task.needs_exploration:                   # 探索型:宽度而非时间
        return tree_of_thoughts(task, model)     # 换搜索范式
    prev = None
    for t in range(max_trials):                  # 反思型:监控平台期
        ans = reflexion_step(task, model)
        if ans == prev:                          # 连续两轮无变化 → 平台期
            break                                # 早停,不空烧 trial
        prev = ans
    return ans

如何避免再次触发

先判任务瓶颈:是"纠错不到位"(Reflexion 的主场)还是"探索不足"(交给宽度搜索 / 多样性采样)。再设能力门槛:模型弱到产不出有效反思,就退回单遍生成。最后给循环装平台期早停——连续若干轮 Evaluator 信号或答案不变,立即停止,别用 max_trials 空烧。Actor 内部用 ReAct/CoT 的细节,见 agent-reasoning-patterns 教程;把 Reflexion 摆进"agent 自我改进"谱系的对比,见 agent-planning 教程。

5.6失败模式六:任务不匹配——初始正确率太高反受其害

症状:在本就高置信、初始正确率高的任务上启用反思,准确率不升反降。

症状

把 Reflexion 套在初始正确率已经很高的任务上,反思频繁地"修正"本来正确的答案,净效果是降准确率。《When Hindsight is Not 20/20》(arXiv 2404.09129)给出量化的 RA(reflection)阈值:反思只在初始正确率 < 20-30% 时净获益;用在高置信答案上有害——HotpotQA 上初始 80.3 → 反思后 76.2(净降 4.1)。

这和论文里 HumanEval 的成功并不矛盾:HumanEval 的 base 是 GPT-4 的 80% pass@1,Reflexion 把它推到 91%——但那是因为编程任务里"失败"由可信的单元测试判定(假阳性仅 1.4%),错误答案被准确识别后才反思。问题出在初始正确率高 + 判据不可靠同时成立时:反思去动了不该动的答案。

根因

回指 02 章 #reward-source:Reflexion 的收益来自"把错误轨迹纠正过来"。当初始正确率已高,大多数答案本就是对的——可纠正的空间小,而误伤正确答案的风险大。收益与风险的天平在初始正确率高时整体倒向风险一侧。这与失败模式二(反思幻觉)同源——都是"对正确答案做了不必要的反思",但这里的判据是任务层面的初始正确率,是选型阶段就能预判的,不必等到运行时才发现翻转。

非对称失败

论文给的更深一层判据:Evaluator 的假阴性可容忍(误判为失败,还能继续反思修正),假阳性致命(误判为通过,提前提交错误答案不可恢复)。这正是论文唯一一次失利的根因——MBPP-Python 77.1% < GPT-4 的 80.1%,因为自生成测试有 16.3% 假阳性(HumanEval 仅 1.4%),错代码被误判通过、提前提交。是 reward 源错了,不是反思错了。

修复:错误写法 / 正确写法

fit_blanket.pypython
# ❌ 错误:对所有任务一刀切上反思,不看初始正确率
def solve(task, actor, evaluator, reflector, memory):
    return reflexion(task, actor, evaluator, reflector, memory)
    # 高置信任务也反思 → 误伤正确答案(HotpotQA 80.3 → 76.2)
fit_gated.pypython
# ✅ 正确:用初始正确率 / 置信度门控是否启用反思
RA_THRESHOLD = 0.30   # 《When Hindsight》:初始正确率 < 20-30% 才净获益

def solve(task, actor, evaluator, reflector, memory):
    first = actor.act(task, reflections=[])
    passed, error = evaluator.evaluate(first, task)   # 需可信判据(低假阳性)
    if passed:
        return first                                  # 已对:不反思,避免误伤
    if estimated_initial_accuracy(task) >= RA_THRESHOLD:
        return first        # 高置信任务:即使这条错了,反思整体期望为负 → 不上
    return reflexion(task, actor, evaluator, reflector, memory)

如何避免再次触发

选型阶段先估初始正确率:本就高置信的任务(初始正确率超过 20-30%)默认不上 Reflexion,或仅在有低假阳性判据时谨慎启用。把"初始正确率是否够低"和"Evaluator 假阳性是否够低"做成两道并列的准入门槛——这正是下一节"什么时候不用"清单的核心两条。

5.7诚实段:什么时候不要用 Reflexion

Reflexion 不是默认选项。把它当成有明确适用边界的工具,以下任一条成立时,默认不用:

Reflexion 的"不适用"判据(满足任一条即默认不上)
判据为什么不适用替代
没有可靠的 Evaluator(无单元测试 / 环境 reward / 标准答案,只能模型自评)无 oracle 时改进消失,且会净降准确率(§5.1)单遍生成 / 工具 grounding(CRITIC)
任务初始正确率已高(> 20-30%)误伤正确答案的风险大于纠错收益(§5.6,HotpotQA 80.3→76.2)单遍 / self-consistency
任务单次就能解 / 不允许重试跨 trial 学习无处发生;Reflexion 的"时间维度"探索失去意义单遍生成
用的是弱模型(自我纠错能力未涌现)产不出有效反思文本,starchat-beta 实测 0.26 = baseline(§5.5)换更强模型 / 单遍
瓶颈是探索不足而非纠错不到位反思只在已走路径附近微调,跳不出(WebShop trial4 平台期,§5.5)Tree of Thoughts / 多样性采样
相同预算下 self-consistency 已够好串行多轮 + 长 prompt 的开销抵不过并行采样(§5.4)self-consistency
现状提醒 · 截至 2026-06

"让模型无 grounding 自己 review 自己"已被 Huang et al.(ICLR 2024)证伪——不要学。值得注意的转向:SCoRe(DeepMind,2024-09,arXiv 2409.12917)用多轮 RL 在自生成数据上训练,得到真正的内在自我纠错(+15.6% MATH),把 Huang 的结论改写为"未经训练的 prompting 不行",而非"原理上不可能"。当下共识(CMU,2026-02):外部反思循环与 o1/R1 这类推理模型互补而非替代,且外部反思循环存在 3-7 轮的能力天花板——这正是 §5.5 平台期的更一般形式。

自测

  1. 同一套 Reflexion 循环,接 HumanEval(单元测试当 Evaluator)收益显著,接"无标准答案的开放问答 + 模型自评"却净降准确率。用 §5.1 的机制解释这个落差,并指出根因链到 02 章哪个 anchor。
  2. Llama-2 在 GSM8K 上从 62.0 掉到 36.5。这个 25.5 的净降里,"correct→incorrect 翻转"扮演什么角色?把反思触发条件改成什么,能直接堵住这个失败模式?
  3. 一位工程师为提升收敛,把 max_trials 从 3 调到 10,用的是 starchat-beta。预测结果,并说明为什么这同时踩中"弱模型"和"成本"两个失败模式。
  4. 《When Hindsight is Not 20/20》说反思只在初始正确率 < 20-30% 才净获益。HotpotQA 上 80.3→76.2 是这条的例证。它和论文 HumanEval 80%→91% 的成功为什么不矛盾?(提示:判据的假阳性率。)
展开参考答案

1. HumanEval 的 Evaluator 是外部、确定的单元测试(假阳性仅 1.4%),"失败"判定可信,反思指向真实错误;开放问答用模型自评、无 ground-truth 锚,判定本身带着和 Actor 同源的偏差,反思被错误信号驱动,把对的改错。根因链到 02-principles.html#reward-source:Reflexion 的学习由 reward 驱动,reward 不可信则方向错。

2. 翻转是净降的主因:31% 的正确答案被反思改错,直接拖垮总分。把触发条件从"每轮都反思"改成"仅当可信 Evaluator 判失败才反思"(并在判通过时立即 return),正确答案不进反思分支,翻转被堵住——这就是 Huang 说的"oracle 决定何时停"。

3. 结果:几乎无提升且成本激增。starchat-beta 自我纠错能力未涌现(实测 0.26 = baseline),产不出有效反思文本,加轮数也补不上;同时 10 轮串行每轮 10-30s,延迟和 token 成本线性上升,在等预算下还不如 self-consistency。踩中 §5.5(弱模型)+ §5.4(成本)。

4. 不矛盾。关键在判据的假阳性率:HumanEval 单元测试假阳性仅 1.4%,错误答案被准确识别后才反思,初始正确率虽不低但纠错指向真实错误;HotpotQA 案例是"初始正确率高 + 判据不可靠"叠加,反思动了不该动的正确答案。论文里 MBPP 失利(77.1 < 80.1)同样源于 16.3% 假阳性。是 reward 源的质量决定成败,不是任务类型。

刚好够不着 · 挑战

给循环加一道"是否值得反思"的护栏

基于 03 章的 reflexion.py 骨架,设计一个 should_reflect(task, first_answer, evaluator) -> bool 护栏,要同时挡住本章的失败模式二(反思幻觉)和六(高置信反改错)。约束:① 判通过立即停;② 用初始正确率阈值(RA < 20-30%)门控;③ 给 Evaluator 留一个"假阳性率"参数,假阳性高于阈值时拒绝据其提前提交。写出函数 + 三行注释说明每个分支堵的是哪个失败模式。把它接进主循环后,§5.2 的 31% 翻转和 §5.6 的 80.3→76.2 各被哪一行挡下?(参考实现留到 05 章 capstone。)

Further reading