本页目录(13 节)

会话定时任务

Spark Work 会话内定时任务:在单个会话作用域创建持久化任务,到点自动用本会话当时的 Agent/模型/权限/工作区续接一轮对话。支持 interval(固定间隔)/ cron(5 字段表达式)/ once(单次定时),用户工具栏和 Agent MCP 工具两个入口,调度引擎托管重试、并发与下次执行。

阅读约 6 分钟最后核对 2026-08-10

有时候你希望 Agent「过一会儿再来看看」「每天定时跟进一下」「某件长任务跑完了再叫醒我继续」。 Spark Work 的会话内定时任务就是为这类场景准备的:在某个会话作用域里创建持久化任务, 到点后调度器自动在这个会话里排一轮新对话,沿用会话当时的 Agent、模型、权限和工作区继续推进。

1. 会话级作用域:任务跟着会话走

每个定时任务都绑定在创建它的那个会话上:scheduled_tasks 表里的scope 为 'session',session_id 指向会话。 触发时它不会新开会话,而是往原会话里排一轮 turn (内部标记 turnSource: 'scheduled_task'、userMessageVisibility: 'hidden'), 因此这一轮用的是会话当前的 Agent、Provider/模型、权限模式、推理强度和工作区目录。 你之后切换模型或调整权限,下一次触发会自动按最新配置执行。

  • 归属校验:Agent 侧工具用「task id + 当前会话 id」双重校验, 任务必须 scope === 'session' 且 sessionId 与当前会话一致才允许读写, 猜 id 也拿不到别的会话或全局任务。
  • 删除会话即级联清理:session_id 外键带ON DELETE CASCADE,删除会话时它的任务随之删除。
  • 归档会暂停:会话被归档时,启用中的会话任务进入 paused 状态; 取消归档时只有归档前启用的那些会被恢复,并重新计算下次执行时间。
  • 会话不存在 / 读不到状态:任务会被停用并记Bound session no longer exists;只是暂时读不到会话状态则推迟重试并记录原因。

2. 两个入口,别搞混作用域

2.1 会话工具栏:只管理本会话

会话顶部工具栏有一个时钟图标按钮(提示文案「计划任务」),点开是会话计划任务面板。有启用中的任务时按钮上会带一个小红点。 面板支持新建、编辑、启停、立即运行、删除,提交时固定scope: 'session' + 当前 sessionId + concurrencyPolicy: 'queue'。

面板表单的默认值:

  • 触发类型 interval,间隔 30 分钟(提交时换算成秒,最小 10 秒)。
  • Cron 预填 0 */1 * * *。
  • skipIfSessionRunning 与 continueOnError 默认勾选。
  • 名称与任务正文必填;once 必须选一个合法时间。

2.2 侧栏「定时任务」:全局任务,不是会话任务

左侧导航的 「定时任务」 页是另一套作用域:它列出的是scope: 'global' 的任务,新建时也按 global 写入(默认 concurrencyPolicy: 'skip')。 全局任务触发时会新建一个会话(默认落在「无项目对话」目录,标题前缀带任务名), 而不是续在原会话里。想「在某个会话里继续」就必须用会话面板或 Agent 工具创建。

用户侧 IPC 通道:scheduled-task:list / get / create / update / delete / toggle / run-now / export / import / export-to-file / import-from-file, 执行记录用 task-execution:list / get / cancel。 「立即运行」会立刻建一条 trigger_type: 'manual' 的执行记录, 并在 10 秒内把新会话 id 还给界面用于跳转,真正的 turn 在后台跑。

3. 字段与默认值

字段含义默认值 / 取值
name / promptTemplate任务名与任务正文(必填,正文最长 10 万字符)—
triggerType触发方式interval / cron / once
intervalSeconds固定间隔秒数最小 10,最大 31536000
cronExpressionCron 表达式必须是 5 字段;非法表达式会在创建/更新时直接报错
runAt单次执行时间(ISO-8601)必须能解析且晚于当前时间
timezone时区默认 'system'(跟随本机);不是 system 时交给 cron-parser 解析
startAt / endAt生效时间窗可选;未到 startAt 时下次执行会被推到 startAt
maxExecutions最多执行多少次默认 0(不限);达到上限后任务自动停用
timeoutSeconds单次执行超时默认 300;Agent 工具可写 10 ~ 86400
maxRetries失败重试次数默认 0(不重试);上限 100
retryDelaySeconds重试基础延迟默认 60
retryBackoff退避策略fixed(默认)/ linear / exponential
concurrencyPolicy上一次还在跑时怎么办会话面板与 Agent 工具写 queue;数据表与全局页默认 skip; 可选 skip / queue / cancel
skipIfSessionRunning会话有运行中/排队中的 turn 时跳过本次默认 true(数据库列默认 1)
continueOnError会话报错后是否继续后续触发默认 true;关掉后该任务在会话进入 error 时会被暂停
historyRetentionDays执行记录保留天数默认 30;每次执行结束后清理过期记录
tags / notifications标签 / Webhook 通知配置通知可挂 onSuccess / onFailure / onRetry / onDisabled

