# 定时任务调度系统技术方案 > 文档版本:v1.0 > 文档状态:待实施 > 编写日期:2026-07-27 > 适用项目:Mao Agent Workbench ## 1. 需求背景 当前 Agent 执行完全依赖用户通过 WebSocket 主动发送消息触发——系统没有任何自动或定时触发 Agent 执行的机制。当用户提出类似"每天早上帮我检查新股申购"、"每天下午5点给我生成日报"等需求时,Agent 无法实现。 核心限制: - Agent 只能被动响应,不能主动发起执行 - 没有后台定时任务能力 - 没有消息注入机制 本方案构建一个通用的**定时任务调度系统**,让用户可以通过自然语言对话创建定时任务,系统按 cron 计划自动向固定 Session 注入消息触发 Agent 执行,复用现有 AgentLoop 引擎,执行结果通过现有 Webhook 通知系统推送。 ## 2. 需求描述 ### 2.1 功能范围 | 模块 | 功能点 | 结论 | |------|--------|------| | 任务创建 | 用户通过自然语言对话让 Agent 创建定时任务(Agent 内置工具) | 做 | | 任务创建 | 通过 UI 表单手动创建定时任务 | 不做,仅对话式 | | 任务管理 | 管理后台(admin/)定时任务管理页面:列表、启用/禁用、删除 | 做 | | 任务管理 | 桌面端(desktop/)定时任务侧栏面板:列表、启用/禁用、删除 | 做 | | 任务管理 | 对话中查看和管理任务(Agent 内置工具:列出、更新、删除) | 做 | | 触发方式 | Cron 表达式定时触发 | 做 | | 触发方式 | 固定间隔触发(如每30分钟) | 做 | | 触发方式 | 外部 Webhook 回调触发 | 不做 | | 触发方式 | 事件驱动触发(如文件变更、外部系统回调) | 不做 | | 执行机制 | 向固定 Session 注入用户消息,复用现有 AgentLoop 执行 | 做 | | 执行机制 | 独立后台执行(不依赖 Session) | 不做 | | Session 策略 | 每个任务绑定固定 Session,历史上下文可累积 | 做 | | Session 策略 | 每次触发新建 Session | 不做 | | Agent 绑定 | 绑定创建任务时的当前 Agent | 做 | | Agent 绑定 | 用户手动指定 Agent | 不做 | | 并发控制 | 上次执行未完成时跳过本次触发 | 做 | | 并发控制 | 取消上次执行,启动新的 | 不做 | | 并发控制 | 排队等待上次完成 | 不做 | | 执行超时 | 不限制执行时间,由用户取消、任务正常完成或真实错误终止 | 做 | | 结果通知 | 复用现有 Webhook 通知系统(钉钉/飞书)+ Session 未读标记 | 做 | | 结果通知 | 邮件通知 | 不做 | | 结果通知 | 微信通知 | 不做 | | Prompt 定义 | Agent 智能解析自然语言,转化为结构化 prompt 和 cron 表达式 | 做 | | Prompt 定义 | 用户直接输入 cron 表达式和 prompt 文本 | 做(Agent 工具也接受结构化输入作为后备) | | 权限 | 用户只能管理自己创建的任务 | 做 | | 权限 | 管理员可管理所有用户的任务 | 做 | ### 2.2 业务规则 1. 定时任务绑定到创建时的 Session 对应的 Agent,执行时复用该 Agent 的 system_prompt、技能、模型配置。 2. 每个任务拥有一个专属 Session,所有执行历史在同一 Session 中累积。 3. 任务触发时,系统检查目标 Session 是否正在执行(phase 为 RUNNING/RESUMING/WAITING_APPROVAL),若是则跳过本次触发,记录跳过日志。 4. 任务触发时,系统向目标 Session 持久化一条 USER 角色消息(内容为任务的 prompt),然后调用 `HarnessService.executeFromEvent()` 启动 Agent 执行。 5. 任务进入 COMPLETED 或 FAILED 终态后,复用现有 Webhook 通知判定逻辑:先尝试 WS 推送,无在线客户端时通过 Webhook 发送。 6. 用户只能管理自己创建的定时任务;管理员可在管理后台查看和管理所有用户的任务。 7. 禁用任务后不再触发,启用后恢复。删除任务为逻辑删除。 8. cron 表达式解析失败时,任务不触发并在日志中记录错误。 ### 2.3 用户流程 ``` 【创建任务】 用户在对话中: "每天早上9点帮我检查新股申购,有新股就提醒我" → Agent 调用 tool_create_scheduled_task({ prompt: "检查今日是否有新股申购...", cron: "0 0 9 * * ?" }) → 后端创建定时任务 + 专属 Session → Agent 回复:"已创建定时任务'新股申购检查',每天早上9点执行" 【任务执行】 Spring Scheduler 扫描到期任务 → 检查目标 Session 是否空闲 → 空闲:持久化 USER 消息 → 调用 HarnessService.executeFromEvent() → 忙碌:跳过本次,记录日志 → Agent 执行完毕 → 通知推送(WS 或 Webhook) 【管理任务】 用户在桌面端侧栏:查看任务列表、启用/禁用、删除 用户在管理后台:管理员查看所有任务、管理 用户在对话中:"列出我的定时任务" / "取消新股提醒任务" ``` ## 3. 当前代码分析 ### 3.1 可复用能力 | 能力 | 当前实现 | 复用方式 | |------|----------|----------| | Agent 执行引擎 | `HarnessService.executeFromEvent()` → `AgentLoop.execute()` | 直接复用,注入 USER 消息后调用 | | 定时调度基础设施 | `@EnableScheduling` 已启用,已有 5 个 `@Scheduled` 任务 | 新增 `ScheduledTaskScheduler` 扫描到期任务 | | Webhook 通知系统 | `TaskCompletionNotificationService` + Outbox 模式 | 任务终态复用相同通知逻辑 | | Session 管理 | `SessionService` 完整的 CRUD + 状态管理 | 为每个定时任务创建专属 Session | | Message 持久化 | `SessionService.saveMessage()` | 直接调用持久化 USER 消息 | | WebSocket 事件推送 | `StreamingWsRegistry` 多设备推送 | 任务执行过程的实时事件自动推送 | | 内置工具注册 | `@Component` 实现 `Tool` 接口自动注册 | 新增 4 个内置工具 | | 管理后台路由 | `admin/src/router/index.ts` Vue Router | 新增定时任务管理路由 | | 桌面端侧栏 | 左侧面板组件体系 | 新增定时任务面板 | ### 3.2 关键代码路径 | 组件 | 文件路径 | |------|----------| | Agent 执行入口 | `backend/.../harness/core/HarnessService.java` | | Agent 循环 | `backend/.../harness/core/AgentLoop.java` | | WS 消息处理 | `backend/.../session/ws/StreamingWsHandler.java` | | WS 连接注册 | `backend/.../session/ws/StreamingWsRegistry.java` | | 工具接口 | `backend/.../harness/tool/Tool.java` | | 工具实现目录 | `backend/.../harness/tool/impl/` | | 现有 Scheduler 示例 | `backend/.../session/service/StaleSessionSweepScheduler.java` | | Webhook 通知 | `backend/.../notification/task/` | | Flyway 迁移 | `backend/src/main/resources/db/migration/` (当前最新 V060) | | 管理后台路由 | `admin/src/router/index.ts` | | 管理后台视图 | `admin/src/views/` | | 桌面端 composables | `desktop/src/composables/` | | 桌面端组件 | `desktop/src/components/` | ## 4. 整体设计 ### 4.1 系统架构 ``` ┌─────────────────────────────────────────────────────────────┐ │ 用户层 │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ │ │ 桌面端侧栏 │ │ 管理后台页面 │ │ 对话式交互 │ │ │ │ (定时任务面板) │ │ (任务管理) │ │ (Agent 内置工具) │ │ │ └──────┬───────┘ └──────┬───────┘ └────────┬─────────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ REST API / WebSocket │ │ │ └──────────────────────────┬───────────────────────────┘ │ └─────────────────────────────┼───────────────────────────────┘ │ ┌─────────────────────────────┼───────────────────────────────┐ │ 后端核心 │ │ │ │ │ ┌───────────────────────────▼───────────────────────────┐ │ │ │ ScheduledTaskScheduler (每分钟扫描) │ │ │ │ ┌─────────────────────────────────────────┐ │ │ │ │ │ 1. 查询 enabled=true 且 next_fire_time │ │ │ │ │ │ <= now 的任务 │ │ │ │ │ │ 2. 检查目标 Session 是否空闲 │ │ │ │ │ │ 3. 空闲 → 持久化 USER 消息 + 触发执行 │ │ │ │ │ │ 4. 忙碌 → 跳过,计算下次触发时间 │ │ │ │ │ │ 5. 更新 next_fire_time │ │ │ │ │ └─────────────────────────────────────────┘ │ │ │ └───────────────────────┬───────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ HarnessService.executeFromEvent() │ │ │ │ (现有 AgentLoop 引擎) │ │ │ └───────────────────────┬──────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ TaskCompletionNotificationService (现有) │ │ │ │ WS 推送 / Webhook (钉钉/飞书) / 未读标记 │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ 数据层 │ │ │ │ scheduled_task 表 + session 表 + message 表 │ │ │ └──────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────┘ ``` ### 4.2 核心数据模型 **scheduled_task 表:** | 字段 | 类型 | 说明 | |------|------|------| | id | BIGINT AUTO_INCREMENT PK | 主键 | | user_id | BIGINT NOT NULL | 创建者用户 ID | | agent_id | BIGINT NOT NULL | 绑定的 Agent ID | | session_id | BIGINT NOT NULL | 专属 Session ID | | name | VARCHAR(200) NOT NULL | 任务名称 | | prompt | TEXT NOT NULL | 触发时注入的 prompt 内容 | | cron_expression | VARCHAR(100) NOT NULL | Cron 表达式(Spring cron 格式:秒 分 时 日 月 周) | | status | VARCHAR(20) NOT NULL DEFAULT 'ACTIVE' | 状态:ACTIVE / PAUSED / DELETED | | last_fire_time | DATETIME | 上次触发时间 | | last_execution_status | VARCHAR(20) | 上次执行状态:COMPLETED / FAILED / SKIPPED | | next_fire_time | DATETIME | 下次触发时间(预计算,用于快速查询) | | fire_count | INT DEFAULT 0 | 累计触发次数 | | created_at | DATETIME NOT NULL | 创建时间 | | updated_at | DATETIME NOT NULL | 更新时间 | | deleted | TINYINT DEFAULT 0 | 逻辑删除标记 | ### 4.3 Cron 表达式格式 使用 Spring `CronExpression`(6 位:秒 分 时 日 月 周),支持: | 用户意图 | Cron 表达式 | 说明 | |----------|-------------|------| | 每天早上 9 点 | `0 0 9 * * ?` | 标准 cron | | 每个工作日早上 9 点 | `0 0 9 * * MON-FRI` | 周一到周五 | | 每 30 分钟 | `0 */30 * * * ?` | 固定间隔 | | 每周一上午 10 点 | `0 0 10 * * MON` | 指定星期 | | 每月 1 号上午 9 点 | `0 0 9 1 * ?` | 指定日期 | ## 5. 实现步骤 ### 5.1 数据库迁移 **文件**:`backend/src/main/resources/db/migration/V061__scheduled_task.sql` ```sql CREATE TABLE scheduled_task ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL, agent_id BIGINT NOT NULL, session_id BIGINT NOT NULL, name VARCHAR(200) NOT NULL, prompt TEXT NOT NULL, cron_expression VARCHAR(100) NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE', last_fire_time DATETIME DEFAULT NULL, last_execution_status VARCHAR(20) DEFAULT NULL, next_fire_time DATETIME DEFAULT NULL, fire_count INT NOT NULL DEFAULT 0, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted TINYINT NOT NULL DEFAULT 0, INDEX idx_user_id (user_id), INDEX idx_status_next_fire (status, next_fire_time, deleted) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ``` ### 5.2 后端 — 实体与 Mapper **新增文件:** | 文件 | 说明 | |------|------| | `backend/.../schedule/entity/ScheduledTask.java` | 实体类,`@TableName("scheduled_task")` | | `backend/.../schedule/mapper/ScheduledTaskMapper.java` | MyBatis-Plus Mapper 接口 | **ScheduledTask 关键字段映射:** ```java @Data @TableName("scheduled_task") public class ScheduledTask { @TableId(type = IdType.AUTO) private Long id; private Long userId; private Long agentId; private Long sessionId; private String name; private String prompt; private String cronExpression; private String status; // ACTIVE / PAUSED private LocalDateTime lastFireTime; private String lastExecutionStatus; // COMPLETED / FAILED / SKIPPED private LocalDateTime nextFireTime; private Integer fireCount; private LocalDateTime createdAt; private LocalDateTime updatedAt; @TableLogic private Integer deleted; } ``` ### 5.3 后端 — Service 层 **新增文件:** | 文件 | 说明 | |------|------| | `backend/.../schedule/service/ScheduledTaskService.java` | 核心业务逻辑 | **ScheduledTaskService 职责:** ```java @Service @RequiredArgsConstructor public class ScheduledTaskService { // 创建定时任务 // 1. 创建专属 Session(title=任务名, agent_id=绑定Agent, session_type=NORMAL) // 2. 计算 next_fire_time // 3. 持久化 scheduled_task 记录 public ScheduledTask createTask(Long userId, Long agentId, String name, String prompt, String cronExpression); // 更新任务(名称、prompt、cron、启用/禁用) // 若 cron 变更,重新计算 next_fire_time public ScheduledTask updateTask(Long taskId, Long userId, ...); // 删除任务(逻辑删除) public void deleteTask(Long taskId, Long userId); // 查询用户的任务列表 public List listByUser(Long userId); // 查询所有任务(管理员用) public Page listAll(PageParams params); // 执行定时任务(由 Scheduler 调用) // 1. 检查 Session 是否空闲 // 2. 空闲 → 持久化 USER 消息 → 调用 HarnessService.executeFromEvent() // 3. 更新 last_fire_time、fire_count、next_fire_time public void executeTask(ScheduledTask task); // 计算下次触发时间 public LocalDateTime calculateNextFireTime(String cronExpression); } ``` ### 5.4 后端 — Scheduler **新增文件:** | 文件 | 说明 | |------|------| | `backend/.../schedule/scheduler/ScheduledTaskScheduler.java` | 定时扫描与触发 | **核心逻辑:** ```java @Component @RequiredArgsConstructor @Slf4j public class ScheduledTaskScheduler { private final ScheduledTaskService scheduledTaskService; private final ScheduledTaskMapper scheduledTaskMapper; // 每分钟扫描一次 @Scheduled(fixedDelay = 60_000) public void scanAndExecute() { LocalDateTime now = LocalDateTime.now(); // 查询:status=ACTIVE AND next_fire_time <= now AND deleted=0 List dueTasks = scheduledTaskMapper.selectList( new LambdaQueryWrapper() .eq(ScheduledTask::getStatus, "ACTIVE") .le(ScheduledTask::getNextFireTime, now) .eq(ScheduledTask::getDeleted, 0) ); for (ScheduledTask task : dueTasks) { try { scheduledTaskService.executeTask(task); } catch (Exception e) { log.error("定时任务执行失败: taskId={}", task.getId(), e); // 更新状态为 FAILED,计算下次触发时间 } } } } ``` ### 5.5 后端 — Agent 内置工具 **新增 4 个工具,放置在 `backend/.../harness/tool/impl/` 目录:** | 工具文件 | 工具名 | 说明 | |----------|--------|------| | `CreateScheduledTaskTool.java` | `create_scheduled_task` | 创建定时任务 | | `ListScheduledTasksTool.java` | `list_scheduled_tasks` | 列出用户的定时任务 | | `UpdateScheduledTaskTool.java` | `update_scheduled_task` | 更新任务(名称、prompt、cron、启用/禁用) | | `DeleteScheduledTaskTool.java` | `delete_scheduled_task` | 删除定时任务 | **CreateScheduledTaskTool 示例结构:** ```java @Component @RequiredArgsConstructor public class CreateScheduledTaskTool implements Tool { private final ScheduledTaskService scheduledTaskService; @Override public String getName() { return "create_scheduled_task"; } @Override public String getDescription() { return "创建定时任务。任务将按照指定的 cron 计划自动执行。" + "例如:每天早上9点检查新股申购、每小时检查服务器状态等。"; } @Override public Map getInputSchema() { // 参数:name(任务名), prompt(执行内容), cron_expression(Spring cron 6位) return Map.of( "type", "object", "properties", Map.of( "name", Map.of("type", "string", "description", "任务名称"), "prompt", Map.of("type", "string", "description", "任务触发时执行的 prompt 内容"), "cron_expression", Map.of("type", "string", "description", "Spring cron 表达式(6位:秒 分 时 日 月 周),如 '0 0 9 * * ?' 表示每天9点") ), "required", List.of("name", "prompt", "cron_expression") ); } @Override public String execute(String arguments, Long sessionId, Long userId, String workspace) { // 1. 解析参数 // 2. 从 sessionId 获取 agent_id // 3. 调用 scheduledTaskService.createTask() // 4. 返回任务创建结果 JSON } } ``` **工具行为指南(toolPrompt)关键内容:** ```markdown ## create_scheduled_task 使用指南 当用户希望创建定时自动执行的任务时使用此工具。 ### cron 表达式规则 - 格式:秒 分 时 日 月 周(Spring 6位 cron) - "每天早上9点" → "0 0 9 * * ?" - "每30分钟" → "0 */30 * * * ?" - "工作日早上9点" → "0 0 9 * * MON-FRI" ### prompt 编写要求 - prompt 应该是完整的、自包含的指令 - 应包含足够的上下文,因为 Agent 执行时只有任务历史,没有用户实时对话 - 示例:"检查今日是否有新股可申购。如果有,列出新股代码、名称、申购价格和上限;如果没有,简要说明即可。" ``` ### 5.6 后端 — REST API **新增文件:** | 文件 | 说明 | |------|------| | `backend/.../schedule/controller/ScheduledTaskController.java` | REST 接口 | **接口定义:** | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/api/v1/scheduled-tasks` | 查询当前用户的任务列表 | 登录用户 | | GET | `/api/v1/scheduled-tasks/all` | 查询所有任务(分页) | 管理员 | | PUT | `/api/v1/scheduled-tasks/{id}` | 更新任务(名称、prompt、cron、状态) | 任务创建者 | | DELETE | `/api/v1/scheduled-tasks/{id}` | 删除任务 | 任务创建者 | > 注:创建任务通过 Agent 工具(`create_scheduled_task`)完成,不提供 REST 创建接口。 > 注:管理员接口需添加 `@RequirePermission` 注解。 ### 5.7 管理后台 — 定时任务管理页面 **新增文件:** | 文件 | 说明 | |------|------| | `admin/src/views/scheduled-tasks/index.vue` | 定时任务管理页面 | **页面功能:** - 任务列表表格:名称、创建者、Agent、Cron 表达式、状态、上次执行状态、上次触发时间、下次触发时间、触发次数 - 操作列:启用/禁用开关、删除按钮 - 支持分页 **路由配置:** 在 `admin/src/router/index.ts` 中新增: ```typescript { path: 'scheduled-tasks', name: 'ScheduledTasks', component: () => import('@/views/scheduled-tasks/index.vue'), meta: { title: '定时任务', icon: 'Clock' } } ``` ### 5.8 桌面端 — 定时任务侧栏面板 **新增文件:** | 文件 | 说明 | |------|------| | `desktop/src/components/ScheduledTaskPanel.vue` | 定时任务面板组件 | | `desktop/src/composables/useScheduledTasks.ts` | 定时任务数据管理 composable | **面板功能:** - 任务列表:名称、状态(Active/Paused)、下次触发时间 - 操作:启用/禁用切换、删除 - 空状态提示:"还没有定时任务,试试对 Agent 说'每天早上9点帮我检查新股'" **集成位置:** 在桌面端左侧面板或设置区域添加入口,点击后展开定时任务面板。 ### 5.9 消息注入与执行流程 **核心执行流程(ScheduledTaskService.executeTask):** ``` executeTask(task) │ ├─ 1. 查询 Session 当前 phase │ └─ 若 phase 为 RUNNING/RESUMING/WAITING_APPROVAL │ → 标记 last_execution_status = 'SKIPPED' │ → 计算 next_fire_time │ → return │ ├─ 2. 持久化 USER 消息 │ └─ sessionService.saveMessage(sessionId, "USER", task.prompt, ...) │ ├─ 3. 更新 Session 状态 │ └─ sessionService.updatePhase(sessionId, "RUNNING") │ ├─ 4. 创建 WsStreamingEventListener(用于推送执行事件) │ ├─ 5. 提交到 agentExecutor 线程池 │ └─ agentExecutor.submit(() -> { │ harnessService.executeFromEvent(sessionId, eventId, listener, cancelFlag) │ }) │ ├─ 6. 更新任务状态 │ ├─ last_fire_time = now │ ├─ fire_count++ │ ├─ next_fire_time = calculateNextFireTime(cron) │ └─ last_execution_status = 根据执行结果设置 │ └─ 7. 通知判定 └─ 复用现有 TaskCompletionNotificationService 逻辑 ``` ## 6. 落地清单 ### 6.1 后端 | 序号 | 任务 | 文件 | 依赖 | |------|------|------|------| | B1 | 创建数据库迁移脚本 | `V061__scheduled_task.sql` | 无 | | B2 | 创建实体类 | `schedule/entity/ScheduledTask.java` | B1 | | B3 | 创建 Mapper | `schedule/mapper/ScheduledTaskMapper.java` | B2 | | B4 | 创建 Service | `schedule/service/ScheduledTaskService.java` | B2, B3 | | B5 | 创建 Scheduler | `schedule/scheduler/ScheduledTaskScheduler.java` | B4 | | B6 | 创建内置工具 `create_scheduled_task` | `harness/tool/impl/CreateScheduledTaskTool.java` | B4 | | B7 | 创建内置工具 `list_scheduled_tasks` | `harness/tool/impl/ListScheduledTasksTool.java` | B4 | | B8 | 创建内置工具 `update_scheduled_task` | `harness/tool/impl/UpdateScheduledTaskTool.java` | B4 | | B9 | 创建内置工具 `delete_scheduled_task` | `harness/tool/impl/DeleteScheduledTaskTool.java` | B4 | | B10 | 创建 REST Controller | `schedule/controller/ScheduledTaskController.java` | B4 | | B11 | 编写单元测试 | `schedule/service/ScheduledTaskServiceTest.java` 等 | B4-B10 | ### 6.2 管理后台 | 序号 | 任务 | 文件 | 依赖 | |------|------|------|------| | A1 | 创建定时任务管理页面 | `admin/src/views/scheduled-tasks/index.vue` | B10 | | A2 | 注册路由 | `admin/src/router/index.ts` 修改 | A1 | ### 6.3 桌面端 | 序号 | 任务 | 文件 | 依赖 | |------|------|------|------| | D1 | 创建定时任务 composable | `desktop/src/composables/useScheduledTasks.ts` | B10 | | D2 | 创建定时任务面板组件 | `desktop/src/components/ScheduledTaskPanel.vue` | D1 | | D3 | 集成到桌面端布局 | 相关布局组件修改 | D2 | | D4 | 更新 package.json version | `desktop/package.json` | 全部 | ### 6.4 执行顺序 ``` Phase 1 — 后端基础(B1 → B2 → B3 → B4) Phase 2 — 后端核心(B5, B6, B7, B8, B9, B10 并行,均依赖 B4) Phase 3 — 后端测试(B11) Phase 4 — 管理后台(A1 → A2,依赖 B10) Phase 5 — 桌面端(D1 → D2 → D3 → D4,依赖 B10) ``` ## 7. 风险与注意事项 | 风险 | 说明 | 应对 | |------|------|------| | Session 消息膨胀 | 定时任务长期运行后 Session 消息量过大 | 现有 CompactionService 自动压缩机制兜底 | | Scheduler 扫描性能 | 任务量大时每分钟全表扫描 | `idx_status_next_fire` 索引保证查询效率,仅查询 `next_fire_time <= now` 的记录 | | 线程池饱和 | 大量定时任务同时触发 | 复用现有 `agentExecutor` 线程池,已有队列机制;跳过策略也避免并发堆积 | | 时区问题 | Cron 触发时间与用户期望不一致 | 使用系统默认时区(Asia/Shanghai),后续可扩展用户级时区设置 | | LLM 费用 | 定时任务频繁调用 LLM 产生费用 | 用户自行控制任务频率;管理员可在管理后台监控触发次数 |