Skip to content
Zegging's Tech Blog
Go back

新的潮流“创建 Loop 来运行 Agent”,Loop 反而是最不重要的那一环

Loop 不重要

克特西比乌斯水钟

世界上最早被记录的人造反馈循环系统是公元前3世纪古希腊机械师克特西比乌斯发明的古希腊水钟(Water Clock,又称克里普西德拉)。水钟内部有一个浮标,随着缓慢滴入的水位上升,浮标也随之上升;当水位过高时,浮标会上浮并自动节流或关闭进水口;当水位下降时,浮标下落从而开启进水阀门。这种 低水位开阀 -> 水位上升 -> 阀门关闭 -> 水位下降重开 的圆形闭环过程,保持了水箱内部水压的恒定,进而确保了水流速度均匀,从而实现了精准计时。

Loop Engineering 其实就是在 Agent 的 loop 之上再构建一个大 Loop,这个大 Loop 负责对 Agent 的小 loop 的输出结果进行测试验证、审核判断,如果不满足就做一次反馈然后让 Agent loop 再次执行。核心是把 人工判断的劳动从快速迭代中解放出来。在水钟的例子里,那个不断上下浮动堵上阀门的浮标就是 “人” 的位置。

核心不是 Loop

部分在循环工程中的含义
目标系统最终要达到什么状态
状态系统当前知道什么
行动者谁去做事
反馈如何知道行动效果
决策规则下一步做什么
边界条件什么不能做、何时必须停

可以抽象成:

St+1=F(St,Ot,At)S_{t+1} = F(S_t, O_t, A_t)

其中:

  • StS_t:当前系统状态;
  • OtO_t:观察到的结果,例如测试失败日志;
  • AtA_t:Agent 执行的操作;
  • F()F():调度与决策机制,决定下一轮如何推进。

Loop 是简单的,是那个通用的或者专业的 Agent,是上一个火热关键词 Harness。最难的是 F()F(),是那个用来替代人工判断的函数,需要通过它的反馈或者决定来判断一件事“做好了没有”、“做对了没有”。

如何设计正确的、有效率的 F()F() 才是 Loop Engineering 的核心。与其说是 Loop Engineering,不如说是 Eval Engineering。

Eval 是困难的,一直都是

Eval(之所以困难,核心不在于“给结果打一个分”本身,而在于:你真正关心的目标通常很复杂、模糊、长期、难观测;而你能计算的分数,往往只是它的代理指标。

指标不能完全反映真实

LLM 训练中可以把人真正想要的质量记为:

U(answer)U(\text{answer})

其中 U 可能同时包含:

  1. 事实正确,推理可靠;
  2. 对问题有针对性;
  3. 表达清楚,不夸大
  4. 不遗漏关键限制;
  5. 遇到不确定内容会说明;
  6. 不造成安全问题;
  7. ……

但训练时我们通常只能构造某个可计算的代理分数:

R^(answer)\hat{R}(\text{answer})

例如:

  • 人类偏好排序;
  • 奖励模型打分;
  • benchmark 准确率;
  • 一个 LLM-as-a-judge 给出的分数;
  • 代码是否通过测试;

问题在于:

R^(answer)U(answer)\hat{R}(\text{answer}) \neq U(\text{answer})

代理指标只覆盖真实目标的一部分,并且根据 Goodhart 定律:当一个指标成为优化目标时,它往往不再是一个好指标。假设你把“用户偏好”作为目标,并训练奖励模型来判断两个回答哪个更好。模型可能发现一些和“真实帮助”相关、但并不等价的表面特征。reward hacking,即模型优化了奖励信号的形式,而没有真正完成你想要的目标。

真正好的回答:

我无法确认这个数字,因为来源不够可靠。

可能更容易拿高偏好分的回答:

根据多个公开来源和行业趋势分析,这个数字大致可确定在 15% 到 20% 之间;不过具体情况仍受地区、时间和统计口径影响。

第二段听起来更全面、更专业,但它可能在没有证据时编了一个范围。

反馈需要迅速

对于短任务,可以直接检查输出。但长期任务可能是:持续维护一个代码库、自动运营客服流程、研究一个复杂问题……这类任务的难点是:

最终质量=f(多步骤的累积决策)\text{最终质量}=f(\text{多步骤的累积决策})

对于快速迭代的任务,如果一次代码改动只需要 5 分钟但是集成测试需要 1 小时来运行……

便宜不好用,好用不便宜

Eval 的一个核心矛盾:可靠评估通常昂贵,便宜评估通常不够可靠

它不是说“便宜的 eval 一定没用”或“贵的 eval 一定正确”,而是说:越接近你真正关心的现实结果,通常越难、越慢、越贵地测量。因为要让评估更接近真实效用,你通常必须付出更多成本,补上廉价指标忽略掉的东西。这些成本不只是钱,也包括时间、专家注意力、工程复杂度、延迟和风险。

真实世界

关于代码

代码的好坏,是一个没有固定标准的事情,每个指标都只能反应代码某个维度上的好和坏。即便是身边最资深的开发对于某段代码的好坏判断也并不都是一致的。如果认为系统健壮是好的唯一指标,Agent 就会给代码里各处完全不需要进行防御性检查的地方加上各种 check,复杂度膨胀又或者是性能下降。众所周知,模型往往会观察到一些局部故障并增加局部防御。

对很多核心系统而言,正确修复方式不是“把每一种坏输入都处理掉”,而是让坏状态根本无法被表达、创建或写入

例如,一个订单状态系统可能只允许:

created → paid → shipped → completed

而不允许:

shipped → created
completed → paid

较弱的写法是到处防御:

if order.status == "completed":
    return

if order.status == "shipped":
    return

if order.status not in VALID_STATES:
    return

较强的设计是从状态模型、类型、数据库约束、状态转换 API 上保证非法转换不可发生:

def ship(order: PaidOrder) -> ShippedOrder:
    ...

或者:

CHECK (status IN ('created', 'paid', 'shipped', 'completed'))

或者在状态机中只暴露合法迁移:

created  --pay-->  paid  --ship--> shipped

在单次修改里,每一步都很容易被解释为“更健壮”。但经过外层 loop 连续迭代,这种循环会不断放大模型“局部防御”的倾向,使代码表面上更健壮,实际上更复杂、更难理解,也更难维护。

充满信心的未来

Bun 从 Zig 迁移到 Rust、优化算法性能、科学实验……我们可以看到一个又一个 Loop Engineering 成功的例子,他们都有一些共性,一些观点和范式被重新发掘出来成为一个新的潮流关键词。如果时尚是一个轮回,Loop Engineering 这些潮流词汇可能也是工程的一个轮回,已有观点和概念的再次发现,

Conant–Ashby:

Every good regulator of a system must be a model of that system. 一个真正有效的调节器,必须以某种形式包含它所调节系统的模型。


Share this post on:

Previous Post
一种 LLM Agent Runtime 的形式化模型
Next Post
在 Ubuntu 裸机上部署一个 self-host 的 E2B 服务