Loop Engineering 101

你已经会 harness engineering —— 给模型准备 prompt、上下文、工具调用协议。下一步是把"一次调用"变成"一个能自我纠正的闭环"。这一页用最短路径把差异讲清楚,配一个可点的 loop 模拟器让你看清 state → action → observation → termination 的实际流转。

state feedback termination observability retry policy

1 · 你现在在哪

Harness engineering 关心的是单次调用的质量:system prompt、tools schema、structured output、上下文注入、token 预算。产出是一个"能被调用好"的 LLM 端点。

Loop engineering 关心的是把这个端点放进反馈回路里:每一步调用之后,环境(真实世界 / 工具 / 判官 / 用户)会返回一个观察,你要用它更新 state 并决定"继续、修正、还是停"。产出是一个能自己走完一个多步任务的 agent。

一句话总结: Harness 让一次调用尽量正确,Loop 让整个过程在偶尔出错时还能收敛。

能力对照

维度Harness engineeringLoop engineering
时间跨度1 次调用N 次调用组成一个 episode
核心数据prompt + tools schemastate + history + observations
失败模式格式错、幻觉、越界震荡、死循环、幻觉累积、goal drift
调优对象system prompt、few-shot、schemastep function、终止条件、retry、reflection
评估输出对不对能不能收敛到目标、代价、鲁棒性
常用工具OpenAI SDK、function callingDSPy / LangGraph / 手写 while + verifier

2 · 一个 loop 的最小骨架

忘掉框架,先记住这 5 个部件。任何 agent loop 都是它们的组合:

┌──────────────────────────────────────────────────────────┐ │ LOOP │ │ │ │ state ──▶ policy(state) ──▶ action ──▶ env(action) │ │ ▲ │ │ │ │ ▼ │ │ └────── update(state, obs) ◀──── observation │ │ │ │ 停 ? ── terminator(state) ── 是 ─▶ done │ │ │ │ │ 否 ──▶ 下一轮 │ └──────────────────────────────────────────────────────────┘

① State

此刻你知道什么。goal、已尝试的动作、错误历史、预算、外部环境快照。

坑: 只把 history 全塞回 LLM 不是 state 管理,是 context 堆积。真正的 state 应该被压缩/结构化:goal / facts_learned / open_hypotheses / budget_left。

② Policy

给定 state,产出下一步 action。这里就是你之前的 harness——一次 LLM 调用。

坑: 不要让 policy 自己决定"我完成了"。让外部 verifier 判断,policy 只负责下动作。

③ Env / Tool

执行 action 的地方:shell、HTTP、数据库、另一个 LLM (judge)、用户回复。

坑: 环境返回值要确定性——同样输入尽量同样输出。否则 loop 无法调试。

④ Observation → Update

拿到环境返回,抽取信号写回 state。不是 "print 出来就完事",要形成下一轮能用的结构化条目。

坑: 最容易错的一步。把 stderr 原封不动塞回 prompt = 模型在噪声里工作。

⑤ Terminator

什么时候停? 至少三个终止器一起用:success(目标达成)、budget(步数/token/钱到上限)、no-progress(连续 N 步 state 无变化)。

坑: 只有 success 一个终止器 = 死循环烧钱。三个都得有。

⑥(隐藏项)Observer

不属于 loop 内部,但等价重要:每一步的 trace 落盘。没有 trace 的 loop 无法调优,只能靠感觉。

做法: 每步记录 {step, state_before, action, observation, state_after, tokens, latency}

3 · 交互式 loop 模拟器

下面这个 loop 要解决一个假想任务:"修复一个 CI 挂掉的 Python 项目"。你可以看到 state 怎么积累、什么时候触发不同的终止条件。挑一个策略,然后一步步 step,或者 run 让它跑完。

Agent Loop policy: react
policy:

State (compressed)

Trajectory

step
0
tokens
0
tool calls
0
no-progress
0
status: running · budget 12 steps · no-progress limit 3

4 · 五个从 harness 迁移过来的直觉调整

  1. 不要把 "history 全塞" 当成 state。 每轮 loop 结束时挤压出一份结构化 state:{goal, facts, hypotheses, budget}。 history 只在 debug 时看。
  2. Policy 决定"下一步",verifier 决定"停不停"。 分开这两个角色。让同一个模型自评"完成了"是 loop 里最贵的 bug。
  3. Retry 有语义。 "试了三次同样的动作" ≠ retry;那是 stall。真正的 retry 需要在错误信号回到 state 之后改变动作
  4. 观察要收敛,不要发散。 tool 输出如果是 10KB 日志,先做一层抽取器(regex / 小模型 / grep)再进 state。不然 5 步以后 context 就烧完了。
  5. Trace 优先于优化。 先把每步 {state,action,obs} 记下来,再谈"要不要加 reflection / rerank / self-consistency"。没有 trace 就是在猜。

5 · 常见反模式

🔄 幽灵循环

模型每步都自信地说"再试一次",但 state 没变化。你没有 no-progress 终止器。

治: 加 stall 检测——连续 N 步 state hash 相同就 abort。

📚 History 肥胖症

第 10 轮的时候 context 有 40k tokens,全是老工具输出。模型开始忽略新信息。

治: 每轮显式 compress,只留最近一次 raw output + 累积 facts。

🎭 假 verifier

policy 自己说"任务完成",然后停了。跑完发现根本没修好。

治: success 判断要用可执行信号(跑测试、看 exit code、抓一次目标 URL)。

💸 无预算

没有 max_steps / max_tokens,凌晨三点 API bill 告警。

治: 硬编码 budget 是最便宜的保险。step ≤ 20,tokens ≤ 200k,超了直接 raise。

🌊 观察发散

工具吐 10KB 日志,你原样塞回 prompt。3 步后 context 爆炸。

治: observation 层做投影:只取 last error / diff / status,其他落盘存 trace。

👻 Goal drift

开始要"修 CI",第 8 步在讨论要不要重构模块系统。

治: state 里 goal 字段 read-only;每步都把它重新拼进 prompt 头部。

6 · 上手清单(今天就能做)

  1. 拿一个你现在用 harness 单次调用完成的任务。
  2. 写一个 while 循环,最多 10 步,里面调用你现有的 policy 函数。
  3. 加一个真正的 verifier:跑测试 / grep 结果 / HTTP 200 / diff 命中——外部信号。
  4. 加三个 terminatorsuccessstep >= 10stall >= 3
  5. 加一个 compressor:每轮末尾更新 facts_learned,raw observation 只保留最近一次。
  6. (step, state, action, obs) 落到 trace.jsonl
  7. 跑 20 个不同的输入,看 trace 里哪里"看起来蠢"——那就是下一轮要优化的地方。
心态: Loop engineering 的调优不是"让模型更聪明",而是"让流程在模型偶尔犯蠢时依然收敛"。你要做的更多是系统设计而不是 prompt 工程。

7 · 值得读的下一步