尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Loop Engineering实战:从循环设计到批量内容质检

Loop Engineering实战:从循环设计到批量内容质检 Loop Engineering这个说法我最早是在折腾自动建站 Agent 时真正吃透它的。当时的场景很典型我给模型下达了一个调研竞品 - 生成栏目 - 批量产出文章的多阶段任务结果前几次跑下来栏目结构完全失控文章风格漂移严重中间任何一步出岔子后面全跟着歪。折腾了几天之后我意识到问题不在模型不够聪明而在于我把一个天然需要多轮迭代的复杂任务硬生生当成了一次性的大号单次生成来设计。后来接触到的这套方法论圈内现在叫Loop Engineering——核心就一句话把大模型应用拆成一个又一个可观测、可控制、可退出的循环然后把这些循环精心设计好让模型在感知 - 推理 - 行动 - 接收反馈 - 再推理的闭环里逼近正确答案。这篇教程不搞虚的我会从底层原理讲起然后手把手带你写一个能跑的计划-执行-校验循环最后用一个完整的批量内容质检项目复盘收尾。适合正在做 Agent 开发、自动化工作流编排或者被模型输出不稳定折磨得够呛的工程师。1. 为什么突然都在谈循环一次任务失败背后的设计缺陷1.1 一个典型的多步任务是怎么被单次化毁掉的先复盘我当时那个自动建站任务的失败链路。我最初的方案非常朴素把调研、栏目规划、文章生成全部塞进一个 System Prompt让模型一口气输出 Markdown 格式的完整站点方案。表面上模型一步到位了但实际上它是在做一个极其困难的贪心解码——它必须同时保证调研数据准确、栏目逻辑自洽、文章风格统一还要自己推断出每一步的中间结论。结果是调研环节引用的数据是模型编的后面所有栏目规划全部建立在不存在的假设上模型预设了整个站点的栏目写完第 3 个栏目之后忘了第 1 个栏目的约束一旦某篇文章质量差系统没有任何机制去发现和修正因为输出已经结束了。这不是模型能力问题GTP-4o 级别的模型做单步推理时相当强。问题出在任务架构上一个需要反馈、修正、累积信息的过程被压成了零反馈的直线推理。1.2 循环的底层逻辑为什么感知-推理-行动-反馈能胜过贪婪解码Loop Engineering 的底层逻辑其实和人的工作方式很像。你写一份方案不会一次落笔成稿——你先收集资料拟框架写初稿然后回看发现问题再改。每一步都是基于上一步的输出状态做新一轮决策。放到模型身上这个循环的通用形式是观察当前状态已有的输出、工具返回结果、外部数据基于状态做推理决定下一步做什么执行动作调用工具、生成片段、发起搜索把动作的结果作为新状态的一部分回到第 1 步。为什么这样做能大幅提升结果质量因为模型的单次推理上下文是有限的而把总任务拆成多个循环轮次之后每轮只需要做一个局部决策模型的注意力可以集中在当前这一步上。更重要的是反馈机制让错误不再被静默传递——校验器发现前面某一步错了循环可以回退重来而不是带着错误一路狂奔到终点。1.3 谁最需要补上这门工程课我观察到需要 Loop Engineering 的不只是做 Agent 框架的底层开发者。任何这类场景其实都在吃循环的红利用 LLM 做批处理任务的批量生成摘要、批量标注数据、批量审核批次里总有 5%~10% 的坏样本单次生成无法救回来循环能做Agent / 工作流编排的核心能力就是控制模型-工具-模型之间的循环做模型评估与数据飞轮的LLM-as-Judge 本身就是一种评估循环。2. 拆解一个标准 Agent Loop五个必须精心设计的部件网上很多讲循环的文章喜欢给一个循环while 大模型调用的简化公式真正落地的时候你会发现一个能稳定工作的循环至少要在下面五个部件上分别做设计。2.1 状态与记忆循环跑起来之后信息靠什么累积循环的本质是状态机。每次迭代模型看到的输入由两部分组成原始任务描述和截至当前的历史轨迹。历史轨迹怎么组织直接决定模型是否会失忆。踩过的坑是把所有轮的对话都原样塞进上下文。看起来信息最全实际上轮次一多早期噪声会覆盖掉关键约束。我现在的做法是引入持久化状态字段——用结构化的 JSON 记录已完成动作、当前产物、待办事项、已知错误每次循环开始时先让模型基于这个状态字段做决策而不是重新读一遍全量历史。这有点像人类的工作台和短期记忆的区分需要精确记忆的数据放在台面上其余过程性内容模糊掉。2.2 行动指令的格式约束别让模型自由发挥循环里模型每次推理的产物不仅是一段思考更是一份行动指令。如果行动指令格式不固定解析层就会形同虚设。我在所有循环项目里都要求模型输出严格的 JSON结构大致是{ reasoning: 这一轮思考当前状态与目标之间的差距, action: generate | verify | search | finish, action_input: { ... } }reasoning字段给模型一个推理的出口action字段限定动作空间action_input承载参数。这套协议的好处是你可以用十几行代码实现一个调度器根据 action 字段把任务分发给对应函数。不要贪图省事让模型直接输出自由文本后面解析、重试、审计全都难受。2.3 反馈解析与错误分级循环不是错了就重跑反馈机制是循环的灵魂但要注意错误的类型不同循环的处理方式完全不同。我把错误分为三级可自纠错误如 JSON 格式不规范、参数缺字段模型自己看一眼错误信息就能修直接让它重来需降级错误如某个工具持续调用失败不应该无限重试而是换一条路径比如搜索超时改成直接跳过并用常识补充原则性错误如生成内容涉及违规、校验器判定整体跑题立刻终止循环向外部告警请求人工介入。如果你不分级所有错误都走再问一遍模型成本会失控而且循环可能围着同一个错误反复转圈。2.4 终止条件防止循环变成无底洞循环必须设计显式终止条件否则跑起来就是事故。我一般同时设三道闸任务完成标志模型输出action finish且校验器确认产物合格最大轮次上限如果超时没有产出合格的最终结果终止并返回当前最佳产物 失败原因语义停滞检测连续两轮动作相同且结果无明显变化判定陷入死循环强制退出。这三道闸缺一不可。只靠模型自己判断做完了是危险的——模型在上下文较长时倾向于尽快收尾质量不够也会说完成了。2.5 成本与速率控制循环越多token 越贵这是 Loop Engineering 最容易被忽略的现实问题。一个 6 轮的循环token 消耗通常是单次生成的 3~5 倍因为每一轮都要把历史轨迹重新编码一遍。上生产之前一定要做成本预算预估每轮平均 token乘以最大轮次得出单任务峰值设置每日总 token 阈值超过就熔断善用固化早期轮次的技巧前几轮中已经完成的、不会再变的产物比如调研结论可以整理成摘要塞回上下文而不是把冗长的原始对话一直堆下去。我在 2.4 节提的停滞检测其实也是成本控制的一部分它不只是防死循环更是防表面上在进步、实际上在原地烧钱的伪工作循环。3. 保姆级实操从零手写一个计划-执行-校验循环这一节进入正题。我带你从零写一个最小可用的 Loop 引擎。我们以生成一篇结构化行业分析报告为任务载体——它足够简单又能体现循环的完整价值。3.1 最小可运行版本先让循环转起来我不会一上来就引入 LangGraph 之类重框架。先用原生 Python OpenAI 兼容接口把循环骨架搭出来你自己能看清每一行在干什么。核心就一个run_loop函数import json from openai import OpenAI client OpenAI() SYSTEM_PROMPT 你是一个行业分析报告生成器。你必须按照下面的协议工作 每次只输出一个 JSON 对象包含字段 - reasoning: 你对当前状态的思考 - action: 只能是 plan / write / review / finish 之一 - action_input: 动作参数 任务生成一份完整的行业分析报告内容包含市场概况、竞争格局、趋势判断三部分。 规则 1. 动作为 plan 时action_input 里给出报告大纲 2. 动作为 write 时一次只写一个章节action_input 里包含 chapter 和 content 3. 动作为 review 时action_input 里包含对已完成内容的问题列表 4. 只有三个章节全部完成且自检通过才能输出 actionfinish def build_messages(state): 把当前状态压缩成消息序列而不是无限堆历史 messages [{role: system, content: SYSTEM_PROMPT}] messages.append({ role: user, content: f当前状态\n{json.dumps(state, ensure_asciiFalse)} }) return messages def run_loop(max_rounds8): state { plan: None, chapters: {}, review_notes: [], done: False } for i in range(max_rounds): resp client.chat.completions.create( modelgpt-4o-mini, messagesbuild_messages(state), response_format{type: json_object} ) decision json.loads(resp.choices[0].message.content) if decision[action] plan: state[plan] decision[action_input] elif decision[action] write: chapter decision[action_input][chapter] state[chapters][chapter] decision[action_input][content] elif decision[action] review: state[review_notes].extend(decision[action_input][problems]) # 如果有问题强制触发重写这里简化为直接标记待重写 elif decision[action] finish: if len(state[chapters]) 3: state[done] True return state return state # 超轮次返回当前最佳状态 if __name__ __main__: result run_loop() print(json.dumps(result, ensure_asciiFalse, indent2))这段代码最值得注意的地方是build_messages它不是把每一轮对话都追加进 messages而是每轮只把state序列化成一段当前状态传给模型。这样做有两个好处模型始终看着结构化的状态做决策不会迷失在冗长历史里同时 token 消耗被压到最低。3.2 加一层硬校验让循环具备自我纠错能力上面这个版本有个明显短板——它把质检完全交给模型自觉。实际上模型在 review 阶段经常放水明明内容跑题了还说质量合格。我的做法是引入硬校验器一个独立于生成模型的规则函数专门检查硬性要求。def hard_validator(state): errors [] # 硬性检查1三个章节必须齐全 for req in [市场概况, 竞争格局, 趋势判断]: if req not in state[chapters]: errors.append(f缺少章节{req}) # 硬性检查2每个章节必须超过200字 for chap, content in state[chapters].items(): if len(content) 200: errors.append(f章节过短{chap} 仅{len(content)}字) # 硬性检查3趋势判断章节必须包含数据依据字样 if 趋势判断 in state[chapters] and 数据 not in state[chapters][趋势判断]: errors.append(趋势判断章节缺少数据依据表述) return errors把它接进循环主逻辑每次模型输出finish之前先跑一遍hard_validator有错误就把错误列表作为反馈塞回下一轮的用户消息让模型带着批评意见重写。这一步其实就是把模型自评和规则校验结合起来——前者兜底语义问题后者兜底硬指标。我在实际项目中把 hard_validator 扩展到了几十条规则覆盖字数、格式、禁词、结构完整性等。经验是凡是能用规则表达的要求千万别省一定要写成确定性代码。模型自评再强也不如一道硬性的 if 判断可靠。3.3 为什么用 JSON 协议而不是自然语言协议可能有读者觉得让模型输出自然语言指令比如请先写市场概况不是更灵活吗我的答案很明确循环控制必须结构化。原因有三可解析性自然语言指令需要再做一层意图解析引入了新的错误源——模型可能说写概况也可能说现在开始市场部分同一个意思两种表达解析器都要覆盖可审计性JSON 记录里每个 action 是什么一目了然出问题了可以快速定位是哪一轮决策、哪个字段导致的约束力当你用 JSON mode 强制模型输出时等效于把动作空间从任意句子压缩成四个枚举值模型的选择难度降低稳定性显著提升。当然 JSON 协议也有代价模型偶尔会输出非法 JSON尤其在上下文很长的时候。所以循环里必须内置一层 JSON 解析重试解析失败就返回错误信息让模型修正这是 2.3 节说的可自纠错误的标准处理方式。4. 三层优化提示词内循环、工具内循环、元评估循环循环写出来能跑只是第一步。真正拉开差距的是你在这个循环的内层做了多少设计。我把循环里常见的优化手段分成三个层次分别对应不同的精细度。4.1 第一层单轮内的自我反思Prompt 级最低成本的优化是在一轮推理内部塞入反思机制。典型做法是在系统提示词里要求模型先输出思考再输出结论。比如生成文章时要求在给出最终版本之前先用 reasoning 字段列出你计划分几个部分、每部分的资料缺口、你准备如何弥补这些缺口。这相当于把原本隐藏的推理过程显式化迫使模型在生成前梳理思路。优点是不额外增加 API 调用次数缺点是反思深度有限模型能发现的问题往往还是浅层的表达问题真正的事实性错误它自己看不见。4.2 第二层工具调用失败的重试与降级工具级如果循环里接入了外部工具搜索、数据库查询、代码执行必须设计工具层的容错。我在实践中形成了一套三次重试 一次降级的原则第一次失败完整重试可能是网络抖动第二次失败缩短参数、放宽约束后再试比如搜索词截断第三次失败换工具比如搜索 A 超时改用搜索 B全部失败降级处理——不阻塞主循环记录缺失信息让模型基于已有知识生成并在结果中标注该数据未经实时核验。这个降级策略是关键。很多 Agent 项目挂在工具不可用导致整个任务失败上而发布到生产环境后工具不稳定是常态。循环的健壮性本质上取决于它在部分失败时能不能换个姿势继续跑。4.3 第三层LLM-as-Judge 的元评估循环评估级当生成任务对质量要求高时我会在外层再加一个元评估循环——让另一个裁判模型或同一模型的高规格版本对生成结果打分不达标就退回重写。这跟 3.2 节的 hard_validator 不同规则的 hard_validator 检查的是硬指标LLM 裁判评估的是软质量逻辑连贯性、信息密度、语气一致性。元评估要注意防裁判幻觉LLM 打分经常两极分化同一个答案换个措辞分数能差 20 分。我的缓解方案是让裁判输出具体的问题清单而不是只给分数同时要求每个问题必须引用原文中的具体句子作为证据。没有证据的问题不采纳。三层循环的关系是层层递进的Prompt 级反思在每一轮内部生效工具级容错在动作执行时生效评估级循环在整条生成链路的最外层兜底。工程上并不会一次上全三层——先跑通一层观察失败模式再决定要不要加下一层。多数批量生成任务加一层 LLM 评估就足够从不可用变成可用。4.4 三层配合时的参数调优经验三个层次叠加之后最需要盯紧的是重试预算的分配。我给一个常用配置供参考层级重试上限触发条件失败后的降级Prompt 内反思0每轮自带无无模型自行纠错工具调用3 次返回异常或超时换工具或跳过硬校验器2 次规则不通过返回当前产物并告警LLM 裁判3 次评分低于阈值采纳现有最优版本注意硬校验器和 LLM 裁判不要同时重试太多轮否则最坏情况是 2×36 次重写循环时间和 token 都扛不住。我的习惯是硬校验重试 2 次不过就接受现实进入人工队列而不是无限纠缠。5. 实战项目批量内容生成与质检 Agent 完整复盘理论讲得再多不如一个完整项目管用。下面是我近期做的一个真实项目——批量行业报告生成与质检系统核心需求是每天自动产出 200 篇短报告并完成质量拦截。整个系统就是一个复杂一点的 Loop。5.1 项目背景与目标客户的原始痛点是他们用单次 Prompt 批量生成产品描述文案结果交付内容里经常混入事实错误型号对不上、风格突变一半正式一半口语、以及格式缺失。人工复查 200 篇要两个编辑干一整天。所以项目目标定得很明确在不改变基础模型的前提下通过 Loop Engineering 把交付合格率从约 82% 提升到 97% 以上同时把人工复核时间压缩到 1 小时以内。5.2 循环设计我设计的流水线是两段式循环第一段生成循环每篇文案走大纲 - 撰写 - 硬校验 - 自修复的循环。硬校验规则包括产品型号必须与输入一致、字数在 300~400 之间、必须包含三个固定营销要素、禁用语清单命中即为失败。第二段质检循环生成通过后进入 LLM 裁判池用独立的 gpt-4o 模型对综合质量打分1-10并对低于 7 分的文案给出问题清单返回第一段重新生成最多 2 轮。第二段质检是独立模型而非生成模型自评规避了自己检查自己时常见的盲区。5.3 关键代码质检反馈的闭环接入把质检结果接回生成循环是这套系统的核心。这个函数把裁判的批评意见格式化成修改指令喂给生成模型def quality_feedback_to_prompt(judge_result): 把裁判模型的评价转成生成模型可执行的重写指令 problems judge_result.get(problems, []) if not problems: return None lines [] for p in problems: evidence p.get(evidence, ) desc p.get(description, ) line f- 问题{desc}原文证据{evidence} lines.append(line) instruction ( 以下是质检人员对初稿的意见请逐条对照修改。 修改时必须保留原文的有效信息不得为了迁就意见而删除关键事实。\n \n.join(lines) ) return instruction这里有个容易踩的坑不要让模型为了改而改。裁判意见有时是主观偏好比如语气可以更活泼硬改可能把原本准确的内容改坏。所以我在指令里专门加了保留有效信息的约束同时在系统提示词里把裁判问题的类型分成必须修正事实错误、结构缺失和可选优化风格、措辞只有前者强制处理。5.4 运行结果与迭代记录项目上线后的指标变化指标单次生成基线加硬校验循环加LLM裁判循环合格率82%91%97.8%平均单篇耗时4s9s21s人工复核时间2人×8h1人×3h1人×50min单篇平均token成本1.0x1.6x2.8x最关键的是人工复核从逐篇全量审变成了只看循环判定失败的那 2%~3%。这个转变意味着整个流程从人机协作但人负责兜底进化成了机器闭环、人只处理异常。5.5 从 85% 到 97% 的优化过程三个关键决策在迭代过程中印象最深的三件事裁判模型也要带产品知识卡。第一次上线时 LLM 裁判完全不知道产品线它只能用通用标准评分常常把合格的电商文案当成不够专业。后来我把产品知识手册压缩成 500 字注入裁判系统提示词误杀率立刻下降了一半。硬校验规则宁可多不可少。规则每增加一条合格率就涨一截。模型的自由度被压缩后输出稳定性的提升立竿见影。真正的风险不是规则太多而是规则自相矛盾——所以每条新规则上线前我都会跑一遍已通过的 50 篇样本文案做回归。每次重写必须看到上一次的完整版本。一开始重写时只传裁判意见不传初稿全文结果模型凭空重构丢失了大量没被点名的问题。后来改成初稿全文 裁判意见 重写要求三件套重写质量才稳定下来。6. 调试循环踩过的坑漂移、死循环、成本爆炸最后聊聊调试循环系统时最常见的三个事故模式。它们我全部真实遇到过每个都花了不少时间才定位。6.1 状态漂移循环越跑越偏症状任务开头模型还遵守规则跑到第 4、5 轮时开始偏离原始要求比如报告里混入了和主题无关的内容或格式逐渐走样。根因早期轮次的历史细节被后续内容稀释模型的注意力漂移了。尤其当状态里累积的信息量大而最关键的原始约束只出现过一次时约束容易被遗忘。修复方案我采用的是一招固定锚点——把任务的核心约束做成不可变的常量每一轮的用户消息里都原样携带而不是只依赖历史。相当于每轮都提醒模型别忘了最初的三个要求是什么实测漂移率下降非常明显。6.2 死循环与震荡模型在两个动作之间反复横跳症状循环日志显示模型反复输出review - write - review - write每次都只是小修小改永不输出 finish同时还平稳烧着 token。根因模型对完成标准的理解模糊。它不敢收尾因为不知道当前状态是否满足要求也不够聪明去大刀阔斧地改所以每次只做微调。修复方案双管齐下。一是把终止条件写得更明确当且仅当三个硬校验全部通过且裁判评分不低于 7 分时输出 finish二是加 3.4 节提过的语义停滞检测——如果连续两轮 review 问题列表完全不变直接判断当前质量已达模型能力极限强制收尾输出。6.3 token 成本爆炸循环数量竟然和文本长度正相关症状任务文本越长循环轮次反而越来越多。长文本更容易被裁判挑出问题每挑一个问题就触发一次全文重写重写后新问题又出现——陷入越改越长、越长越改的恶性循环。根因把长文本当成一个整体循环处理裁判发现问题就是全局问题重写就是全局重写。修复方案分段循环。把长文本拆成多个区块如按章节每个区块独立走生成循环区块验收后再拼接再走一次全局质检。这样全局质检只做一次不受单区块重写影响。成本直接从 2.8x 降到 1.9x合格率反而没有下降。6.4 我的调试方法论先看日志再谈模型循环系统出问题第一反应永远不要是换更强的模型或改提示词而是先检查循环日志。我在所有循环里都埋了结构化日志每轮记录轮次、决策动作、状态变化、token 消耗、是否超时。定位问题三步走看是不是状态更新逻辑有 bug比如写入了错误字段导致模型决策信息缺失看是不是终止条件不合理任务已完成但条件永远不满足最后再怀疑提示词和模型。线上跑过十几个循环系统后我得出的结论是循环系统的绝大多数故障出在工程侧的决定性代码上而不是模型侧。把日志埋好、把状态设计清楚比反复调 Prompt 划算得多。最后再分享一个我的个人习惯任何循环系统上线前我都会用一组固定的魔鬼样本做回归——每个样本专门针对一种失败模式设计格式缺失的、事实错误的、风格偏移的。循环迭代过程中每改一次代码就重跑一遍这组样本看行为变化。这个习惯帮我挡掉了无数次修好了 A bug 却引入了 B bug的尴尬。Loop Engineering 的魅力也正在于此当模型的不可预测性被循环结构框定之后剩下的系统行为是可以被你逐渐驯服的。
返回列表