内容更新:2026年9月28日

本文目录
  1. 循环到底是什么
  2. 循环实际控制什么
  3. 控制流:计划、行动、观测
  4. 停止条件 —— 最难的部分
  5. 预算:步数、token、时间、成本
  6. 验证:闭合循环
  7. 错误恢复与死循环
  8. 循环拓扑
  9. 跨迭代的上下文
  10. 用实证调循环
  11. 坑与取舍
  12. 常见问题
  13. 实例:修复一个失败的测试
  14. 参考资料与后续学习

AI Agent 循环会反复调用模型、执行获得授权的工具调用,再把结果送回模型,直到任务验证通过或达到限制。本文把这部分控制流设计称为 Loop Engineering(循环工程):重点是停止条件、预算、验证与错误恢复。它是 Harness 工程的一部分;后者还包括工具、权限与执行环境。

⚡ 快速要点
  • 循环把一锤子模型变成 Agent。 调用 → 行动 → 观测 → 重复,这个循环是探索、行动、自我纠错发生的地方。
  • 停止是最难的部分。 知道任务何时真正完成——既不早停、也不空转到天荒地老——是核心的控制流问题。
  • 每个循环都要有预算。 步数、token、时间、成本的硬上限不可省:它们是兜底,防止一个犯迷糊的 Agent 无限烧资源。
  • 验证闭合循环。 跑测试/构建并反馈失败,可以提供进展证据;通过不完整的测试并不证明整个任务正确。
  • 恢复是工程的大头。 工具报错、输出格式坏、Agent 反复重试一个失败动作("死循环"),都需要重试、死循环检测、逃生出口。
  • 拓扑是设计选择: 单循环、反思(行动 → 批评 → 重试)、计划-执行、编排器 + 子 Agent,各自适配不同任务。
  • 循环靠实证调优 —— 停止条件、预算、上下文处理的小改动会大幅摆动真实成功率,所以你用评测和轨迹分析来量。
tldr

模型提出下一步,循环决定能否执行、记录结果并检查进度。先采用单循环并明确退出原因;只有评测显示收益时,再增加反思或多 Agent。

AGENT 循环 —— 一次迭代 循环控制器 退出 拼装上下文system + 历史 + 观测 调用模型唯一的 LLM 调用 判定调工具?还是完成? 执行动作工具 / 命令 观测结果追加进状态 预算:步数 · token · 成本 停止条件 验证器(测试 / 构建) 恢复 · 死循环检测 最终答案已验证 · 预算内 放弃 / 升级上报 行动 循环回去 完成 治理
Agent 循环解剖 —— 拼装上下文 → 调用模型 → 判定 → 执行 → 观测 → 循环回去;一个控制器(预算、停止条件、验证器、恢复)治理每一次迭代,只有答案被验证或预算耗尽才退出

循环到底是什么

下面是 Python 风格伪代码,不是可以直接接入 SDK 的完整实现。它展示消息顺序与退出路径;辅助函数代表下文解释的运行时职责,具体实现取决于模型与工具 API。

循环 —— 以及藏在里面的决定
def run(state, ledger, deadline):
    recent_failures = deque(maxlen=3)  # per-task state, not shared globally
    try:
        for step in range(MAX_STEPS):
            reply = metered_model(state, ledger, deadline)
            state.append(reply)  # preserve calls before their results
            if reply.tool_calls:
                for call in reply.tool_calls:
                    result = authorized_tool(call, ledger, deadline)
                    state.append(tool_result(call.id, result))
                    if guard(call, result, recent_failures):
                        return incomplete(state, reason="repeated_failure")
            else:
                check = metered_verify(state, reply, ledger, deadline)
                if check.passed:
                    return completed(reply, check, ledger.spent)
                state.append(verification_feedback(check))
            state = compact_preserving_call_pairs(state)
    except LimitReached as limit:
        return incomplete(state, reason=limit.reason)
    return incomplete(state, reason="step_limit")

metered_model、authorized_tool 与 metered_verify 必须在开始前预留有上限的额度,执行单次限制与共享截止时间,并在返回前结算实际用量,包括成功返回的最后一次调用。预留失败或超时时,辅助函数抛出带有原因的 LimitReached,由循环返回未完成状态,不能继续发起付费尝试;用量不明确时保留预留额度,直到核对完成。每条工具结果必须关联对应的调用 ID,压缩也必须保留尚未完成的调用/结果关系。执行工具前先检查授权。接口边界可参见 工具调用与 MCP。

循环实际控制什么

模型选择和循环设计都会影响结果。比较循环方案时,应固定模型和任务集,每次只改变一个决策:停止规则、验证、重试上限或上下文处理。测量完成率、误报成功、延迟与成本,避免把“所有前沿模型能力趋同”这类未经验证的判断当作结论。

控制流:计划、行动、观测

循环体的经典写法是一个三拍循环,常被称为 ReAct(reason + act):模型推理下一步该干嘛,发出一个动作(工具调用),harness 返回一个观测,这个观测回到上下文里供下一次推理用。威力在于反馈:Agent 不是一上来就预测整个解,而是一次走一个有据的步子,根据每个动作揭示的东西去适应。

