DeepSeek Harness 的定时调度:文档写 cron,协议与源码里没有

2026-08-16

先说清楚这篇文章的边界:DeepSeek Harness 这个仓库建立于 2026-08-13,我们采集事实的时间是 2026-08-16,前后只差三天;根 package.json 里的版本是 0.1.0-rc.5,GitHub 上一个 Release 都没有,README 自述处于开发者预览阶段并明写未来会有破坏兼容性的变更。下面提到的每一个字段名、文件路径、行号,都随时可能变。我们也没有安装过 dsh、没有跑过它的任何命令,全部内容来自读文件。

现象:照着机制表写,找不到那个 kind

如果你打算给 dsh 写一个「到点提醒」类的插件,最顺手的入口是 docs/cookbook/extension-cookbook.md 末尾那张「产品特性 → 插件机制」对照表。表里有一行专门讲定时任务,左边一格写的是 Scheduled tasks (cron),右边一格给的机制里写着:定时器触发后,在空闲时调用 followup(…, {source: {kind: 'cron', …}})。这一行在 docs/cookbook/extension-cookbook.md:124

看到这行,很自然的下一步是去仓库里找 kind: 'cron' 长什么样、有哪些字段可填。找不到。

另外两处文档的口径正好相反

同一个仓库里,专门讲 schedule 的两份文件写的是另一回事。

docs/subsystems/schedule.md:94 在讲固定频率输入时写道:every_seconds 是每条记录的间隔、最小 300 秒、锚定创建时间,它只支持固定频率复发,「协议里没有日历规则或 Cron 表达式」,也没有复发时区、没有共享冷却、没有跨记录的准入闸门。

packages/schedule/schedule/README.md:114 在「已知局限」小节里给了同义的一条:固定间隔而非日历规则、最快五分钟一次,「日历或 Cron 表达式不属于该协议的一部分」。

到这里,三处文档的位置已经很清楚:cookbook 的机制表一行、schedule 子系统文档的固定频率小节、schedule 包 README 的已知局限小节。前者写的是 cron 语义,后两者明写协议里没有 Cron 表达式。这两者不一致,以我们实读的仓库状态为准。 我们不推断哪一处才是作者的本意,也不据此评价这个项目——说完差异就停。

源码里实际发出去的来源是什么

真正决定你能不能写出那个 kind 的,是源码。

packages/schedule/schedule/src/runtime.ts:273 是 schedule 到期后组装那条回到会话里的消息的地方。这条消息上带的来源是:

source: { kind: 'plugin', plugin: 'schedule' }

也就是说,到期提醒是以「插件」这个来源投进去的,插件名叫 schedule,而不是一个叫 cron 的独立来源类型。基础的 MessageSourceMap 一共只声明了四种来源:userpluginmodeltool(这份声明在 packages/extensions/tool-cordis/src/api-catalog.ts:3454 里有一份镜像)。

我们对全仓的 .ts / .tsx 做过一次大小写不敏感的 “cron” 搜索(排除 node_modules.gitdistvendor),只命中 1 处,而且是一段注释packages/core/session/src/types.ts:260,是一段列举语境的注释里出现了 “cron” 这个词。至于 kind: 'cron' 这个来源类型本身,我们没能在仓库里找到对应实现。

怎么自己确认这件事(三步)

这三步不需要装任何东西,clone 下来就能做,Windows 与 Linux/macOS 都一样:

  1. 看那张表:打开 docs/cookbook/extension-cookbook.md,翻到末尾的机制对照表,找 Scheduled tasks (cron) 那一行,确认它写的是不是 {source: {kind: 'cron', …}}
  2. 看协议怎么说:打开 docs/subsystems/schedule.md 的固定频率小节,以及 packages/schedule/schedule/README.md 的已知局限小节,确认「没有 Cron 表达式」这句还在不在。
  3. 看源码里发的是什么:在 packages/schedule/schedule/src/runtime.ts 里找 source:,看它构造出来的 kind 是不是 plugin;再对全仓 TS 搜一次 cron,看命中数是不是仍然只有那一处注释。

三步的结论如果与本文一致,说明你遇到的就是同一件事;如果第 3 步里已经出现了 kind: 'cron' 的实现,那说明仓库已经变了——毕竟这是一个建仓才三天、版本仍是 0.1.0-rc.5 的开发者预览项目,以你手上那份仓库的当前内容为准。

那 schedule 到底给了什么