会话任务的 agent_id、team_id、model_id、workspace_id一律写 null,permission_mode 写 'auto': 这是刻意的设计——会话任务只跟随会话当时的运行时配置,不给自己绑一份会过期的快照。

4. 三种触发方式怎么选

触发类型用法校验与计算
interval固定间隔轮询,轮询场景建议 ≥ 60 秒需要 intervalSeconds ≥ 10;下次执行 = 当前时间 + 间隔秒数
cron「每天 9 点」「工作日每半小时」这类计划必须 5 字段并通过 cron-parser 解析;非 system 时区会带 tz 计算
once未来某一刻的一次性唤醒需要未来的 runAt;执行一次后任务自动停用;被跳过的单次任务会在 60 秒后重试

会话任务的这层校验只在 scope === 'session' 时强制执行,报错文案分别是 「interval must be at least 10 seconds」「requires a valid five-field cron expression」 「runAt must be in the future」。

5. Agent 也能自己建任务

Agent 通过 mcp__spark_platform__session_schedule_* 这组工具自助管理会话定时任务, 适合「每 5 分钟检查一下部署状态,好了告诉我」这类需求:

工具作用
session_schedule_list列出当前会话的任务(先查再建,避免重复轮询)
session_schedule_create新建任务(interval / cron / once)
session_schedule_update改频率、指令、启停
session_schedule_get查看单个任务详情(同样做归属校验)
session_schedule_delete删除任务

这组工具与用户面板的差异:

  • 会话绑定由运行时提供,模型不能传 sessionId:参数里没有会话字段, 工具被注入到哪个会话就只作用于哪个会话;工具说明也明确要求不要询问或编造 session id。
  • 入参是严格 schema:多传未知字段会直接报错;intervalSeconds、cronExpression 等都按上面的上限校验。
  • 默认值偏向安全:skipIfSessionRunning 与continueOnError 默认 true,concurrencyPolicy 默认queue,timeoutSeconds 默认 300,historyRetentionDays 默认 30。
  • 生命周期要求写进了系统提示词:创建后简短告知用户并结束本轮; 每次唤醒先看真实状态,还没完成就留着,任务完成后必须调用session_schedule_delete 清理,不能无限空跑。禁用只是暂停,不算清理。

6. 调度引擎的行为

6.1 并发与会话忙

  • 调度器是桌面主进程里的定时器,每 1 秒 tick 一次:应用没运行时不会触发。
  • 同一任务已有 running 的执行记录时:skip 跳过本次并重算下次时间;cancel 把旧执行标记为 canceled 再开新的;queue 直接再开一个。
  • skipIfSessionRunning 生效时,只要会话有正在执行或已排队等待的 turn, 本次触发就顺延(once 任务顺延 60 秒后重试)。
  • 会话报 error 且 continueOnError 为 false 时,任务被暂停而不是继续触发。

6.2 重试

失败后按 maxRetries 与退避策略重试,重试的延迟公式:

  • fixed:baseDelay × 1
  • linear:baseDelay × (attempt + 1)
  • exponential:baseDelay × 2^attempt

每次重试都是一条独立执行记录(trigger_type: 'retry'、带retry_attempt 与 parent_execution_id),所以你能在记录里看出是第几次尝试。 超过 timeoutSeconds 的执行记为 timeout 而不是 failed。

6.3 生命周期与错过的运行

  • 错过的运行不补跑:应用关闭期间到点的会话任务,在下次启动时会被跳过—— 周期任务直接挪到下一个未来时间点,错过的单次任务会被停用。
  • 执行状态:running / completed / failed / cancelled / timeout。
  • 任务状态:idle / running / disabled / error。
  • 达到 maxExecutions 或单次任务执行完成后,任务自动 disabled。
  • 每次执行结束按 historyRetentionDays 清理历史执行记录。

7. 触发时注入了什么

触发的那一轮不是普通用户消息,系统会把它包成 [Scheduled Task Context]:

  • 说明这轮由调度器发起、不是用户手动发送。
  • 任务名、任务 id(会话任务专属),并提示任务完成后要调用session_schedule_delete 清理。
  • 执行触发来源、配置的计划、下次执行时间与时区。
  • 明确要求「不要反过来问用户间隔 / Cron / 触发时间」,按既有计划执行。
  • 最后跟 [Task Instructions],即你写的任务正文。

