事件驱动的 Agent 调度:为什么 Cron + REST 调用无法胜任循环 AI 工作负载
团队调度循环 AI Agent 任务最常见的方式,也是最危险的方式:一个每隔 N 分钟触发一次 REST 调用的 Cron 条目,它启动一个 LLM 工作流,任务要么完成,要么悄无声息地失败。这个模式在预发布环境看起来没什么问题,但在生产环境中,它会制造出一类极难发现、难以恢复、难以推理的故障。
Cron 诞生于 1975 年,最初是为运维脚本设计的。它所内建的假设——运行时间短、无状态执行、触发即忘——在每一个维度上都与 LLM 工作负载的现实相悖。循环 AI Agent 任务是长时运行的、有状态的、成本高昂的,其失败方式会在多次重试中不断叠加。用 Cron 来调度它们,不只是可靠性风险,更是可见性风险:出问题时,你往往浑然不知。
Cron 在 LLM 任务上的具体失效方式
这些失效模式并非纸上谈兵,而是 Cron 的假设与 LLM 任务实际行为之间落差的必然结果。
静默失败。 Cron 捕获的是退出码,而非业务结果。一个在等待 LLM API 时超时、捕获异常、以 exit 0 退出的 Agent,在 Cron 眼中是"成功"。没有主动埋点,你根本不知道这个任务是否产出了结果、消耗了多少 Token,或者是否已经部分更新了下游状态。团队通常是在用户投诉后才发现这些静默故障——而不是通过监控大盘。
负载下的并发执行。 Cron 按固定间隔调度任务,完全不感知上一次运行是否仍在执行中。如果一个 LLM 任务通常耗时 45 秒,但因为 API 提供商在高峰期响应变慢而运行了 90 秒,下一个 Cron 触发点会直接打入一个仍在运行的 Worker。现在,两个实例同时针对同一份数据运行,可能向同一条记录写入相互冲突的结果。对于多步骤 Agent 工作流而言,这尤其具有破坏性,因为第二个实例会在第一个完成之前就污染中间状态。
惊群效应。 在规模化场景下,Cron 会制造同步问题。如果你有 200 个租户账户,每个账户都设有计划在整点运行的 Agent 任务,那么 200 个任务会同时触发。API 速率限制被打满,Token 预算耗尽,LLM 提供商开始批量返回 429。此时所有任务同时重试,问题进一步加剧。普通后台任务尚可消化,因为它们快速且廉价;LLM 任务却慢而昂贵,这意味着恢复窗口更长,其间的成本更高。
提供商故障放大。 当 LLM API 遭遇持续故障——不是立即返回 500,而是缓慢超时——Cron 任务会耗尽整个超时窗口才失败。如果你的 Agent 设置了 3 分钟超时,而提供商宕机 20 分钟,你会收获六七次完整超时时长的连续失败,每一次都占据一个 Cron 调 度槽。整个调度窗口陷入阻塞,本应在故障期间运行的任务就这样消失了,没有任何补偿机制。
零成本归因。 Cron 完全不知道是哪个租户、哪个工作流或哪个请求导致了某次 Token 消耗。当一张大额 LLM 账单到来,某个 Agent 的消耗量是预期的 10 倍,Cron 基础设施里没有任何信息帮你溯源。所有曾经规模化过 LLM 工作负载的生产团队都以惨痛的方式发现了这个问题。
事件驱动架构能带来什么
解决之道并不在于某个具体工具,而在于采用正确的模型:生产者将工作推入队列;Worker 异步拉取消费;队列本身成为任务状态的事实来源。
这个模型能解决上述每一种失效。Worker 可以使用分布式锁或幂等性检查,确保同一任务同时只有一个实例在处理,消除并发执行问题。消息可以通过抖动在时间上分散,打破惊群效应的同步性。死信队列捕获持续失败的任务以供检查和重放,而非静默丢弃。由于每条消息都携带元数据——租户 ID、工作流类型、触发事件——成本归因成为可能。
还有一个不那么显眼的好处:解耦接收与执行。用 Cron,"决定做什么"和"真正去做"发生在同一进程的同一时刻。有了队列,任务创建是同步且快速的;任务执行是异步的,能够承受背压。当 LLM 提供商宕机时,新任务持续入队;当提供商恢复时,Worker 在重试策略和速率限制的约束下,以受控方式消化积压。
你需要的四个架构原 语
任何生产级 Agent 调度系统都需要以下四样东西。工具因人而异,原语一成不变。
幂等性键。 每个任务必须携带一个派生自其所代表的业务操作的唯一标识符——而非入队时随机生成的 UUID。使用业务自然键:account_id + workflow_type + time_window。在处理消息前,Worker 检查该键是否已在持久化存储中成功执行过。如果是,则确认消息并退出。这是保护你免受重复执行的关键——所有消息中间件在至少一次投递语义下都会重复投递消息。
死信队列。 当一个任务耗尽所有重试次数后,不应被静默丢弃,而应连同原始 Payload、完整的重试历史和每次失败原因,一起移入一个独立的队列。死信队列是你做事后分析、编写重放脚本、发现系统性问题的地方。一个从不增长的死信队列,要么意味着一切正常,要么意味着失败正在被静默吞噬——通过对死信队列深度设置告警,你能判断是哪种情况。
指数退避加抖动。 失败后立即重试会让大多数故障场景雪上加霜。正确的模式:每次失败后将等待间隔翻倍(1s → 2s → 4s → 8s),再加上随机抖动因子。抖动能防止所有失败任务在同一时刻重试引发的重试风暴。研究一致表明,与固定间隔重试相比,这能将重试引发的负载峰值降低 60–80%。
熔断器。 当 LLM 提供商持续返回错误时,单个 Worker 级别的熔断器还不够——你需要一个共享状态来检测"提供商宕机了",并在故障窗口期内阻止所有 Worker 尝试调用,快速失败而非等待完整超时。这能将一次 20 分钟的提 供商故障,从"20 分钟完整超时失败持续消耗 Cron 调度槽"变为"20 分钟快速失败,提供商恢复后迅速清空积压"。
多步骤 Agent 的断点续跑
标准消息队列模式假设任务耗时短,失败后可以从头重试。AI Agent 往往做不到这一点。一个已经抓取了五个 URL、解析了三份文档、向数据库写入了中间结果的 10 步研究型 Agent,不能简单地从第一步重来——那意味着重新产生 API 费用,并可能污染已经写入的状态。
多步骤 Agent 工作流的正确原语是断点续跑:在每个步骤边界持久化执行状态。失败时,任务从最近的断点恢复,而不是从头开始。
Temporal 通过持久化执行原生支持这一能力。工作流以顺序的命令式代码编写,平台在底层透明地处理状态检查点和确定性重放。当 Worker 在工作流执行中途崩溃时,新的 Worker 接管工作流 ID 并从最近的断点重放,跳过已完成的步骤。2025 年发布的 AWS Lambda Durable Functions 在 Serverless 场景中提供了类似的模型——写顺序代码,平台负责检查点。
这与 Celery 或 BullMQ 有本质区别。两者都是无状态的任务队列:可以重试失败的任务,但不知道这个失败的任务是一个 10 步工作流的第 5 步,前 4 步已经完成。你必须自己构建这套追踪机制,而大多数团队不会这么做,导致 Agent 失败要么触发完整重启,要么需要人工介入。
Lindy AI 在 2025 年的案例研究展示了缺少这一能力的代价。他们最初使用 BullMQ,发现底层服务超时时会出现静默失败,Pod 停机打断工作流时没有持 久化恢复机制,也无法看清 Agent 为何失败。迁移到 Temporal Cloud 后,他们每天处理 250 万个 Temporal Action,静默故障大幅减少。在该规模下维护 BullMQ 的运维复杂度,已经超过了采用专用基础设施的成本。
不可跳过的可观测性层
这套架构一个被低估的价值在于:一个合理的消息队列设置会迫使你为原本不可见的事情埋点。
每条消息都应携带贯穿整个执行过程的结构化元数据:租户或账户标识符、工作流类型、模型层级、触发事件、入队时间戳。Worker 在每个步骤记录包含 Token 数量、延迟和成本估算的结构化日志。汇总后,这些数据形成对 Agent 运行状况的实时视图。
这一点很重要,因为 AI 工作负载的成本可变性远超传统软件。两个看起来一模一样的任务,因为输入复杂度、上下文长度和模型决定的工具调用次数不同,成本可能相差 10 倍。没有按任务的成本归因,你对单位经济指标完全是盲飞。处理高 LLM 请求量的团队报告说,仅仅通过增加监控来揭示哪些具体工作流消耗了不成比例的 Token 预算,就节省了大量运营成本。
再配合死信队列告警——当死信队列深度超过阈值时触发通知——你就得到了一个远比"Agent 任务 X 失败"这条淹没在日志文件里的信息更具可操作性的故障信号。