把差异放下之后,值得把 schedule 协议真正提供的东西看清楚,这样你才知道自己要补的是哪一段。

docs/subsystems/schedule.md:5 给的定位是:schedule 拥有持久的提醒,这些提醒以普通的后续对话轮次回到原始的活跃 Session。v1 支持三种规则(:9):

规则语义
after_seconds正安全整数秒的延时
at绝对时刻
every_seconds固定频率,不低于五分钟

创建时会把首个目标规范化成四位年份的 RFC 3339 UTC scheduledAtat 接受两种输入形态:带偏移量的严格 RFC 3339 字符串,或者 LocalAtInput { date, time, time_zone }:73-86)。有一条容易被忽略的硬规矩写在 :88:Schedule 从不读取浏览器、Session、进程或模型上下文的时区——无偏移的字符串会被拒绝,非未来的目标会被拒绝,夏令时空档内的本地时间也会被拒绝;夏令时重叠的情况取更早的那个瞬间(:90)。

补齐语义同样是刻意收窄的:会话冷了或者忙过好几个目标时,一条 Every 记录只贡献它最近的那一次到期,直接推进到决策时刻之后的第一个对齐目标,不枚举、不持久化、不重放错过的间隔。

模型侧的三个工具名是 schedule_createschedule_listschedule_delete;稳定错误码有九个:invalid_promptinvalid_selectorinvalid_ruleinvalid_time_zonenot_futuretime_out_of_rangefrequency_too_highcorrupt_schedule_loginternal_error,持久化屏障失败时另报 persistence_uncertaindocs/subsystems/schedule.md:178)。

投递边界也写得很直白(:186):admission 之后、持久 dispatch 之前有一个窄崩溃窗口,恢复后可能重复提醒内容,所以这条边界的定性是「best-effort at-least-once」,不是 exactly-once。

还有一个容易漏掉的前提

packages/schedule/schedule/README.md:111-115 的已知局限里第一条是只在会话内投递:提醒只有在其原始 Session 仍然存活时才按时跑,冷会话没有外部通知通道,只有恢复之后才会处理逾期记录;失败之后也不起私有重试定时器,靠后续的 Agent 活动重算。

另外,我们对全仓 *.yml(排除 node_modules)grep 了 dsh-schedule只在 examples/web-schedule/cordis.yml:9 出现一次packages/bundle/*/cordis.patch.yml 这几个出厂组合文件里没有它。也就是说,如果你的组合是从出厂 bundle 起步的,schedule 这一行需要你自己加进来。这只是「组合里在不在」的事实陈述,不代表这个能力好不好用。

什么情况说明不是这个原因

如果你遇到的现象是「定时提醒没到点」「到点了但模型没有反应」,那不一定与本文说的文档差异有关,先看这几条:会话是不是已经冷了(文档写明只在会话内投递);every_seconds 的间隔是不是低于文档写明的 300 秒最小值;at 传的字符串是不是没带偏移量(文档明写无偏移量的字符串会被拒绝);目标时刻是不是已经过去(文档明写非未来的目标会被拒绝);本地时间是不是落在夏令时空档里(文档明写这种也会被拒绝)。协议给出的九个稳定错误码里,frequency_too_highnot_futureinvalid_time_zoneinvalid_rule 都落在这一类拒绝路径上,具体哪种输入对应哪个码,请以你手上仓库的源码为准。这几条与 cookbook 那一行写没写 cron 是两码事。

反过来,只有当你照着 cookbook 机制表去找 kind: 'cron' 这个来源类型、结果在仓库里找不到的时候,本文说的差异才是你的原因。

最后重复一遍那条限定:这个仓库处于开发者预览、版本 0.1.0-rc.5、没有任何 Release,README 明写会有破坏兼容性的变更。上面所有行号、字段名、默认值都以你手上仓库的当前内容为准。

延伸阅读


本文依据 DeepSeek Harness 官方仓库(github.com/deepseek-ai/deepseek-harness)的 README、docs/ 下的 架构与子系统文档、以及 packages/ 下的源码整理,核对日 2026-08-16,对应仓库快照 47f9438(版本 0.1.0-rc.5)。 本文内容为仓库源码与文档口径,我们没有安装、也没有运行过这个项目, 因此不涉及界面外观、操作手感与运行速度的任何描述。 该仓库建立于 2026-08-13,README 自述处于开发者预览阶段并明确说明未来会有破坏兼容性的变更, 文中出现的命令、配置与默认值随时可能变动,请以仓库最新内容为准。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。