deerflow-code/offline-backend-20260512/backend/docs/SCHEDULED_TASK_CHANGE_AND_RELEASE_ZH.md
2026-09-07 18:24:55 +08:00

17 KiB
Raw Blame History

定时任务与 Element 投递改动说明及内网发版文档

本文档对应本次新增的「类 Hermes 任务调度」能力,包含两部分:

  • 修改说明:说明改了哪些位置,以及为什么这样设计。
  • 发版说明:基于现有 start_backend.0509.sh 的离线部署方式,说明如何重新发版到内网。

1. 本次功能目标

本次改动解决三个核心场景:

  1. 用户可以在任意智能体聊天中创建定时任务,例如「每天 9 点生成 AI 发展日报」。
  2. 定时任务执行结果统一发送到当前用户唯一的「定时任务助手」聊天框,避免和普通聊天混在一起。
  3. 用户可以在任务管理页面查看、修改、暂停、恢复、删除、立即触发自己的全部定时任务,并可以填写 Element/Matrix 用户 ID,让任务结果同步投递到 Element 私聊。

2. 后端修改说明

2.1 新增定时任务 API

位置:

  • app/gateway/routers/scheduled_tasks.py
  • app/gateway/app.py

作用:

  • 新增 /api/scheduled-tasks API。
  • 支持获取定时任务专属会话、任务列表、创建任务、修改任务、删除任务、暂停/恢复、立即触发。
  • 支持 /api/scheduled-tasks/delivery-profile 保存当前用户的 Element 用户 ID。

为什么这样做:

  • 页面上的任务管理能力需要稳定的 CRUD API。
  • Element 用户 ID 是用户级配置,不应该写在系统环境变量里,所以单独做成用户投递配置。
  • 每个用户只保留一个定时任务会话,因此提供 /thread 接口让前端直接进入该会话。

2.2 新增定时任务持久化

位置:

  • packages/harness/deerflow/persistence/scheduled_tasks/model.py
  • packages/harness/deerflow/persistence/scheduled_tasks/base.py
  • packages/harness/deerflow/persistence/scheduled_tasks/sql.py
  • packages/harness/deerflow/persistence/scheduled_tasks/memory.py
  • packages/harness/deerflow/persistence/scheduled_tasks/__init__.py
  • packages/harness/deerflow/persistence/models/__init__.py
  • app/gateway/deps.py

新增数据表:

  • scheduler_threads:记录每个用户唯一的定时任务聊天线程。
  • scheduled_tasks:记录定时任务本体,包括 prompt、cron、时区、下次执行时间、执行智能体上下文。
  • scheduled_task_runs:记录每次任务执行,避免重复执行同一计划时间。
  • scheduled_task_delivery_profiles:记录用户自己的 Element 用户 ID。

为什么这样做:

  • 定时任务需要跨进程、跨重启保存,所以不能只存在内存里。
  • 单独记录 run 可以判断任务是否成功、失败,也可以防止同一计划时间被重复执行。
  • Element 投递目标是用户个人信息,和任务本体分开更清晰。

2.3 新增调度运行服务

位置:

  • packages/harness/deerflow/runtime/scheduler/service.py
  • packages/harness/deerflow/runtime/scheduler/registry.py
  • packages/harness/deerflow/runtime/scheduler/__init__.py
  • app/gateway/app.py

作用:

  • 后端启动时创建 ScheduledTaskService。
  • 服务每 30 秒扫描一次到期任务。
  • 到期后把任务 prompt 投递到该用户的定时任务专属聊天线程。
  • 任务运行时会恢复创建任务时的智能体上下文,例如自定义智能体名、模型、思考模式等。
  • 定时任务执行期间禁用再次创建定时任务,避免任务递归创建任务。

为什么这样做:

  • 用户希望「可以在智能体聊天中创建任务,并复用智能体技能生成日报」,因此任务必须保存创建时的 agent/context。
  • 所有结果集中到专属聊天线程,可以满足「任务结果从定时任务聊天框发给我」的交互要求。
  • 当前 RunManager 对同一 thread 的并发运行有限制,所以调度服务内部加了运行锁,避免多个报告同时写入同一个定时任务会话导致冲突。

2.4 新增智能体工具 scheduled_task

位置:

  • packages/harness/deerflow/tools/builtins/scheduled_task_tool.py
  • packages/harness/deerflow/tools/builtins/__init__.py
  • packages/harness/deerflow/tools/tools.py
  • packages/harness/deerflow/agents/lead_agent/agent.py

作用:

  • 智能体可以通过工具创建、查询、更新、删除、暂停、恢复、触发定时任务。
  • 用户不需要进入「定时任务聊天框」也能创建任务,可以直接在普通聊天或智能体聊天中说需求。
  • 定时任务执行时不会暴露 scheduled_task 工具,避免任务运行中再次调度自身。

