尧图精选

In-Context Feedback Learning:让大模型在上下文中自我修正的实战指南

🕒 发布时间:2026/10/1 23:51:18 📁 来源:尧图网络
先聊个我最近经常遇到的局面。我在做基于大语言模型的自动化任务时最烦的不是模型不懂规则而是同一个问题让它重新生成一遍十有八九还是犯一模一样的错。你给它加提示词、换示例、调温度折腾半天结果它照样忽略某个边界条件照样生成乱七八糟的字段。后来我换了个思路不再把生成当单次调用而是把模型每一次的输出连同反馈一起塞回上下文让它看到自己的行为引发的后果然后在这个反馈基础上重新生成。这个思路被圈里人叫做 in-context feedback learning —— 直接说就是把反馈信号也作为上下文的一部分让模型在推理阶段自我修正。这篇文章我打算把这个方法掰开揉碎从原理到落地附上我实际跑过的案例和踩过的坑适合正在做 LLM 应用开发、Prompt 工程或者被模型屡教不改折磨的读者。你不用上梯度、不用微调就能让模型在一轮轮对话里越改越准。1. 一次生成定生死单轮输出才是当前应用最大的隐性瓶颈很多团队用 LLM 的方式本质上还是投喂需求等待答案。需求描述得不够细就继续往 prompt 里堆描述效果不对就换一个模型、调一调 temperature。这种方式看似灵活实际上是把大模型当作一次性的答案生成器用完就扔。可模型的输出本质上是一个概率采样过程你那句请仔细一点根本改变不了它在隐空间里的出发位置。真正能修正行为的不是抽象的仔细而是某个具体的、由它的输出引发的外部信号。1.1 三个日常场景里的回环缺失我先把最常见的三个场景摆出来。第一个是代码生成。让模型写一个处理 JSON 的函数它生成了丢到 Python 环境里一跑报错说某个字段可能不存在。你重新让它写它大概率还是无视这个分支因为 prompt 里没有告诉它上一个版本是因为什么报错的。第二个是文案生成。你让模型给某个产品写介绍它写出一版你说不够有感染力再来一遍它可能只是把形容词换得更华丽但结构问题、信息顺序问题原封不动。第三个是数据分析。模型给了一个结论你其实知道它用了错误的数据口径但你没有把口径错误作为反馈喂回上下文于是它下一轮依然沿用错误的假设产生连锁跑偏。这三个场景的共同点是什么它们的成败都取决于第一轮之后有没有反馈通道。现实任务几乎不存在一次生成直接完美的总是要经过检查、修改、再验证。但当前的调用方式把生成和修正割裂开了模型在两轮之间没有记忆也就没有学习。1.2 从 In-context Learning 到 In-context Feedback Learning熟悉大模型的朋友都知道 In-context Learning(ICL)就是在 prompt 里放几个示例让模型模仿示例的模式来执行新任务。它的机制本质上是通过上下文文本改变模型内部生成序列时的条件分布。你给两个输入-输出示例再给一个新输入模型会顺着示例的格式、推理路径续写下去。但 ICL 有一个天生的盲区示例描述的是别人怎么做是对的模型并不清楚我自己刚才做错了什么。In-context feedback learning 和 ICL 的区别就在这里——feedback learning 提供的不是静态示例而是针对模型自身输出的反馈信号。这个反馈可以是编译器报错、测试结果、用户的修改痕迹甚至是另一个模型的评判。它们不改变模型权重只改变生成时的条件上下文却能对后续输出产生方向性影响。你可以把它理解为考试时老师用红笔标注出你的错误步骤然后让你在同一张答卷上重做。这比单纯给一份标准答案更能帮学生定位问题因为标注是针对个体错误路径的。2. 反馈进上下文的机制模型为什么会越改越好说清楚了概念接下来得回答一个关键问题反馈为什么对 Transformer 架构有效我的理解是语言模型生成后面的 token 时会对前面出现的所有 token 做注意力加权。当反馈信息尤其是包含错误原因 正确动作这类强因果提示的文本出现在上下文里它就像一个高优先级的路标会在后续解码时持续施加影响。反馈不直接修改参数但它改变了模型接下来最可能生成什么的条件。2.1 反馈进入 Prompt 的本质给模型一个行为校正信号我用一个特别生活化的类比来解释。想象你在学做菜第一次炒出来的菜太咸。有两位师傅指导你。第一位师傅说再试一次努力做得更好一点。你会很茫然不知道该从哪改。第二位师傅说你刚才放了 20 毫升酱油这道菜只需要 10 毫升另外你是在快出锅前才加的盐应该在腌制时放。你照着这个反馈重做大概率立刻就能改善。大模型也是一样的。如果你只说请重新生成一个更好的版本模型根本没有足够的信息判断好与不好的边界在哪里。可一旦你将具体的失败模式、报错信息、错误步骤编号放入上下文模型就能以这些信息为锚点主动绕开原来的错误路径。这背后其实是语言模型对文本内因果链条的强建模能力它擅长根据上一段分析推导出下一段行动。你给它一个诊断书它自然能写出治疗方案。2.2 三种我在生产中验证过的反馈形态反馈不是只能来自人不同任务的反馈源差别很大。我根据实践把它们分成三类都是可以放进上下文的信号。第一类是真值反馈。我把模型输出和标准答案做一个 diff或者把输出喂给校验函数然后把差异部分直接贴在 prompt 里。典型例子是结构化抽取任务模型把一个日期字段抽错了我把标准格式和模型输出放在一起让它自己看差别。这类反馈最硬核效果也最可预期。第二类是执行反馈。这种在代码和 Agent 场景下尤其常见。模型写的代码拿去运行解释器报错模型调的 API 返回了异常状态码模型生成的 SQL 执行结果不符合预期。把这些执行结果原样塞进上下文模型往往能根据报错栈反推自己的逻辑错误。这类反馈的优点是客观、具体缺点是模型容易被一个表面报错带偏需要在反馈里补充期望行为以防止它去修一些无关的东西。第三类是人类偏好反馈。用户对生成结果的划线编辑、点赞、批注都被我结构化为简短的文本信号。比如第三段论证偏离主题请回到成本优先框架。这类反馈最软但最高频。它的关键在于把用户的情绪化表达翻译成模型能执行的动作指令。2.3 反馈要明确且可操作而不是单纯说你错了这里我得强调不是所有反馈都能提升模型表现。我见过最没用的反馈就是输出质量低请重新生成。模型收到这种反馈后只会把文本整体重写一遍甚至把本来对的部分也改错。我总结过一个反馈质量的分级从低到高大概是这样反馈等级示例对模型行为的引导力无反馈无模型凭概率重新采样随机性大结果型反馈你错了模型知道要改但不知从何改起行为型反馈你忽略了空值分支模型能在特定维度上主动规避归因型反馈因为第 2 步没有判空导致后续字段全部 KeyError模型能重建因果链一次性修正所有连带错误我几乎总是要求反馈里至少包含两部分信息一是可观察到的错误现象二是能指引方向的错误原因或修正策略。只给现象模型可能会头痛医头只给原因模型可能不知道现象有多严重。两者都齐全才能在解码时形成足够的约束力。3. 五步闭环实操把反馈学习流程落到具体项目里原理讲完总要落到工程实现。我目前实践下来一个完整的 in-context feedback learning 闭环分五步收集反馈、结构化整理、重组上下文、再生成、校验结果。这几个步骤看似简单但每一步都有不少细节细节决定了最终效果能好到什么程度。3.1 闭环流程总览从生成到修正的循环迭代一个典型流程长这样我先用一个基础 prompt 让模型生成初步结果。这个结果会被送到外部的检查器或人工手里产生反馈信息。接着我把原始 prompt、模型上一次的输出、以及新产生的反馈拼接成一个新的 prompt再让模型生成修订版。修订版再次送检如果还有问题就继续迭代。这个流程在概念上非常接近控制论里的闭环控制输出被测量后馈送到输入端形成修正。和那些调 prompt 调半天的静态做法相比feedback learning 的核心优势在于每一次迭代都是针对当前模型的实际行为反馈而不是凭空猜模型哪里会出错。我建议一开始不要把迭代轮数设成固定的3 次或5 次而是设一个校验是否通过的停止条件。比如代码任务以编译通过和测试全绿作为停止条件抽取任务用字段完整性校验作为停止条件。只有校验通过才结束循环。如果不通过就把新的失败信息继续加入上下文。3.2 反馈缓冲区的设计保存哪些内容、丢哪些内容既然要把反馈放进上下文就绕不开 token 窗口限制。我专门维护了一个反馈缓冲区这个缓冲区其实是一个结构化的字符串每次迭代结束时更新。缓冲区里我固定保留三类信息任务原始要求、当前模型输出摘要、按时间顺序排列的历史反馈列表。任务信息无论如何都在防止模型在迭代中忘了初心模型输出摘要控制在 100 token 以内只记录输出形态历史反馈列表是重点我每次给每一条反馈编号并注明它属于哪一轮。例如第 1 轮反馈输出缺少空值判断导致 KeyError 第 2 轮反馈空值已处理但仍未覆盖字符串类型输入断言失败 第 3 轮反馈字符串类型已覆盖但返回值格式与约定不一致。注意我给每条反馈都标明了轮次和当前状态。这看起来麻烦但非常有价值因为模型能据此推断哪些问题已解决、哪些还在局限。3.3 重组成可复用的修正 Prompt 模板缓冲区设计好之后重组成 prompt 就有固定套路了。我最终使用的模板长成下面这样你可以直接抄去用语言模型是 text 场景【任务原始要求】 {base_instruction} 【模型上一轮输出】 {previous_output} 【历史反馈记录】 {feedback_buffer} 【本轮修正目标】 请根据以上反馈分析失败原因输出修正后的完整答案。 要求 1. 不得重复历史反馈中已指出的错误 2. 保留上一轮输出中已正确的部分 3. 对修正逻辑给出 2 行以内的简要说明。这个模板的关键在于保留上一轮正确部分这一条。很多失败的迭代就是因为模型重写得太彻底把本来对的代码逻辑推倒重来引入新 bug。加上这条要求之后模型会倾向于做外科手术式修改而不是整体推翻。3.4 防止模型改过头回归检查与正确片段锁死有了模板最容易踩的坑就是改过头。我打个比方模型原本的代码有 4 个函数其中 3 个是对的只有 1 个有问题。你把反馈给它它重写时可能把 4 个函数全改了而且改完 2 个变成错的。解决办法除了在模板里写明保留正确部分我还会额外做一层人工保险显式地把已知正确的内容片段放进一个不可修改区并在 prompt 中声明。比如生成代码时我可以在 prompt 里写以下片段已验证正确必须原样保留{correct_fragment}。生成文档时则可以把用户认可的开头和结尾锁死。这个思路实际上是把回归测试的思想搬到了 prompt 工程里防止模型在修正一个 bug 时顺手破坏其他功能。4. 三条实战案例全记录修代码、改文案、纠推理讲了这么多不如直接看三条我在真实任务里跑过的案例。每一条我都记录了输入、反馈、输出变化和最终效果尽量还原当时迭代的过程。你对照自己的项目应该能找到相似的影子。4.1 代码修复让模型拿着编译报错去改而不是赌运气前阵子我让模型写一段 Python 脚本从嵌套 JSON 中提取指定字段。第一版代码长这样def extract_field(data, path): keys path.split(.) for key in keys: data data[key] return data这个函数看起来很直白但一跑就崩如果中间某个 key 不存在直接 KeyError。我把这个输出连同报错信息KeyError: address一起放进上下文并附上反馈函数未处理中间键缺失的情况当 path 指向的嵌套层级中任何一级不存在时应返回 None 而不是抛异常。模型第二轮给出的代码是def extract_field(data, path): keys path.split(.) for key in keys: if not isinstance(data, dict) or key not in data: return None data data[key] return data这个版本已经非常接近完美防御了。你看反馈中我不仅给了报错还给了期望行为——应返回 None。这就避免模型去改一些无关紧要的变量命名。整轮迭代只花了一次额外调用。4.2 文档与文案打磨把用户的划线编辑变成上下文中的数值化信号代码案例相对容易因为编译器给了精确的错误位置。文案类任务更麻烦反馈往往是定性的。我做产品说明文案时第一次输出被老板划了三处产品核心卖点埋得太深、技术参数堆砌太密、缺少使用场景联想。我把这三条点评结构化还加了一句当前文案共 420 字目标控制在 350 字以内。模型第二轮输出时明显把卖点提到了第一段技术参数合并成一行在中间补了一个办公场景。我没有输出前那么直奔主题但这次已经能让老板靠它做基础沟通了。这个案例给的经验是文案反馈要做到可操作得把主观描述翻译成具体的结构动作比如第 2 段移到第 1 段之后删除第 4 段的技术参数表新增一个具体用户场景。模型对位置变化增删模块这类指令的执行准确度远高于对更有感染力的理解能力。4.3 数学推理题纠错反馈需要引导到过程而不是答案最后是推理类任务。我拿一道鸡兔同笼变体题测试模型第一轮直接把步骤跳了答案也给错。如果我只给反馈答案是错的请重算模型有可能换一种算法但还是错。所以我的反馈写得更细第一步设未知数正确但第二步你的方程左边少了脚数系数。请补全所有步骤的推导再给出答案。模型第二轮会把方程展开、合并同类项、代入验证都写出来最后答案自然就对了。推理任务和代码任务本质类似模型犯错点往往在某个步骤的跳变上。反馈应该明确指出跳变的步骤并强制模型补全过程。这比单纯告诉它用另一种方法有效得多因为模型并不缺方法它缺的是对自己的步骤做结构化检查。5. 和相邻技术方案的边界总有人问这和微调、RAG、Self-Refine 有什么区别在我分享这个方法之后收到最多的评论是这不就是微调吗这不是 Self-Refine 吗和 RAG 有什么区别 我必须认真对待这个问题因为方案边界搞清楚你才能选对工具。它们确实都在做给模型更多信息这件事但信息进入系统的位置和方式完全不同。5.1 先划清界限这不是微调也不是 RLHF最根本的差异是微调会更新模型权重而 in-context feedback learning 完全在推理阶段进行模型参数动都不动。权重更新意味着模型永久改变了行为基线适合那种高频、稳定、可重复的错误场景比如一个客服系统要把公司内部专有名词统一翻译。但微调的代价是训练数据准备、GPU 资源、模型版本管理一套下来没有两周也得一周。反馈学习则更像是测试时修正。它的成本就是多几次 prompt 调用几分钟内能看到效果。我实际用下来对处理频率低、每次错误模式都不一样的任务反馈学习的性价比碾压微调。比如你要给几十份不同格式的合同做抽取A 公司的错误模式和 B 公司的错误模式完全不同你不可能为每一家微调一个模型但你可以给每一家都套上同样的反馈循环。对比项微调In-context Feedback Learning参数更新是否部署成本高需要模型版本环境低纯 Prompt 级改动错误模式变化快时需要反复训练慢直接改 prompt 里的反馈即可适用场景高频、稳定、通用性强的模式低频、个体差异大、快速迭代场景5.2 与 Self-Refine 的微妙关系外部反馈比自我批判更可靠Self-Refine 的思路是让模型自己评价自己的输出生成反馈再修订。它和 feedback learning 很像区别在于 feedback 的来源。Self-Refine 的反馈来自模型自身本质上依赖模型已有的知识去发现问题但模型往往不知道自己不知道尤其是遇到领域性很强的错误时它连问题在哪都识别不出来。In-context feedback learning 并不排斥自我批判但它强调外部信号的重要性。编译器报错、测试断言、用户否定、另一个模型的判断这些都是模型自身视角之外的信号可靠性更高。我的建议是两者组合使用先用外部检查器收集硬错误把硬错误喂回上下文再让模型基于硬错误做一段自我总结。比如代码场景我会在 prompt 里写根据上一轮报错请先分析根因再给出修订。 这一步本质上是把模型当作自己的调试助手但信息来源已不再是凭空脑补。5.3 与 RAG 的关系反馈是经验RAG 是知识很多人想到 RAG第一个念头是它也往 prompt 里塞东西。但 RAG 塞的是检索来的外部知识比如公司文档、产品手册、领域论文解决的是模型不知道这些信息的问题。而反馈学习塞的是模型自己的行为后果解决的是模型知道了知识但没按照知识正确执行的问题。你可以边检索边反馈RAG 负责让模型获得知识feedback 负责让模型在使用知识的过程中不断被校正。两者是互补的不冲突。举个例子让模型写一个产品指标解释RAG 先给它几条权威口径反馈循环再根据它写出来的表述偏差进行第二轮修正这样才能同时保证知识来源正确和表达准确。总而言之边界认识非常重要。微调、RAG、Self-Refine、feedback learning解决的是不同层面的问题。feedback learning 的核心优势在于低成本、快速、针对个体行为它不需要训练资源也不需要额外的知识库只要你能把某种形式的反馈文本化就能进入这个循环。6. 落地时最常见的四个坑以及我的应对策略既然是真实验证过的方案踩过的坑也得老实交代。以下四个问题是我在实际落地时反复遇到的几乎每个接入 feedback learning 的项目都逃不掉你可以提前做好心理建设和工程准备。6.1 上下文越滚越胖token 膨胀与成本失控这是最直接的问题。每多一轮迭代历史反馈 输出摘要就多几百 token迭代到第 5 轮时prompt 可能已经是原始长度的 3 倍。大模型的计费是按 token 来的多轮迭代时间越长成本越高同时响应延迟也会变大因为模型要处理一大段冗长的历史。我的策略是给反馈缓冲区设一个硬上限比如只保留最近 3 条反馈更早的反馈压缩成一行摘要。另外当模型连续两轮都在解决同一个问题时我会认为它对这个反馈的理解已经接近稳定没必要继续重复灌输直接合并成一条终端反馈此前已指出 XX 问题请确认本轮输出不再包含该类错误。 这样既保留了上下文约束力又控制了长度。6.2 反馈顺序影响效果模型只关心最近的指责Transformer 的注意力天然有近因偏好输入序列末尾的信息往往对后续生成影响更大。我测试时发现如果你把新一轮反馈排在旧反馈后面模型会只盯着新反馈修正忽略了之前一直没解决的旧问题。比如第 3 轮它修好了空值问题第 4 轮你又反馈了一个格式化问题它可能顺手把空值处理又弄丢了。我解决这个问题的办法是把已解决的问题和未解决问题分两个区段写在 prompt 里。已解决区段用一句话概括这些问题已修复保持现状未解决区段放在 prompt 更靠后的位置用更强烈的指令语气。这相当于给模型一个记忆清单让它知道哪些要保住、哪些要改动。6.3 模型把反馈当作任务要求而非行为描述反馈污染这是个非常隐蔽的坑。当你把历史反馈一股脑塞进上下文后模型可能会把反馈本身当成当前任务的一部分来执行导致输出形式被反馈文本污染。比如我在文案任务中写第二版应该增加使用场景模型在第三版的开头直接说根据您的反馈我增加了使用场景而不是直接给改写后的文案。显然模型误以为复述反馈也是输出的一部分。我处理反馈污染的办法是给输出施加更严格的格式约束在 prompt 末尾明确声明输出只包含最终答案禁止任何解释性标题、反馈回顾或对话开场白。 这一行强指令能有效压制模型把反馈当成答案内容的倾向。6.4 延迟不是免费的需要做合理的工程取舍每一次反馈重生成都是一次完整的前向推理这在大模型应用里意味着多几百毫秒甚至几秒的延迟。在后台跑离线批量任务时无所谓但做在线服务时就必须考虑。我在一个实时问答服务里试过这种循环用户等不及 4 轮迭代于是我把迭代上线改成最多 2 轮且第一轮必须强校验第二轮才开始反馈重生成。这样虽然牺牲了一部分极限效果但延迟可接受。我的原则是在线场景优先保证实时性用更高质量的初始 prompt 降低需要反馈的概率离线场景可以大胆用满 5 轮甚至更多因为延迟无所谓准确率优先。我在实际项目中体会最深的一点是in-context feedback learning 并不是某种花哨的算法它更像一种工程习惯——每次生成之后都多问一句结果回去之后能不能给模型一个明确的、可执行的反馈。当你开始把一个一个外部信号组织成上下文中的修正指令时你其实是在把一次性的工具调用变成一条有记忆、能迭代的行为链条。这个方法不一定适合所有任务但我建议你先挑一个错误模式相对清晰、反馈容易结构化的场景试起比如代码报错修复或字段抽取校验跑通一次闭环你就能感受到它和反复重新生成之间的差别。做 LLM 应用到最后拼的往往不是谁调的模型大而是谁手里的反馈回路更密、更清晰。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →