询问大型语言模型现在几点,你得到的回答虽然语气自信,但几乎肯定是不准确的。这并非因为模型坏了,而是因为它的内部没有时钟。Transformer 是一种无状态的文本补全引擎:它将 token 映射到 token。在整个流水线中,没有任何地方会接收到“现在是 14:32 UTC”这样的信号。模型感知不到当前时刻 —— 这是你必须在每一轮对话中主动提供给它的东西,否则它就会从陈旧的训练数据中臆造一个时间。
这种无声的失败往往在最糟糕的时刻浮出面。你的智能体认为现在是星期一,因为会话是在星期一开启的,于是它在星期二、星期三依然坚信这一点,直到它为一个已经过去的日子设定了“明天早上”的提醒。它利用数小时前就已冻结的 now 来分析“过去 24 小时”的日志。它在转换跨时区的会议时间时,因为误判了夏令时的边界而导致一小时的偏差。这些在传统意义上都不像是“幻觉”。其输出流畅、合理且逻辑自洽,只是它锚定在了一个不再存在的时刻。
我喜欢将此类问题归为 上下文漂移 (context drift ):即智能体对世界的认知与实际情况之间的无声偏离。这并不是模型凭空捏造,而是它基于曾经正确的事实进行操作,并保持着与对待正确事实时一样的完美自信。时间是这种漂移最纯粹的表现形式,因为根据定义,时间始终在流逝 —— 当你还在握着旧答案时,正确答案已经变了。
为什么工具救不了你
显而易见的修复方案是给智能体一个 get_current_time 工具。这是一个很好的直觉,你也确实应该配备这个工具。但它本身并不能解决问题,因为工具是被动的。智能体必须 决定 去调用它。而只有当智能体有理由怀疑它所认知的当前时间时,它才会决定去检查时间。
而这种怀疑几乎从未产生过。如果系统提示词说现在是星期一,模型为什么要质疑它?它没有感知时间流逝的能力。在上下文窗口内部,“星期一”就像“水是湿的”一样是不争的事实。一个智能体从未想过要调用的工具不是安全网,而是一个折叠在衣柜里的安全网。
这是大多数智能体架构中的感知偏差。我们在 记忆 (检索、长期存储、对话历史)上投入了大量精力,而这些都是回顾性的。我们在 行动 (工具调用、函数执行)上投入了大量精力,而这些是向外推进的。几乎没有人构建的是 现在时 频道:即智能体对其操作时刻的、环境化的、始终当前的感知。记忆告诉智能体发生了什么,工具让它能执行操作,但除非你刻意且持续地进行同步,否则两者都无法告诉它 现在是什么时间 。
因此,真正的设计问题不在于“智能体是否应该有一个时钟工具”,而在于“如何让正确的时间在每一轮对话中呈现在模型面前,而无需模型主动询问”。这个问题的答案直接引出了第二个问题,而大多数团队只有在推理账单翻了三倍之后才会发现它。
Prompt 缓存陷阱
保持智能体感知最新状态的最简单方法是将精确时间戳直接写入系统提示词:Current time: 2026-07-01T14:32:07Z。每次请求都保证新鲜。问题解决了 —— 除非你无意中禁用了整个系统提示词的 Prompt 缓存(Prompt caching)。
Prompt 缓存通过匹配前缀起作用。供应商会缓存提示词中领先的、不变的部分,并跳过对该部分的重复计算,这是实现大幅降低延迟和成本节省的关键。然而,提示词前端的任何微小变化都会使随后的整个缓存链失效。精确到秒的时间戳在每次调用时都是不同的。因此,即使系统提示词中剩余的一万个 token 逐字节一致,也永远无法命中缓存。你每一轮都要支付全额费用来重新计算前缀,而且通常还要承受额外的延迟。很多团队上线后看到成本激增,花了好几天寻找性能倒退的原因,最后才意识到那一行动态代码才是罪魁祸首。
有两种简洁的解决方案,且它们并不互斥:
粗化粒度。 在缓存区域只放入当前 日期 —— 2026-07-01 —— 而不是完整的时间戳。日期每天只更新一次,因此在整个会话的请求中,系统提示词保持稳定,缓存依然有效。对于惊人数量的相对推理场景来说,这已经足够了。
将动态时间移出缓存区域。 保持系统提示词静态,并将精确的 now 注入到 用户消息 或末尾不被缓存的片段中。这样缓存前缀保持完整,而新鲜的时间戳随原本就会变化的部分一起传递。
这个经验可以推广到时间之外:任何随请求变化的内容都不应该出现在缓存前缀中。时间只是最常见的“惯犯”,因为将其放在开头显得非常自然。
相对时间才是真正 Bug 的藏身之处 给模型一个绝对时间戳是必要的,但还远远不够,因为人类和业务逻辑使用的是“相对”时间。“上周二”、“周末前”、“下一个可用时段”、“自上次同步以来”。解析这些词语需要一个锚点——一个具体的 now(现在)——模型以此为基础进行算术运算。如果给错了锚点,或者让它进行日历计算,错误就会变得微妙且代价高昂。
时区则是噩梦的开始。纽约和伦敦之间的时差并非 固定不变——它会发生波动,因为这两个地区进入和退出夏令时(Daylight Saving Time)的日期不同,因此在每年春秋两季的几周内,通常的 5 小时时差会变成 4 小时。一个记住了“CET 是 UTC+2”的模型在半年时间里都会出错,因为 CET 其实是 UTC+1,只有 CEST 才是 UTC+2。专门用于探测此类问题的基准测试(如跨日历推理、时间原语)一致发现,无论是什么模型、提示词或任务表述,日期时间推理都是不可靠的。这不是换一个模型就能解决的弱点。
然后是标题中提到的那个 Bug。夏令时转换会产生一个不存在的小时,以及一个会出现两次的小时。春季,时钟从 1:59 直接跳到 3:00——那天根本没有 2:30。秋季,1:30 会出现两次。一个设置为“夜间每 20 分钟一次”或“每天凌晨 2:30”的智能体(Agent),在一年中仅有的这两次转换之夜,会静默地漏掉一次运行、触发两次或直接抛错。这类 Bug 在 4 月份能通过所有测试,却会在 11 月凌晨 2 点把你从睡梦中叫醒报警。这其实不是 AI 的 Bug,而是系统编程中最古老的 Bug,但智能体重新引入了它,因为它们是用流利的自然语言推理时间的,而这正是这些边界情况潜伏的层级。
为没有时钟的世界而设计 每一个修复方案的核心思路都是一致的:不要再把当前时间视为模型已知的东西,而要将其视为你提供的基础设施。 以下是几个具体的模式。
注入,而非寄希望于它。 在每一轮对话中,将当前时刻作为背景上下文放在模型面前,而不是依赖它去调用工具。这样做成本很低——一个格式良好的时间上下文块不到 100 个 token——而且它消除了反应延迟。智能体永远不需要“决定”去了解现在是几点。
处处显式携带时区。 时区感知必须从操作系统时钟一直贯穿到模型在会话开始时读取的自然语言指令。错过任何一层——比如运行 UTC 的服务器、太平洋时区的用户、或者一个没有限定条件的“今天”指令——歧义就会立刻重新渗透进来。为智能体看到或发出的每个时间戳附加一个显式的 IANA 时区(例如 America/New_York,而不是 "EST"),因为仅凭缩写无法判断夏令时是否生效。
统一参考时钟。 当时间戳来自不同地区的多个服务时,在模型看到它们之前,将它们归一化到同一个标准中(通常选择 UTC),这样模型是基于一个一致的参考标准进行推理,而不是基于一堆混合的本地时钟。仅在呈现给用户的最边缘环节才转换为用户所在的时区。
在需要确定性的地方冻结 now。 对于任何你想要测试、回放或重现的内容——评估(Evals)、定时任务、审计追踪——传递一个显式的 now,而不是让每个组件独立读取系统时钟。一个冻结的、注入的时钟,决定了你是能在 7 月运行时间测试并得到 11 月的答案,还是只能在 Bug 触发的那天才能看到真相。这是确定性系统中的标准做法;智能体更需要它,正是因为它们对时间的推理非常流利,以至于失败看起来并不像失败。
更深层的转变在于思维方式。我们习惯了时间是环境的一部分——环境提供的服务如此可靠,以至于我们忘记了它其实是一个依赖项。LLM 没有环境。它只有上下文窗口,任何不在窗口内的东西对它来说都不存在。当前时刻并没有特权;它只是另一个事实,你要么正确地提供它,要么就眼睁睁看着模型胡言乱语。将时钟视为一等公民输入——注入、带时区、归一化且可冻结——一整类午夜报警就会消失。如果你把它当成模型已经知道的东西,那么你就发布了一个极具耐心的 Bug,它会静静等待一年中破坏力最大的那个凌晨 2 点。
会员专享
余下内容仅对会员开放。 会员可读完整内容 —— 每一个公开发布的观点背后,那些框架、决策与推理的全貌。
— 完整文章,包含未公开存档的部分 — 可落地的工作框架,附带权衡与决策依据 — 新文章抢先看,先于公开发布 登录以继续阅读→ 随时取消 · 一次订阅,畅读全部