Skip to content

定时任务的设计 #36

Description

@mengnankkkk

总体设计

现有聊天能力在 AgentService.java 和 SessionService.java 里,前端聊天页在 ChatView.vue。定时任务建议复用这些能力。

执行流程:

用户创建定时任务
  -> 后端保存任务配置
  -> Scheduler 注册/刷新任务
  -> 到点触发
  -> 创建一次执行记录
  -> 包装提示词并调用 Agent
  -> Agent 工具调用、思考、结果进入同一个聊天 session
  -> 前端任务页展示状态,聊天页展示执行过程

后端模块

新增一个 scheduled_task 模块即可,不建议一开始引入 Quartz,Spring 自带 ThreadPoolTaskScheduler + CronTrigger 足够。等以后多实例部署、任务分片、分布式锁再升级 Quartz 或 ShedLock。

建议新增:

controller/ScheduledTaskController.java
service/ScheduledTaskService.java
service/ScheduledTaskRunner.java
service/ScheduledTaskRegistry.java
mapper/ScheduledTaskMapper.java
mapper/ScheduledTaskRunMapper.java
entity/ScheduledTask.java
entity/ScheduledTaskRun.java
dto/scheduled/*

职责划分:

ScheduledTaskController
负责 REST API:列表、新建、修改、删除、启停、立即执行。

ScheduledTaskService
负责参数校验、任务 CRUD、计算下次执行时间、刷新调度注册。

ScheduledTaskRegistry
维护 taskId -> ScheduledFuture 映射,支持启动时加载、编辑后重排、删除后取消。

ScheduledTaskRunner
真正执行任务:创建 run 记录、调用 Agent、更新执行状态。

数据库设计

建议至少两张表:任务定义表 + 执行记录表。

CREATE TABLE scheduled_task (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    title VARCHAR(255) NOT NULL,
    cron_expression VARCHAR(100) NOT NULL,
    timezone VARCHAR(64) NOT NULL DEFAULT 'Asia/Shanghai',
    prompt TEXT NOT NULL,
    remark TEXT,
    enabled TINYINT(1) NOT NULL DEFAULT 1,
    session_id VARCHAR(128) NOT NULL,
    last_run_status VARCHAR(32),
    last_run_time DATETIME,
    next_run_time DATETIME,
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

CREATE TABLE scheduled_task_run (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    task_id BIGINT NOT NULL,
    session_id VARCHAR(128) NOT NULL,
    trigger_type VARCHAR(32) NOT NULL,
    status VARCHAR(32) NOT NULL,
    prompt_snapshot TEXT NOT NULL,
    error_message TEXT,
    started_at DATETIME,
    finished_at DATETIME,
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    KEY idx_task_time (task_id, create_time),
    KEY idx_status (status)
);

状态建议:

任务启用状态:enabled / disabled
执行状态:PENDING / RUNNING / SUCCESS / FAILED / WAITING_INPUT
触发类型:SCHEDULED / MANUAL

session_id 很重要。我建议每个定时任务绑定一个固定聊天会话,比如:

scheduled_task_1
scheduled_task_2

这样“每天早上八点查询昨天营业额”的每次执行都会追加到同一个聊天会话里,用户从任务页点进去就能看到完整过程和历史结果。

AI 执行方式

定时任务不要直接把原始 prompt 丢给 AI,建议包装一下,解决“昨天”“今天”这类相对时间问题:

这是一个系统定时任务,请根据用户提示执行数据分析。

任务标题:每日营业额查询
触发时间:2026-07-13 08:00:00
时区:Asia/Shanghai

用户提示词:
查询昨天的营业额,并给出同比/环比变化。

这样 AI 知道“昨天”是相对哪一天。

后端执行时可以复用现有 AgentService.chatStream(sessionId, message, null),即使没有前端 SSE 连接,也可以在后端订阅并消费完整 Flux,让 Agent 把消息保存进 agentscope_sessions。执行结束后更新 scheduled_task_run.status

如果执行过程中 Agent 触发 ask_user,定时任务应标记为 WAITING_INPUT,前端任务页显示“等待用户输入”,用户点进聊天页后继续回答。

接口设计

GET    /api/scheduled-tasks
GET    /api/scheduled-tasks/{id}
POST   /api/scheduled-tasks
PUT    /api/scheduled-tasks/{id}
DELETE /api/scheduled-tasks/{id}

POST   /api/scheduled-tasks/{id}/enable
POST   /api/scheduled-tasks/{id}/disable
POST   /api/scheduled-tasks/{id}/run

GET    /api/scheduled-tasks/{id}/runs

创建/编辑请求建议:

{
  "title": "每日营业额查询",
  "cronExpression": "0 0 8 * * *",
  "timezone": "Asia/Shanghai",
  "prompt": "查询昨天的营业额,并给出简短总结",
  "remark": "每天早上发一次日报",
  "enabled": true
}

Spring cron 是 6 段,0 0 8 * * * 表示每天 08:00。

前端页面

新增路由:

/scheduled-tasks

侧边栏加一个“定时任务”。

页面结构建议:

顶部工具栏:
新建任务、刷新

任务表格:
标题
执行时间
备注
启用状态
最近执行状态
上次执行时间
下次执行时间
操作:编辑 / 立即执行 / 启停 / 删除 / 查看聊天

编辑抽屉或弹窗:
标题
执行规则
时区
备注
提示词 textarea
是否启用

执行规则前端可以先做简单模式:

每天:选择时间 -> 生成 cron
每周:选择星期 + 时间 -> 生成 cron
每月:选择日期 + 时间 -> 生成 cron
高级:直接填写 cron

比如“每天早上八点”前端生成:

0 0 8 * * *

“查看聊天”按钮跳转:

/chat/{sessionId}

这样执行过程会直接走现有 TracePanel,工具调用、SQL 执行结果、最终总结都能展示。

推荐实现顺序

  1. 后端先加数据库表、实体、mapper、CRUD API。
  2. ThreadPoolTaskScheduler 和动态注册逻辑。
  3. 实现“立即执行”,确认能写入聊天会话。
  4. 实现定时触发,到点自动执行。
  5. 前端加任务管理页和任务编辑弹窗。
  6. 任务页增加“查看聊天”和“立即执行后跳转聊天”。

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions