pi 北京时间扩展(缓存友好版)
让 pi 知道「现在是什么时候」,同时不把 prompt cache 的前缀打掉。这两件事本来是矛盾的 —— 时间每轮都在变,而缓存靠「前缀不变」命中;位置放错,钱就烧在重算历史上了。
本技能提供三样东西(都在 assets/):
| 文件 | 作用 |
|---|---|
beijing-time.js |
pi 扩展本体:每轮注入当前时间(放在本轮最后一条消息里)+ 注册 beijing_now 工具 |
cache-prefix-audit.js |
自检脚本:扫 pi 会话日志,找出「前缀失效」的调用(钱漏在哪) |
install.py |
安装器:doctor / install / uninstall(覆盖前先备份,不递归删除) |
装完是什么样
- pi 每轮都知道当前时间 —— 注入的内容长这样,出现在本轮请求的最后(
display:false,不占聊天显示):
## 当前时间(北京时间)
2026-10-06 星期二 00:26(UTC+08:00)
本次会话开始于 2026-09-28 21:24(已进行 7 天 2 小时 59 分)。
凡是涉及「今天 / 现在 / 还有几天 / 是否已过期 / 截至最新」的判断,一律以上面这行为准,
不要靠记忆或从文件时间戳推断。需要秒级精度或算倒计时时,调用 beijing_now 工具。
- 多了一个
beijing_now工具(需要秒级时间或倒计时时用):
当前北京时间:2026-10-06 星期二 00:26:31(UTC+08:00)
ISO 8601:2026-10-06T00:26:31+08:00
UTC:2026-10-05T16:26:31Z
今天已过:26 分(距今日结束还有 23 小时 33 分)
本次会话已进行:7 天 2 小时 59 分
目标:明日结束(23:59:59) → 2026-10-06 星期二 23:59(北京时间)
⏳ 还有 1 天 23 小时 33 分(约 2.0 天)
自然日:还隔 1 天(含今天共 2 天)
支持的写法:2026-09-30、2026-09-30 17:00、9月30日、9/30、今天 / 明天 / 后天(只给日期时按当天 23:59:59 算,符合「截止日」语义)。
- 会话开始时间会被记进日志(
pi.appendEntry,只写 session 条目、不进 LLM 上下文),事后翻日志能知道「这次会话是什么时候开的」。
三步装好
> 你不需要手动做这些 —— 把技能页上那句提示词发给你正在用的 AI,它会照着本文档执行。
cd <本技能目录>/assets
python install.py doctor # 体检:python / 扩展目录 / 是不是已经装过同名扩展
python install.py install # 复制 beijing-time.js 到 ~/.pi/agent/extensions/(已存在则先备份)
然后在 pi 里 /reload(或直接新开会话)—— 不用重启任何东西。
不想用脚本也可以手装:把 assets/beijing-time.js 复制到 ~/.pi/agent/extensions/(全局)或 <项目>/.pi/extensions/(仅该项目),再 /reload。pi 同时支持 .ts 与 .js 扩展。
⚠️ 同一个扩展只装一份:.js 和 .ts 都放进去会变成每轮注入两次(白烧 token,且「本次会话开始于」会出现两行)。install.py doctor 会提示。
怎么验证
| 验证项 | 做法 | 期望 |
|---|---|---|
| 时间进来了 | 开新会话,直接问「现在几点」 | 直接答出,不调用工具 |
| 倒计时工具 | 问「离 9 月 30 日还有几天」 | 调用 beijing_now |
| 缓存安全 | node assets/cache-prefix-audit.js --order |
条目顺序里 beijing-time/now 出现在 user 消息之后(请求尾部);"前缀疑似失效 0 次" |
为什么不能把时间写进系统提示(这个技能的存在理由)
pi 默认不把时间写进上下文 —— 因为 prompt cache(DeepSeek / Anthropic 都是)是前缀匹配:
[系统提示][工具定义][历史消息 ……][最新一条用户消息] ← 请求顺序
↑ 在这里改一行(哪怕只改末尾),它后面的一切全部失去命中
- 系统提示在最前面,所以「在系统提示里写当前时间」= 每轮都把整段上下文重算一遍;
- 未命中单价是命中价的 50 倍(DeepSeek 高峰:命中 ¥0.04 / 未命中 ¥2.0 每百万 token);
- 放在整条请求的末尾(本轮最后一条消息)就没有这个问题 —— 改末尾不影响任何前缀。
本机实测(2026-10-05,pi 1.0.x + DeepSeek):
| 指标 | 数值 |
|---|---|
| 当天总花费 | ¥24.88 |
| 其中缓存未命中 | ¥15.6(约 63%) |
| 症状 | 跨分钟的新用户消息 cacheRead 恒为 3456,而 input 达 264K~616K token |
| 规模 | 当天 22 次这样的调用,共 7.35M 未命中 token(≈ 当天可见未命中花费的 88%) |
| 对照 | 同一分钟内发起的轮次整段命中(cacheRead 23 万+);同一轮里的后续工具循环请求命中 26 万~51 万 |
| 修复后 | 当天前缀失效事件 4 次全部在改动之前,之后 0 次;次日 0 点后抽查 90 次调用,命中率 98.8%、0 次失效 |
控制变量:同一台机器、同一个模型、同一批会话日志(~/.pi/agent/sessions/**/*.jsonl 里 usage 字段的 input / cacheRead / output)。
自查:你的 pi 有没有在偷偷烧钱
node assets/cache-prefix-audit.js # 今天(北京时间)动过的会话
node assets/cache-prefix-audit.js --all # 全部会话
node assets/cache-prefix-audit.js --order # 额外打印条目顺序,看动态块注入在哪
判定标准:
| 现象 | 含义 |
|---|---|
本次 cacheRead ≈ 上一条的 cacheRead+input+output |
前缀保住了 ✓ |
cacheRead 很小(例如 3456)而 input 几十万 |
前缀失效 ⚠️:这段上下文按未命中价重算了一遍 |
脚本会列出每次失效的时间、token 数,并按高峰价(¥2/百万)折算成金额 —— 那就是白花的钱。价格常量在脚本顶部(PEAK / OFFPEAK),换了模型/价格自己改。
这条检查不限于时间:任何「每轮都要变」的内容放进请求前缀都会踩同一个坑 —— 随机数、实时状态、每轮重新生成的摘要、按会话轮换的 flag……判断标准只有一条:它每轮都会变吗?会变就放请求末尾,别放前缀。
常见问题
| 问题 | 答案 |
|---|---|
| 这是给谁用的? | pi 编码助手(@earendil-works/pi-coding-agent)。它是 pi 的扩展(extension),不是 pi-web 的功能,也不是 Claude Skills 那种纯提示词技能 |
| 会上传数据 / 联网吗? | 不会。扩展只用 Intl.DateTimeFormat 算墙上时间、写自己的 session 条目、注册一个本地工具。install.py 同样不联网 |
| 时区只能北京吗? | 默认是(TIME_ZONE = "Asia/Shanghai")。改文件顶部的 TIME_ZONE(如 America/New_York)即可,倒计时按该时区的墙上时间算 |
| 我只想要工具、不想每轮注入时间 | 删掉 pi.on("before_agent_start", …) 那一段,只留 registerTool |
beijing_now 不出现 / 时间没进来 |
① 装完没 /reload(或没新开会话);② 文件没放进 ~/.pi/agent/extensions/;③ 扩展报错 —— 看 pi 启动时的扩展日志;④ pi 版本太老(从 before_agent_start 返回 message 的写法在 pi 1.0.x 实测可用) |
| 会不会重复注入? | 只有当 beijing-time.js 和 beijing-time.ts 同时存在时才会。install.py doctor 会查这件事 |
| 怎么卸载 | python install.py uninstall(先备份再移除),然后 /reload |
版本历史
| 版本 | 主要变化 |
|---|---|
| 1.0.0 | 首个公开版:缓存友好注入(本轮最后一条消息,不碰系统提示)+ beijing_now 工具(秒级 / 倒计时 / 自然日差)+ 会话起始时间元数据 + cache-prefix-audit.js 自检 + install.py(doctor/install/uninstall,覆盖前备份) |
依赖与致谢
- 无第三方依赖:扩展只用到 pi 的扩展 API 与 Node 的
Intl;自检脚本是纯 Node(fs/path) - pi(
@earendil-works/pi-coding-agent,MIT,作者 Mario Zechner)—— 扩展 API、before_agent_start的返回值契约({ message?, systemPrompt? }) - 计费语义(命中/未命中/输出三档、未命中单价是命中价的 50 倍)来自 DeepSeek 官方价格页;换模型请改自检脚本顶部的价格常量
- 这个技能的由来:2026-10-05 排查「一天花 ¥24.88、未命中 ¥15.6」时定位到的根因就是「时间写进了系统提示」,改对位置后写成可复用的扩展并把教训带上
给 AI 的执行清单
python install.py doctor—— 逐项看输出;若已存在beijing-time.ts,告诉用户「同一个扩展只装一份」,问清楚是替换还是放弃python install.py install- 提醒用户在 pi 里
/reload或新开会话(不要提示重启 pi-web —— 这跟 pi-web 无关) - 验证:新会话问「现在几点」(应直接答出)与「离 X 还有几天」(应调用
beijing_now) - 可选:
node assets/cache-prefix-audit.js告诉用户当前命中率、有没有前缀失效 - 若用户本来就有旧版扩展:只备份、不删除,由用户决定去留