Loop Engineering 是一种模式,不是一个功能

Yaqin Hei··12分钟阅读
中文EN
Loop Engineering 是一种模式,不是一个功能

本文原题《Loop Engineering Is a Pattern, Not a Feature》,作者 Mike Piccolo——据其 LinkedIn 与 iii 官网公开资料,他是 iii(iii.dev,由 Motia LLC 出品)的创始人,此前是 FullStack Labs 的联合创始人兼 CTO,现为该公司董事会成员。需要先说清楚的是:这是一篇产品方创始人写的文章,后半篇基本是 iii 的实操演示。中文翻译、重排、配图本地化:Yaqin Hei(非原创,已保留原文出处)。

译者的话:为什么翻一篇软文

先把利益关系摆在桌上:作者是他所推销那个产品的老板。你在信息流里刷到这篇是不会知道这件事的,而知道之后,该怎么读它就变了——后半篇的 iii 演示当广告看,前半篇的那个判断当真话看。这两半可以拆开,因为前半篇跟他的产品其实没关系。

那个判断是:你的 agent loop 里,「这一轮该不该结束」不该由模型来答。

这话戳到的是国内现在做 agent 最普遍的一个默认动作。Claude Code、Cursor 这类 harness 教会了所有人一件事——万物皆可交给模型决定,于是「全 agentic」成了不假思索的审美。可 loop 里真正在跑的东西,大部分根本不是判断题:这批文件扫完没有、7 个子任务回来了几个、下一步该派给谁。这些是调度,不是判断。把调度也塞给模型,你买到的不是智能,是三样东西:延迟、账单,以及最难受的那个——偶尔出错

偶尔出错才是要命的地方。模型比对两个文件列表是否相同,大多数时候是对的。「大多数时候」意味着这个 loop 会偶尔早停、偶尔晚停、偶尔停不下来,而且不复现。你查不出来,因为下次跑它又好了。

对我自己最实际的改变,是它换掉了我做 agent 设计评审时的第一个问题。以前我上来先看 prompt 写得怎么样、工具给全了没有。现在我先问一句:这个 loop 的收工判定,是谁做的? 如果答案是「模型自己看着办」,那后面 prompt 写得再漂亮,这套系统的可靠性上限已经被锁死了——收工判定必须是一个能写单元测试的函数。这一问,把「这个 agent 靠不靠谱」从一个玄学话题,变成了一个在评审会上当场能问出口、当场能回答的问题。

下周你就能用的动作也很简单:拿你手上任何一个在跑的 agent,把它每一轮要做的决策一条条列出来,逐条问「这条非得让模型来吗」。我列过一遍,能砍掉的比想象中多得多。剩下砍不掉的那几条,才是真正需要判断的地方——那才是你该付 token 的地方

顺带说一句:loop engineering 这个话题本站已经翻过两篇(Karpathy 方法论四种 loop 分别什么时候用),它们讲的是怎么把 loop 搭起来;这篇补的是另一半——loop 搭起来之后,该由谁来开关它

以下是正文

昨天所有人都在做 agentic map-reduce。今天所有人都在做 loop engineering。明天又会有人来卖你下一个风口,而门票还是老两样:你的钱,和你的架构。

这就是 2026 年 AI 基础设施圈的循环,万一你侥幸躲过了没看见:一个有用的技术从实践里冒出来,被起了个名字,几周之内就有十来款产品把这个技术做成了自己的功能。每款产品都是一座围墙花园(自成一体、进去就不容易出来的封闭生态),有自己的一套做法、自己的配置结构、自己对「你能干什么、不能干什么」的主张。而你想要那个技术,就得连整座花园一起收下。

拿 loop 来这么干,尤其大胆——因为 loop 差不多是编程构造里最基础的那一档。一个 agentic loop 就是:调一次模型,看看返回了什么,决定要不要再来一轮。这不是一个产品品类!这是一个 while 语句。

