定时调度
一句话版:
@deepseek-ai/dsh-schedule给 live agent 提供 3 个会话内工具(schedule_create/schedule_list/schedule_delete)管理持久提醒:会话事件日志拥有提醒状态,timer 和模型 follow-up 只是日志的可丢弃投影。
让 agent 定时做点事的能力。核心洞察:提醒状态不存"内存定时器",而是存会话事件日志;这样折叠/恢复只认日志,timer 崩了也没关系。
一、三个工具
| 工具 | 输入 | 行为 |
|---|---|---|
schedule_create | after_seconds(正整数)/ at(绝对时间)/ every_seconds(≥5 分钟) | 创建提醒 |
schedule_list | : | 列出活动提醒(含 scheduled / overdue 状态) |
schedule_delete | id | 删除提醒 |
schedule_dispatch不是工具:dispatch 是schedule/change事件里的内部 operation。到点投递由运行时做(见下)。
二、状态模型(关键设计)
会话事件日志(唯一真源)
├── schedule/change 事件(create/delete/dispatch 联合,版本 1)
├── timer(可丢弃投影)
└── 模型 follow-up(可丢弃投影)
重要性质:
- 会话折叠 = 完整日志;fork 只折叠
seedLength之后:子会话不继承父会话提醒 - 每次读取前先
ctx.sessions.flush(session);持久化不可用 →persistence_uncertain,绝不把未确认状态当结果 - 回放拒绝:未知版本、重复 id、形状不匹配的 dispatch、对非活动记录的转换
- 每次成功 preflight 后、以及真实 barrier 成功后,回调
onDurableChange驱动 owner 重算
三、时间语义(严格)
at 支持两种:
| 形式 | 示例 |
|---|---|
| RFC 3339 字符串 | 2026-08-12T10:00:00Z 或带偏移 |
| 本地形式 | { date, time, time_zone }:必须显式 UTC 或 IANA 时区 |
拒绝:缺时区、无偏移字符串、DST 缺口内时间(重叠取较早)、非未来目标。创建后只保留规范化 UTC:Schedule 不读浏览器/会话/模型上下文时区。
最小间隔:every_seconds 不能小于 MIN_EVERY_INTERVAL_SECONDS = 300(5 分钟)。
四、运行时投递:提醒怎么变成真实轮次
到点的提醒不是"改个状态",而是被投递成 agent 的一条真实 user message:
每个根 agent 一个 ScheduleRuntime 维护循环
→ 定时器用 whenIdle() + MAX_TIMER_DELAY_MS(2147483647) 等待空闲相位
→ 到点用 agent.runMaintenance(...) 认领空闲相位
→ 构造 user message,agent.followup(message) 注入一次"跟进轮次"
→ 这正是模型实际收到并处理的 next-turn 输入
工具调用还带可取消事务(runCancellableScheduleTransaction / exec.signal),防止重复投递。
五、组合要求
- 加载顺序:
ctx.sessions、ctx.agents、ctx.tools、ctx.sessionPersistence+ flush 持久化监听器之后;静态注入失败 = 组合直接失败 - 只监听后续
agent/created:加载时已存在的 agent 和子 agent 不会获得 Schedule - schedule/change 的 durably-commit 投递屏障与
onDurableChangeobserver 负责在 create/delete 后驱动重算
六、验证
# 会话日志里找 schedule/change 事件(默认 zstd、两级目录)
zstdcat ~/.dsh/sessions/*/*/session.jsonl.zstd | jq -r 'select(.type == "schedule/change")'
# 看投递成什么轮次
zstdcat ~/.dsh/sessions/*/*/session.jsonl.zstd | grep -E "user/message|schedule/" | tail