17 KiB
定时任务与 Element 投递改动说明及内网发版文档
本文档对应本次新增的「类 Hermes 任务调度」能力,包含两部分:
- 修改说明:说明改了哪些位置,以及为什么这样设计。
- 发版说明:基于现有
start_backend.0509.sh的离线部署方式,说明如何重新发版到内网。
1. 本次功能目标
本次改动解决三个核心场景:
- 用户可以在任意智能体聊天中创建定时任务,例如「每天 9 点生成 AI 发展日报」。
- 定时任务执行结果统一发送到当前用户唯一的「定时任务助手」聊天框,避免和普通聊天混在一起。
- 用户可以在任务管理页面查看、修改、暂停、恢复、删除、立即触发自己的全部定时任务,并可以填写 Element/Matrix 用户 ID,让任务结果同步投递到 Element 私聊。
2. 后端修改说明
2.1 新增定时任务 API
位置:
app/gateway/routers/scheduled_tasks.pyapp/gateway/app.py
作用:
- 新增
/api/scheduled-tasksAPI。 - 支持获取定时任务专属会话、任务列表、创建任务、修改任务、删除任务、暂停/恢复、立即触发。
- 支持
/api/scheduled-tasks/delivery-profile保存当前用户的 Element 用户 ID。
为什么这样做:
- 页面上的任务管理能力需要稳定的 CRUD API。
- Element 用户 ID 是用户级配置,不应该写在系统环境变量里,所以单独做成用户投递配置。
- 每个用户只保留一个定时任务会话,因此提供
/thread接口让前端直接进入该会话。
2.2 新增定时任务持久化
位置:
packages/harness/deerflow/persistence/scheduled_tasks/model.pypackages/harness/deerflow/persistence/scheduled_tasks/base.pypackages/harness/deerflow/persistence/scheduled_tasks/sql.pypackages/harness/deerflow/persistence/scheduled_tasks/memory.pypackages/harness/deerflow/persistence/scheduled_tasks/__init__.pypackages/harness/deerflow/persistence/models/__init__.pyapp/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.pypackages/harness/deerflow/runtime/scheduler/registry.pypackages/harness/deerflow/runtime/scheduler/__init__.pyapp/gateway/app.py
作用:
- 后端启动时创建
ScheduledTaskService。 - 服务每 30 秒扫描一次到期任务。
- 到期后把任务 prompt 投递到该用户的定时任务专属聊天线程。
- 任务运行时会恢复创建任务时的智能体上下文,例如自定义智能体名、模型、思考模式等。
- 定时任务执行期间禁用再次创建定时任务,避免任务递归创建任务。
为什么这样做:
- 用户希望「可以在智能体聊天中创建任务,并复用智能体技能生成日报」,因此任务必须保存创建时的 agent/context。
- 所有结果集中到专属聊天线程,可以满足「任务结果从定时任务聊天框发给我」的交互要求。
- 当前 RunManager 对同一 thread 的并发运行有限制,所以调度服务内部加了运行锁,避免多个报告同时写入同一个定时任务会话导致冲突。
2.4 新增智能体工具 scheduled_task
位置:
packages/harness/deerflow/tools/builtins/scheduled_task_tool.pypackages/harness/deerflow/tools/builtins/__init__.pypackages/harness/deerflow/tools/tools.pypackages/harness/deerflow/agents/lead_agent/agent.py
作用:
- 智能体可以通过工具创建、查询、更新、删除、暂停、恢复、触发定时任务。
- 用户不需要进入「定时任务聊天框」也能创建任务,可以直接在普通聊天或智能体聊天中说需求。
- 定时任务执行时不会暴露
scheduled_task工具,避免任务运行中再次调度自身。
为什么这样做:
- 这是满足「我可能会使用智能体中的某一些技能去实现日报生成」的关键。
- 如果只在任务管理页面创建任务,就无法自然复用智能体上下文和工具能力。
2.5 新增 Element/Matrix 私聊投递
位置:
packages/harness/deerflow/runtime/scheduler/element.pypackages/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.tsfrontend-web/src/core/scheduled-tasks/hooks.tsfrontend-web/src/core/scheduled-tasks/types.tsfrontend-web/src/core/scheduled-tasks/index.ts
作用:
- 封装
/api/scheduled-tasks后端接口。 - 页面通过 React Query 自动加载任务列表和 Element 投递配置。
为什么这样做:
- 和现有前端数据请求方式保持一致。
- 任务列表、任务操作、Element 配置保存都能统一刷新缓存。
3.2 新增定时任务聊天页与任务管理页
位置:
frontend-web/src/pages/ScheduledChatPage.tsxfrontend-web/src/pages/ScheduledTasksPage.tsxfrontend-web/src/App.tsx
作用:
/workspace/scheduled:进入当前用户唯一的定时任务聊天框。/workspace/scheduled/tasks:查看、编辑、删除、暂停、恢复、立即运行任务;填写 Element 用户 ID。
为什么这样做:
- 聊天结果和任务管理是两个不同工作流:一个看结果,一个管理规则。
- 两个入口都挂在 workspace 下,用户不会离开原来的工作区。
3.3 左侧栏入口与普通会话过滤
位置:
frontend-web/src/components/workspace/workspace-nav-chat-list.tsxfrontend-web/src/components/workspace/recent-chat-list.tsxfrontend-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.zipstart_backend.scheduler.shstop_backend.shconfig.yaml.example.env.exampleextensions_config.jsonskills/data/docs/SCHEDULED_TASK_CHANGE_AND_RELEASE_ZH.mdpatch_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_0509bind-mount Python 文件覆盖镜像内代码。 - 通过
--env-file .env注入配置。 - 通过
-v data:/app/backend/.deer-flow保留运行数据。
本次继续沿用这个模式,调整两点:
PATCH_BASE建议改成新的目录,例如patch_20260512_scheduler。- 启动脚本在容器启动前检查并安装
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 联调步骤
- 确认内网 Matrix/Element homeserver 地址,例如:
http://matrix.intranet.local:8008
- 修改
.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
- 重启后端:
./stop_backend.sh
./start_backend.scheduler.sh
-
用户进入前端任务管理页面,填写自己的 Element 用户 ID。
-
创建一个测试任务并点击「立即运行」。
-
检查:
- 定时任务聊天框是否收到结果。
- 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*是本地运行数据变更,不建议纳入代码发版。