Files
freedak f7a720204a Update: 将子项目从 submodule 转为完整内容
- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用
- 添加所有子项目的完整源代码
- 保留原始 .git 为 .git.bak 备份
2026-07-04 19:20:46 +08:00

12 KiB
Raw Permalink Blame History

定时任务 (Cron)

NomiFun 中的一个定时任务是一个在你选择的时间触发的循环 (或一次性) 任务,它会驱动一个 AI agent 去做某件事。你可以从定时任务页面 配置它、按需运行它、给它附加一个个性化的技能让 agent 在 该任务下始终以正确的方式行事,并且你可以使用一个内置的 cron 技能在聊天中让任何 agent 帮你管理任务。

找的是应该尽快运行的一次性异步工作,而不是按时钟来的?参见 AutoWork & Requirements。需要一个 实时 shell?参见 应用内终端

定时任务列表

一个任务做什么

nomifun-cron 是一个后端调度器 + 执行器:

  • 调度器用一个 5 字段 (Unix) 或 6 字段 (秒前缀) 的 cron 表达式 为每个已启用的任务计算下一次触发时间 —— 两者都接受; 5 字段表达式会通过在前面加 0 作为秒被规范化为 6 字段。 调度也可以是一个绝对时间戳 (At { at_ms }) 或一个固定间隔 (Every { every_ms })。
  • 每个任务的时区 (例如 Asia/ShanghaiAmerica/Los_Angeles) 会被尊重,所以 0 9 * * MON 表示那个时区的 09:00,而不是 UTC。
  • 执行器在定时器触发时驱动该任务的 agent。两种执行模式:
    • new_conversation —— 每次触发开启一个新会话。该任务 携带 workspace、agent、model 和 prompt;执行器会创建会话、 广播一个 cron_trigger 工件 (这样聊天 UI 会显示 "本会话由一个定时任务发起"),然后发送 prompt。
    • existing —— 复用拥有该任务的会话。每次触发把 prompt 作为一条新消息发送到同一个线程中。适合 "提醒我"、 "总结今天" 或任何延续性重要的任务。
  • 一个忙碌守卫防止同一个会话被并发进入。如果上一次运行 在下一次触发到来时还在进行中,新的运行会被跳过 (记录为 skipped)。
  • 一个漏触发处理器会在启动时和操作系统从睡眠中醒来后运行 (/api/cron/internal/system-resume)。它会遍历每个 next_run 在过去的已启用任务并发出一条系统消息,让你能看到漏掉一次 触发 (例如笔记本休眠时),然后为下一个 cron 节拍重新装定定时器。
  • 每次触发会以一个状态被记录 —— ok / error / skipped / missed —— 并且 (在适用时) 附带一个指向所产生会话的链接, 这样详情页可以向你展示运行历史。

创建一个任务

从侧边栏打开 定时任务 (路由:/scheduled) 并按 New task。对话框涵盖四个区域。

频率

从一组小的预设中选择 —— Manual (无自动调度,仅通过 Run now 触发)、 HourlyDailyWeekdays (MON-FRI)、WeeklyCustom。预设会在 builder 中渲染出可编辑的 cron 表达式;选 Custom 直接键入。Builder 在你键入时进行校验。

Cron 语法速查 (5 字段 —— 秒字段会自动添加):

*  *  *  *  *
│  │  │  │  └─ 星期几      (06 或 SUNSATMON-FRI 可用)
│  │  │  └──── 月          (112 或 JAN–DEC)
│  │  └─────── 月中第几天   (1–31)
│  └────────── 小时         (0–23)
└───────────── 分钟         (0–59)

任务的时区在创建时设置 (默认是你浏览器的 IANA 时区) 并存储在 该行中;如果一个任务存储的时区因故无效,详情页会提供一键 修复到你的本地时区。

Agent

选择每次触发运行的 agent。选择器中会显示三种类型:

  • CLI agent —— claude / codex / gemini (后端在 PATH 上检测到的任何一个)。该任务记录后端标签并端到端使用 ACP。
  • Nomi (内置) —— 使用 Nomi 自有引擎以及你选择的 provider/model。
  • Preset assistant —— 预先配置的 agent 人格;该任务记录 assistant id。

Advanced 部分让你覆盖 workspace (agent 的工作目录)、model 以及任意的 config_options 键值对,它们会被转发给 agent 工厂。 Workspace 路径不能包含空白片段 —— 这一点在服务端被强制; 表单会把错误显式呈现出来。

执行模式

选择 new_conversationexisting (在 UI 中当你同时选择具体 是哪一个时被称为 "specified conversation")。详情页之后会向你 展示得到的会话。

Prompt + 名称

Prompt 是每次发送给 agent 的内容。请把它写成一个自包含的 指令 —— agent 看不到你原本 "我想要这个" 的框架,只看到这个 prompt。诸如下面这些模式:

  • Reply with a short weekly meeting reminder that includes the current date and time.
  • Search for the latest AI news from this week and produce a concise bullet-point summary report.
  • Run the weekly database health check and post the results back here.

…比重述用户愿望要好。Name 只是一个标签。

创建定时任务对话框

运行、暂停、删除

