异步任务与定时调度
Molore的大量能力是「到点自己跑」的:图谱自进化、自动打标、每日复盘、每周回顾、回填迁移、各类清扫。这些任务全部登记在启动钩子注册表(app/core/startup.py 的 STARTUP_HOOKS)里,调度器用 APScheduler / threading.Timer 实现。整条域的设计纪律只有三条:事件驱动必配定时兜底、撞锁记 pending 补跑、时限按执行体配速分级。
调度任务总览
| 任务 | 触发口径 | 执行体 | 代码位置 |
|---|---|---|---|
| 图谱自进化 | 停笔 300s 防抖 + 距上次成功 ≥1800s | graphifyy CLI 子进程 | app/services/graphify_service.py |
| 自动打标兜底扫描 | 启动 120s 首扫 + 每小时 | 本地 0.8b,零成本 | app/services/autotag_service.py |
| wiki 规则 lint | 启动 120s 首扫 + 每小时 | 零模型纯 SQL | app/services/wiki_service/lint.py |
| KU title 回填 | 启动 180s 首扫 + 每小时 | 零成本解析优先,本地模型兜底 | app/services/knowledge_title.py |
| 每日复盘 | 每天 08:00(Asia/Shanghai) | 本地 0.8b,零成本 | app/services/daily_review_service.py |
| 每周回顾 | 每周一 08:00(Asia/Shanghai) | 本地 0.8b,零成本 | app/services/weekly_review_service.py |
| 每日日报 | 每天 08:30(CST) | 本地生成落库 + data/reports/*.md | app/services/daily_report.py |
| 表格文档回填 | 启动 120s 一次性 | 重抽取 + 重索引,marker 幂等 | app/services/document_service.py |
| 标签日扫 | 每日 | 清幽灵关联 + 回收闲置 30 天空标签 | app/services/tag_maintenance.py |
| 同步 oplog GC | 每日 | 清理过期/被覆盖的 sync_operations | app/services/sync_maintenance.py |
| 计费调度 | 周期 | 订阅到期与自动续费 | app/core/billing_scheduler.py |
| RSS 自动抓取 | 用户设置驱动 | rss_auto 开关 | app/services/rss_scheduler.py |
| 插件自动同步 | 周期 | manifest 声明 | app/services/plugin_scheduler.py |
图谱自进化:防抖 + 重入锁
写入内容不再立即重建图谱——全量重建等于全语料逐篇 LLM 抽取,代价太高。自进化(graphify_service.py)采用防抖口径:
- 停笔静默期
_EVOLVE_QUIET_SECONDS = 300:内容变更触发后置 dirty 并重排防抖计时器(threading.Timer),连续书写期间不打扰; - 最小间隔
_EVOLVE_MIN_INTERVAL = 1800:距上次成功构建不足 30 分钟不重复起建; - 撞锁不丢触发:计时器到期复核状态,构建已在跑或条件不满足时记
evolve_pending(_bump_evolve_pending),构建完成后若期间有新内容(dirty)且成功,自动补排一轮——重入锁撞锁不能直接丢触发; - 重启恢复:
evolve_dirty=True的行在启动钩子里重排防抖计时器,把重启丢掉的排定重建补回来;中断的构建同钩子里恢复; - 事件监听按会话派发:内容提交走 before_commit 捕获、after_commit 投递(
after_commit里session.new已清空,必须先暂存_pending);lifespan 重入有防重复挂载守卫,一次提交不会触发 N 次自进化。
自进化配置存 user.settings["graphify"]["auto_evolve"](enabled + model),不动表结构;模型必须是构建允许的档位,推理档模型禁入图谱构建。
事件驱动必配定时兜底
凡 event-driven 的自动链路,一律加「启动首扫 + 每小时补扫」保险。这条纪律来自实捕事故:云端自动打标曾因事件链某一环(进程重启窗口/线程异常/监听脱落)静默失效近三周,零日志无人发现。现在的标配形态(以自动打标为例,autotag_service.py):
- 内容变更事件监听触发即时处理;
- 启动后 120s 首扫一遍存量(延迟 120s 是让启动主链路先就绪的约定口径);
- 每小时兜底补扫,函数自带重入锁,扫到即处理、扫空即退;
- 兜底扫描幂等,与事件链互不冲突——事件链是低延迟路径,兜底是存活证据。
同形态的还有 wiki lint(120s + 每小时,零模型纯 SQL)与 KU title 回填(180s + 每小时)。
每日复盘与每周回顾
- 每天 08:00(
Asia/Shanghai,daily_review_service.py)为活跃用户生成昨日复盘;每周一 08:00(weekly_review_service.py)生成上周回顾。 - 全程本地
qwen3.5:0.8b,零成本;失败不落假复盘——模型不可用就跳过当天,绝不生成兜底内容充数(自动生成链路只走本地模型、绝不自动扣费的红线)。 - 补课链 = 启动首查 + 每小时复查:错过 08:00 窗口(机器没开)的复盘在下次启动后补上,不依赖用户在场。
- 素材口径:
index_only仓库模式内容不进复盘素材(tenant_scope.semantic_visible过滤)。
一次性回填任务
管线升级后的存量追平走「启动 120s 后台跑一次 + marker 落 system_configs」形态,代表是 backfill_md_table_documents(document_service.py,marker extract_md_tables_v1):重抽取有差异才更新、FTS 重同步、向量重嵌,全部成才落 marker,中途失败下次启动重跑,天然幂等。详见内容入库的存量回填节。
时限按执行体配速分级
定时任务的超时不许拍脑袋,必须按执行体真实配速分级(血泪纪律:撞上强制思考的推理档,大图必超时卡死):
- 推理档模型(如 GLM 5.3 Flash,强制思考)长任务按分钟计,JSON 类调用整体超时 180s 起配;
- 图谱构建 CLI 子进程超时就文档数配速计算:
max(1800, min(上限, 文档数 × 单篇配速)),小规模 30 分钟起步,大规模按量放宽; - 本地 0.8b 零成本链按秒~十秒级配;
- 排队类(Ollama 本地推理门)超 120s 视为过载,生成路 yield
[Error:]、嵌入路返回[],沿用降级语义不悬挂。
下一步
- 内容入库流程 —— 打标与回填服务的上游口径
- 检索问答全流程 —— 兜底扫描维持的索引如何被消费
- FastAPI 后端设计 —— 启动钩子注册表与事件监听纪律
- 知识图谱 —— 自进化构建出的图谱如何使用
- 故障排查 —— 自动任务没跑时先看哪里