把失败经验写成文字存入上下文,不需要更新参数也能改进后续尝试

背景

传统强化学习训练 Agent 需要大量 episode、精心设计的奖励函数和梯度反向传播,对已经训练好的大型 LLM,在推理时进行参数更新并不现实。

Reflexion(Shinn et al., 2023)提出了一种不更新参数的替代方式:让 Agent 在每次失败后用自然语言写下反思,把反思存入记忆,在下一轮试错时读取。


核心机制

系统结构

Reflexion 由三个模块组成:

┌─────────────────────────────────────────────────────────┐
│                     Reflexion                           │
│                                                         │
│  Actor          Evaluator        Self-Reflection        │
│  (LLM)    →    (判断结果)    →   (LLM 写反思)           │
│    ↑                                    │               │
│    └──────── 记忆(反思文本)────────────┘               │
│                                                         │
└─────────────────────────────────────────────────────────┘

Actor:执行动作的 LLM,生成代码、做决策或写答案。

Evaluator:判断结果好坏,可以是代码执行结果(测试通过或失败)、外部评判模型,或环境反馈。

Self-Reflection:当 Evaluator 判定失败后,LLM 生成一段自然语言反思,说明哪里错了、为什么错、下次应该怎么做。

记忆的形式

反思存入一个文本列表,在下一次 episode 开始时追加到 prompt 前面:

[过往反思]
- 第1次:我用了二分查找,但没有处理数组为空的情况,导致 IndexError
- 第2次:加了空数组检查,但忘记处理目标值不在数组中的情况,返回了错误的 -1
- 第3次需要注意:确保同时处理空数组和目标值缺失两种边界情况

[当前任务]
实现一个二分查找函数,找不到时返回 -1...

这个机制本质上是把跨 episode 的学习信号打包成自然语言,放回上下文,用 prompt 的方式模拟记忆更新。


在代码生成上的效果

Reflexion 在 HumanEval 上的实验结果:

方法pass@1
GPT-4(直接生成)67.0%
GPT-4 + 执行反馈重试73.5%
GPT-4 + Reflexion91.0%

从 73.5% 到 91.0% 的差距说明:仅仅重试还不够,需要理解失败原因。反思的作用是把"失败"转化为"因为 X 所以失败、下次要做 Y"。

在决策任务(AlfWorld 文字游戏)上:

方法成功率
ReAct(无反思)45%
ReAct + Reflexion97%

与其他迭代改进方法的对比

方法如何利用失败信号跨 episode 记忆是否更新参数
直接重试
Self-Debugging(第06篇)读错误信息并修复无(单轮)
CodeRL(第03篇)测试结果作为 RL 奖励通过参数
Reflexion自然语言反思有(文本记忆)

Reflexion 填补的是"不更新参数但需要跨轮次记忆"这个位置:比 Self-Debugging 多了记忆机制,比 CodeRL 少了参数更新的成本。


局限性

记忆长度受限:反思文本随尝试次数增加,最终会超出上下文窗口。论文通常限制记忆为最近 1-3 次,但更早的经验会被丢弃。

反思质量依赖模型能力:如果 LLM 无法准确诊断失败原因,生成的反思是错误的,下一轮会被错误信息误导。模型能力越弱,这个问题越突出。

需要明确的评判标准:Evaluator 需要给出清晰的好坏判断。对于开放域生成任务(写文章、翻译),很难给出精确的对比信号,Reflexion 在这类任务上效果有限。

无法弥补知识缺口:如果失败的根本原因是模型不知道某个知识点,反思也帮不了忙。这时候需要的是 RAG 或工具调用来获取外部知识。


工程实现

一个简化的 Reflexion 循环:

def reflexion_loop(task, max_attempts=5):
    memory = []

    for attempt in range(max_attempts):
        context = "\n".join([
            "[过往反思]",
            *[f"- 第{i+1}次:{m}" for i, m in enumerate(memory)],
            "\n[当前任务]",
            task
        ]) if memory else task

        code = llm.generate(f"请解决以下任务:\n{context}")

        result = executor.run(code)
        if result.passed:
            return code

        reflection = llm.generate(f"""
        任务:{task}
        你的代码:{code}
        执行结果:{result.error or result.output}

        请用 1-2 句话总结:你犯了什么错误,以及下次应该怎么改进。
        """)

        memory.append(reflection)

    return None

Self-Reflection 这步要求模型主动诊断失败原因,而不只是描述失败现象。"代码报了 IndexError"是现象描述;"我没有检查列表为空的情况"才是有用的反思。


后续影响

Reflexion 之后,多篇工作在此基础上延伸:

  • LATS(Language Agent Tree Search):把反思机制嵌入树搜索,每条路径失败后生成反思
  • ExpeL(Experience Learning):把成功案例也存入记忆,不只学失败经验
  • Agent Hospital:把类似机制用于医疗诊断 Agent,让模型从历史案例中积累经验

Reflexion 说明了一件事:在不更新权重的情况下,用语言描述的经验也可以对后续尝试产生影响。这对实际部署中的 Agent 系统有一定参考价值,因为参数更新的成本和门槛往往比调整 prompt 高得多。

版权声明: 如无特别声明,本文版权归 sshipanoo 所有,转载请注明本文链接。

(采用 CC BY-NC-SA 4.0 许可协议进行授权)

本文标题:08. Reflexion:用语言反思替代梯度更新

本文链接:https://www.sshipanoo.com/blog/ai/code-agent-harness/08-Reflexion/

本文最后一次更新为 天前,文章中的某些内容可能已过时!