列表视图 (/scheduled) 显示每一个任务、它的下一次触发,以及 一个启用开关。在详情页 (/scheduled/:job_id) 你可以:

  • Run now —— 立即触发该任务,无视调度。忙碌守卫仍然适用。
  • Pause / Resume —— 停止后续触发但不删除该行。
  • Edit —— 与创建相同的对话框,处于编辑模式。
  • Delete —— 删除该任务及其按任务生成的技能目录。此前运行创建的 会话会保留在会话列表中,可按需单独删除。

详情页还会列出本任务创建的会话,按活跃度排序 —— 当任务 以 new_conversation 模式运行并为每次触发各分出一个线程时 非常有用。

定时任务详情

保持唤醒

只有当宿主进程在运行时,cron 任务才会触发。列表页有一个 NomiFun 运行时保持系统唤醒开关,它会请求 OS 抑制睡眠 (WindowsSetThreadExecutionStatemacOScaffeinate Linux 上若可用为 systemd-inhibit),这样你在笔记本上设置的 任务不会在合上盖子那一刻悄悄漏触发。

如果一次触发还是因为系统进入睡眠 (或 NomiFun 没在运行) 而漏掉, 下一次启动/唤醒时的漏触发处理器会记录一次 missed 运行并 向受影响的会话中投送一条系统消息,然后为下一次正常触发 重新装定定时器。

附加到任务的技能

一个技能是一个 SKILL.md 文件,agent 会在加入会话时读取它 —— 与 NomiFun 其他地方使用的相同机制,但作用域是按任务的。 你可以在详情页编写/编辑该技能;在幕后该文件会被写入数据 目录下的 cron/skills/cron-<job_id>/SKILL.md,执行器会在每次 触发时把它注入到 agent 的会话中。

用例:

  • 任务输出的一致人格 (风格、语气、格式)。
  • 工具/MCP 偏好 (启用哪些服务器、忽略哪些)。
  • 工作区特定的约定 (commit 信息风格、目录布局、部署细节)。

任务有自己的技能目录 (以 job id 命名,前缀 cron-),所以共享同一 workspace 的两个任务可以承载不同的行为而不冲突。删除任务会 移除其技能目录。

还有一个自动的技能建议检测器,它会在运行期间观察 agent 的 输出;当它产出一个干净的候选技能时 (符合预期格式且不只是占位 模板),检测器会在会话中创建一个 skill_suggest 工件,让你 可以审查并一键将其保存为该任务的技能。

在聊天中管理任务 —— 内置的 cron 技能

NomiFun 附带一个名为 cron 的内置自动注入技能,任何 agent 都可以 在你让它"设置一个提醒"、"每周一安排 X" 等时加载它。 然后会话中间件会观察 agent 的回复中以下的指令块,并通过 cron 服务运行它们:

指令 含义
[CRON_LIST] 列出当前会话作用域内的 cron 任务。
[CRON_CREATE]…[/CRON_CREATE] 创建一个任务 (字段:namescheduleschedule_descriptionmessage)。
[CRON_UPDATE: <id>]…[/CRON_UPDATE] 原地更新一个已有任务。
[CRON_DELETE: <id>] 按 id 删除一个任务。

中间件会从用户看到的内容中剥离这些块,并把系统响应 (Created cron job 'X'No scheduled tasks 等) 投回到会话中。 所以在聊天里看起来像是正常的来回;幕后是 agent 发出了一个 指令,平台执行了它。

该技能在设计上被限制为每个会话一个任务 —— 这让循环保持 简单 ("查询,然后行动") 并避免了你重新询问时重复任务堆积。 要一次管理多个任务,请直接使用定时任务页面。

路由 & API

内容 位置
列表页面 /scheduled
详情页面 /scheduled/:job_id
列出 / 创建任务 GET /api/cron/jobsPOST /api/cron/jobs
获取 / 更新 / 删除 `GET
立即运行 POST /api/cron/jobs/:id/run
列出某任务的会话 GET /api/cron/jobs/:id/conversations
每任务技能 `GET
系统恢复 (内部) POST /api/cron/internal/system-resume (需要内部 header)

UI 订阅的实时事件:cron.job-createdcron.job-updatedcron.job-removedcron.job-executed。漏触发会作为一次 cron.job-executed 事件上报,payload 中的状态是 missed

故障排查

  • 任务没有按时触发。 那时宿主在运行且处于唤醒状态吗? 如果你合上了笔记本或关闭了应用,请查看唤醒后的下一条会话 条目 —— 漏触发处理器会投送一条 missed 通知并重新装定 定时器。
  • 我的 cron 表达式被拒绝了。 5 字段 (m h dom mon dow) 和 6 字段 (s m h dom mon dow) 形式都是合法的。在本地用 crontab.guru 或对话框中的 builder 校验。
  • 任务运行了但 agent 做错了事。 重新阅读 prompt,假设你没有 其他上下文。它必须告诉 agent 准确要产出什么。然后考虑附加 一个技能以锁定行为。
  • 两次定时触发碰撞了。 忙碌守卫会在 existing 模式下跳过 重叠的运行 (该运行被记录为 skipped)。如果你预期触发会 长时间运行,请把任务切换到 new_conversation 模式,让每次 触发各得一个线程。
  • 聊天中的 cron 指令什么也没做。 如果 cron 服务没有接入 (例如某些测试 harness),中间件会 no-op;在正常 app build 中 它总是接入的。如果指令格式错误 (缺失闭合标签、缺失 schedule),它会被静默丢弃 —— 用更干净的输入重新提示 agent。