任务正文支持模板变量,会在写入提示词前替换:{{date}}、{{time}}、{{taskName}}、{{triggerType}}、{{executionCount}}、{{interval}}、{{cronExpression}}、{{runAt}}、{{timezone}}、{{nextRunAt}}。 认不出的变量保持原样,方便你发现拼写错误。

8. 使用规范与排查

  • 轮询类任务的间隔尽量 ≥ 60 秒,避免空转过快消耗 token。
  • 目标达成(长任务完成、条件满足)后必须调用 session_schedule_delete 清理, 不要让任务无限空跑;只想临时停机用「停用」。
  • 一次性提醒用 once,不要用 interval 模拟。
  • 在任务正文里写清「检查什么、状态在哪、什么算完成、还没完成时怎么办、什么时候删任务」, 无人值守的那一轮才不需要向你追问。
  • 任务定义与执行记录都落本地 SQLite(scheduled_tasks / task_executions); 触发时复用本会话的模型与权限配置,不会绕过权限模式或注入额外能力。

常见问题对照:

现象原因处理
到点了但没执行应用没运行,或会话有排队/运行中的 turn 被跳过,或任务被停用/暂停看任务的 status、lastError 与 nextRunAt,确认应用在运行
任务突然自己停了单次任务已执行、达到 maxExecutions、或会话归档被暂停检查任务状态;归档场景取消归档后会恢复
Cron 保存失败表达式不是 5 字段或无法解析改成标准 5 字段,例如 0 9 * * *
Agent 说建好了但找不到任务建在别的会话(Agent 工具只作用于当前会话)在当前会话的「计划任务」面板里查;会话面板只显示本会话任务
想跨会话复用同一套计划会话任务绑定创建它的会话用侧栏「定时任务」的全局任务(会新建会话),或每个会话各建一份

速查表

作用域
session(scheduled_tasks.scope=session,触发时在原会话续接)
触发类型
interval / cron / once
interval 下限
10 秒(建议 ≥ 60 秒)
用户入口
会话顶栏「计划任务」时钟按钮(有启用任务时带红点)
全局入口
侧栏「定时任务」(scope=global,会新建会话)
Agent 入口
mcp__spark_platform__session_schedule_{list,get,create,update,delete}
默认超时
timeoutSeconds = 300
默认并发策略
会话面板与 Agent 工具写 queue;全局页/数据表默认 skip
重试
maxRetries 默认 0;retryBackoff = fixed / linear / exponential
历史保留
historyRetentionDays 默认 30 天
调度精度
主进程每 1 秒 tick;应用未运行不触发、不补跑

常见问题

展开 6 条常见问题
  • 定时任务触发时会新开一个会话吗?

    会话任务不会。它绑定创建它的会话,触发时在原会话里排一轮 turn(内部标记 turnSource=scheduled_task、消息默认隐藏),沿用会话当时的 Agent、模型、权限和工作区。侧栏「定时任务」里的全局任务相反:它会新建一个会话。

  • 轮询间隔最小能设多少?

    会话任务 10 秒(校验文案就是 interval must be at least 10 seconds),Agent 工具上限 31536000 秒。但轮询场景建议 ≥ 60 秒:每次触发都是一轮真实的模型调用。

  • Agent 能自己管理定时任务吗?

    能。Agent 通过 mcp__spark_platform__session_schedule_{list,get,create,update,delete} 管理当前会话的任务,参数里没有 sessionId——绑定由运行时提供,不接受模型传入的会话 id。任务完成后 Agent 需要自己 delete 清理。

  • 应用关掉的时候会补跑吗?

    不会。调度器跑在桌面主进程里、每秒 tick,应用关闭期间到点的任务在下次启动时会被跳过:周期任务挪到下一个未来时间点,错过的单次任务直接停用。

  • 会话正在执行别的任务时,定时任务会排队还是被跳过?

    默认会跳过:skipIfSessionRunning 默认 true,只要会话有运行中或排队中的 turn,本次触发就顺延。单次任务顺延 60 秒后重试。另外 concurrencyPolicy 管的是「同一个任务的上一次执行还没跑完」这种情况,取值 skip / queue / cancel。

  • 任务里能用变量吗?

    可以。任务正文支持 {{date}}、{{time}}、{{taskName}}、{{triggerType}}、{{executionCount}}、{{interval}}、{{cronExpression}}、{{runAt}}、{{timezone}}、{{nextRunAt}},触发时先替换再注入 [Scheduled Task Context];认不出的变量保持原样。