跳到主要内容

时间上下文注入:让 LLM 真正知道今天是几号

· 阅读需 12 分钟
Tian Pan
Software Engineer

你的 LLM 功能已经上线。用户开始问那些涉及时间的问题——"最新政策是什么?""帮我总结本周发生的事""这条信息还是最新的吗?"——模型自信、流畅地回答,却答错了。

模型不知道今天是几号。它从来都不知道。你熟悉的聊天界面让你忘了这件事,因为那些界面在背后悄悄注入了当前日期。但你的 API 集成不会。你发布的系统在不知道自己处于时间轴哪个位置的情况下,仍然在推理时间相关的问题——这是一类 bug,会在你还没想到去找它之前就出现在生产环境里。

这不是换一个更聪明的模型就能绕过去的 LLM 局限——这是一个需要显式工程手段来填补的架构空白。如何正确地做到这一点——不破坏提示缓存、不在分布式工作节点之间引入时区 bug、不把三年前的文档当成"最新"内容检索出来——正是本文要讲的。

模型没有时钟,而且这是设计如此

LLM 是在静态文本快照上训练出来的。训练过程产生的是一个将词元映射到可能下一词元的函数,而不是一个能查询系统状态的过程。它没有内部时钟,没有"现在"的概念。

有的是:一个基于训练数据中日期分布的强先验,用来判断哪些日期是合理的。当你在没有日期上下文的情况下问模型"现在是哪年?",它不会随机猜——它会给出一个看起来符合训练分布的答案。GPT-4o 的训练截止日期是 2023 年 10 月;截至 2026 年初,这已经是 29 个月的差距。Llama 4 的截止日期是 2024 年 8 月。这些模型不知道自己正在被 2026 年使用,除非你告诉它们。

那些让人感觉"紧跟时事"的聊天界面,其实在做的是一种可以称为"编排剧场"的操作:底层模型和 API 版本有着相同的训练截止日期;界面用一段系统提示把它包裹起来,写着"今天是 2026 年 4 月 20 日",有时还给它配备一个搜索工具。模型本身在时间上是冻结的,新鲜感是注入进去的。

一旦你理解了这一点,前进的路就很清晰:你也来注入日期。但朴素的注入会产生三种独特的生产故障模式,在写第一行代码之前值得先搞清楚。

三种会在生产环境找上门的故障模式

自信地给出错误日期。 一个团队构建了客户分析管道。系统提示里包含"最近的转化"和"当前的流失风险"这类措辞。没有日期锚点,模型就根据训练数据的先验来理解"最近"——然后给出错误的判断,而且错得很难审计。六个月前的记录看起来是"最近的",上周的记录看起来是"非常近期的"。模型的输出连贯而错误,错误是隐形的,直到有人把 LLM 的分类结果和真实数据对比,才会被发现。

修复方法很简单:在系统提示前加一句 f"Today is {date.today().isoformat()}." ,再把提示里所有的"最近"替换成"过去 30 天内转化的"。但没有思考时间上下文的团队会先发布出问题的版本,因为它会静默地失败。

破坏缓存的时间戳。 一个团队意识到需要时间上下文,于是注入当前时间:f"Current date and time: {datetime.now()}"。这会产生每秒都不同的字符串,意味着每个请求都会导致提示缓存未命中。一个工程团队发现这个问题时,他们的 LLM 响应时间已经从几秒变成了 50 秒以上——缓存在每个请求上都失效了,模型每次都要重新处理完整的系统提示。另一个团队追查零缓存读取词元的原因,最终定位到一行代码:"Current date and time: ${dateTime}" 格式化到了秒级精度。

修复方法:只注入日期,不注入时间。2026-04-20 在 24 小时内是稳定的,能给模型提供绝大多数场景所需的时间上下文。如果确实需要时间精度,把时刻放到用户消息轮次里。

