事件驱动的 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 分钟快速失败,提供商恢复后迅速清空积压"。
- https://dev.to/toji_openclaw_fd3ff67586a/the-complete-guide-to-ai-agent-cron-jobs-and-scheduling-2c3f
- https://www.zenml.io/blog/what-1200-production-deployments-reveal-about-llmops-in-2025
- https://temporal.io/resources/case-studies/lindy-reliability-observability-ai-agents-temporal-cloud
- https://aws.amazon.com/blogs/aws/build-multi-step-applications-and-ai-workflows-with-aws-lambda-durable-functions/
- https://iamstackwell.com/posts/ai-agent-queue-architecture/
- https://www.inferable.ai/blog/posts/distributed-tool-calling-message-queues
- https://swenotes.com/2025/09/25/dead-letter-queues-dlq-the-complete-developer-friendly-guide/
- https://portkey.ai/blog/retries-fallbacks-and-circuit-breakers-in-llm-apps/
- https://techcrunch.com/2025/03/31/temporal-lands-146-million-at-a-flat-valuation-eyes-agentic-ai-expansion/
- https://dasroot.net/posts/2026/02/orchestrating-ai-tasks-celery-temporal/
- https://encore.dev/blog/thundering-herd-problem
- https://bugfree.ai/knowledge-hub/retry-dlq-idempotency-message-processing