在 iii 上,loop engineering 是你照着一个模式实现出来的东西,不是一个你必须另外买、另外接入的功能或产品。

这篇文章讲的就是它长什么样。

turn 编排器,以及 agentic 编排的麻烦

每一套 loop engineering 方案的核心部件,都是 turn 编排器(turn orchestrator,负责跑完 loop 的一轮):它跑一轮循环,并对外暴露生命周期钩子——比如「一轮开始时」「工具被调用时」「一轮完成时」。这些钩子就是你挂自己逻辑的地方。

编排层有多种实现方式。Claude Code、Codex 这类 harness(把模型包成 agent 的那层驱动壳)是全 agentic 的,意思是每一轮的每件事都由模型来定。如果每一次循环的决策者都是模型,那就意味着大量的 LLM 调用,带来延迟和成本——但最要命的是,它给结果带来了波动

LLM 是概率性的。设想你有这么一个任务:模型要扫一堆文件找安全漏洞。要知道所有文件是不是都处理完了,你可能需要把「已处理文件列表」和「全部文件列表」比一比,看两者是否一致。现在,你去问当下任何一个旗舰 LLM「这两个文件列表相同吗」——有时候你会拿到正确答案,但有些时候答案是错的。于是,一个靠文件列表比对来收工的 LLM 编排 loop,可能会结束得太早、太晚,或者根本不结束。这可不理想!

对于开放式的活儿,用 LLM 当 turn 编排器也许是可以接受的——因为好处(模型能对一组相当宽泛的标准做判断,从而知道这一轮到底完没完)盖过了它可能犯的错。

绝大多数被编排的工作根本没那么开放——它们是数值的、机械的、相当直白的。在这种场景下用 LLM 去判断一个 loop 结束没有,既浪费又贵,还慢,而且结果不稳。

全 agentic 编排的替代方案

我们早就有比 LLM 更擅长判断输入的东西了——它稳定、快、便宜、准确率高:函数!

你不必 100% 的时间都用 LLM 当 turn 编排器。不管当下最流行的那几个 harness 把你带成了什么印象,不全程 agentic 是完全 OK 的。

事实上,大多数 turn 编排根本不需要 LLM,做成一个简单函数反而更好。

LLM 编排的 loop 与函数编排的 loop:每一步决策的耗时与成本对比

同一个 loop 的两种编排方式。左边每一步都要问模型:约 1,842ms、约 $0.0210 一次决策;右边交给函数:低于 0.1ms、约 $0.00000001 一次决策。一个 10 步的任务,光编排就差出 18.4 秒和 $0.21。

接着上面那个文件列表的例子:如果为了知道自己干完没有,你需要比对两个文件列表是否一致——大多数编程语言的标准库里早就有现成的做法了。排序,然后比较。一两次函数调用,飞快,基本免费,而且每次都对

在 iii 里用 trigger 和 function 编排一轮

对刚接触 iii 的读者:它是一套简单得不太讲道理的软件工程方法。在 iii 里,万物皆为 Worker、Function 或 Trigger——即工作单元、函数、触发器。

我用了直接基于这三个原语工作的 iii harness,搭了一条工作流:扫描一个 GitHub 仓库找安全漏洞。

如果换成市面上其他 harness 那种标准的 LLM 驱动编排,这活儿会为每一轮多生出一堆 LLM 调用,加钱又加延迟。而在这个例子里,iii 用一个函数来判断一轮是否完成。

我是从这条 prompt 开始的(以下为原文 prompt 的中译,技术标识符保留原样):

