AI 是被动的
你跟 AI 说”帮我改这段代码”,它秒回。你说”一小时后提醒我开会”,它说”好的”——然后就没有然后了。
这不是某个产品的 bug,而是大多数主流 AI 助手的通病:它们是被动的。 你问它才答,你不问它就沉默。它没有”时间”的概念,没有”未来”的概念,更没有”主动发起”的能力。虽然一些开源或闭源的 Agent 框架已经支持定时任务,但像豆包、DeepSeek 等主流 AI 助手,仍然不具备这一能力。
chat.nvim 新增了定时任务功能,目的就是给 AI 装上”时间感”——让它能在未来的某个时刻,主动找你。
核心思路
要打破 AI 的被动性,核心问题是:如何在未来的某个时刻,让 AI 像收到用户消息一样开始工作?
解法不复杂,就三步:
- 记住一个任务(什么时间、做什么)
- 到点触发
- 把消息注入会话,AI 像收到用户消息一样处理
思路简单,但魔鬼在细节。下面讲每个环节的设计取舍。
时间模型:统一为绝对时间戳
用户可以三种方式描述时间:
- “1 小时后” —— 相对延迟
- “明天早上 9 点” —— 绝对时刻
- “每天晚上 10 点” —— 周期循环
三选其一,互斥。
无论用户怎么描述,内部统一转换为 Unix 时间戳。”1 小时后”变成 当前时间 + 3600,”每天”变成从创建时间起每 86400 秒一次。
为什么?因为调度引擎只需要处理一种时间模型,不需要区分”延迟”和”定时”,不需要为不同类型写不同逻辑。统一抽象,消灭分支。
触发机制:不轮询,用定时器
最直觉的方案是轮询:每隔几秒扫描一遍任务列表,看有没有到点的。
这很浪费。100 个任务里有 99 个还在等待,你却每秒扫一遍。
更好的方案是每个任务一个独立定时器,到点才唤醒,不到点 CPU 零开销。这是事件驱动而非轮询驱动。
Neovim 内置 libuv,uv.new_timer() 就是干这个的,精确到毫秒级,而且不阻塞主线程。
周期任务的防漂移
周期性任务有个坑:如果 Neovim 中途重启了,恢复后下次触发时间怎么算?
直觉做法是”当前时间 + interval”。但这会导致节奏漂移——你设的每天 10 点,重启后变成 10:03,再重启变成 10:07……
正确做法是:基于创建时间和已执行次数算下次触发。
下次触发 = 创建时间 + (已执行次数 + 1) × 间隔
这样无论重启多少次,第 7 次执行永远在创建时间 + 7 × 间隔那个点。节奏锁定在创建那一刻。
24.8 天上限
libuv 的 timer 有个硬限制:最大超时 2³¹-1 毫秒,约 24.8 天。
如果用户设了一个 30 天后的任务,怎么办?
解法是分片。先设一个 24.8 天的 timer,到点后醒来一看——还没到真正的触发时间,重新算剩余延迟,再设一个新 timer。两次接力,覆盖任意时长。
上限设为 30 天,既覆盖合理需求,又不至于让一个 bug 任务永远挂在系统里。
调度与执行解耦
这是整个设计中最关键的决策。
调度引擎只负责一件事:到点,往消息队列里 push 一条消息。它不调用 LLM API,不关心 AI 是否在线,不关心网络状态。
为什么不直接到点调 API?因为职责一旦混合,复杂度就上来了:
- AI 正在处理另一个请求怎么办?
- 网络断了怎么办?
- 会话被删了怎么办?
- 用户正在打字怎么办?
解耦之后,调度引擎的逻辑极其简单:到点 → push → 完事。剩下的交给消息队列处理。
消息队列:等一个窗口
任务触发后,消息进入队列。队列负责在”合适的时机”把消息送给 AI。
什么是合适的时机?会话空闲的时候。
- 如果 AI 没在忙,立即发送
- 如果 AI 正在处理别的请求,排队等,每隔几秒检查一次,等它空闲了再注入
这就像同事给你发了消息,但你在开会——消息不会丢,等你开完会再看。
连续 3 次发送失败才丢弃,防止偶发故障吞消息。
持久化与恢复
Neovim 不是常驻进程,用户随时可能关掉。定时任务必须跨重启存活。
方案很直接:每次创建、取消、触发时,把全部任务写一份 JSON 到磁盘。下次启动时加载。
但有个细节:libuv timer 是内存对象,无法序列化。持久化时必须排除 timer 句柄,恢复时重新创建。
恢复时还有两条不同策略:
- 过期的一次性任务:直接跳过。已经过了触发时间,没有意义了
- 过期的周期性任务:必须恢复。周期任务还要继续执行,不能因为重启就断
一次完整流程
你说:"1 小时后提醒我开会"
│
▼
AI 调用工具,计算 trigger_at = 当前时间 + 3600
│
▼
调度引擎创建任务,写入磁盘,arm 一个 3600 秒的定时器
│
···(1 小时后)···
│
▼
定时器触发 → 往会话的消息队列 push "提醒我开会"
│
▼
队列检测会话空闲 → 发送给 AI
│
▼
AI 收到消息,像你亲自说的一样,回复提醒
怎么用
直接用自然语言跟 AI 说就行:
1 小时后提醒我跟进苏北人民医院的项目
AI 会自动计算时间并创建定时任务。
三种模式:
- 一次性:
delay_seconds=3600或trigger_at=1717200000 - 周期性:
interval=86400(每天),可选repeat_count=7限制次数 - 管理:
action="list"查看,action="cancel"取消
| 特性 | 说明 |
|---|---|
| 持久化 | Neovim 重启后依然有效 |
| 最大延时 | 30 天 |
| 最小间隔 | 10 秒 |
| 精度 | 秒级(基于 Unix 时间戳) |
| 依赖 | 零外部依赖,纯 Neovim + libuv |


