为什么这样做:

  • 这是满足「我可能会使用智能体中的某一些技能去实现日报生成」的关键。
  • 如果只在任务管理页面创建任务,就无法自然复用智能体上下文和工具能力。

2.5 新增 Element/Matrix 私聊投递

位置:

  • packages/harness/deerflow/runtime/scheduler/element.py
  • packages/harness/deerflow/runtime/scheduler/service.py
  • .env

作用:

  • 定时任务成功后,后端会读取当前用户保存的 element_user_id。
  • 如果系统启用了 Element 配置,则机器人会创建 Matrix DM 房间并发送文本结果。
  • 支持三种机器人认证方式:
    • ELEMENT_ACCESS_TOKEN:直接使用已有 token,优先级最高。
    • ELEMENT_AUTO_REGISTER=true:后端自动注册机器人用户,并缓存 token。
    • ELEMENT_BOT_USERNAME + ELEMENT_BOT_PASSWORD:使用已有机器人账号登录。

为什么这样做:

  • default_room_id 不适合这个需求,因为你要发到每个用户自己的私聊。
  • 用户在任务管理页面填写自己的 Element 用户 ID,后端机器人按用户发 DM,更符合多用户场景。
  • 自动注册适合内网环境快速启用;账号密码和 token 方式适合更严格的运维管理。

2.6 新增运行依赖

位置:

  • pyproject.toml

新增:

croniter>=6.2.2

作用:

  • 用于根据 cron 表达式计算下一次执行时间。

发版注意:

  • 本次可以继续使用 deerflow-backend-offline:20260505 原镜像。
  • 因为新增了 croniter,启动脚本会在容器启动时先检查该依赖;如果缺失,则从内网 Python 私服安装。
  • 只要内网容器能访问 Python 私服,就不需要重新构建或导入新镜像。

3. 前端修改说明

3.1 新增定时任务 API 与 hooks

位置:

  • frontend-web/src/core/scheduled-tasks/api.ts
  • frontend-web/src/core/scheduled-tasks/hooks.ts
  • frontend-web/src/core/scheduled-tasks/types.ts
  • frontend-web/src/core/scheduled-tasks/index.ts

作用:

  • 封装 /api/scheduled-tasks 后端接口。
  • 页面通过 React Query 自动加载任务列表和 Element 投递配置。

为什么这样做:

  • 和现有前端数据请求方式保持一致。
  • 任务列表、任务操作、Element 配置保存都能统一刷新缓存。

3.2 新增定时任务聊天页与任务管理页

位置:

  • frontend-web/src/pages/ScheduledChatPage.tsx
  • frontend-web/src/pages/ScheduledTasksPage.tsx
  • frontend-web/src/App.tsx

作用:

  • /workspace/scheduled:进入当前用户唯一的定时任务聊天框。
  • /workspace/scheduled/tasks:查看、编辑、删除、暂停、恢复、立即运行任务;填写 Element 用户 ID。

为什么这样做:

  • 聊天结果和任务管理是两个不同工作流:一个看结果,一个管理规则。
  • 两个入口都挂在 workspace 下,用户不会离开原来的工作区。

3.3 左侧栏入口与普通会话过滤

位置:

  • frontend-web/src/components/workspace/workspace-nav-chat-list.tsx
  • frontend-web/src/components/workspace/recent-chat-list.tsx
  • frontend-web/src/pages/ChatsPage.tsx

作用:

  • 左侧栏新增「定时任务」和「任务管理」入口。
  • 普通最近聊天列表中过滤 metadata.thread_type === "scheduler" 的系统会话。

为什么这样做:

  • 定时任务会话是系统专用会话,不应该和普通用户聊天列表混在一起。
  • 用户仍然可以通过专门入口进入该会话查看报告。

4. 配置说明

运行时配置文件:

  • offline-backend-20260505/bundle/.env

当前写入的是注释示例,默认不会启用 Element:

# ELEMENT_ENABLED=true
# ELEMENT_HOMESERVER_URL=http://your-element-homeserver:8008
# ELEMENT_AUTO_REGISTER=true
# ELEMENT_BOT_USERNAME=scheduled-bot
# ELEMENT_STATE_FILE=.deer-flow/data/element-bot-token

内网启用时建议:

ELEMENT_ENABLED=true
ELEMENT_HOMESERVER_URL=http://你的内网Matrix地址:8008
ELEMENT_AUTO_REGISTER=true
ELEMENT_BOT_USERNAME=scheduled-bot
ELEMENT_STATE_FILE=.deer-flow/data/element-bot-token

如果不允许自动注册:

ELEMENT_ENABLED=true
ELEMENT_HOMESERVER_URL=http://你的内网Matrix地址:8008
ELEMENT_AUTO_REGISTER=false
ELEMENT_BOT_USERNAME=scheduled-bot
ELEMENT_BOT_PASSWORD=你的机器人密码
ELEMENT_STATE_FILE=.deer-flow/data/element-bot-token

用户个人 Element ID 不写在 .env,由用户在任务管理页面填写,例如:

@zhangsan:matrix.intranet.local

5. 内网发版方案

5.1 推荐方案

推荐发布一个新的增量包目录,例如:

offline-backend-20260512-scheduler/

本次按你的原部署方式处理:继续使用 0509 原镜像,只复制或挂载代码 patch。新增依赖 croniter 由启动脚本通过内网 Python 私服安装。

推荐产物:

  • frontend-vite-20260512.zip
  • start_backend.scheduler.sh
  • stop_backend.sh
  • config.yaml.example
  • .env.example
  • extensions_config.json
  • skills/
  • data/
  • docs/SCHEDULED_TASK_CHANGE_AND_RELEASE_ZH.md
  • patch_20260512_scheduler/

5.2 后端发版文件清单

需要进入新包或 patch 包的后端文件:

backend/app/gateway/app.py
backend/app/gateway/deps.py
backend/app/gateway/routers/scheduled_tasks.py
backend/packages/harness/deerflow/agents/lead_agent/agent.py
backend/packages/harness/deerflow/persistence/models/__init__.py
backend/packages/harness/deerflow/persistence/scheduled_tasks/
backend/packages/harness/deerflow/runtime/scheduler/
backend/packages/harness/deerflow/tools/builtins/__init__.py
backend/packages/harness/deerflow/tools/builtins/scheduled_task_tool.py
backend/packages/harness/deerflow/tools/tools.py
backend/pyproject.toml
backend/.env

注意:

  • 不建议把本地 .deer-flow/data/deerflow.db* 作为代码发版内容,它们是运行数据。
  • 内网环境自己的 data/ 目录要保留,这是历史会话和任务数据所在位置。

5.3 前端发版文件清单

需要构建并发布新的前端静态资源。

涉及源码:

frontend-web/src/App.tsx
frontend-web/src/core/scheduled-tasks/
frontend-web/src/pages/ScheduledChatPage.tsx
frontend-web/src/pages/ScheduledTasksPage.tsx
frontend-web/src/components/workspace/workspace-nav-chat-list.tsx
frontend-web/src/components/workspace/recent-chat-list.tsx
frontend-web/src/pages/ChatsPage.tsx

构建命令:

cd frontend-web
pnpm install --offline
pnpm run build

产物:

frontend-web/dist/

将 dist/ 按你原来的方式打包成 frontend-vite-20260512.zip 或覆盖原静态资源目录。

5.4 适配 start_backend.0509.sh 的部署方式

0509 脚本核心逻辑是:

  • 使用老镜像 deerflow-backend-offline:20260505。
  • 通过 patch_0509 bind-mount Python 文件覆盖镜像内代码。
  • 通过 --env-file .env 注入配置。
  • 通过 -v data:/app/backend/.deer-flow 保留运行数据。

本次继续沿用这个模式,调整两点:

  1. PATCH_BASE 建议改成新的目录,例如 patch_20260512_scheduler。
  2. 启动脚本在容器启动前检查并安装 croniter,依赖来源使用内网 Python 私服。

示例脚本差异:

IMAGE_TAG="${IMAGE_TAG:-deerflow-backend-offline:20260505}"
PATCH_BASE="${SCRIPT_DIR}/patch_20260512_scheduler"
SCHEDULER_INSTALL_DEPS="${SCHEDULER_INSTALL_DEPS:-1}"
SCHEDULER_PIP_PACKAGES="${SCHEDULER_PIP_PACKAGES:-croniter>=6.2.2}"

容器启动命令中会执行:

uv run --no-sync python -c "import croniter" >/dev/null 2>&1 || uv pip install "${SCHEDULER_PIP_PACKAGES:-croniter>=6.2.2}"

5.5 Python 私服配置

如果内网 Python 私服需要配置索引地址,可以写到 bundle/.env 中:

UV_INDEX_URL=http://your-python-mirror/simple
PIP_INDEX_URL=http://your-python-mirror/simple
UV_TRUSTED_HOST=your-python-mirror
PIP_TRUSTED_HOST=your-python-mirror

如果私服使用 HTTPS 且证书正常,一般不需要 *_TRUSTED_HOST。

也可以在启动时临时覆盖安装包名:

SCHEDULER_PIP_PACKAGES='croniter>=6.2.2' ./start_backend.scheduler.sh

5.6 内网服务器部署步骤

在内网服务器上:

cd /opt/deerflow/offline-backend-20260512-scheduler/bundle