扫描一个 GitHub 仓库,找出真实的安全漏洞,并给我一个实时结果看板。
仓库与子目录:https://github.com/wonderwhy-er/DesktopCommanderMCP/tree/main/src/handlers
用你手上现有的 worker 和协调方式自己想办法做出来——我描述的是结果,不是实现。
我要的结果:
覆盖选定目录树(跳过 vendored、生成的、二进制文件、PDF、图片)。只要真实、具体的漏洞——每条带文件、行号、
严重级别、一句话描述。不要代码风格类的挑刺。并行扫描,让大仓库能快速跑完,而不是一个文件一个文件来。
把每一条发现都持久化进数据库,让结果能留存、能查询。自己可靠地检测完成:知道每一部分都真的扫过了,
把这次运行标记为完成,把总数告诉我——不需要我盯着看或轮询。把发现按严重级别分组,呈现在一个简单的
网页看板上。
这套方案必须具备的性质(什么叫做对了,而不是怎么去做):
批量数据在 worker 之间直接流转,不要路由回这个对话。并行单元之间彼此独立:不要有一个大家抢着更新的
共享计数器。按需使用 fp::worker。任何时刻最多 5 个子 agent(系统内置了这个上限)。用 State worker 记录
状态、挂响应式钩子、做调度——比如某个子 agent 干完活、腾出名额之后就启动一个新的。任何地方都不要用
cron。这个 loop 必须是纯响应式的。指定的所有文件都必须被审查。已经能干这活儿的 worker 和 UI 直接复用,
不要重造。跑收尾那一步的东西,需要有写结果和写完成信号的权限,而不只是读权限。现在就开始:先摸清有
什么可用,定下方案,把你要做的事写成计划,等我批准再往下走。

harness 按我的要求先出了个计划,过程中把系统里以及 iii Worker 注册表里所有可用的 worker 都看了一遍。它发现注册表里有一个数据库 worker,但还没装。于是它把这个 worker 加进系统、启动起来,好拿它当数据库用。

harness 从注册表里发现并装上数据库 worker

harness 在注册表里发现数据库 worker 未安装,自行 worker::add 装上并启动——右侧是系统里已连接的 25 个 worker。

作为对比,其他 harness 会花掉相当多的 token 和精力去选一个数据库、把它加进来、再加一个 SDK、然后生成一堆用这个 SDK 所需的集成代码。用 iii,加一个 worker 并用起来只花了几秒钟和一小把 token。

接着 harness 建好了存放漏洞发现的表,并启动了另一路工作:创建一个网页界面并用 HTTP 提供服务。

现在说调度:在 prompt 里,我特意要求调度由 trigger 和 function 来做,而不是 LLM 层的编排。harness 注册了相应的 trigger,得到了顺滑、快速、便宜的流程控制。

注册一个 trigger:当 state 满足条件,就调用指定函数

一条注册好的 trigger:WHEN state 里 file_result:1 就位,THENfp::pipe,并 expect 全部 7 个 file_result 到齐——收工判定是一次列表比对,不是一次模型调用。

这些反应发生时,你能在主对话里看到它们被逐条记录下来:

主对话里实时滚动的 trigger 触发与等待记录

TRIGGER FIREDREACTION WAITING — 1/7 ARRIVED2/7 ARRIVED 交替出现:计数由引擎维护,模型不参与。

随后 harness 分析了全部文件——每个子 agent 领一批文件——把结果写进前面加好的数据库,再通过 UI 展示出来。

漏洞扫描结果看板,按严重级别分组

最终看板:结果落在 SQLite 里,子 agent 每扫完一批就更新一次,顶部标记 FINISHED

把探索性会话变成一个 worker

在这轮初步分析之后,我想把它变成一条更稳定的工作流,好在别的会话里用、也好接进我系统更大的一块——所以我让 harness 把这个扫描器变成一个 worker。

在会话里直接要求:把扫描器变成它自己的 worker

一句话交办:「现在把这个扫描器变成它自己的 worker。」

它照做了,并且当场就在会话里测了。你能看到这个 worker 热重载、加入系统。曾经的一次性 loop,现在成了一个可复用的漏洞扫描器。

worker 热重载,新函数上线并冒烟测试通过

worker 热重载后两个新函数直接可用,先冒烟测试 vulnscan::fetch-batch,再把协调器改成调它——子 agent 的任务从一大串降到两次函数调用。