第一个真正的设计选择就在这里:每轮做多少。 每步一个工具调用最简单、最好恢复,但慢、啰嗦。允许模型一次请求好几个工具调用(比如并行读)能砍延迟和 token 成本——代价是其中一个失败时错误处理更难。可以先并行独立读取、串行有依赖的写入。若要并行修改状态,需要明确所有权或冲突处理;选择取决于工具及其副作用。

停止条件 —— 最难的部分

任何循环里都有一个貌似简单实则最难的问题:"我们完成了吗?"停太早,Agent 交回一个做了一半的任务;不停,它要么永远转,要么把一个本来不错的答案一直"改进"到坏掉。没有单一正确答案——你要组合几个信号:

  • 模型自报完成 —— 模型不再调工具、给出最终答案。必要但不充分:模型动不动就提前宣布胜利。
  • 验证门控 —— 只有检查通过(测试绿、构建成功、输出符合 schema)"完成"才算数。这是最强信号,因为它锚在环境里,而不是模型的意见。
  • 目标/退出标准 —— 一个显式谓词("函数存在且所有测试通过"),由循环来求值,而不是信一段话。
  • 预算耗尽 —— 不管状态如何的硬停;是兜底,不是目标。
要点

绝不让模型成为"完成"的唯一裁判。模型宣布成功是一个停止请求,不是完成的证明——任务只要有验证检查,就用它来门控。"它说它做完了"是 Agent 版的"在我机器上能编译"。

预算:步数、token、时间、成本

循环里的 LLM 默认是个无底洞——一个犯迷糊的 Agent 会乐呵呵地走 200 步、烧着钱原地打转。预算是让循环能无人值守安全运行的硬上限,一个认真的循环会同时盯几个:

预算限什么 / 为什么
步数 / 迭代数限制模型轮数;还需限制每轮工具调用和嵌套任务,因为一次回复可以提出多个动作。
token 预算累计各次调用计费的输入、输出与缓存 token。仅统计 token 数会遗漏不同模型价格及工具费用。
墙钟 / 超时限制单个工具调用(挂死的命令)和整个任务;保护对延迟敏感的调用方。
美元成本每任务或每用户的上限;你实际计费的依据,也是运维关心的限。

微妙之处不在于有预算,而在于快撞上时怎么办。好的循环会优雅降级:步数预算见底时,它可以从"探索"切到"收尾"("你还剩 2 步——总结你发现的然后停"),而不是思考到一半被一刀切。预算不只是个开关;它是循环能去推理的一个信号。

验证:闭合循环

验证提供模型之外的证据,例如测试结果、构建输出或目标产物的检查。它改善反馈,但不保证收敛或正确。应根据验收标准设计检查;过弱的检查仍会放过错误结果。浏览器功能除了单元测试,可能还需要实际交互验证。

检查失败时,反馈简洁的错误和足以采取行动的证据;必要时把完整日志保存在提示词之外。评估额外尝试带来的完成率收益是否值得其延迟和成本。LLM 评测既要统计完成任务,也要统计误报成功。

错误恢复与死循环

Agent 会以单次调用永远不会的方式失败,生产循环里大部分工程就是这些恢复路径。大头有:

  • 工具报错 —— 命令失败或文件不在。修法是把一个可读、可操作的错误当作观测返回("文件未找到:你是不是想要 X?"),而不是一坨原始堆栈——错误文本是模型据以行动的反馈。
  • 输出格式坏 —— 模型给工具调用吐了非法 JSON。用解析错误重新提示(可选配约束解码),而不是让循环崩掉。
  • 死循环(doom loop) —— Agent 一遍遍重复同一个失败动作,坚信这次会成。这是 Agent 的标志性失败,需要显式的死循环检测。

一个最小检测器可以用当前任务独有的缓冲区,记录最近三次连续失败。规范化参数,使用稳定的错误类别,并在成功后清空记录,避免把合理的重复读取误判成失败。三次相同失败意味着该换策略或停止,并不说明所有重试都不安全。

检测死循环并打破它
from collections import deque
import json

def guard(action, result, recent):
    if result.ok:
        recent.clear()  # successful repetitions are not failures
        return False
    args = json.dumps(action.args, sort_keys=True, separators=(",", ":"))
    signature = (action.name, args, result.error_code)
    recent.append(signature)  # use a stable error category, not a raw log
    return len(recent) == 3 and len(set(recent)) == 1

recent_failures = deque(maxlen=3)  # create one buffer per task

循环拓扑

朴素的 while 循环是基线,但"循环工程"越来越意味着为任务挑对循环的形状。四种常见拓扑:

单循环 行动 → 观测 重复直到完成 反思 草稿 批评 修订 计划 → 执行 做计划 s1 s2 s3 每步是自己的小循环 编排器 + 子 Agent 主循环 子 A 子 B 子 C 扇出 · 合并结果
四种循环拓扑 —— 单一行动/观测循环;反思循环(草稿 → 批评 → 修订);计划-执行(一个计划,每步是自己的小循环);以及编排器把活扇出给有干净上下文的子 Agent、再合并
  • 单循环 —— 默认的 ReAct 循环。最适合开放式任务,下一步取决于上一个观测;最易构建和调试。
  • 反思 —— 产出答案后,一个独立的批评步骤找毛病并触发修订。加了一拍自审,能抓住第一遍漏掉的错误,代价是额外的模型调用。
  • 计划-执行 —— Agent 先起草一个计划,再执行各步(每步可能是自己的小循环)。让长任务保持连贯,也给你一个在花钱执行前先检视计划的检查点。
  • 编排器 + 子 Agent —— 一个主循环把有边界的活(搜代码库、审 diff)委派给有自己干净上下文的新子 Agent,后者只回一个结论。这限制了任一上下文窗口要装多少,也使能并行;共享写入仍需要协调。

跨迭代的上下文

每次循环迭代都把观测追加进状态,所以长任务必然朝着上下文窗口的天花板膨胀。管理这种增长是核心的循环工程活——循环得在甩掉噪声的同时,把目标和关键事实留在视野里。标准做法是压缩(compaction):当状态逼近窗口时,总结或丢弃更早的轮次,保留目标、计划和近期结果。相关手法还有:把细节卸到一个 Agent 可按需重读的外部便签上,以及上面那个子 Agent 模式——它通过把重活发给子进程,让主循环的上下文保持精简。这事做砸了,Agent 会在任务中途"忘了"自己在干嘛;做对了,它能在一个问题上工作得比任何单个窗口都久。

用实证调循环

你没法靠直觉调循环,因为真正重要的改动——不同的停止条件、更紧的预算、何时压缩、要不要加一拍反思——会以出人意料的幅度相互作用、摆动真实成功率。认真的循环工作是实证的:把 Agent 跑在基准任务上,量成功率、步数、成本,并检视轨迹(完整的调用与观测序列)看运行在哪出错——提前停、死循环、预算爆掉。然后改一处、再量。循环是一个你对着数据来调的系统,就像你调任何控制系统一样。

面试提示

被要求"设计一个 Agent"时,别急着跳到模型。先讲循环:"它是一个调用-行动-观测的循环;有意思的决定是停止条件、预算、循环内验证、以及从死循环里恢复。"这个定调表明你懂 Agent 的可靠性到底从哪来。

坑与取舍

  • 信模型自报的"完成"。 没有验证门,Agent 凭自己一句话就停、交回做了一半的活。用真实检查门控。
  • 没有死循环检测。 只靠预算意味着一个死循环会把整个预算浪费在一个错误上;早早检测重复。
  • 预算没有优雅降级。 思考到一半被硬切,浪费了已经做的功;让循环在接近上限时收尾。
  • 拓扑过度工程。 反思和多 Agent 增加模型调用、延迟、成本;很多任务用一个紧凑单循环更好。只在更简单的循环明显失败时才加结构。
  • 上下文腐烂。 跳过压缩,窗口会被陈旧观测填满,质量悄悄下降;每次迭代都管理状态。
  • 盲调。 没有轨迹分析,你会修错地方——你看到成功率低,却不知道是提前停、死循环、还是恢复差。
小结

有效的循环应让完成证据和退出原因可检查。比较方案时固定模型、工具和任务;除非测量到的收益值得额外调用或协调成本,否则保留简单设计。

常见问题

AI Agent 的循环工程是什么?

它是对反复执行模型与工具的控制流进行设计,包括状态更新、动作授权、结果验证、预算执行和退出条件,是 Agent harness 的一部分。

AI Agent 应该何时停止?

任务验收通过时,以成功状态停止。预算耗尽、重复失败、取消或缺少必要输入时,明确返回未完成状态。模型自称“完成”并不足以证明成功。

如何阻止 Agent 反复失败?

使用规范化参数与稳定错误类别记录重复失败。成功动作应重置连续失败计数,同时保留步数、时间和成本限制。

写操作超时后可以直接重试吗?

超时不代表写入失败。重试前应查询操作状态,或复用稳定的幂等键。用新键重放同一操作可能重复产生副作用。

实例:修复一个失败的测试

给编码 Agent 一份 bug 描述和失败测试,要求最终交付补丁与相关测试结果。不同情况应产生不同的退出原因:

观测循环应对
针对性检查通过,且改动符合需求记录验证证据,结算全部调用后结束。
相同的文件不存在错误连续发生三次停止当前方法,核对路径,或返回未完成。
工具提交写入后超时结果未知。先查询状态,或复用该操作的幂等键,再决定是否重放。
剩余额度不足以验证保存补丁并标记尚未验证,不应宣布完成。

上面的最小检测器不处理交替失败、无进展但调用成功的轮询,或语义等价的不同调用。应增加与任务对应的进展信号,例如新通过的检查,并单独限制轮询。日志可记录任务 ID、步骤、工具名、结果和退出原因,不记录密钥或原始工具参数。

参考资料与后续学习

Anthropic 的 Building Effective Agents 介绍了简单 Agent 模式与环境反馈。本文伪代码和失败案例是本站补充的解释性例子。继续阅读 Harness 设计、工具调用与 MCP和 Agent 评测。

← 上一篇
Harness 工程