每个人都在问 "循环到底是什么?" 但没人问的是:是什么在驱动这个循环?
AI 领域的讨论已经聚焦在循环作为 Agent 系统的核心原语上。Matt Van Horn(@mvanhorn)追溯了 Agent 循环的发展脉络——从 ReAct 到工具使用,再到编排循环,再到循环监督循环。Addy Osmani(@addyosmani)拆解了循环内部的构建模块:自动化、工作树、技能、连接器、子 Agent。Van Horn 强调的核心是持久性,认为无法在重启后存活的循环算不上真正的循环。Osmani 的关键思路是编排:设计系统来提示 Agent,而不是由你来提示。
我想把他们的观点再推进一步。持久性不仅仅是循环的一个属性,而是整个底层执行层的特征。重要事实是:持久的编排是构建 Agent 循环架构的基础。我们来拆解这个架构。
循环在哪些地方会出问题
/loop 和 /goal 模式能很好地处理单 Agent、单会话的任务。Agent 循环执行直到任务完成。这覆盖了很多场景。但下一阶段(Van Horn 框架中的第 5 阶段)就会出问题:
- 循环监督其他循环
- 循环按计划运行,而不仅仅由人类触发
- 循环能在进程重启、部署和崩溃后存活
- 循环能生成子 Agent 并等待结果(有时长达数小时)
- 循环需要事后可观测
这不是提示工程的问题,而是基础设施的问题。
Van Horn 引用了 @runes_leo 的话:"在 AI 编程中,成本最高的不再是写代码,而是管理 Agent 循环。" 终端里的一个 while True 循环无法给你任何这些能力。虚拟机或沙箱中的长期运行进程也不行。
想想当你在服务器上运行 Agent 循环时会发生什么。进程会死亡或重启——部署、OOM、抢占式实例回收。循环重启了。但之前它在做什么?执行到哪一步了?它是否已经发送了那条 Slack 消息?是否已经调用了子 Agent?
你不知道。它从头开始,重新获取已经拥有的数据,重新调用 LLM 做出已经做过的决策,发送重复通知,生成重复的子 Agent。一觉醒来,你看到三条一模一样的 Slack 消息和一脸困惑的团队。
解决方法不是 "更好的错误处理"——而是一种执行模型,其中每一步都有检查点,每个决策都持久化,恢复意味着从最后成功的步骤继续。
Agent 循环架构的三层
三个层次,每个层次对应一个具体的原语。
第一层:循环
循环是一个 cron 加上一个决策者。它按计划(或触发器)运行,评估状态,并决定下一步做什么。
这就是 Van Horn 定义的具体化:cron 从未拥有的是中间的决策环节。是 Agent 做决策,而不是你。cron 是心跳,LLM 是决策者,步骤是持久执行过程中检查点进度的机制。
1export const infraHealthCheck = inngest.createFunction(2 { id: "infra-health-check" },3 { cron: "*/30 * * * *" }, // 每 30 分钟4 async ({ step }) => {5 const metrics = await step.run("fetch-service-metrics", async () => {6 return await fetchServiceMetrics(); // 错误率、延迟、内存、CPU7 });89 const assessment = await step.run("assess-health", async () => {10 return await callLLM({11 prompt: `根据这些服务指标,将系统整体健康状况分类为 "normal"、"degraded" 或 "critical"。解释你的推理。12 指标:${JSON.stringify(metrics)}`,13 });14 });1516 if (assessment.status === "degraded" || assessment.status === "critical") {17 await step.invoke("triage-incident", {18 function: incidentTriage,19 data: { metrics, assessment, services: assessment.affectedServices },20 });21 }22 }23);
每周一上午 9 点,循环触发。它获取数据,询问 LLM 是否需要生成报告,如果需要则调用一个技能。如果进程在步骤之间重启,已完成步骤不会重新执行。这就是循环——不是 LLM,而是 LLM 周围的循环。
第二层:技能
在这个语境中,技能不是提示词。它是一个持久化的工作流。多步骤、可重试、可组合、可独立部署。
Van Horn 说:"循环是管道,技能才是它调用的资产。" 这才是能产生复利的部分。系统每学习一个新技能,所有循环都会变得更强大。
1export const incidentTriage = inngest.createFunction(2 { id: "incident-triage", retries: 3 },3 { event: "infra.incident.triage" },4 async ({ event, step }) => {5 const details = await step.run("fetch-detailed-metrics", async () => {6 return await fetchDetailedMetrics({ services: event.data.services });7 });89 const deploys = await step.run("fetch-deploy-history", async () => {10 return await fetchRecentDeploys({ since: hoursAgo(2) });11 });1213 const analysis = await step.run("correlate-incident", async () => {14 return await callLLM({15 prompt: `将这些服务指标与最近部署进行关联分析。识别可能的根因和严重程度。16 指标:${JSON.stringify(details)}17 最近部署:${JSON.stringify(deploys)}`,18 });19 });2021 await step.run("post-triage-summary", async () => {22 await slack.postMessage({23 channel: "#incidents",24 text: formatTriageSummary({25 analysis,26 affectedServices: event.data.services,27 recommendedActions: analysis.recommendations,28 }),29 });30 });3132 return analysis;33 }34);
这个技能负责获取数据、分类并路由。它是一个具有内置容错能力的工作单元。技能可以是一个 AI 工作流(中间包含 LLM),也可以是确定性代码。
第三层:编排器
编排器是驱动一切运行的引擎:调度 cron、执行步骤、管理重试、强制并发限制、存储运行历史,以及热部署新函数/工作流而不中断正在运行的流程。
这一层很少有人谈论,因为它本应是透明的。但它是基础。
大多数人对 Agent 的理解是 "LLM + 工具"。Agent 循环架构将其重新定义为:Agent = 循环 + 技能 + 编排。LLM 和工具位于循环内部,它们可以被替换或调整而架构保持不变。编排使得架构成为可能。
出问题时会怎样
正常路径很简单。但这是在生产环境中运行的软件,事情真的会完全按计划进行吗?
你的事件分类技能触发,但指标 API 超时了。这次读取必须访问磁盘,内存缓存中没有数据。调用此 API 的步骤现在重试并再次访问 API。数据部分缓存,API 完成。技能继续执行下一步,就像什么都没发生过一样。
有时,情况可能没那么简单。如果 API 密钥过期,或者你的托管服务商宕机 30 分钟,所有重试都用完了。现在会发生什么?你必须处理失败。
1export const incidentTriage = inngest.createFunction(2 {3 id: "incident-triage",4 retries: 3,5 onFailure: async ({ error, event, step }) => {6 // 函数在耗尽重试次数后失败。7 // 我们仍然拥有原始事件数据。没有任何数据丢失。8 await step.run("notify-failure", async () => {9 await slack.postMessage({10 channel: "#agent-ops",11 text: `⚠️ 事件分类失败:${error.message}。` +12 `将在下一次健康检查周期重试。` +13 `受影响的服务:${event.data.services.join(", ")}`,14 });15 });16 },17 },18 { event: "infra.incident.triage" },19 async ({ event, step }) => {20 /* 与上方技能相同的逻辑 */21 }22);
\onFailure\ 处理程序在所有重试耗尽后触发。它会向运维频道发送消息,以便有人知道。事件被保留,没有丢失。下一次预定的运行会在失败的地方继续。
持久化的编排必须提供:针对暂时性错误的步骤级重试,以及针对不可恢复错误的失败处理钩子。没有这些,当事情出错(它们总会出错)时,你会数小时甚至数天后才发现。
暂时性错误也很昂贵。如果你的技能或 Agent 从头开始重试,你会多次调用 LLM,白白浪费 token。LLM 调用是可以设置检查点的。现在把这个乘以系统中 10 个或 30 个 Agent。费用会很高。
步骤级检查点不仅仅是一个正确性功能,更是省钱利器。
能自建技能的 Agent
这里变得更有趣了。系统不是静态的,它被设计成可以自我进化与扩展。
Agent 不仅在循环内部运行——它还能编写新的循环,并将它们注册到编排引擎中。每个部署的函数都是一个持久化的技能,可以独立运行,可由循环或 Agent 触发,或按计划运行,具有自己的重试逻辑。技能会复利增长。
这是一个具有编排感知能力的 Agent。
具体是这样工作的:AI Agent 可以将编排 SDK 作为一个工具访问。它可以编写新函数,将其注册到引擎,然后这些函数立即开始运行。Agent 进程可以热加载新函数,而无需重启或中断正在进行的运行。
让我们通过一个具体例子来理解:
1. 人类表达需求。 工程师说:"我们的服务在夜间经常出现延迟高峰,但直到早上才有人发现。" 这就是触发器。Agent 不需要从环境数据中推断模糊的模式。它有明确的指令。
2. Agent 编写技能。 两个多步骤函数:一个是健康检查循环,每 30 分钟运行一次,拉取错误率、延迟和资源使用情况,由 LLM 将系统健康状况分类为正常、降级或严重。另一个是事件分类技能,获取详细指标和最近部署历史,用 LLM 关联根因,并将分类摘要和推荐操作发布到 Slack。错误处理:如果指标 API 不可用,则回退并重试;如果 LLM 失败,则降级为基于规则的严重性分类。
3. Agent 部署技能。 Agent 编写函数代码,由侧车进程拾取。新函数自动注册,立即生效,无需部署流程、无需 PR。
4. 技能自主运行。 每 30 分钟,引擎触发健康检查。如果发现问题,则调用分类技能。没有人工干预。完全持久化。
5. Agent 根据信号进行迭代。 这是人们经常忽略的部分,所以让我具体说明 "迭代" 是什么意思。Agent 不会神奇地注意到模式。它有一个独立的审查循环:一个由 cron 触发的函数,每周运行一次,从编排器读取运行历史,并评估性能:
1export const reviewSkillPerformance = inngest.createFunction(2 { id: "review-skill-performance" },3 { cron: "0 10 * * 5" }, // 每周五上午 10 点4 async ({ step }) => {5 const runs = await step.run("fetch-run-history", async () => {6 return await getInngestRuns({7 functionId: "incident-triage",8 since: daysAgo(7),9 });10 });1112 const analysis = await step.run("analyze-performance", async () => {13 const successRate = runs.filter(r => r.status === "completed").length / runs.length;14 const avgDuration = average(runs.map(r => r.duration));15 const incidents = await fetchIncidentOutcomes(); // 事件是否与真实故障相关?1617 return await callLLM({18 prompt: `回顾过去一周这个技能的性能表现。19 成功率:${successRate}20 平均持续时间:${avgDuration}ms21 与真实故障相关的事件:${incidents.confirmed}/${incidents.total}22 误报:${incidents.falsePositives}23 团队对告警采取了行动:${incidents.actedOn}/${incidents.total}2425 我们应该调整阈值或分类方式吗?具体哪些更改?`,26 });27 });2829 if (analysis.shouldModify) {30 await step.invoke("update-skill", {31 function: coreAgent,32 data: { prompt: `基于以下建议更改更新 incident-triage 技能:${analysis.proposedChanges}` },33 });34 }35 }36);
"审查" 本身就是一个函数。它读取运行历史,检查事件是否与真实故障相关,并将该信号输入 LLM。如果健康检查不断将某服务标记为 "降级",但团队因为阈值过于敏感而忽略它,审查循环会捕捉到这一点,并更新技能以调整分类。这不是魔法,而是一个 cron 任务,LLM 坐在决策者的位置上。
如何验证? Agent 编写代码的质量取决于围绕它的护栏。代码可以进行类型检查。Agent 可以自己调用该函数来测试它,因为它能够与编排引擎交互。虽然这并非万无一失,但你给了核心 Agent 在其运行的系统内原生调试它所编写的技能的能力。审查循环会捕获初始调试中未发现的问题。
更进一步,Agent 可以使用 onFailure 钩子来触发自身评估某个特定的失败。这是一个不断改进的反馈循环。
如何处理冲突? 流程控制,特别是并发控制或单例模式,可以处理简单的情况(并发:{ limit: 1, key: "[event.data.service" }]),这意味着每个服务每次只能运行一个事件分类。但更深层次的问题是:如果两个健康检查同时检测到同一个服务的问题会怎样?编排器会将它们排队。第二个分类等待第一个完成后才开始。没有重复告警,没有竞态条件。这不是理论上的,而是任何任务队列中都会使用的并发原语。
Agent 不仅仅是执行任务。它正在为自己构建基础设施。每个技能都会在创建它的对话结束后持续存在。杀死 Agent 进程并重启,技能继续运行。替换底层模型,技能继续运行。Agent 是短暂的,但其输出是持久的。

Agent 循环架构系统概览
开发者的视角
这一点很重要,因为如果开发者看不到 Agent 部署了什么、无法调试问题、无法审计凌晨 3 点运行了什么,那么整个架构就是一个巨大的隐患。
编排引擎存储每一次运行、每一步、每一个输入、每一个输出、每一次重试。Agent 上周二部署的一个技能在凌晨 4 点失败了?你可以精确地看到哪个步骤失败了、输入是什么、抛出了什么错误、在放弃之前重试了多少次。完整的步骤级追踪是编排引擎本身的输出。
这不是事后才加上的仪表盘,而是持久化执行固有的特性。每一个 step.run() 都是一个检查点,每一个检查点都是可观测的。当编写代码的不是人类时,可观测性不再是锦上添花,而是信任的基石。
日常开发者的工作流程是这样的:早上查看运行仪表板——哪些技能在夜间运行了,哪些成功了,哪些失败了。如果 Agent 编写的某个技能行为异常,你可以直接阅读代码、编辑它、删除它,或者告诉 Agent 修复它。Agent 是作者,但代码属于你。Agent 及其技能仍然是一个需要你打理的花园。
为什么持久性是基础
Van Horn 说:"这些东西必须能在重启后存活。"
以下是持久性在实践中意味着什么:
需求 | 含义 | 为什么基本 while 循环会失败 |
|---|---|---|
独立步骤重试 | 如果 5 步中的第 3 步失败,只重试第 3 步,而不是步骤 1 和 2 | 循环重启会从头重新运行所有步骤 |
子 Agent 生命周期 | 生成一个子任务,等待它(可能数小时),如果父任务被取消则取消子任务 | 没有内置的父子生命周期管理 |
保证事件传递 | 如果事件在 Agent 宕机时触发,它仍应被处理 | 进程不运行时事件会丢失 |
事后可观测性 | 查看事后发生了什么:每一步、每个决策、每次重试 | 日志是唯一选择,而且它们是临时的 |
热部署无停机 | 部署新函数版本而不中断正在进行的运行 | 进程重启会杀死所有运行 |
并发控制 | 一次只运行 N 个技能实例 | 没有内置的并发原语 |
"在容器里运行" 能保证运行时间,但无法保证正确性。崩溃后重启的容器可以恢复进程,但每个正在进行的循环都会从头开始,每一步重新执行,每次 LLM 调用都重新进行。循环看起来在运行,但它是盲目运行的。
与现有工具的比较
有些工具可能为你提供这种系统的 "漂亮" 交钥匙解决方案,或者你可能选择拼凑一些底层工具,创建自己的系统。两种选择都没错,但正确的架构层应该允许你和你的 Agent 随着时间进化——灵活、动态、持久。
持久化执行原语非常适合 Agent,并且 Agent 可以轻松编写它们,同时可观测性和 API 能让 Agent 自身具备编排感知能力。
一个可运行的示例
我们正在 Inngest 内部测试这些模式,你可以在 "utah" 项目仓库中看到这个概念:https://github.com/inngest/utah。这是一个建立在 Inngest 持久化编排之上的 Agent 框架,它本身也具有编排感知能力。
该系统有一个侧车进程,使主 Agent 能够在其自己的工作空间中编写和编辑 Inngest 函数,从而用 "技能"(在本文语境中)扩展自身。很快,我们计划提供一个完整的系统,包含作为示例的启动循环,但这些想法可以更清晰地展示本文中的概念。
复利循环
Satya Nadella 最近的帖子指出了业界已经感受到的一点:护城河不是模型,而是循环。
他的框架中有两种资本:人力资本——团队多年来积累的知识和判断力;以及他所谓的 token 资本——公司在基础模型之上构建的 AI 工作流、决策模式和习得的技能。
核心论点:两者叠加复利。每个改进的工作流产生更好的信号,更好的信号带来更精准的 AI 行为,更精准的行为释放人类注意力用于更高判断力的工作。这是一个爬山机器。
这正是 Agent 循环架构具体实现的内容:
- Agent 部署的每一个持久化技能,都是编码为可执行基础设施的机构化知识。它持续存在,无论是否有人关注,它都会运行。
- 一个由 cron 触发的审查循环,用于评估技能性能并迭代。这就是爬山机器的现实体现——不是幻灯片中的飞轮图,而是一个带有 cron 触发器的函数。
- 如果你的技能在进程重启时消失,复利就会重置为零。持久性才是让投资持续存在的原因。
Nadella 的关键点是:"公司应该能够更换一个'通才'模型,而不会失去建立在其学习系统中的'公司老将'的专业知识。" 这正是技能库模式。持久化函数不在乎哪个 LLM 调用它们。
据此构建
之前的讨论一直围绕 Agent 做什么:循环、工具、推理、上下文工程。接下来的讨论应该围绕什么来运行 Agent。
三层:循环、技能、编排器。循环是工作单元,技能是资产,编排引擎使两者持久化。侧车模式是模型:Agent 编写自己的持久化技能,部署它们,审查它们的表现,并进行迭代。这不是思想实验,而是一个可工作的模型。
我们构建了 Inngest 作为实现这一目标的编排引擎:step.run()、step.invoke()、cron 触发器、事件驱动控制流、并发控制以及完整的步骤级可观测性。但这个架构模式比任何单一工具都更宏大。如果你在生产环境中构建 Agent 循环,请定义这三个层次。
原语已经存在。据此构建。