然后我开了一个全新的对话,让系统用这个刚建好的 worker 去做分析,所有编排都无声无息地发生在 worker 内部:

新会话里直接复用 vulnscan worker,编排全部在 worker 内部完成

新会话里 vulnscan::fetch-batch 被直接调用;这一次注册了 57 条 trigger、触发 19 次,主对话里几乎看不到编排的痕迹。

原文还附了一段 5 分 58 秒的完整流程录屏,可在原推文查看。

沿着 LLM 技术栈往下走

大多数情况下,用函数做编排是最高效、最省钱、最可靠的选择。

但下面这些情形里,用 LLM 来编排是说得通的,iii 也会这么做。

当你从零搭一条工作流时,最合理的起手式是一次探索性的 agentic 会话。你还不知道这个问题长什么形状,而一个能打的模型是摸清它的最快途径。一开始就让它全 agentic 好了。看着它做。

但探索是一个阶段,它不该成为你的架构。一旦你能看清解法的样子,接下来的活儿就是把尽可能多的部分挪进确定性的代码——因为确定性代码在每一项指标上都更好:成本、延迟、可靠性、可测试性、可调试性。每一轮迭代你都该问自己那一个问题:

这里面真正还需要 LLM 的,最小的那一块是什么?

这一块之外的所有东西,都要沿着技术栈往下走:

  • 1 · 这事能用代码做吗? 能就用代码。免费、瞬时、而且正确。
  • 2 · 这事能用 ML 做吗? 一个分类器、一次 embedding 检索、一个小的微调模型——能就用它。比 LLM 更便宜、更快,准确率也更可预期。
  • 3 · 否则,才用 LLM。 用能干成活儿的最小那个。探索期先上 Fable 这样的前沿模型,然后往下削:换更小的模型、换更便宜的供应商,最后也许干脆不要 LLM。

**每往下走一级,成本和延迟都是数量级的改善,可靠性则上一个台阶。**一套按这条路子长成的成熟系统,只在真正需要判断的那些接缝处才有 LLM 调用,其余地方全是确定性的、有单元测试的函数。

在 iii 上,这条演进路径是自然发生的:你从一次探索性的 LLM 会话出发,做出一个靠代码加 LLM 调用干活的 worker,再一步步推进到更多代码、更少 LLM 调用。

为什么 iii 是干这件事的对路系统

在「从探索走向生产代码」这件事上,没有别家能做得像 iii 这样稳健和彻底,原因是结构性的。

这条往下走的路,只有在「一个步骤的 LLM 版本」和「它的代码版本」可以互换时才走得通。 在 iii 上,它们天然可互换:两者都是 Function,都由 Trigger 调用,都托管在 Worker 里。当 vulnscan::fetch-batch 从一次 Sonnet 5 调用变成一次数据库查询时,所有调它的地方一行都不用改。

这几个小原语在另一个方向上同样划算:正是它们让 iii 系统对 LLM 而言好用又可靠。一个在 iii 系统里干活的 agent,脑子里只需要装三个概念、一份永远准确的「现在有什么」的实时视图,以及一批名副其实的函数 ID。让你能把逻辑从 LLM 里搬出来的那种极简,同时也让 LLM 使得动剩下的那部分。

于是 iii 上的 loop engineering 最终长成这样:一个 loop,它的每一轮由 trigger 控制,它的校验器是有单元测试的函数,它的 LLM 调用被限制在真正需要判断的最小几块上,而它的每一个组件都能被独立观测、测试和替换。

读一读宣言,或者把你的 agent 指向 iii.dev/AGENTS.md,用 Worker、Function 和 Trigger 搭出你的第一个 loop。


原文:Loop Engineering Is a Pattern, Not a Feature · 作者 Mike Piccolo(@mfpiccolo) · 中文翻译与配图本地化:Yaqin Hei

Subscribe for updates

Get the latest AI engineering posts delivered to your inbox.

评论