# 1. 备份旧配置和数据
cp -a config.yaml config.yaml.bak.$(date +%Y%m%d%H%M%S)
cp -a .env .env.bak.$(date +%Y%m%d%H%M%S)
cp -a data data.bak.$(date +%Y%m%d%H%M%S)

# 2. 合并配置
# 保留原来的 AUTH_JWT_SECRET、模型 key、CORS 等配置。
# 追加或打开 Element、Python 私服相关配置。
vi .env

# 3. 启动后端,继续使用原镜像 deerflow-backend-offline:20260505
chmod +x start_backend.scheduler.sh stop_backend.sh
./start_backend.scheduler.sh

# 4. 查看日志
docker logs -f deerflow-backend

本次已补充一个适配脚本:

offline-backend-20260505/bundle/start_backend.scheduler.sh

它和 0509 脚本保持相同的 Docker 运行方式,同时支持:

  • 默认使用 deerflow-backend-offline:20260505。
  • 默认从 patch_20260512_scheduler 挂载 Python patch。
  • 允许通过环境变量覆盖 IMAGE_TAG、PATCH_BASE、HOST_PORT、CONTAINER_NAME。
  • 启动前自动检查并安装 croniter,可通过 SCHEDULER_INSTALL_DEPS=0 关闭。

如果仍使用 0509 脚本名称,需要注意 0509 脚本里的 PATCH_BASE 是脚本内固定赋值,外部传环境变量不会生效。请直接编辑脚本里的:

PATCH_BASE="${SCRIPT_DIR}/patch_20260512_scheduler"

同时把镜像变量改成:

IMAGE_TAG="${IMAGE_TAG:-deerflow-backend-offline:20260505}"

5.7 数据库表创建说明

本次新增的 SQLAlchemy model 会在后端启动初始化时注册。

内网启动后检查日志:

docker logs deerflow-backend --tail 200

如果看到定时任务相关表不存在的错误,需要确认当前项目是否在启动时自动 create_all。

手动验证 SQLite 数据库:

sqlite3 data/data/deerflow.db ".tables"

应能看到:

scheduled_tasks
scheduled_task_runs
scheduled_task_delivery_profiles
scheduler_threads

实际数据库路径以你当前 DEER_FLOW_HOME 和已有部署结构为准。0509 脚本中挂载的是:

bundle/data -> /app/backend/.deer-flow

5.8 Element 联调步骤

  1. 确认内网 Matrix/Element homeserver 地址,例如:
http://matrix.intranet.local:8008
  1. 修改 .env:
ELEMENT_ENABLED=true
ELEMENT_HOMESERVER_URL=http://matrix.intranet.local:8008
ELEMENT_AUTO_REGISTER=true
ELEMENT_BOT_USERNAME=scheduled-bot
ELEMENT_STATE_FILE=.deer-flow/data/element-bot-token
  1. 重启后端:
./stop_backend.sh
./start_backend.scheduler.sh
  1. 用户进入前端任务管理页面,填写自己的 Element 用户 ID。

  2. 创建一个测试任务并点击「立即运行」。

  3. 检查:

  • 定时任务聊天框是否收到结果。
  • Element 私聊是否收到机器人消息。
  • 后端日志是否有 Failed to deliver scheduled task。

6. 验证清单

后端验证:

docker logs deerflow-backend --tail 200
curl http://127.0.0.1:8001/openapi.json | grep scheduled-tasks

前端验证:

  • 左侧栏出现「定时任务」和「任务管理」。
  • 普通聊天列表不显示系统定时任务会话。
  • 在智能体聊天中输入「每天 9 点生成 AI 发展日报」可以创建任务。
  • 进入任务管理页可以看到任务,且可以修改、暂停、恢复、删除、立即运行。
  • 进入定时任务聊天框可以看到执行结果。

Element 验证:

  • 未配置 ELEMENT_HOMESERVER_URL 时,任务仍然正常写入定时任务聊天框,不影响主流程。
  • 配置 Element 且用户填写 Element ID 后,任务成功时会发送 Matrix DM。

7. 回滚方案

后端回滚:

./stop_backend.sh
docker load -i deerflow-backend-offline-image-20260505.tar
IMAGE_TAG=deerflow-backend-offline:20260505 bash start_backend.sh

前端回滚:

  • 恢复上一版前端静态资源包。

数据说明:

  • 新增表可以保留,不影响老版本读取原有聊天数据。
  • 如果需要彻底清理定时任务数据,请先备份数据库,再清理新增的四张表。

8. 已验证事项

本地已做过:

  • 前端 npm run build 通过。
  • 新增后端定时任务与 Element 文件做过 python -m py_compile。

限制说明:

  • 本地全量后端 compileall 曾遇到项目中既有 Python 版本语法兼容问题,不是本次新增文件导致。
  • 当前工作区中的 .deer-flow/data/deerflow.db* 是本地运行数据变更,不建议纳入代码发版。