先想一个问题:Agent 怎么知道当前日期和时间?

问题:模型没有时钟
模型训练完就没有实时时钟。「最近一周的提交」「今天的日程」「这个文件是昨天下午改的吗」——这些判断都依赖「现在是什么时候」。 不告诉它时间,它会默认自己活在训练数据截止的那一年。告诉它,又怎么告诉?最直接的做法:往系统提示词里注入时间
最直观的答案:在系统提示词(SP)里加一行当前时间,精确到秒。 理由很充分。一行字,几十个 token,和整段系统提示词、几千 token 的工具定义比起来,几乎可以忽略。工作目录、平台、shell 本来就在环境信息里,再加一行date 看起来也很顺。
很多 harness 的第一版,都会走这条路。我们也差点——或者曾经——这么想。
它怎么坏:时间一变,前缀整段作废
问题在于:时间每轮都在变,有时甚至每秒都在变。 系统提示词在请求最前面。时间写在那儿,等于每一轮都重写前缀的开头。后面的工具定义、历史消息,哪怕一个字没动,也全部作废——缓存按前缀匹配,前面一个字节变了,后面就全部 miss。 公开旁证:有人发现 Claude Code 有段时间在系统提示词里塞了一个每次请求都不同的字段,结果第三方服务商的缓存命中率掉到接近 0。主线不是别人翻了车,而是我们自己也会走的那条直觉路径,结构一模一样:一个会变的小字段,放在了前缀开头。 一行看起来很便宜的date,乘上轮数和请求数之后,真正贵的是它弄坏的那一整段前缀。
那为什么前缀这么重要?

- 放在最前面、每次都变的东西,代价最大。 它会让后面所有内容都没法命中缓存。精确到秒的时间如果塞进系统提示词,每一轮都让整段前缀失效——系统提示词、工具定义、历史消息,全跟着重算。
- 写进历史、写完不再改的东西,影响有限。 第 5 轮写进去的内容,到第 6 轮已经是前缀的一部分,只要不被改写,后面一直能命中。代价主要是首次写入,以及 context 一轮轮变长——这正是 03 要管的那头。
- 只挂在本次请求末尾、不进历史的东西,永远不会被缓存。 它不弄坏别人的缓存,但每发一次就是一份全价输入。
- 不变的放前面,会变的往后放。 当前时间不进系统提示词,而是跟着每轮用户消息走。
- 写进历史的东西不回头改。 想调整内容,只改以后的新轮次。
- 精度够用就行。 精确到秒的时间对模型几乎没用,却保证了每一份都和上一份不一样。
先看一眼:一次请求里装了什么

MCode 现在怎么做

# Environment,只放会话内不变的东西。 工作目录、是不是 git 仓库、平台、shell、OS、模型、语言、region、数据目录——这些一次会话里基本定死,所以放得靠前,稳稳待在可缓存的前缀里。这里没有当前时间。公开代码里对应 environment-prompt-composer 一类组装逻辑。
当前时间写进每轮用户消息开头的 <agent-context>。 由 buildSlimAgentContextBlock 生成,从第 2 轮起大致长这样(示意,字段值是我们编的):
- 精度够用就行。 #459 把
date从精确到秒改成精确到分钟(formatReminderDate)。时区和偏移量还在;秒数每轮都变,对模型几乎没用。位置对了,再把「毫无必要的每轮必变」削掉。 - 写进历史的东西不回头改。 想调整,只改以后的新轮次。已经写进去的
<agent-context>字节不变,前面攒下来的前缀才站得住。
回头看

- 01:别靠堆 subagent 来管 context
- 02:工具定义按需加载,别全塞进每一轮
- 03:长历史该压就压,要紧的落到 memory
- 04:别让模型干等,把「停顿税」压下去——护的是缓存的寿命
- 这篇:别让一行时间拖垮前缀——护的是缓存的命中
给做 agent 的几条自查
这几个改动都在 MiniMax Code 的公开仓库里,欢迎来看代码、提 issue、拍砖。