午夜跨日。 一个系统提示在晚上 11:45 组装并缓存,写着 "Today is April 19, 2026."。应用层缓存的 TTL 是一小时。到凌晨 12:15,从该缓存服务的请求看到的还是"4 月 19 日"。一个计算"明天"的智能体算出来是 4 月 20 日——也就是今天。调度功能产生差一天的错误。这个 bug 是间歇性的(只在午夜前后两小时的窗口期出现),且依赖环境(只在高流量时段跨午夜的时区才会出现)。

这些不是只有设计糟糕的系统才会遇到的边缘情况,而是任何没有专门针对时间上下文进行设计的系统的默认行为。

正确的日期注入模式

生产安全的方案根据缓存稳定性来区分什么放哪里:

系统提示(稳定,对缓存友好): 用 ISO 8601 格式、以 UTC 为基准的日期。仅此而已。

system = f"Today's date is {datetime.utcnow().date().isoformat()} UTC.\n" + base_instructions

这使缓存每天失效一次,是可以接受的。它给模型提供了一个全局的日期参考系,也不会在不同区域运行的分布式工作节点之间造成时区混乱。

用户消息(易变,每次请求): 如果需要时刻或用户本地时区,放这里。

[Context: user's local time is 14:30, timezone: America/New_York]

这不会破坏系统提示缓存,同时能让模型回答时区相关的问题,而不会让稳定的前缀变得不稳定。

长时运行的会话: 暴露一个 get_current_time(timezone) 工具供智能体调用。跨越午夜的会话需要新鲜的时间数据;工具调用是获取真实世界状态的正确机制,而不是依赖会话开始时注入的过期时间戳。

一个不太直观的细节:位置很重要。针对日期敏感查询的研究表明,把日期放在系统提示的开头——在其他指令之前——比放在末尾能产生更准确的时间推理。日期为模型解读后续指令提供了锚点。这是正确的心智模型:日期是上下文,后续提示应该在这个上下文中被解读。

另一个借鉴自 Anthropic 处理 Claude 自身系统提示方式的细节:如果你的模型有知识截止日期,在系统提示中声明一个略早于实际截止日期的日期。在实际截止日期前两个月的"缓冲区",是因为训练数据最后几个月的代表性不足。这段时期的事件存在于训练数据中,但覆盖率更低,使得模型的表示不够可靠。显式声明对该时期的不确定性,比隐含地表示有把握更准确。

最后:在提示中定义所有相对时间术语。没有锚点,"最近"、"最新"、"当前"、"即将"和"快要"都是未定义的。一个写着"分析近期客户活动"的提示,在没有日期上下文的情况下让模型无从处理。用明确的范围替换它们:"过去 30 天内"、"在 {start_date}{end_date} 之间"。即使在你已经注入了当前日期之后,这条规则也适用——"最近"在没有定义时间窗口之前仍然是模糊的。

时间感知检索:RAG 系统在哪里静默出错

RAG 中的日期注入问题在结构上不同于提示注入问题,也更难被发现。

标准语义搜索通过向量相似性检索文档——文档的嵌入与查询嵌入的匹配程度。嵌入编码的是语义意义,而不是时间有效性。一篇 2022 年关于"Acme 公司 CEO"的文章和一篇 2025 年的文章,如果讨论同一话题,它们的嵌入会很相似。标准检索会将它们混合在一起,不考虑时效性。如果 2022 年的文档在语义相似度上排名更高,模型就会收到错误信息并自信地呈现出来。

正确的架构在语义排名之前加入基于元数据的过滤。每个文档块都应至少索引 created_at,对于有明确生效和失效日期的内容(法规文件、定价表、政策页面),理想情况下还应有 valid_fromvalid_until。查询在语义排名运行之前过滤 valid_until >= now。时间上已失效的文档永远不会进入排名阶段。

除硬过滤外,时效衰减评分能改善没有明确过期日期的内容的结果。一个同时权衡语义相似度和文档年龄的融合评分公式:

score(q, d, t) = α · cosine(q, d) + (1−α) · 0.5^(age_days / h)

其中 α = 0.7 权重语义相似度,h(以天为单位的半衰期)控制相关性随时间衰减的速度。对于新闻或市场数据,h = 7 天合理;对于技术文档,h = 90 天可能更合适。这些参数可以按语料库调整。

性能差异是显著的:纯余弦相似度检索在时间有效性很重要的查询上(如"X 的当前政策是什么?")准确率几乎为零。带元数据过滤的时效加权检索在同样的查询上接近完美。这不是边际改进——这是有效系统和从陈旧数据中生成幻觉系统之间的区别。

另一个 RAG 特有的问题是检索触发:知道什么时候该检索、什么时候该从模型的参数知识中回答。对于时间敏感的语料库,一个合理的默认值是:对任何可能在模型训练截止日期后发生变化的内容都进行检索。经过校准的路由器(预测检索是否能改善答案的模型)可以在不牺牲时效性的前提下减少约 30% 的不必要检索调用,但它们需要标注数据来训练,并增加系统复杂性。对大多数团队来说,更简单的规则——"如果查询可能与时间相关,就检索"——是正确的起点。

测试你构建的内容

时间上下文处理中的漏洞不会出现在标准 LLM 评估中。你必须构建专门针对它们的测试用例。

日期边界测试: 用合成的"当前日期"值在月末、年末和夏令时切换点运行你的提示。一个在 4 月 20 日工作正常的系统,可能在 3 月 31 日(月末)、12 月 31 日(年末)或 11 月 3 日凌晨 2 点(夏令时切换)失败。这些是日期算术中差一错误会浮现的日期。

午夜跨日测试: 模拟在晚上 11:58 和凌晨 12:02 到达的请求,使用 11:45 PM 缓存的系统提示。验证缓存中注入的日期对两个请求都反映了正确的天——或者你的缓存失效策略处理了这个过渡。

陈旧检索测试: 对于 RAG 系统,验证超过 valid_until 阈值的文档不会出现在结果中。创建过去有明确过期日期的合成文档并确认它们被过滤掉。创建不同时间段的语义相似文档,并验证时效评分将较新的文档排名更高。

时间推理探针: 向你的系统提出需要知道当前日期的直接问题:"距离季度末还有多少天?""过去 30 天发生了什么?""这条信息是最新的吗?"验证答案是否基于注入的日期,而不是模型训练时的先验。

日志记录是另一项关键投入。当智能体产生日期派生的输出时,将注入的日期连同输出一起记录下来。这为"陈旧日期"事件创建了审计跟踪——当用户反映系统给了他们错误的日期相对信息时,你需要知道是日期注入失败了,还是模型从正确的日期出发推理出了错误结论。没有日志,你无法区分这两种情况。

总结

模型不知道今天是几号。它使用训练数据先验来推理时间,而这些先验在部署时已经落后了几个月到几年。如果你的系统依赖时间感知——而大多数生产系统最终都会依赖——你需要显式地把这种感知工程化进去。

规则很简单:

  • 在系统提示中注入日期(ISO 8601,UTC);永远不要在那里注入时刻
  • 把精确时间戳放在用户消息或可调用工具中
  • 对于长时会话,暴露一个时钟工具,而不是依赖会话开始时的注入
  • 用明确的数值窗口定义所有相对时间术语
  • 为每个 RAG 文档块添加 valid_from / valid_until 元数据,并在语义排名之前过滤
  • 对时间敏感的语料库应用时效衰减评分
  • 在日期边界进行测试:月末、年末、夏令时、午夜过渡

好消息:这些都不难实现。坏消息:它不会自动发生,而且失败足够安静,以至于很多团队是通过用户投诉而不是监控发现问题的。从一开始就将时间上下文感知纳入 AI 架构的团队,能够免受整整一类生产事故的困扰。

Let's stay in touch and Follow me for more thoughts and updates