跳转到主要内容

你的定时 Agent 有四个时钟,而你信任的是错误的那一个

阅读需 2 分钟Tian PanTian Pan

一个每日站会总结被安排在 UTC 时间 09:00。定时任务(Cron)准时触发。两秒钟后,一个工作节点容器组(Worker pod)启动。LLM 调用又耗费了四十秒的往返时间。模型在撰写总结时认为现在是去年的 2 月,因为那是其训练数据最后确信的时间点。工具层在 UTC 时间 09:00:42 根据挂钟时间(Wall clock)发送了 Slack 消息,模型从未提及具体日期,因为没人要求它这样做。消息进入了正确的频道,昨天的站会笔记被总结成了“今天的”,而且整整三周都没有人察觉。

这并不是任何单一组件的 bug。这是一种在四个不同的时钟之间、谁也没有写下来的契约,而这四个时钟都认为自己知道“现在”是什么时候。

定时任务有它自己的时刻 —— 触发时间,这是系统中唯一在定义上准时的东西。工作节点有一个挂钟时间,可能比调度器晚几百毫秒,如果容器组是冷启动的,延迟会更长。模型有一个基于“感觉”的“现在”,它源自其上下文出现的任何时间戳,加上对其训练截止日期的模糊记忆。工具 API 有它自己的挂钟时间,这可能完全是另一台机器,也是副作用真正发生的时刻。在一个健康的系统中,由于间隔很小且动作粒度较粗,这四个时刻会坍缩为一个。而在一个不健康的系统中 —— 也就是说,在大多数生产环境的 Agent 系统中 —— 间隔成了实际的行为逻辑,团队交付的是一个语义取决于每一轮竞态中哪个时钟获胜的调度器。

四个时钟,一条 Slack 消息

缓慢地梳理从定时任务到 Agent 再到工具的链路,分歧就会显现出来。

