前言¶
DSH 自带 @deepseek-ai/dsh-schedule 这套「自动化任务」,但它是排期提醒:到点把一条消息投回任务绑定的会话,用那个会话的历史继续干活。回执只表示「消息进了收件箱」,不表示模型执行完成,也没有暂停和手动运行。
下面介绍的 dsh-routines(维护者 leinaldo,分类「工作流」)把语义换成任务运行器:到点新开一个对话跑一轮,跑完有状态、耗时、步数和 token 回执,可以回看那轮会话。目标是照 Claude 桌面 App 本地定时任务(Routines / CCD scheduled tasks)的行为,给 DSH 补一套定时任务。
这是什么¶
一句话定位:一个 DSH 插件,用一份 JSON 索引管理定时任务,每次触发都新建会话、独立上下文跑一轮。
解决的实际问题是——内置提醒到点只投递消息,你不知道模型到底跑没跑完、跑得怎么样;而 dsh-routines 把这一轮当成一次真正的运行来记账,失败、超时都能查到。
与内置自动化任务的区别¶
| 维度 | @deepseek-ai/dsh-schedule(内置) |
dsh-routines(本插件) |
|---|---|---|
| 运行位置 | 投回绑定会话,复用其历史 | 新建会话,每轮独立上下文 |
| 执行回执 | 只表示消息进入收件箱 | turn/end 后记录状态/耗时/步数/token |
| 手动运行 | 无 | 「立即运行」,不改排期 |
| 停用/启用 | 无 | 支持 |
| 一次性任务 | 无 | 支持,跑完自动停用 |
因为每轮不共享上下文,任务里那条指令(prompt)必须自包含——写清楚要做什么,不要指望它会读到上一次的对话。
调度与运行语义¶
调度能力:
- cron:五字段,本地时区;支持
*、a、a-b、a-b/n、列表、月份和星期英文缩写。 - 一次性:ISO 时间点,只允许未来时间;跑完自动停用;超过 7 天未跑的到点任务直接停用。
- 仅手动:cron 与一次性都不填,只保留在列表里,点「立即运行」。
- 心跳:启动立即扫一次,之后每 60 秒扫一次。
- 补跑:错过时在
max(创建时间, 7 天前, 启用时刻)之后找最近一个槽补跑一次;迟到超过 2 分钟标记为missed。 - 抖动:cron 任务派发延迟 =
sha256(taskId) % (min(10, 相邻间隔-1) * 60)秒,同一任务恒定,避免整点同时触发。 - 重试:建会话或投递失败按 5/15/30 分钟重试,最多三次;模型回合失败不重试。
- 并发:同一任务同时只跑一次。
运行一轮的过程:
ctx.sessionController.create({ cwd })新建会话,工作目录取任务配置的,否则取最近会话的。resolveAgent后把标题改成「任务名 · 月-日 时:分」;会话 id 记进隐藏名单,从左侧列表过滤掉。- 订阅
session/event,投递指令,等turn/end:reason.kind === "completed"记成功,其余(error、blocked、aborted、timeout等)记失败并保留原因;45 分钟没结束按超时失败处理。 - 运行记录写进索引,每个任务保留最近 50 条。
- 任务由对话里的工具创建且开着通知时,跑完向创建它的那个会话发一条完成/失败消息;面板建的任务没有创建会话,不发。
状态只存在一个文件里:
~/.dsh/routines/
scheduled-tasks.json # 任务索引:名称、描述、指令、调度、运行记录
旧版本写在 <taskId>/SKILL.md 正文的任务,读取索引时会自动搬进 prompt 并删掉目录。
安装与启用¶
从插件市场安装,索引源是作者自己用 GitHub Pages 提供的:
https://leinaldo.github.io/dsh-routines/market/index.json
- 打开 插件 → 发现 → 添加索引源,填上面的 URL。
- 在列表里找到 定时任务,点 安装。
- 重启 DSH,左侧栏出现「定时任务」。
本地开发方式:
~/.dsh/plugins/dsh-routines
- 把仓库目录放到
~/.dsh/plugins/dsh-routines。 - 在
~/.dsh/profiles/desktop/package.json的dependencies加"dsh-routines": "file:/Users/leinaldo/.dsh/plugins/dsh-routines"(路径按本机调整)。 - 在同一个文件的
dsh.profile.bundles末尾加"dsh-routines"。 - 在 profile 目录执行
pnpm install,然后重启 DSH。
宿主代码在启动时加载,改完必须重启才生效;客户端面板随页面刷新重载。
典型用法¶
在侧边栏面板里操作:「新建」填名称、描述、指令、调度方式、工作目录;卡片上「立即运行」不改排期,只推 lastRunAt;「停用」只停自动派发,停用后手动运行也会被拒绝。「运行记录」展开后每条都有状态、时间、耗时、token、摘要与「查看会话」,点进去就是那次运行的会话——运行会话不出现在左侧列表,只从这里进入。
让模型直接建任务,不必先开面板:
| 工具 | 作用 |
|---|---|
routine_create |
建任务:name、prompt 必填,cron_expression / fire_at / cwd / notify_on_completion 可选 |
routine_list |
列出所有任务、下次运行时间与指令 |
routine_update |
只改传入的字段;enabled:false 停用 |
routine_delete |
删任务连同运行记录 |
routine_run |
立即跑一次,不改排期 |
在对话里说「每天 12 点帮我总结 AI 资讯」时,模型会按工具描述在内置 schedule_* 与本插件之间选。想稳定走本插件,点名「用定时任务建」,或直接说「每次新开对话跑」。
HTTP 接口(面板底层就是这些):
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /dsh-routines/tasks |
任务列表视图(含下次运行、最近一次状态、运行中标记) |
| GET | /dsh-routines/task?id= |
单个任务(含 prompt)与全部运行记录 |
| GET | /dsh-routines/defaults |
默认工作目录 |
| POST | /dsh-routines/tasks/create |
{name, description?, prompt, cronExpression?\|fireAt?, cwd?, notifyOnCompletion?} |
| POST | /dsh-routines/tasks/update |
同上字段 + id,另支持 enabled、disableJitter |
| POST | /dsh-routines/tasks/delete |
{id},任务文件与运行记录一起删 |
| POST | /dsh-routines/tasks/run |
{id},立即运行(不改排期) |
适用场景与注意¶
适合这些情况:已经有一个想在固定时间跑的独立任务,希望每轮都是干净上下文、跑完能查回执;或者想用一次性任务做「跑到点就不动」的提醒。
几个需要留意的点:
- 插件以当前 dsh 进程权限运行,装之前应先读一遍源码和许可证(MIT,见仓库
LICENSE)。 - 需要审批的任务会停在审批卡上,直到超时记失败——没有无人值守审批策略。
- 没有系统通知,只有发给创建会话的完成消息和面板状态;也没有费用字段,DSH 宿主只暴露 token,不暴露价格。
- 运行会话的隐藏只做在网页端,代价是名单更新依赖这套客户端代码,宿主改列表实现时要跟着改。
- 存储根目录只取
DSH_HOME或~/.dsh,不读ctx.baseDir——GUI 启动时后者常是/,会导致面板永远读到空索引。
小结¶
dsh-routines 把 DSH 的定时动作从「提醒投递」升级为「运行记账」,调度语义、补跑和抖动都按 Claude Routines 对齐,代价是指令必须自包含、每轮新开会话。
- 目录页:https://www.skillhub.cn/plugins/leinaldo/dsh-routines
- GitHub:https://github.com/leinaldo/dsh-routines