年初我在调一个多步骤AI Agent的时候,反复撞到同一堵墙:单独的每一步提示词都写得很“对”,但串起来之后,输出质量就是忽上忽下,同样的输入隔一天结果能差出一大截。调试到烦躁的时候,我忽然意识到问题的关键不在于哪一句Prompt写得不好,而在于整个流程里缺少一个最基础的东西——反馈回路。后来我去翻了翻社区里的讨论,发现这个话题早就有了自己的名字:Loop Engineering,循环工程。简单说,它就是把“输出—评估—修正—再输出”这条循环回路当成系统的一等公民来设计和工程化,而不是靠运气去期待模型每次都对。
这篇文章就是围绕Loop Engineering写的保姆级教程加一份完整的项目实战记录。我会先把这个概念拆明白,讲清楚它和Prompt Engineering到底有什么区别,然后给你一套可以直接照抄的循环设计参数表,再手把手搭一个文档审校自动循环,最后用一个代码审查项目演示怎么把它真正落地到工作流里。无论你是刚接触AI应用开发,还是已经在用Agent做自动化,这篇都能帮你少走不少弯路。
1. 从一句提示词到一条反馈回路:Loop Engineering到底在解决什么
1.1 一次性提示词的天花板
过去两年大多数人调AI的方式是这样的:写一段很详细的提示词,把背景、要求、格式都交代清楚,然后调用一次大模型,拿到结果。不满意?那就改提示词,再调用一次。本质上这是一个线性的试错过程,每一次调用之间没有记忆、没有状态、没有结构化反馈。它解决的问题是“给定足够清晰的指令,模型能不能给出合格答案”。
但真实世界的任务绝大多数不是单次问答,而是多步骤、多约束、需要反复校验的工程任务。比如写一份技术方案,你需要先出框架,再填细节,再检查逻辑漏洞,再核对术语一致性。再比如修一段代码,你先得定位问题,再修改,再跑测试验证,再排查新引入的错误。这类任务有一个共同特点:完成质量不取决于单次输出的上限,而取决于迭代修正的下限。
换句话说,Prompt Engineering考的是“如何让模型第一次就答好”,而Loop Engineering考的是“如何设计一套机制,让模型即使第一次答得不够好,也能快速收敛到足够好的答案”。这两种思路没有优劣之分,但如果你正在构建AI Agent、自动化流水线或者任何涉及多步骤决策的系统,只盯着单次提示词,你会很快撞到天花板。
1.2 循环的最小单元:生成、评估、修正、控制
一个最基本的Loop Engineering系统,拆到不能再拆,就是四个角色在循环里各司其职。
| 角色 | 职责 | 类比 |
|---|---|---|
| 生成器(Generator) | 产生当前版本的输出,可能是文档、代码、方案 | 炒菜的厨师 |
| 评估器(Evaluator) | 按既定标准检查输出质量,找出问题和不达标项 | 尝菜的老饕 |
| 修正器(Refiner) | 根据评估结果修改输出,生成新版本 | 听到反馈后调整火候的厨师 |
| 控制器(Controller) | 维护循环状态,决定是否继续迭代,记录历史 | 决定“这道菜能不能上桌”的店主 |
用一句话概括:生成器负责“写”,评估器负责“骂”,修正器负责“改”,控制器负责“什么时候停”。这四个角色在代码层面不一定是四个独立的模型调用,有时候生成器和修正器可以用同一个模型加不同后缀的提示词来实现,评估器和控制器也可以合并成一个带逻辑判断的函数。但无论怎么合并,这四个职能必须都存在,缺一个循环就会崩塌。
最小循环的流程长这样:
# 一个极简的Loop Engineering骨架 def run_loop(generate, evaluate, refine, should_stop, max_rounds=3): result = generate() history = [] for round_index in range(max_rounds): evaluation = evaluate(result) history.append(evaluation) if should_stop(evaluation): break result = refine(result, evaluation) return result, history代码里最关键的参数是max_rounds和should_stop。前者是刹车,后者是方向盘。很多新手搭循环只知道“能转起来”,却不知道这两个参数才是决定系统会不会原地空转、会不会成本爆炸的核心。后面我会专门展开讲。
1.3 为什么这个概念最近频繁被提起
Loop Engineering之所以在最近几个月突然热起来,背景是AI应用的形态变了。早期的大模型应用是“问答型”,用户问一个问题,模型回一段话,对话结束。而现在的AI应用是“任务完成型”,用户丢进来一个目标,系统要自己规划、执行、验证、纠错,直到任务真正落地。Agent就是这样,它本质上不是一个聪明的提示词写手,而是一台装了方向盘的循环机器。
如果你用过Claude Code或者Cursor的Agent模式,你会发现它们做事的风格不是一次性甩给你一大段结果,而是会先列出计划,然后一步一步执行,并且在执行过程中不断检查前面的产出,发现问题就回头修。这背后的机制就是Loop Engineering。虽然这些产品的内部实现不叫这个名字,但它们把“迭代—反馈—修正”显式化成了流程的一部分,而不是靠每一次随机发挥。
所以我的判断是,未来一两年的AI应用开发,写提示词的能力会被逐渐工具化,真正拉开差距的是谁能在自己的系统里设计出更高效的循环结构。这也是我写这篇文章的初衷——把那些散落在工程实践里的循环设计套路,整理成一套能直接上手的方法论。
2. 动手搭循环之前,先把这五个参数想清楚
2.1 循环类型:是“反思自己”还是“验证结果”还是“分工协作”
不是所有循环长得一样。第一个要决策的是:你的场景适合哪种循环结构。
最常见的三种类型分别是反思循环、验证循环和分解循环。
反思循环(Reflection Loop)是让模型自己批评自己的输出,典型做法是“请你重新审视上面这段内容,找出逻辑漏洞,然后修改”。实现成本最低,一个模型就能跑,但天花板也最低,因为评估器和生成器共享同一套知识盲区,模型很难发现它自己都不知道的错误。适合用来优化表达、格式、连贯性这类表层问题。
验证循环(Verification Loop)是用外部工具或规则来当评估器,而不是让模型自己当裁判。典型做法是“跑一遍测试用例”“执行一次代码检查工具”“用正则校验格式”。这是最可靠的结构,因为评估标准是客观的,不存在模型自嗨的问题。代价是需要额外开发成本,你得先有那个能验证的“尺子”。
分解循环(Decomposition Loop)针对超长、超复杂的任务,把一个巨型任务拆成若干小任务,每个小任务独立执行和验证,再汇总结果。这其实是一种嵌套循环:外层拆任务、内层做验证或反思。适合方案设计、大型文档创作、复杂系统迁移这类一次搞不定的活。
选型的核心原则很简单:能用外部工具验证的,绝对不要依赖模型自我批评。我自己见过太多人试图让模型检查代码逻辑漏洞,结果模型自信满满地指出一个根本不是问题的问题,还煞有介事地改出一段更烂的代码。究其原因,就是评估器不靠谱。
2.2 最大迭代深度:先给循环装一个刹车
第二个要决策的是最大迭代次数。这个参数看似简单,却是整个系统最重要的成本控制器。
每次循环都会产生至少两次模型调用(一次生成、一次评估),如果循环跑了N轮,成本就是单次调用的2N倍(甚至更高,因为修正器往往还需要重新生成上下文)。所以最大迭代深度直接决定了你的系统烧钱速度。
我的经验值是:面向文本质量的任务(文档、文案、结构化内容),默认设3轮;面向代码验证的任务(有测试当评估器),默认设5轮;面向纯探索性的任务(头脑风暴、竞品分析),直接不设循环。为什么文档类控制在3轮?因为根据我实测,文本类任务大多数能在2轮内收敛,跑到第4轮基本就开始“为改而改”——模型为了响应批评意见,会把本来没问题的段落也重构一遍。
设置最大深度之外,还应该在每一轮里做一个成本预估打印。养成习惯后,你很快会对“一轮循环究竟消耗多少token”建立直觉。例如一次文档审校循环,输入一个3000字的初稿,每轮评估约消耗2000 token,修正约消耗3500 token,三轮下来总消耗接近2万token。没有这个直觉之前,很容易写出一个跑一次就要5万token的“豪华循环”。
2.3 评估标准:把“质量不错”翻译成可执行检查清单
评估器是整个循环里决定生死的一环,但也是最常被糊弄的一环。很多人的评估提示词长这样:“请检查这段文字的质量,指出错误并打分。”
这个指令等于没写。因为“质量”“错误”这些词太抽象,模型只能凭感觉输出一个看起来合理的评分,而你根本不知道它依据什么。评估标准必须拆成可逐项判断的检查项,每一项都要能用“是/否”或者“通过/不通过”来回答。
我常用的一种做法是给评估器一张结构化的检查卡:
检查维度: 1. 事实准确性:文中是否存在明显的逻辑矛盾、事实错误?逐条列出 2. 完整性:是否遗漏了任务要求中的任何必填模块? 3. 格式合规:标题层级、列表、代码块是否符合规范? 4. 冗余度:是否存在可删除的重复表述章节? 5. 一致性:术语使用前后是否一致? 输出格式要求: { "verdict": "pass" 或 "fail", "failed_items": ["检查项1", "检查项3"], "issues": ["具体问题描述1", "具体问题描述3"] }当你把评估维度具象化成一张表,评估器的输出就变成可编程的数据了,控制器才能判断“到底要不要继续迭代”。同时我发现,给评估器提供“在哪里判断”的锚点比让它自由寻找更高效,比如“重点检查第2节的技术参数与第4节的复用方案是否冲突”,能显著降低误报率。
2.4 状态容器:循环之间共享什么、遗忘什么
第三个常见错误是每一轮循环都“失忆”。生成器产出了新一版内容,但下一轮的评估器不记得上一轮的问题清单,修正器也不记得自己上一轮做过哪些修改。结果就是同一个问题修了又犯,犯了又修,无限打转。
设计循环时,你必须在状态容器里至少存四样东西:当前最新版本结果、累计发现的问题列表、已经尝试过的修复方向、每一轮的评估明细。这四样信息能让修正器知道“哪些坑已经踩过了”,从而避免重复劳动。
不过状态也不是存得越多越好。上下文长度有限,如果每轮都把之前所有版本的全文带进去,第3轮开始上下文就会爆炸。我建议只保留当前版本、上一轮问题清单、以及历代问题摘要(可以用模型把已解决的历史问题压缩成一行摘要)。这相当于给循环加了一个“遗忘机制”,只记住关键的增量信息。
2.5 终止和升级路径:循环到底是全自动还是半自动
最后一个要决策的是循环的终点。是让控制器在评估全通过时自动结束,还是在达到最大迭代次数后交给人工兜底?我的建议是任何面向生产场景的循环都必须设计“升级路径”:当循环无法收敛,也就是评估始终不通过但迭代次数已用完时,系统应该把当前版本、问题清单、循环历史打包交给人类,而不是默默输出一个次品。
这种设计带来的最大好处是安全可控。“全自动循环”是理想态,但现实里总有评估器漏判的角落,保留一个最终人工判断节点,既是对系统负责,也让使用的人对AI产出建立信任门槛。
3. 保姆级实操:从零构建一个文档审校自动循环
3.1 场景设定与循环设计
拿一个真实的场景来练手:自动审校一份技术方案文档。假设你刚用AI起草了一篇3000字的架构设计文档,里面覆盖了系统模块划分、数据流、部署方案、风险控制四个部分。你的目标是让循环自动把这份初稿打磨到可以直接提交评审的程度。
先用前面五个参数设计一下这个系统:
- 循环类型:反思循环为主,因为文档质量没有外部工具能客观验证
- 最大迭代深度:3轮
- 评估标准:五维检查卡(准确性、完整性、格式、冗余、一致性)
- 状态容器:保留当前版本 + 本轮问题清单 + 已修复摘要
- 终止条件:五项检查全部通过,或者第3轮结束
3.2 构造评估清单和修正提示词
评估器的提示词模板,我建议写成这种结构化格式:
你是一名资深文档评审。以下是一份技术方案文档,请按下列检查项严格评审。 文档内容: {{DOCUMENT}} 检查项: 1. factual_accuracy:文档内部是否存在逻辑矛盾或不严谨表述? 2. completeness:是否缺少必要章节或内容覆盖? 3. format_compliance:标题层级、段落结构是否清晰? 4. conciseness:是否存在冗余重复内容? 5. consistency:术语文档中使用是否统一? 输出要求: - 对每个检查项给出 pass 或 fail - fail 的检查项必须列出具体的原文摘录和修改意见 - 结果以 JSON 格式返回注意“必须列出具体的原文摘录和修改意见”这句话。它非常重要,逼着评估器给出可执行的反馈,而不是空泛地说“需要提高表达质量”。
修正器的提示词则要携带状态容器里的历史信息:
你是文档修订专家。请根据评审意见修改文档,遵守以下规则: 1. 只修改评审意见中指出的问题,不要动其他内容 2. 如果某个修改会导致前后冲突,必须同步修改相关位置 3. 上一轮已经尝试过的修复方向(见下)不要再重复尝试 4. 修改完成后,用一句话列出你改了哪些位置 评审意见: {{EVALUATION_RESULT}} 已尝试修复方向: {{HISTORY_SUMMARY}} 待修改文档: {{DOCUMENT}}3.3 循环主逻辑代码实现
下面是一个可以直接跑的Python骨架。为了让代码不依赖具体模型服务商,我用call_llm()函数占位,你接OpenAI、Anthropic还是本地模型都行。
import copy from typing import Dict, List, Callable # 假设你封装了统一的LLM调用接口 call_llm = Callable[[List[Dict]], str] class DocumentReviewLoop: def __init__(self, generate, evaluate, refine): self.generate = generate self.evaluate = evaluate self.refine = refine self.state = { "current": None, # 当前版本 "all_issues": [], # 累计问题 "fix_attemps": [], # 已尝试修复方向(压缩摘要) } def run(self, seed_document: str, max_rounds: int = 3): self.state["current"] = seed_document for i in range(1, max_rounds + 1): print(f"--- 第{i}轮评审 ---") # 1. 评估当前版本 eval_result = self.evaluate( self.state["current"], self.state["all_issues"], ) self.state["all_issues"].append( {"round": i, "issues": eval_result["issues"]} ) # 2. 终止条件:没有失败项,提前停 if not eval_result["failed_items"]: print("评估全部通过,提前退出") break if i == max_rounds: print("达到最大迭代轮数,交给人工") break # 3. 修正当前版本 new_doc = self.refine( self.state["current"], eval_result, self.state["fix_attemps"], ) # 4. 记录修复摘要,用于下一轮的“别重复尝试” fix_summary = summarize_changes(new_doc, self.state["current"]) self.state["fix_attemps"].append(fix_summary) self.state["current"] = new_doc return self.state["current"], self.state["all_issues"]代码里有一个容易被忽略的细节:在终止判定时,我用的是“没有失败项”,而不是“评分达到多少分”。理由很简单,评分是连续值,边界模糊;而“是否有失败项”是离散值,边界清晰。控制器拿到离散值才能做布尔判断,这是工程上的刚需。
3.4 实测效果与调优记录
我拿一份真实的架构文档跑了一遍,初稿约3200字。第一轮评估,五项检查里fail了三项:完整性缺了部署方案的故障回退机制、一致性上有两三处用了不同术语、冗余度有一段两段重了。修正器改完后,第二轮评估变成了只fail一项,第三轮全部通过,循环在第3轮自然收敛。整个流程耗时约40秒,消耗约1.8万token。
过程中发现了一个很典型的调优点:第一轮修正后,修正器为了补全“部署故障回退机制”,新增了一段和原有高可用章节重叠度极高的内容,反而触发了冗余度检查。这让我意识到修正器的指令里必须加一条“如果新旧内容重叠,优先把重叠部分合并”,否则循环会在“补全”和“去重”之间来回拉锯。加完这条之后,第二轮就开始收敛了。
4. 项目实战:把Loop Engineering嵌进代码审查全流程
4.1 项目背景:PR质检为什么需要循环而不是单次审查
文档审校只是热身,真正让Loop Engineering发挥价值的是代码审查。在一家做数据服务的小团队里,我曾经搭过一个AI PR审查助手。在动手之前,我观察了团队小一个月:每个人的PR都要过一遍人工Review,成本高,效率低;另一边,代码里的低级问题(没加空指针保护、日志关键字段缺失、API版本没更新)其实高度同质化,用规则就能查出来,但规则引擎可扩展性差。
问题是,如果只用AI单次审查,效果很一般。我测试过直接把diff丢给模型让它提意见,它会提一些“提高代码可读性”“简化逻辑结构”这种不痛不痒的废话,真正关键的“此处漏了判空”反而漏掉。原因很清晰:一次性的模型调用缺乏自我校验,它不会发现自己的审查漏项。
用Loop Engineering的思路重新设计后,系统变成了:分析diff → 生成审查意见 → 用规则和测试验证这些意见的真实性 → 让模型基于验证结果补充第二轮更深入的分析。每一轮的评估器都是客观外部工具(代码规则文件、编译结果、单元测试),而不是模型的自我感觉。这正是前面提到的验证循环。
4.2 架构设计:分析、建议、验证、再分析的闭环
整个系统的流程是这样的:
- 抓取差异:通过Git API读取PR的diff
- 第一轮分析:模型读取diff和仓库规则文档,输出初步审查意见
- 规则验证:每条审查意见会映射到仓库规则文件里的具体条目,不能映射的直接丢弃
- 测试验证:对于涉及修复建议的意见,生成对应的代码改动草案,跑一次单元测试和静态检查工具
- 第二轮分析:把测试结果、静态检查输出、规则匹配结果一起打包给模型,让它输出最终审查报告
- 收敛判定:如果二轮分析没有新增超过阈值的新问题,结束;否则再补一轮
这套架构和文档审校的最大区别是:评估器不再是一句“请检查质量”,而是真的跑了一次工具链,用机器可验证的结果来筛选AI的幻觉意见。
4.3 关键实现:diff处理、审查循环、测试门禁
def run_pr_review_loop(diff_text, repo_rules, working_dir): # 第一轮:生成初步意见 prelim_issues = agent_analyze(diff_text, repo_rules) # 用规则文件过滤,保留能落到具体规则的结论 filtered = [i for i in prelim_issues if rule_match(i, repo_rules)] # 对每个意见尝试生成补丁草稿并跑测试 verified = [] for issue in filtered: patch_file = generate_patch(issue, diff_text, working_dir) test_result = run_tests(patch_file, working_dir) linter_output = run_linter(patch_file, working_dir) if test_result.passed and not has_critical_lint(linter_output): verified.append({**issue, "test_pass": True}) else: # 涉及测试失败的意见单独标记,交给二轮分析判断 verified.append({**issue, "test_pass": False, "reason": test_result.summary}) # 第二轮:把工具链输出打包给模型,输出最终报告 final_report = agent_final_report(diff_text, verified, linter_summary, test_summary) return final_report这段代码的精髓是test_pass这个字段。有了它,审查报告自动分成两级:已通过工具验证的确凿问题和模型推断但工具未证实的问题。人看报告的时候优先级一目了然。测试门禁拦截掉的不只是模型幻觉,也拦截住了那些“看似合理、实则危险”的批量修改建议。
4.4 实测效果与上线数据
系统上线跑了约三周,我把数据整理了一下:
| 指标 | 上线前(纯人工Review) | 上线后(Loop系统 + 人工) |
|---|---|---|
| 单个PR平均审查时间 | 约45分钟 | 约8分钟(含人工复核) |
| 漏掉的中低级问题 | 基线 | 降低约55% |
| 模型幻觉性建议 | 无对比 | 过滤掉约80% |
| 上线期间新增的缺陷逃逸率 | 约9% | 约4% |
其中幻觉性建议的过滤率是最让我满意的。没有规则匹配和第二轮测试验证这两道关卡时,模型十条建议里大概有两三条是错的,用Loop结构把工具结果喂回模型之后,错误建议被自己的修正流程推翻了,最终到达人眼的报告已经全部经过验证。这验证了一个观点:评价循环的优劣,不要看它“提出了多少问题”,要看它“提出的问题里有多少经得起验证”。经不起验证的问题提得再多,反而会让工程师对系统失去信任。
5. 我在实际项目中踩过的坑和最终的收敛法则
5.1 自我强化的死循环:输出越来越长、越来越离谱
第一次跑文档审校循环的时候,我碰到的现象是:每一轮修正,文档都会变长,从3200字一路长到5700字。原因是修正器看到评估意见后,本能地用“补充内容”来响应批评——你说我不完整,那我把能想到的都写进去;你说我术语不一致,我把每个术语都解释一遍。结果第2轮之后,文档出现了大量重复。而评估器的检查项里没有“字数失控”这一项,于是它并没有发现新引入的问题,循环就在这种虚假的“改善”里继续空转。
这个坑的根因是:评估器只检查它被要求检查的东西,不会主动发现新问题。我后来在系统里加了一条硬约束:“如果本次修改后,文档长度超过上一轮的1.2倍,视为修改失败,回滚到上一版。”这就是用外部规则给循环套了一个方向盘。从此之后,文本类的循环再没出现过长度失控。
5.2 成本失控:一次循环烧掉几十万token
另一个项目里我犯过一个更大的错误:把一个PPT大纲生成任务做成了循环结构,每轮循环都要求“重新生成完整大纲”而不是“基于差异修改”。加上评估器要求读一遍完整大纲,3轮循环下来,输入输出token消耗超过了10万。而事实上同一个任务,设计成增量修改之后,3轮消耗只有1.2万token左右,成本相差8倍。
教训有两条。第一条是修正器必须尽量基于差异做增量修改,而不是每次都整体重写;第二条是评估器不要重复读取完整上下文,可以让控制器把复杂评估拆成多个小评估器,分别检查不同部分。模型API按token计费,节约token就是节约成本。我后来所有循环系统的评估器都设计成“只针对问题段做上下文切片”,效果极其明显。
5.3 评估器偏差:AI给AI打分的不确定
还有一次,我在评估器里写了“请根据经验判断该方案是否可行”,结果同一个方案在不同轮次里分别拿到了8分和3分。问题不在于模型不稳定,而在于“可行性”这个概念本身就是主观的。没有把“可行”细化成“有先例、有数据支撑、有预算、排期在两个月内”这些子问题之前,任何打分都是噪声。
后来我把这类主观评估全部强制转向“存在性判断”:一个方案是否提及了客户确认、是否包含回滚预案、是否列出技术预研步骤。存在性比优劣性更容易判定,循环的收敛速度也快得多。把主观问题客观化,是我在设计Loop时最常用的一条方法论。
5.4 我沉淀下来的四条收敛法则
实战踩过这些坑之后,我把Loop Engineering里“让循环能收敛”的经验压缩成四条规则:
- 外部先于自我:能用工具、测试、规则引擎当评估器的,永远不要让模型自我评估兜底,工具不会幻觉
- 增量优先于重写:修正器只改被指出的问题,不能借解题之机重写全文,否则每次循环都在引入新变量
- 离散优于连续:评估器输出尽量用“通过/不通过”的检查项,而不是凭空打分,连续值会让控制器的条件判断失灵
- 刹车必须存在:任何循环都要有最大迭代深度和人工升级路径,两者是系统安全的最后兜底
这四条法则对文本任务、代码任务、Agent任务都适用。你现在再回去看那些做得好的AI Agent产品,会发现它们的内部循环无一例外都遵守了这几条。
最后再分享一点实际体会:Loop Engineering不是银弹,它不会让一个平庸的提示词变成爆款生成器,它真正解决的是系统稳定性问题。它把人类做事时那种“做完检查、发现问题、回头修改”的自然过程,显式地变成机器可以执行的空间。我强烈建议你从一个小任务开始练手,比如给文档加个自动审校循环,跑通第一个闭环之后再往复杂场景扩展。循环的设计思路一旦上手,你再回头调那些Agent应用时,思路会完全不一样。