当 AI 也有了“反射”:深度解析 Reflection 框架与自我纠错闭环

当 AI 也有了“反射”:深度解析 Reflection 框架与自我纠错闭环 前言做 AI Agent 的开发者大多遇到过一种很微妙的挫败感第一次生成代码变量名看着很合理运行时才发现少初始化了一个字段第一次回答问题逻辑很顺但关键事实没有校验第一次规划任务步骤很完整却漏掉了用户真正要求交付的文件第一次总结会议句子很流畅但把“待确认”写成了“已完成”。这不是某一个模型的偶发问题而是 LLM 的基本特征它生成的是高概率文本不是天然可靠的工程结果。于是Agent 工程开始从“让模型一次答对”转向“让系统有能力发现错误、修正错误、沉淀经验”。这个方向通常被称为Reflection中文可以叫“反射”或“反思”。注意这里的 Reflection 不是 Java/Python 里的运行时反射而是指Agent 在执行任务后用自然语言反馈、外部验证结果或历史经验审查自己的行为并把教训反馈到下一轮决策中。一句话概括Reflection 不是让模型变聪明而是给 Agent 加一个“复盘-纠错-记忆”的工程闭环。本文会拆解三类主流 Reflection 范式Reflexion、Self-Refine、CRITIC并给出一个可运行的 Python 版自我纠错闭环最后讲清楚长期运行时最容易被忽略的“记忆代谢机制”。背景或问题为什么 Agent 需要反思很多 Agent 系统最开始都是这样写的用户目标 - LLM 生成答案/计划/代码 - 直接返回这个流程适合 Demo但一上生产就会暴露三个问题。1. 单次生成不可控LLM 是概率生成模型。哪怕同一个 Prompt在不同采样参数、上下文长度、工具结果顺序下也可能输出不同答案。这意味着如果系统只给模型一次机会它就没有“看见自己错误”的机会。2. 错误会在多轮任务中放大Agent 往往不是只回答一句话而是执行一串任务理解需求 - 制定计划 - 调用工具 - 读取结果 - 修改文件 - 运行测试 - 总结交付如果第一步理解错了后面每一步都会建立在错误假设上。没有反思机制时错误会像滚雪球一样越滚越大。3. 经验无法复用即使这次通过人工提示修好了问题如果系统没有把教训沉淀下来下次遇到相似场景仍然会重复犯错。比如教训这个项目的测试必须使用 pytest -q tests/api不要只运行单文件测试。 教训用户更关注性能问题代码审查时应优先检查 N1 查询和内存峰值。 教训生成 SQL 前必须确认租户隔离条件不能假设全局查询安全。这些经验如果只停留在当前对话里Agent 就只是“临时被纠正”只有写入可检索的记忆它才开始具备持续学习的雏形。核心思路Reflection 的三大主流范式Reflection 不是一个单一算法而是一组工程模式。根据“反馈从哪里来”可以分为三类口头反思、自我修正、工具校验。范式代表方法反馈来源适合场景核心优势口头反思Reflexion任务成功/失败结果 LLM 总结代码调试、交互式任务、游戏环境、工具链任务不改模型权重也能复用失败经验自我修正Self-Refine模型自己审查自己的输出文案润色、回答优化、代码初步改进实现简单适合低成本迭代工具校验CRITIC搜索、代码执行器、测试、数据库等外部工具事实校验、代码运行、数学计算、数据分析能用客观结果约束幻觉1. Reflexion把失败变成“口头经验”Reflexion 的核心思想是不更新模型参数而是把任务反馈转化为自然语言反思存入记忆下一次执行时再检索出来使用。它的典型流程是执行任务 - 得到成功/失败信号 - 生成反思文本 - 写入记忆 - 下次任务前读出相关反思比如一个代码 Agent 第一次修 Bug 失败测试报错AttributeError: NoneType object has no attribute items反思模块可以生成上次失败原因parse_config 在配置文件缺失时返回 None调用方直接遍历 items 导致崩溃。以后修改配置解析逻辑时需要先补默认空字典并增加缺失配置文件的测试。这个反思不是给人看的总结而是给 Agent 下次使用的“经验缓存”。它的价值在于失败不再只是失败而会变成下一轮推理的上下文资产。这里容易混淆一个概念Generative Agents 论文里也有 reflection 机制用于把日常观察合成为更高层次的记忆Reflexion 则更强调任务执行后的语言反馈和试错学习。两者都属于“反思式记忆”的大方向但工程目标不完全一样。2. Self-Refine让模型先当作者再当审稿人Self-Refine 的思路更直接初稿 - 自我反馈 - 修改 - 再反馈 - 再修改它通常不依赖外部工具而是让模型用不同角色审查自己的输出。例如生成一段接口文档第 1 轮生成接口说明 第 2 轮检查是否缺少参数、错误码、示例 第 3 轮根据反馈补齐文档或者生成代码第 1 轮写出函数 第 2 轮检查边界条件、异常处理、复杂度 第 3 轮重写更健壮的版本Self-Refine 的优点是实现成本低不需要额外环境缺点也很明显如果模型自己不知道哪里错它可能会一本正经地反复润色错误答案。所以 Self-Refine 更适合“质量提升”不适合单独承担高风险事实校验。3. CRITIC用工具把反思落到客观证据上CRITIC 的关键差异是引入外部工具。与其让模型空想“我哪里可能错了”不如让它去查代码是否能运行交给解释器或测试框架数学是否算对交给计算器API 是否存在查官方文档SQL 是否正确在只读环境里执行 explain事实是否可靠用搜索或知识库检索验证。它的典型流程是生成初稿 - 调用工具校验 - 工具返回证据 - 模型根据证据修正这也是生产环境里最值得优先落地的一类 Reflection凡是能被工具验证的就不要只靠模型自信。实现步骤构建“规划-执行-验证-反思-记忆”闭环一个可落地的 Reflection Agent通常不是在最后补一句“请你反思一下”而是把反思拆进完整执行链路。例如 MUSE 这类经验驱动的自进化 Agent就把流程抽象成Plan-Execute-Reflect-Memorize规划执行 Agent 负责完成子任务独立的 Reflect Agent 负责检查执行质量再把成功路径或失败原因写入记忆模块。这个思路对业务系统很有启发反思模块不应该只是“润色回答”而应该像一个独立验收员拿着用户目标、执行轨迹和工具结果逐项核对。推荐拆成五个模块模块职责输入输出Planner拆解任务制定步骤用户目标、历史反思待执行计划Executor执行计划生成结果当前计划、上下文代码、答案、文件、工具调用Verifier验证结果是否可靠执行结果、用户目标、外部工具通过/失败、错误证据Reflector复盘失败原因和改进策略错误证据、执行轨迹反思文本Memory存取可复用经验反思文本、当前任务可检索经验三类验证别只检查“答案像不像”生产级 Reflection 最容易犯的错误是只让模型评价“回答质量”。真正有用的验证至少应该有三类。1. 真实性验证检查输出是否符合事实或工具结果。例如代码是否通过测试命令是否真的执行成功引用的 API 参数是否存在数字计算是否和程序结果一致。2. 交付物验证检查是否完成了用户真正要求的目标。例如用户说“生成一篇 Markdown 文章并保存到 drafts 目录”那只在聊天里输出文章是不合格的必须确认文件真的存在结构完整路径正确。3. 数据保真性验证检查多轮传递中关键数据有没有丢失、变形或被模型脑补。例如用户指定 Python 3.11最终代码却写了 Python 3.8 的语法原始金额是 1299.00摘要里变成了 129错误日志里的TimeoutError被总结成了ConnectionError。很多 Agent 事故不是因为模型完全不会而是因为中间某一轮把关键事实“轻轻改了一下”。代码示例一个可运行的 Reflection 闭环下面用纯 Python 写一个最小可运行示例。为了方便复现不依赖真实大模型 API而是用MockLLM模拟“第一次犯错、收到反思后修正”的过程。这个例子模拟一个代码 Agent用户要求实现safe_divide(a, b)当b 0时返回None。第一轮模型忘了处理除零验证器通过测试发现错误反思器写入经验第二轮再生成时通过测试。# reflection_loop_demo.pyfromdataclassesimportdataclass,fieldfromtypingimportCallabledataclassclassReflection:task_hint:strlesson:strscore:float1.0dataclassclassReflectionMemory:items:list[Reflection]field(default_factorylist)defsearch(self,query:str,limit:int3)-list[Reflection]:演示版检索关键词命中即可。生产环境可替换为 embedding 向量库。scored[]foriteminself.items:hit0forwordinquery.lower().split():ifwordinitem.task_hint.lower()orwordinitem.lesson.lower():hit1ifhit:scored.append((hit*item.score,item))scored.sort(keylambdax:x[0],reverseTrue)return[itemfor_,iteminscored[:limit]]defadd(self,task_hint:str,lesson:str)-None:self.items.append(Reflection(task_hinttask_hint,lessonlesson))classMockLLM:模拟 LLM没有反思时犯错看到除零相关反思后修正。defgenerate_code(self,task:str,lessons:list[Reflection])-str:lesson_text\n.join(x.lessonforxinlessons)if除零inlesson_textorzeroinlesson_text.lower():return def safe_divide(a, b): if b 0: return None return a / b .strip()return def safe_divide(a, b): return a / b .strip()defreflect(self,task:str,code:str,error:str)-str:return(本次失败原因实现 safe_divide 时没有处理 b 0 的边界条件导致 ZeroDivisionError。以后遇到除法函数时必须先检查除零场景并补充 b 0 的测试。)defverify_code(code:str)-tuple[bool,str]:用真实执行结果做校验而不是只让模型评价自己。namespace:dict[str,Callable]{}try:exec(code,namespace)fnnamespace[safe_divide]assertfn(10,2)5assertfn(10,0)isNonereturnTrue,所有测试通过exceptExceptionasexc:returnFalse,f{type(exc).__name__}:{exc}defrun_reflection_agent(task:str,max_rounds:int3)-str:llmMockLLM()memoryReflectionMemory()forround_noinrange(1,max_rounds1):lessonsmemory.search(task)codellm.generate_code(task,lessons)ok,evidenceverify_code(code)print(f\n[第{round_no}轮] 验证结果{evidence})print(code)ifok:returncode lessonllm.reflect(task,code,evidence)memory.add(task_hinttask,lessonlesson)print(f[反思写入]{lesson})raiseRuntimeError(达到最大反思轮次后仍未通过验证)if__name____main__:final_coderun_reflection_agent(实现 safe_divide(a, b)正常返回 a / bb 为 0 时返回 None)print(\n 最终代码 )print(final_code)运行输出类似[第 1 轮] 验证结果ZeroDivisionError: division by zero def safe_divide(a, b): return a / b [反思写入] 本次失败原因实现 safe_divide 时没有处理 b 0 的边界条件... [第 2 轮] 验证结果所有测试通过 def safe_divide(a, b): if b 0: return None return a / b 最终代码 def safe_divide(a, b): if b 0: return None return a / b这个 Demo 虽然很小但已经包含 Reflection 的关键骨架MockLLM.generate_code对应执行器verify_code对应工具校验MockLLM.reflect对应反思器ReflectionMemory对应可检索经验第二轮生成会使用第一轮失败教训。真实工程中只需要把MockLLM换成模型调用把verify_code扩展成测试、搜索、SQL explain、Schema 校验等工具就能搭出一个基础 Reflection Agent。常见问题与避坑1. Reflection 不是无限重试很多人一听“反思”就写出这样的循环while not success: ask_llm_to_reflect() retry()这是危险的。生产环境必须设置最大反思轮次例如 2-3 轮最大 Token 预算最大工具调用次数失败后的降级策略需要人工确认的边界。否则 Agent 可能在一个不可修复的问题上烧掉大量成本。2. 不是所有任务都值得开启反思Reflection 会增加延迟和 Token 成本。它更适合高价值、低容错任务自动改代码数据分析报告合同/财务/合规类摘要多步骤工具调用生产环境变更建议长链路 Agent 工作流。如果只是改一句普通文案Self-Refine 一轮足够如果只是分类一个低风险标签可能根本不需要 Reflection。3. 反思内容必须结构化否则记忆会变成流水账糟糕的反思这次做得不好下次要更仔细。可复用价值几乎为零。更好的反思失败场景生成 safe_divide 函数。 失败原因没有处理 b 0。 修正策略除法函数必须先补零值分支并加入 b 0 的测试。 适用范围数学工具函数、金额计算、比例计算。建议把反思存成结构化字段{task_type:code_generation,failure_mode:missing_edge_case,lesson:除法函数必须处理 b 0并加入对应测试,applies_to:[division,numeric_function,unit_test],status:active}这样后续才能检索、去重、衰减和合并。记忆代谢机制反思、合并与遗忘Reflection 最容易被低估的部分不是“怎么写入记忆”而是“怎么不被记忆拖垮”。如果 Agent 每次失败都往向量库里追加一条反思几个月后记忆库会变成一堆重复、过期、互相冲突的碎片。成熟的 Agent 记忆系统需要代谢机制。1. 自我反思把原始过程提炼成元知识原始轨迹通常很长用户输入、模型计划、工具调用、错误日志、修改 diff、测试结果...不要直接把整段轨迹塞进长期记忆。更好的方式是异步复盘把它提炼成元知识在这个项目里用户更关注性能和稳定性。代码审查时优先检查 N1 查询、内存峰值、连接池耗尽和超时重试而不是先纠结命名风格。元知识比原始对话更短也更容易在未来任务中命中。2. 合并把碎片整理成稳定规则如果用户 10 次提到同一个事实项目使用 Java 21。 这个服务是 Spring Boot。 数据库是 PostgreSQL。 部署在 Kubernetes。系统不应该保存 10 条零散记忆而应该合并成一个项目画像{project_profile:{language:Java 21,framework:Spring Boot,database:PostgreSQL,runtime:Kubernetes}}合并能减少向量库冗余也能让检索结果更稳定。3. 遗忘过时记忆比没有记忆更危险长期记忆不是越多越好。过时信息会误导模型。常见策略有三种权重衰减给每条记忆维护分数并随时间下降score base_score * e^(-lambda * age_days)近期高频命中的经验优先被召回长期不用的经验逐渐降低权重。冲突解决如果新旧事实矛盾新事实应覆盖旧事实。例如旧记忆项目使用 Java 8。 新记忆项目已升级到 Java 21。旧记忆不应该继续参与检索应标记为deprecated或建立指向新事实的 superseded 关系。定期清理很多向量库支持软删除但软删除不等于索引彻底干净。长期运行后建议定期做压缩、重建索引或 Vacuum避免旧向量继续影响召回质量和存储成本。工程落地建议什么时候用哪种 Reflection可以按任务风险选择策略。场景推荐策略文案润色、普通摘要Self-Refine 1 轮代码生成、Bug 修复CRITIC 测试执行 Reflexion 记忆事实问答、技术调研CRITIC 检索/搜索校验多步骤 Agent 工作流Plan-and-Execute 每步 Verifier 失败反思用户长期偏好学习Reflexion 结构化长期记忆 合并/遗忘高风险操作如删库、转账、发邮件工具校验 权限校验 人工确认不允许只靠反思一个实用原则是能用代码验证的就用代码能用数据验证的就用数据只能用语言判断的再让模型反思。总结Reflection 的价值不是让 Agent 每次都“想得更久”而是让它具备三个能力发现错误通过自评、测试、搜索、Schema 校验等方式发现问题修正错误把失败证据转化为下一轮生成的约束沉淀经验把可复用教训写入记忆并在未来任务中检索使用。Reflexion 解决“失败经验怎么复用”Self-Refine 解决“输出质量怎么迭代”CRITIC 解决“事实和执行结果怎么校验”。三者并不是互斥关系生产系统里往往会组合使用。真正成熟的 Agent不是永远不犯错而是能在可控成本内发现错误、纠正错误并逐渐减少重复犯错。从这个角度看Reflection 标志着 Agent 从一个简单的“输入-输出函数”开始变成一个具备自适应能力的软件系统。