调度器的时钟(Scheduler's clock) 是预期的触发时刻。如果你的定时任务设置为 0 9 * * *,契约就是“在 09:00 触发”。大多数调度器在正常负载下能在一秒内履行这一契约。在队列背压或控制平面降级的情况下,触发可能会推迟 —— 原定于 09:00 的任务可能直到 09:04 才真正调用工作节点。这种推迟很少会反馈给 Agent。

工作节点的时钟(Worker's clock) 是运行 Agent 循环的机器上的挂钟时间。在正常情况下,NTP 会将其与 UTC 的误差保持在几毫秒内,但没有明确设置 TZ=UTC 的容器可能会陷入本地时间解析的偏差中,而冷启动的容器组在启动的第一秒内可能会有 100 毫秒或更多的偏移。分布式系统文献几十年来一直在记录节点间的时钟偏移(Clock skew)是一个持续存在的背景问题;Agent 系统继承了其中的每一个痛点。

模型的心理“现在”(Model's mental "now") 是这四个时钟中最难以捉摸的一个。模型无法访问挂钟。它根据你在提示词(Prompt)中放入的内容及其训练截止日期来推理时间。截至 2026 年年中,截止日期散布在过去的 12 到 18 个月之间 —— 不同的供应商、不同的模型、不同的训练运行都锚定在不同的“知识视界”上。当提示词对日期保持沉默时,模型会填入训练期间认为最具代表性的日期。有时是截止月份;有时是更早的月份;有时则在相对时间推理(“两小时后发送”)中出现离谱的错误。

工具 API 的时钟(Tool API's clock) 是副作用真正触发时的挂钟时间。这通常与工作节点不在同一台机器上 —— 你的工作节点调用 Slack 的 API,Slack 用 Slack 的时钟为消息盖戳。如果你的工具层没有携带明确的时间负载,“这件事发生的时间”就是一个除了接收系统的日志之外,在任何地方都不存在的事实。

如果团队没有将这四个时钟命名为独立的实体,那么该系统的时间语义就会演变为:在任何给定步骤中,恰好拥有权威地位的组件说了算。那不是契约,那是等待复合的意外。

看起来不像时间 Bug 的失效模式

这类 bug 保持隐形的隐匿原因是其症状很少看起来像时间 bug。它们看起来更像内容 bug、重试 bug 或“模型幻觉了一个日期”的 bug。以下是几个值得识别的模式:

模型总结了昨天却称其为今天。 定时任务触发了每日站会。提示词没有指定日期。模型写道:“这是团队今天正在做的工作”,然后列出了看起来合理的任务项,因为它们总是看起来很合理,并锚定在模型隐性相信的任何日期上。总结被发布了,粗看没问题,但悄无声息地引用了过期的工单。评估套件(Eval suite)没有模型心理日期与实际日期偏离数月的合成用例,因此评估永远捕捉不到这一点。

“明天的提醒”永远不会触发,或者触发在过去。 模型被要求安排一个后续任务。它根据其内部日期(假设是 2025-08-15)计算出“明天”,并输出 2025-08-16T09:00:00Z 作为计划时间。调度器忠实地尝试触发一个过去的任务,这要么导致静默错误,要么作为补录(Backfill)立即触发。用户明天不会收到提醒;他们现在就会收到,或者永远收不到。模型的行为与其信念是一致的;系统在没有检查这些信念的情况下,给予了它们操作上的权重。

重试跨越了日期边界并落在错误的一天。 定时任务在 23:59:30 触发。Agent 对模型的第一次调用因供应商临时故障而失败。重试策略退避并在次日 00:00:15 触发第二次调用。工具现在发送了一个动作 —— “发布当日总结” —— 该动作落在了第二天的频道中,总结的却是昨天的数据。在文献定义的意义上,重试是幂等的:相同的 Key,相同的调用。但相对于预期的触发时间,它并不是幂等的,因为没有哪一层将该时间作为事实标准(Ground truth)来传递。

夏令时(DST)切换导致重复或漏掉运行。 在时钟拨快的那天,一个安排在“当地时间 2:30”的任务并不存在;它会被静默跳过。在时钟拨回的那天,同一个任务会存在两次并运行两次,这取决于调度器的夏令时策略。工业界已有记录,像 Kubernetes CronJobs 这样的分布式调度器在夏令时边界触发了数千次重复调用,导致了五位数的云成本事故。Agent 在已有的所有时钟问题之上,又继承了这一点。

定时任务所指的“上午 9 点”不是提示词所指的“上午 9 点”。 定时任务表达式是 America/New_York 时区的 0 9 * * *。系统提示词写着“每日上午 9 点的站会”。模型在没有时区上下文的情况下,将“上午 9 点”推理为用户的本地时间,并发送了一条对旧金山凌晨 6 点的读者说“早上好”的消息。两种正确的时区解释产生了冲突,而团队在没有做出选择的情况下交付了两者。

时间即状态。像对待状态一样传递它。

弥合这些差距的模式都有一个共同点:停止将时间视为环境上下文,开始将其视为传递的状态,并将 cron 的意图作为权威来源。

会员专享

余下内容仅对会员开放。

会员可读完整内容 —— 每一个公开发布的观点背后,那些框架、决策与推理的全貌。

  • 完整文章,包含未公开存档的部分
  • 可落地的工作框架,附带权衡与决策依据
  • 新文章抢先看,先于公开发布

随时取消 · 一次订阅,畅读全部

参考资料

保持联系,关注我获取更多内容

阅读需 9 分钟

那个因等待另一个 Agent 而死锁的 Agent

两个能力出色的 Agent 可能会在你的账单飞涨时永远互相等待。为什么是协作——而非模型能力——导致了 Agent 群体的崩溃,以及分布式系统准则如何修复这一问题。

insider
ai-agents
阅读需 12 分钟

智能体集群并发:在没有死锁或惊群效应的情况下协调数十个智能体

LLM 智能体集群是一个小型分布式系统,而不是单个智能体的三十个副本。准入控制、AIMD 背压、熔断器和外部状态协调是防止并发集群自我崩溃的关键。

insider
ai-agents
阅读需 8 分钟

异步 Agent 的静默失败:为何你的 AI 任务悄然终止却无人察觉

异步 AI 任务会静默而自信地失败——HTTP 200,仪表盘一片绿,客户最终投诉才发现。本文介绍死信队列、幂等键和 Saga 日志如何从传统分布式系统迁移到 AI Agent 场景以解决这一问题。

insider
ai-agents
阅读需 10 分钟

你的重试逻辑正在给 Agent 传达错误的教训

为不稳定的网络设计的重试策略假设调用者是正确的。但 Agent 让调用者变成了不可靠的部分,盲目的重试会悄悄地将真实的 Bug 洗白成绿色的勾。

ai-agents
reliability
阅读需 10 分钟

你的智能体需要的是监督者,而不是重试循环

当长时间运行的智能体失败时,重试循环回答了错误的问题。Erlang 的 OTP 监督树在三十年前就定义了真正的决策路径:是彻底重启、从检查点重启,还是升级给人工处理 —— 以及如何将这些策略映射到智能体流水线中。

insider
ai-agents