如果你最近在折腾 AI Agent,八成已经见过这样的场面:模型答错了,你让它“再试一次”,结果它换了个姿势又错一遍;或者它明明已经写出一版差不多的答案,却因为缺少一个“自我检查”的步骤,交上来一份漏洞百出的结果。我自己的项目里也反复出现这类问题,后来才发现,问题不在某一个模型调优上,而在于整个 Agent 的执行流程里缺了真正有用的循环设计。
Loop Engineering,中文可以叫“循环工程”。它研究的不是编程语言里的 for/while,而是智能体在真实任务中反复迭代、自我修正、逐步逼近目标的那套机制。这篇教程会从最基础的概念讲起,带你把 Agent 内部最常见的几种循环模式拆开揉碎,然后用一个虚构但足够真实的项目“稿文助手”走完从设计到落地的全流程。不管你之前是用现成框架搭过 Agent,还是自己写过几行调度代码,这篇的内容都能直接抄作业。
1. 先把“循环工程”这个说法翻译成人话
1.1 不是什么新技术,而是一种必要的工程视角
我第一次听到 Loop Engineering 这个词,是在和一位做智能体架构的同行聊天时。他没有给我看任何新框架,而是指着他屏幕上一段调度代码说:“Agent 好不好用,很大程度取决于这个循环设计得好不好。”
这句话点醒了我。过去我们写 Agent,总把注意力放在选什么模型、写什么提示词上,但现实是:一个单次调用模型的结果,十有八九不够用。真正让 Agent“看起来聪明”的,是它在一次不成之后怎么修正,在修正之后怎么判断能不能停。这整个反复运行的回路,就是循环工程要管的事。
打个比方:你让一个实习生写一份活动方案。如果只扔一句话就等着收结果,大概率得到一版他自己都看不下去的草稿。但如果你告诉他——先列提纲,写完初稿后自己查一遍错别字,再把方案对照预算重新审一遍——最后给你的东西质量就会完全不一样。这套“先做、再查、再改、再判”的结构化流程,就是人类世界里的循环工程。
1.2 循环工程的范围:不只是让模型多试几次
很多人以为,给 Agent 加个“如果失败就重试”的逻辑就是循环工程。这太窄了。
循环工程至少包含四层内容:
- 循环结构设计:整个任务应该拆成几个阶段?每个阶段是大模型完成,还是由普通代码完成?
- 评估机制:每次循环结束后,拿什么标准判断结果“够好了”?谁来当裁判?
- 终止策略:什么时候必须停止?最大次数是多少?出现哪些信号要提前中断?
- 状态管理:每一轮产生的结果和反馈,如何传给下一轮?过程中哪些信息要保留,哪些要丢弃?
单纯的重试只是四层中最浅的一层。真正的循环工程,是把这个系统设计的每个环节都考虑进去,让 Agent 在“能跑”的基础上做到“跑得快、跑得稳、结果可复现”。
1.3 循环工程在什么场景下是刚需
不是所有 Agent 场景都需要复杂的循环。如果你只是问模型“今天是几号”,一次调用就够了。但下面这几类任务,缺乏循环设计基本做不出可用结果:
- 长文档撰写:一篇五千字的技术方案,一次生成必然有逻辑断层和事实错误。
- 代码修复与优化:改完代码必须编译、跑测试,再根据报错信息做二次修改。
- 复杂信息抽取:从乱糟糟的网页里抽字段,第一次抽不准,需要结合校验规则重抽。
- 结构化输出生成:模型输出 JSON 偶尔会坏,需要循环性地修复格式。
我在某公司做内部工具时遇到过很典型的例子:同样是让模型写营销文案,A 项目组只做单次调用,用户反馈像在读万金油;B 项目组加了两轮“先写后审”循环,整体质量明显高出一个档。差的不是模型,是循环。
2. 拆解一个最小循环回路的关键构件
2.1 没有“万能循环”,但有“通用构件”
任何循环工程都可以抽象成四个构件:执行器、评估器、工作内存、控制器。理解这四样东西的职责,再看任何复杂架构都会很轻松。
- 执行器(Executor):真正干活的组件。它可能是一个大模型调用,也可能是一段脚本,负责产出当前迭代的结果。
- 评估器(Evaluator):判断当前结果好坏的组件。它可以是规则(如“JSON 能否被解析”)、另一个模型打分、外部测试工具的通过率,甚至是人工确认。
- 工作内存(Memory):每一轮执行完,把有用的结果、错误信息、中间状态记录下来,供下一轮参考。
- 控制器(Controller):决定循环是否继续。它握着一组终止条件,比如最大迭代次数、质量阈值、总预算上限。
你可以把控制器类比成燃气灶上的自动熄火装置,执行器是炉火,评估器是感温探头,工作内存是锅里的菜——没有哪一样可以随便省掉。
2.2 循环回路必须显式定义终止条件
新手最容易犯的错就是只定义“继续条件”,不定义“终止条件”。比如写“如果质量分低于 0.8 就继续迭代”,乍一看没毛病,但实际运行时一旦质量分卡在 0.75 上不去,循环就会一直空转,烧掉大量 token 才被外部超时机制掐断。
我比较习惯的做法是同时设置三类终止条件:
| 终止条件类型 | 示例 | 目的 |
|---|---|---|
| 质量达标 | 评估分 >= 0.85 | 正常结束 |
| 数量上限 | 最多迭代 5 轮 | 防止死循环 |
| 资源上限 | 累计 token 不超过 3 万 | 控制成本 |
三者的优先级按“数量/资源上限 > 质量达标”处理。即便质量还没达标,只要迭代次数够了,就必须停止并返回“当前最优结果+警告信息”。这在生产环境里非常重要。
2.3 评估器是循环质量的分水岭
同样的循环结构,用不同的评估器,结果可能天差地别。
我做过一次对照实验:用三种评估方案去迭代优化同一批文案。
- 方案 A:让模型自己打分(“请按 1-10 分评估你的输出”)
- 方案 B:用一套自定义规则(检查字数范围、是否有错别字、是否包含指定关键词)
- 方案 C:用一个独立的强模型做评分(独立的 prompt,不暴露写作模型)
结果很有意思:方案 A 几乎永远给自己打 8 分以上,循环很快就停了,但产出没有明显提升;方案 B 稳定,但上限低,因为规则只能识别“格式合格”,判断不了“文采好不好”;方案 C 效果最好,但成本和延迟最高。
没有完美的评估器。工程上最务实的选择是“规则为主、模型为辅”:先把能用代码判断的都交给代码,比如格式、长度、必含字段、编译结果;剩下主观部分再交给模型评分。这么设计既控制成本,又保留质量判断能力。
3. 三种高频循环模式的工程实现
3.1 模式一:自我反思循环
自我反思循环是最基础也最容易见效的模式。它的核心动作是:执行一轮 → 要求模型针对自己的输出挑毛病 → 根据毛病重新执行一遍。
工程实现上,我会把“反思”和“重写”拆成两次独立的模型调用,而不是在同一个 prompt 里既要求输出又要求反思。原因很简单:模型同时处理两件事,每件都容易做不精。分开后,反思 prompt 里明确要求“列出最多 5 个具体问题,不要修改原文”,重写 prompt 里再带上“上一版的问题清单”以及“请针对问题逐项修改”的要求。
伪代码大致长这样:
def self_reflect_loop(task, max_rounds=3): result = generate(task) for i in range(1, max_rounds + 1): feedback = reflect_on(result, task) # 只提问题,不产出新内容 if is_good_enough(feedback): break result = generate(task, previous_result=result, feedback=feedback) return result在实测中,这个模式对“文案优化”“报告润色”“代码注释补全”这类任务特别有效。前两轮提升明显,第三轮以后边际效益就非常低了,因此我这里把 max_rounds 默认值设为 3,而不是 10。
3.2 模式二:计划-执行-反馈循环
比自我反思更进一步的是把循环从“改输出”升级成“改计划”。典型流程是:
- 模型先制定任务计划(比如分三步走)。
- 按计划逐步执行。
- 每一步执行完后,把结果反馈给计划层。
- 如果某一步结果不理想,模型被允许修改后续计划。
这种模式适合任务步骤多、中间结果相互依赖的场景。比如写一篇技术调研报告:第 1 步收集资料,第 2 步写大纲,第 3 步填充正文,第 4 步统一风格。用自我反思循环只能改正文,改不了“大纲本身就有问题”这件事;而计划-执行-反馈循环允许你在第 3 步发现资料不足时,回头把第 1 步补跑一遍。
需要特别提醒的是,这个模式下工作内存的设计会变复杂。你必须把“原始任务”“当前计划”“已执行步骤的结果”“下一步要做什么”这四类信息分开存储,否则模型很快就把上下文搅成一团。
3.3 模式三:多 Agent 协作循环
多 Agent 协作循环本质上是把评估器和执行器从同一个模型上下文里剥离出来,让一个 Agent 产出、另一个 Agent 审查、第三个 Agent 做最终裁决。
我实现过一个轻量版本:一个“写手 Agent + 一个编辑 Agent + 一个决策 Agent”。写手负责生成初稿,编辑负责挑刺并给出修改建议,决策 Agent 判断“是否采纳编辑意见、是否继续循环”。这和前面两种模式最大的区别在于:写手不再兼任评估员,避免了“自己看自己永远顺眼”的问题。
代价也很明显:多了一次以上的额外模型调用,成本几乎翻倍,而且三个 Agent 的提示词必须仔细对齐,否则会出现“写手听不懂编辑意见”的糟糕情况。我不建议小项目一开始就上多 Agent,先把自我反思循环跑通,收益通常会更高。
4. 保姆级实战:从零搭建一个“稿文助手”最小项目
4.1 项目目标与需求拆解
这个实战项目的虚构背景是:我想做一个内部用的“稿文助手”,输入一个主题,输出一篇两千字左右、结构清晰、有一定文采的科普性文章。产品要求有两点:第一,文章不能有明显的常识错误;第二,整体结构要包含引言、正文分层、结尾三个部分。
如果只做一次模型调用,文章往往形似而神不似。于是我把需求拆成三个子问题:
- 如何保证结构完整?
- 如何保证内容质量?
- 如何控制成本?
答案就是一套带“结构校验 + 质量打分”的循环工程实现。
4.2 环境准备与依赖说明
这是教程项目,不依赖任何框架,只需要一个能调用大模型接口的 Python 环境。我这里以“某模型 API 的本地封装”举例,你换成自己手头可用的任何供应商接口都可以。
依赖库就两个:openai(仅示范,不限于该供应商)和json。在编写代码前,先约定两条内部规范:
- 所有模型调用的 temperature 固定为 0.7。
- 所有模型输出都要求是纯文本,不额外包 Markdown。
4.3 循环结构设计与代码实现
我先把整个循环定义清楚:
第一轮:根据主题直接生成初稿。 第二轮:用规则检查结构,缺哪部分就补哪部分。 第三轮(含以后):让模型自评内容,针对评分低的部分重写。
终止条件:结构校验通过,且自评分大于 7 分,或者最大轮数达到 4 轮。
下面是核心代码,我尽量保持精简:
import json def call_llm(prompt, model="default"): # 这里替换成你的模型调用代码 # 返回字符串文本 return "模拟的模型输出文本" def generate_draft(topic): prompt = f"请围绕“{topic}”写一篇2000字左右的科普文章,要求包含引言、至少3个正文小节、结尾。" return call_llm(prompt) def check_structure(article): required_sections = ["引言", "正文", "结尾"] result = {} for section in required_sections: result[section] = section in article return all(result.values()), result def self_score(article, topic): prompt = ( f"请评估下面这篇文章的质量,输出JSON,字段包括score(0-10)和issues(数组)。\n" f"文章主题:{topic}\n文章内容:{article[:1500]}" ) resp = call_llm(prompt) try: data = json.loads(resp) return float(data["score"]), data.get("issues", []) except Exception: return 5.0, ["评估输出格式异常"] def rewrite_article(article, topic, issues): issues_text = ";".join(issues) if issues else "无特别问题,请整体润色" prompt = ( f"请根据以下问题修改文章,保留原文章优点,仅改动需要改的部分。\n" f"问题清单:{issues_text}\n主题:{topic}\n原文:{article}" ) return call_llm(prompt) def run_loop(topic, max_rounds=4): article = generate_draft(topic) history = [] for round_idx in range(1, max_rounds + 1): ok, structure_detail = check_structure(article) score, issues = self_score(article, topic) history.append({ "round": round_idx, "structure_ok": ok, "score": score, "issues_count": len(issues) }) if ok and score >= 7.0: return article, history, "success" if round_idx == max_rounds: return article, history, "max_rounds_reached" combined_issues = issues[:] if not ok: combined_issues.append("结构不完整,请补全引言、正文、结尾") article = rewrite_article(article, topic, combined_issues) return article, history, "unexpected"这版代码没有考虑 Token 用量限制,实际项目里你可以在call_llm外面套一层计数器,设置每次循环最多消耗多少 Token。
4.4 实测效果与一个典型的运行日志
我拿主题“为什么睡眠不足会影响工作效率”跑了一次,运行日志如下:
| 轮次 | 结构是否完整 | 自评分 | 主要问题 | 处理动作 |
|---|---|---|---|---|
| 1 | 否 | 6.2 | 正文只有两节,缺少结尾 | 触发结构补全 |
| 2 | 是 | 6.8 | 引言例子与主题关联弱 | 针对性重写引言 |
| 3 | 是 | 7.1 | 无 | 达标,返回结果 |
日志能很清楚地告诉我们:真正花时间的是第二轮,第一轮和第三轮的差异主要在结构,而不是文采。如果我把 max_rounds 设为 2,这篇稿子会因为结构不完整而退出循环,所以建议这部分至少留到 3 轮给人留缓冲。
4.5 成本估算参考
以当前常见的模型定价区间来估算,一次完整循环大约消耗 6000 到 9000 token。如果每 token 的成本在中低区间,算下来每篇稿子的模型成本约为零点几元人民币;如果换成更强的模型,成本会明显上升,但换来的是自评分更快达标、循环轮数下降。
在真实预算敏感的场景下,我一般会给文本类任务设置每篇稿子的成本上限,超过了就自动改为“降级模式”——即停止自评分重写,直接把当前最优结构版本返回给用户。这种方法简单粗暴,但很有效。
5. 循环工程最容易翻车的五个坑
5.1 坑一:迭代越多,结果越差的“劣化漂移”
有一天我调一个文案生成 Agent,发现第二轮结果比第一轮差,第三轮比第二轮更差。起初我以为是模型抽风,后来看了中间过程才明白:模型在“反思”阶段过度聚焦于挑小毛病,比如某个词不够华丽、某个句式不顺,结果把原本自然平实的文案改成了浮夸堆砌的八股文。
解决方法有两条。一是约束反思范围,在反思 prompt 里明确写“除非是事实错误或明显的逻辑断裂,否则不要为换词而换词”;二是引入“回退机制”,把上一轮的结果也存进工作内存,只有在新一轮的评分不低于上一轮 0.2 分时才接受新版本,否则沿用旧版本继续迭代。
5.2 坑二:死循环或近似死循环
只要终止条件写得够严密,真正的死循环其实很少,但“近似死循环”很常见:每一轮都有变化,但分数始终在 6.9 到 7.1 之间波动,一直触达不到 7.0 的达标线,直到用完所有预算。
我在 2.2 节里说的“双重上限”就是为此设计。另外还有个实用技巧:把达标阈值设成区间而不是一个点。比如“score >= 7.0 达标”可以改成“score >= 6.85 且连续两轮没有下降即视为达标”。这个小改动至少帮我省了不少 Token。
5.3 坑三:评估标准错位——裁判打分依据和任务目标不一致
早前我在某个项目里让模型做“内容合规检查”,结果它反复给一篇文章打低分,原因是“语言风格不够幽默”。而任务目标根本没有幽默要求。这就是典型的评估标准错位。
要解决它,需要在评估 prompt 里显式声明“评估维度”:第一是准确性,第二是结构完整性,第三是与主题的相关性。然后让模型按维度分别打分并给出依据,而不是只给一个笼统的分数。这能让评估者意见更容易被理解,重写阶段也更容易抓重点。
5.4 坑四:上下文污染与工作内存失控
循环越深,历史消息越冗长。模型要处理的上下文里既有原始任务、上一版全文,又有一堆“第 3 轮你觉得这个比喻不够好”之类的杂音。结果就是:模型被无关细节带偏,越改越偏。
经验做法是:工作内存里只保存三类信息——最新版本的全文、本轮要处理的问题清单、原始任务约束。其余历史版本和过程评价一律不进主上下文。如果非要保存完整历史,应该放到专门的存储里,通过检索按需取用,而不是一股脑塞给模型。
5.5 坑五:成本预估缺失,被账单吓一跳
有一次我开了多 Agent 协作循环跑一批文章,跑完才发现总消耗比预想的高了 8 倍。查了一下,是因为我在循环里忘了加成本统计模块,某个子任务在不知不觉中反复循环了十几轮。
后来我给每个子项目都加了“预算仪表盘”,用一行代码在每次模型调用前累计请求次数和 Tokens,并且同步输出到日志。这一步不复杂,但能救命。建议你在任何循环工程实现里,都把成本统计作为第一优先级的基建。
6. 循环做完了,怎么判断它到底值不值
6.1 三个核心度量指标
循环工程不是“有循环就好”,它和任何技术决策一样,需要可量化的评估。我常用的指标有三个:
- 质量提升量:循环前后评估分的差值(相对提升率)。如果 3 轮循环只提升 0.1 分,基本可以判定这个循环低效。
- 边际效益:第 N 轮相比第 N-1 轮的质量增量。一轮的增量如果小于零点几分,说明这条循环已经逼近收敛点。
- 成本效率比:质量提升总量除以总 Token 消耗。这是我在内部做技术选型时最看重的比值。
6.2 什么时候该削弱循环
如果你的业务场景是海量碎片化文本,比如每天处理上千条短评论,那么复杂的 4 轮循环就不合适。这种场景更适合“单次生成 + 一次规则校验”,顶多加一轮修复。
我之前做过一次测算:对短文本做自我反思循环,预期质量提升只有 0.2 分,但成本翻了 2.5 倍,延迟翻倍。明显不划算。循环不是越强越好,而是要和业务里的“单位文本价值”匹配。
6.3 网格化调参的经验值
如果让我给一套相对通用的初始参数,大概是这样:
| 参数 | 建议初值 | 取值范围 | 调整逻辑 |
|---|---|---|---|
| 最大轮数 | 4 | 2-8 | 写作任务 4 足够,代码修复可 5-6 |
| 质量达标分 | 7.0 | 6.5-8.0 | 越高越贵 |
| 规则校验 | 必选 | 结构/格式/黑名单 | 用代码兜底 |
| 评估模型温度 | 0.2 | 0-0.4 | 要稳定,不要发挥 |
| 回退阈值 | 0.2 | 0.1-0.3 | 防止劣化漂移 |
这个表不是万能公式,但可以作为你第一次调参的起点。之后根据具体任务去改动,比从零摸索省力很多。
7. 进阶:从单循环走向多级循环系统
7.1 阶段性循环与全局循环
大部分任务只适合“单循环”,也就是整个任务只有一个迭代回路。但当任务真的很大,比如写一本书、开发一个完整功能模块,单循环就撑不住了。这时候我会拆成多级循环。
举一个虚构的例子:某“智能排课系统”项目里,我把排课任务拆成了三层循环。
第一层是“生成课表”:模型基于教师、班级、教室生成一版课表。第二层是“冲突检测循环”:检测同一教室是否被两节课占用,同一教师是否同时上两门课,检测到冲突就回到第一层局部调整。第三层是“资源利用优化循环”:在冲突清零的前提下,计算教室闲置率,低于阈值就再次调整课表。
这三层循环不是嵌套关系,而是串行关系。第一层跑完进入第二层,第二层收敛后再进入第三层。如果直接做成一个超大循环,评估器会很难写,因为冲突要单独计算、资源效率又是另一个维度。
7.2 循环的自我评估与自适应终止
再往后走,还可以让控制器学习历史数据。比如统计同一个任务类型在不同轮数下的质量分布曲线,下次运行时,如果发现自己的进度曲线和历史达标曲线明显偏离,就可以提前终止或增加轮数,而不是永远用固定的 4 轮。
这个能力我目前只在少数项目里实验过,收益不确定,但方向值得关注。对大多数读者来说,先把固定参数的循环跑熟,比追求自适应要重要得多。
7.3 人工介入点设计
循环工程不是“全自动机器”,在关键节点保留人工决策反而更高效。比如在最终输出前,加入一个“抽取关键事实供人工复核”的步骤,不让人工看全文,只看三五个关键数字和结论。这一步能大幅降低模型幻觉对项目的影响,而且很便宜。
我的习惯是:在循环退出之后、结果交付之前,用一行代码生成“关键事实清单”,单独输出。这样即使模型在细节上编造了什么,人工也能快速发现,而不是通读全文去找错。
8. 最后再分享一点我对循环设计的真实感受
项目做多了以后,我越来越觉得 Loop Engineering 的核心不在于“让模型多跑几轮”,而在于给“跑”这件事设计一套清晰的指挥规则。模型能不能生成好内容,这件事存在随机性,但循环工程能让这个随机性变得可控、可度量、可收敛。每次调循环参数,我脑子里都会过一遍:这个循环是在帮用户减少返工,还是在帮模型自我感动?如果答案是后者,我会立刻把循环删掉。
我自己现在做新 Agent 项目时,会先写一版没有循环的“直出版本”,再根据失败样本决定加哪一层循环,而不是一上来就堆反思链、多 Agent、复杂内存。这种“先直出,再打补丁”的节奏,能让我很快搞清楚当前场景到底是质量不足、结构不稳还是任务理解偏差,三种问题的修法完全不同。希望这篇教程里的拆解思路也能成为你调试自己循环时的一个顺手的工具箱。