那个把上周 Slack 消息当成昨天消息来读的智能体
你的运营 Agent 通过引用一条内容为 “我们明天发版” 的 Slack 消息,回答了一个关于即将到来的发布会的问题。Agent 将其视为当前的计划,并开始撰写沟通稿。然而,这条消息是六周前发布的。发版早就完成了。检索流水线(retrieval pipeline)根据你衡量的每一项指标——与 “发布日期” 的语义相似度、高于阈值的 top-1 置信度、与项目匹配的来源频道——抓取到了正确的文本块(chunk),而 Agent 基于一句仅在编写时的会议语境下才有意义的话制定了计划。
这里的 Bug 不在模型本身。Bug 在于,“明天” 并不是一个日期。它是一个指向时钟的指针,而该消息编写时的时钟并不是 Agent 阅读时的时钟。你的检索流水线索引了消息的正文,却丢弃了其框架(frame)。
这种失败在 Agent 阅读持久性文本时随处可见。一份写着 “本周末前” 的会议纪要,在三个月后的某个周二被喂给 Agent,Agent 就会表现得好像指的是这周。一条 Q3 的工单备注承诺 “我周一发送”,在 10 月的检索中浮现,Agent 就会等待一份已经在 8 月到达的邮件。一封 4 月份的客户 支持回复说 “一小时后回电”,在 5 月份掉进回电队列,Agent 拨通了电话。
这种 “基于检索文本” 的世界观让每一句话都像是发生在现在。当内容是客观事实(例如 “API 端点是 /v2/orders”)时,这没有问题;但当内容包含指示词(deictic)时就很危险——即那些含义取决于说话地点和时间的词。
当指示词脱离语境,它们就是不可信的输入
在语言学中,指示词(deictic expressions)是指那些指代对象仅相对于话语而存在的词。这里、那里、现在、昨天、明天、下周、两天后、后天、上个季度、最近、很快、我们、你。这些词本身没有任何意义。它们是变量,而非数值。它们周围的对话环境——谁在说、何时、何地、对谁说——才是其绑定的环境(binding environment)。
检索流水线从该环境中提取一个句子并将其交给模型,实际上是在评估一个带有未绑定变量的表达式,并假装结果是一个常量。模型没有原生的方法来恢复绑定,因为绑定最初就不在句子里。它要么必须凭空捏造一个绑定(通常是:Agent 自己的 “现在”),要么拒绝处理这个句子。在实践中,模型每次都会默默地捏造一个。
思考这一问题的正确方式,就像安全专家看待不可信输入那样。从语料库中检索出的文档正文并不是 Agent 可以直接引用的文本;它是 “需要根据其来源进行解析的文本”。相对日期、相对代词和相对地点就是那些未解析的部分。如果你不会在没有参数绑定的情况下将 SQL 片段粘贴到查询中,那么你也不应该在没有解析元数据的情况下将带有指示词的句子粘贴到提示词(prompt)中。
目前已发表的研究正开始跟上生产团队在实践中辛苦摸索出的经验。最近关于时间敏感型检索增强生成(RAG)的基准测试,包括 ChronoQA 混合显式和隐式时间表达的测试,证明了即使是顶尖系统,在处理时间范围隐含在文档中的问题时也会崩溃。另一项关于 “时间盲性 Agent”(temporally blind agents)的研究表明,LLM Agent 默认采用静态的上下文视角——假设世界在消息之间没有发生变化——而这种假设在实际会话长度下是最先崩溃的部分。
归一化应当发生在索引阶段,而非推理阶段
下意识的反应是在提示词中解决。加上一行:“在解释检索文档中的相对日期时,请根据文档的编写时间戳进行解析。” 这在简单情况下有效,但在所有复杂情况下都会失败。模型必须首先检测到存在相对日期,然后在你包装文本块的任何模式(schema)中定位编写时间戳,接着计算偏移量,并在整个推理过程中保留解析出的数值而不丢失。这些步骤中的每一步都有可能失败,失败会叠加,而且失败模式是无声的——模型很少会说 “我不确定这里如何解析‘明天’”;它只会随手选一个。
更干净的干预方式是在 检索流水线中处理一次,在文本块到达模型之前就完成。时间表达式归一化(Temporal expression normalization)并不是一个新问题。在信息提取领域,像 SUTime 和 HeidelTime 这样的基于规则的标注器已经应用了十多年,它们将每个相对时间表达式映射到锚定在文档创建时间的 TIMEX3 格式中。这些系统的输出不是猜测,而是:“鉴于文档创建时间为 2026-03-12,该文档中的短语 ‘下周一’ 解析为 2026-03-16。” 你可以将解析后的结果直接写入索引文本本身。
在实践中,这看起来像是摄取流水线(ingestion pipeline)中的一个预处理小步骤:
- 提取每份文档的编写时间戳(Slack 消息、工单备注、会议转录稿、邮件)。
- 对正文运行时间标注器。
- 重写每个检测到的相对表达式,在括号或方括号中包含其解析后的绝对形式——“我们明天 [2026-04-03] 发版”、“本周末前 [2026-04-07 当周]”、“一小时后回电 [2026-05-22T14:30Z 左右]”。
- 索引重写后的文本。保留原始文本作为单独字段用于显示。
现在指示词不再是未绑定的。模型检索到的文本块已经包含了它自己的解析结果。不需要提示词工程,不需要每次调用时推导那些在摄取时就已知的信息。变量在流水线中绑定环境依然可见的那一刻,就已经被绑定了一次。
这种方法对评估(evals)有一个非常有用的副作用。一旦时间戳被内联到文本中,你就可以构建时间测试用例——“Agent 是否正确得出了这条消息中的发布已经完成的结论?”——通过对解析形式进行断言,而不是针对模型的解释进行断言。测试变成了检索层的属性,而不是模型推理能力的属性,并且在你升级模型时,它不再是一个不断变化的目标。
当标准化出错时,创作元数据是后备方案
索引时的标准化是正确的默认设置,但它本身也存在失败模式。标注器(Tagger)并非完美无缺;模棱两可的表达(例如在周末前后的“周一”且没有周次限定符,“下个月”在当月最后一天说出,或者像“早些时候”这样的惯用语)有时会被错误解析,而一个被错误解析并固化在索引中的结果,比未解析的结果更糟糕。对冲方案是为每个数据块(chunk)附加结构化字段形式的创作元数据——authored_at、channel_id、author、thread_id——并教导 prompt 将其自身的 now 视为一个独立的、特殊的数值。
这种组合才是有效的。内联解析可以免费处理简单情况;可见的创作时间戳则为模型在解析缺失、错误或有争议时提供了一条恢复路径。Prompt 的任务随之变得小而明确:“如果你在检索到的块中看到日期,优先使用显式的绝对格式;如果只存在相对格式,请根据 authored_at 进行解析,而不是根据当前日期。”
这与前端处理来自 API 的日期的方式如出一辙:服务器发送一个 ISO 字符串,客户端根据用户的语言环境进行渲染,而且在任何时候,都没有人会信任数据库列中一个含义不明的 “3/4/26” 能表达他们想要的含义。这里的原则是拒绝让含义不明的日期以静态形式存在于你的系统中。
检索流水线 掌控着现在时
更深层的转变是观念上的。检索不仅仅是获取文本的行为。检索是为了让在文本产生时并不在场的东西能够使用该文本而进行的准备行为。任何依赖于“在场”的东西——时间、地点、人物、双方共同可见的内容——都必须被显性化,否则就会丢失。文本本身是不完整的;文本周围的系统才使其完整。
这就是为什么即使 embeddings 变得越来越好,那种“将一切塞进向量数据库并寄希望于奇迹”的 RAG 版本仍会不断产生这些类别的 Bug。更好的 embeddings 能找到更好的邻居,但它们无法解决指示语(deixis)问题。它们不会将“明天”锚定到具体日期。它们不会告诉模型,那个写下“我会处理”的人现在正在休产假。检索相关性指标和答案正确性指标衡量的是不同的东西,在任何包含大量受时间、人物或地点限制的内容的语料库中,这两者之间的差距正是智能体(agent)悄悄出错的地方。
一个有用的操作框架:索引中的每个块都有一个“言语行为时间戳”(speech act timestamp)和一个“检索时间戳”(retrieval timestamp)。该块的真实性是这两者的函数。状态更新在言语行为发生时是真实的;它在检索时是否仍具有可操作性,取决于其声明的半衰期。像“我明天发货”这样的承诺半衰期为一天。像“速率限制为 100 次/分钟”这样的配置值半衰期则可能是数月或数年。无法区分这些情况的检索流水线,最终会导致其智能体在今天给昨天的用户回电话。
针对今天解析明天该做什么
如果你正在寻找一个切入点,最小的防御性干预是进行为期一周的检测练习:
- 从你的生产流量中抽取几百个检索到的块作为样本。标注哪些块包含至少一个相对时间表达式。这个比例会让你感到惊讶,特别是对于 Slack、转录文本和工单评论语料库——在对话类来源中,这个比例通常高达 20-40%。
- 对于每个带有相对表达式的块,检查解析后的含义对模型是否可见。通常情况下是不可见的。
- 挑选一个此类比例最高的语料库。在其摄取(ingestion)过程中增加一个时间标准化步骤。重新构建索引。
- 构建三到四个评估案例,其中正确答案需要根据旧的时间戳解析相对日期(例如,“这次发货发生了吗?”“这个优惠还有效吗?”)。针对旧流水线和新流水线分别运行这些案例。
数据将证明在其他数据源推广这种处理方式的必要性。这种提升并不显眼。它不会体现在你的检索召回率仪表盘上。它会表现为一种 Bug 的消失:即智能体信誓旦旦地根据一个在问题提出前就已经过期的句子采取行动。
你的索引不是转录本。它是一系列声明,每个声明都附带一个时钟。时钟是让声明产生意义的关键。如果你在摄取时丢弃了时钟,那么无论在推理时进行多少 prompt 引导都无法找回它——而阅读结果的智能体将继续把上周的“明天”当成今天的后一天。
- https://arxiv.org/html/2603.16862v1
- https://arxiv.org/abs/2508.12282
- https://arxiv.org/html/2510.23853v2
- https://nlp.stanford.edu/pubs/lrec2012-sutime.pdf
- https://arxiv.org/html/2404.07775v1
- https://www.damiangalarza.com/posts/2026-01-07-llm-date-time-context-production/
- https://ragflow.io/blog/rag-review-2025-from-rag-to-context
- https://www.elastic.co/search-labs/blog/context-poisoning-llm
