Skip to content

feat(scheduler+dashboard): 5s 心跳统一调度引擎与 Dashboard 子页面 - #555

Merged
ThreeFish-AI merged 12 commits into
feature/1.x.xfrom
ThreeFish-AI/dashboard-heartbeat-scheduler
May 17, 2026
Merged

feat(scheduler+dashboard): 5s 心跳统一调度引擎与 Dashboard 子页面#555
ThreeFish-AI merged 12 commits into
feature/1.x.xfrom
ThreeFish-AI/dashboard-heartbeat-scheduler

Conversation

@ThreeFish-AI

Copy link
Copy Markdown
Owner

背景

  • 本次变更要解决的问题:系统中存在 10+ 个散落的周期性任务(6 个后端 startup hook + 4 处前端 setInterval),缺乏统一注册中心、执行历史与多维统计;同时 Home 页只有聊天界面,无法概览所有定时调度任务的运行状况,更无法支撑"24/7 自驱 Agent 在后台持续完成任务"的需求。
  • 关联上下文/Issue/文档:参考业界经典心跳模式 — MindStudio Heartbeat Pattern、Heartbeat-Driven Cognitive Scheduling for LLM Agents、Scheduling Agent Supervisor Pattern;PostgreSQL FOR UPDATE SKIP LOCKED 保证多 worker 并发安全。

核心变更

  • 统一心跳调度引擎(PR1)AsyncScheduler 升级到 5s 心跳,新增 ScheduledTaskRegistry 单例 + scheduled_tasks / task_executions 双表 + Alembic 0034。6 个旧任务(skill_invoke / pipeline_watchdog / session_title_inspect / cache_warm / pgvector_check / agent_inspection)改写为统一 handler,支持 interval / cron / oneshot 三类触发判定。Feature flag NEGENTROPY_UNIFIED_SCHEDULER_ENABLED 支持灰度回退。
  • 聚合 API 与 SSE(PR2)/scheduler/* 8 个端点(kpis / tasks / executions / stats / run / toggle / stream),SSE 流通过 ExecutionBus asyncio fan-out 推送实时执行事件,stats / kpis 加 10s in-memory TTL cache。
  • Home 拆分 + Dashboard 子页面(PR3 + PR4)app/(home)/{studio,dashboard} 嵌套段路由 + 根 / 自动 redirect 到 /studio;Dashboard 含 KpiRow + FilterBar + DimensionCharts(recharts 3.8.1)+ TaskTable + ExecutionTimeline + TaskDetailDrawer 六大组件,支持按角色 / 场景 / 参与人 / Agent / 作用多维筛选与统计。
  • 前端轮询统一收口(Phase 3-A):抽象 useHeartbeatPoll 通用 hook,统一 4 处散落 setInterval 节拍(5s 与后端心跳对齐),自动 visibility 暂停 + online 恢复触发 + callback 错误隔离。
  • 生产级 Agent 巡检(Phase 3-B)agent_inspection handler 升级三件套 — ContextPack 上下文包构建器 + Token 预算门控(24h 累计 vs token_budget)+ 指数退避策略(连续失败 ≥ 3 进入 60s→1h 退避窗口);新增 faculty_deep_check / scheduled_tasks_summary 两种巡检类型。

风险与回滚

  • 主要风险:(1) 6 个旧任务一次性迁移到统一调度框架,回归面较广;(2) 5s 心跳扫表频率较旧 60s 提升 12×,对 scheduled_tasks 表索引依赖加重;(3) Dashboard 引入 recharts +60KB gzip 依赖;(4) 前端轮询统一改造改变了 visibility 暂停语义,后台标签页停止轮询。
  • 回滚方式:(1) 一键 NEGENTROPY_UNIFIED_SCHEDULER_ENABLED=false 跳过 Registry 启动,旧调度路径保留;(2) NEGENTROPY_SCHEDULER_HEARTBEAT_SECONDS 可改回 60;(3) scheduled_tasks / task_executions 表通过 alembic downgrade 0033 回滚;(4) 旧 /skills/{id}/schedules/* API 与 skill_schedules 表零行为变更,前端 Dashboard 段独立 chunk 不影响 Studio 首屏。

验证证据

  • 后端单元测试:560 个 engine + scheduler API 测试全过(含 30 个 PR1 新增 + 11 个 Phase 3-B 新增)。覆盖 AsyncScheduler 三类触发判定、6 个 handler 注册、ContextPack 构建、Token 预算 gate、指数退避单调性与上限截断、/scheduler/* API 序列化与缓存。
  • 前端单元测试:vitest 88 文件 649 个测试全过(含 5 个 useHeartbeatPoll 新增)。覆盖 fireImmediately / enabled / 错误隔离 / unmount 清理 / hidden 暂停。
  • E2E:Playwright Dashboard 4/4 通过 — 根重定向、KPI 卡片 + 任务表 + 时间线 + 图表展示、HomeNav Tab 切换、Detail Drawer + Run Now 操作链路。
  • 覆盖率/关键截图:Next.js build 通过(/dashboard/studio/api/scheduler/* 全部正确注册);TypeScript + ESLint 0 错误;ruff + ruff-format 全过。

影响范围

  • 前端:新增 app/(home)/{studio,dashboard}/app/api/scheduler/*hooks/useHeartbeatPoll.tscomponents/ui/HomeNav.tsx;改造 app/page.tsx(→ redirect)、app/knowledge/pipelines/page.tsxfeatures/knowledge/components/DocumentViewDialog.tsxapp/knowledge/documents/[id]/[id]/page.tsx;新增 recharts 3.8.1 依赖。
  • 后端:新增 engine/schedulers/{registry.py,handlers/}models/scheduled_task.pyinterface/scheduler_api.pydb/migrations/versions/0034_unified_scheduler.py;改造 engine/bootstrap.py(6 个 startup hook 合并为单一 ensure_registry_started())、engine/schedulers/async_scheduler.py(5s 心跳 + cron 触发器)。
  • GitHub Actions / 文档:无 CI 配置变更;本 PR 全程依据 AGENTS.md "上下文驱动 + 最小干预 + 复用驱动 + 正交分解" 原则推进。

Next Best Action

  • 在 Staging 环境观察 ≥ 24h 调度任务执行情况(关注 scheduled_tasks_summary 巡检的 failed_ratio 指标),确认无回归后再切 prod;
  • Phase 5 演进路径:扩展 agent_inspection 调用真实 Faculties 完成多 Agent 协作巡检(需开通 LLM 凭证 + 预算管控);Dashboard 增加 cross-tenant 视图、SLA 告警、Anomaly Detection。

🤖 Generated with Claude Code

新增统一调度框架:scheduled_tasks + task_executions 双表 + ScheduledTaskRegistry
单例 + 6 个 handler(skill_invoke / pipeline_watchdog / session_title_inspect /
cache_warm / pgvector_check / agent_inspection)。AsyncScheduler 升级为 5s 心跳
+ interval/cron/oneshot 三类触发判定,保留旧 register() API 兼容性。bootstrap.py
6 个 startup hook 合并为单一 ensure_registry_started() 入口;migration 0034 把
现存 skill_schedules 行回填为 scheduled_tasks 记录(保留旧表与 FK 兼容现有
/skills/{id}/schedules/* API)。Feature flag NEGENTROPY_UNIFIED_SCHEDULER_ENABLED
与 NEGENTROPY_SCHEDULER_HEARTBEAT_SECONDS 支持灰度回退。

参考业界经典心跳模式:MindStudio Heartbeat Pattern(调度层 + 共享状态层 + 持久化
层三层架构)、Scheduling Agent Supervisor Pattern(监督 + 心跳健康检测);
PostgreSQL FOR UPDATE SKIP LOCKED 保证多 worker 并发安全。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
提供 Dashboard 所需的 8 个端点:
- GET  /scheduler/kpis           顶部 KPI 卡片(runs/success_rate/avg_latency)
- GET  /scheduler/tasks          任务清单(5 维筛选 + 最近 3 次状态)
- GET  /scheduler/tasks/{id}     单任务详情 + 最近 50 次执行
- GET  /scheduler/executions     执行历史(分页 + 多维筛选)
- GET  /scheduler/stats          按 role/scenario/agent/owner/handler_kind 分组聚合
- POST /scheduler/tasks/{id}/run    手动触发
- POST /scheduler/tasks/{id}/toggle 启停开关
- GET  /scheduler/stream         SSE 实时执行事件 + 5s 心跳保活

性能:stats / kpis 加 10s in-memory TTL cache 防高频请求;executions 端点
联表 ScheduledTask 一次完成 5 维筛选避免 N+1。SSE 通道由 ExecutionBus
asyncio.Queue fan-out 总线驱动,每个 SSE 连接独立队列、满则丢最旧消息。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
路由结构变更(Plan §3 方案 A):
- 根 / → server-side redirect('/studio'),无 hydration mismatch;
- app/(home)/layout.tsx 承载共享 Suspense + HomeNav;
- app/(home)/studio/page.tsx 平移自原 app/page.tsx(聊天界面零行为变化,
  sessionId / CopilotKit Provider / NdjsonHttpAgent 完整保留);
- app/(home)/dashboard/ 路由占位,下个 commit 填充内容。

HomeNav 仿 MemoryNav 风格的 rounded-full 胶囊 Tab。设计取舍:sessionId 仅
Studio 携带,Dashboard URL 干净;在 Tab 之间切换时若有 sessionId 自动保留在
Studio 链接上,避免会话状态丢失。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
Dashboard 主页面与 6 个子组件:
- KpiRow:6 卡片 KPI(tasks/runs/success_rate/running/failed/avg_latency)
- FilterBar:role/scenario/agent/owner/category/window 多维筛选 + SSE 状态徽章
- DimensionCharts:recharts 三图(Role × Success/Failed 堆叠 + Scenario × Runs
  水平条 + Owner × Runs 甜甜圈),暗色模式与 Tailwind 主题适配
- TaskTable:sticky header + 最近 3 次 ◐◐◐ 状态点 + 点击行打开 Drawer
- ExecutionTimeline:执行历史时间线 + SSE 新增条目 800ms 高亮
- TaskDetailDrawer:Run Now / Toggle Enabled + 最近 50 次执行 + payload JSON

Hooks:
- useSchedulerData:并行 fetch 6 端点 + 30s 兜底定时刷新 + pushExecution 增量更新
- useSchedulerStream:EventSource + exponential backoff 重连 + ref 防 stale 闭包

反向代理:app/api/scheduler/[...path]/route.ts catch-all 复用 interface/_proxy
鉴权透传;stream/route.ts 用 ReadableStream 直通 SSE 不走 .text() 缓冲,确保
心跳逐条到达浏览器。

E2E 测试覆盖:根 / 跳到 /studio、Dashboard 显示 KPI/表/时间线/图表、HomeNav
Tab 切换、点击行打开 Drawer + Run Now 操作链路。新增 recharts 3.8.1 依赖。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
实跑 Playwright Dashboard E2E 暴露两处问题:
1. getByRole('button', {name: 'Close'}) 同时命中 backdrop 的 aria-label='Close drawer'
   和真实"Close"按钮 → strict mode violation。改用 exact: true 精确匹配。
2. recharts ResponsiveContainer 在 SSR prerender 阶段(无浏览器视口)测得
   width(-1)/height(-1) 输出告警。给 ChartCard 加 min-h-[14rem] + style.minWidth=1
   保底非负尺寸,浏览器加载后 ResponsiveContainer 会自然重新测量到 100% 宽高。

E2E 现 4/4 通过:根重定向 / Dashboard 展示 / Tab 切换 / Drawer + Run Now。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
引入通用 hook ``useHeartbeatPoll``,承担四项职责(替代散落的裸 setInterval):
1. 默认 5000ms 节拍,与后端 NEGENTROPY_SCHEDULER_HEARTBEAT_SECONDS=5 对齐,让
   前端"自然刷新节奏"与后端"调度心跳节奏"在用户感知层面同步;
2. enabled gate 解耦状态机,避免父组件 useEffect 重复创建/销毁;
3. document.hidden 时自动暂停,回前台 / online 恢复立即触发一次(消除"后台
   标签持续打 API"的旧问题);
4. callback 错误隔离,单次 throw 不停掉节拍。

统一收口四处轮询:
- app/knowledge/pipelines/page.tsx running-state polling(5s)
- features/knowledge/components/DocumentViewDialog.tsx 文档摄取进度
- app/knowledge/documents/[id]/page.tsx markdown 处理进度
- app/(home)/dashboard/_hooks/useSchedulerData.ts SSE 兜底刷新

保留 pipelines 页面的 bootstrap polling(1s 探测窗口,与心跳节奏正交,强行 5s
会拖慢检测窗口)。新增 5 个 hook 单测覆盖 fireImmediately / enabled / 错误
隔离 / unmount 清理。前端 vitest 88 文件 649 测试全过。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
把 Phase 4 最小骨架 agent_inspection handler 升级为生产可用:
1. **ContextPack**:每次 tick 从 task_executions 取最近 10 条 status + 24h tokens
   累计 + payload + consecutive_failures,构造 dataclass 喂给具体巡检逻辑;
2. **Token 预算门控**:调用任何耗 token 的 Faculty 前预检 ``task.token_budget``,
   超额返回 status=ok + skipped 标识,不消耗预算也不报错;token_budget=None
   视为无限制,=0 永不放行;
3. **退避策略**:consecutive_failures >= 3 时按指数退避(60s/120s/240s.../1h
   ceiling + ±10% jitter)写 backoff_until,让 Registry 心跳跳过本任务直到
   窗口结束(AWS Builder's Library 经典做法)。

新增两种 inspection_type:
- ``faculty_deep_check``:扫描每个 Faculty 模块的 LlmAgent/Agent 实例(不构造
  新的、不触发 LLM),暴露 tools 缺失 / 配置漂移等隐性故障;
- ``scheduled_tasks_summary``:巡检调度框架自身的 last_status 分布,failed
  占比 > 50% 时返回系统级告警。

Registry 默认任务追加 scheduled_tasks_summary_demo(每 10min,token_budget=10K)
+ 给 faculty_health demo 任务加 100K token 预算,让 Dashboard 默认即可见生产
特性的运作。

新增 11 个单测(ContextPack / token gate / backoff 单调性 / 上限截断 /
faculty_health pass),engine 套件 560 个测试全过。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
… 冗余注释);

- scheduler_api.py: list_tasks 的 recent_executions 改用 ROW_NUMBER OVER PARTITION BY task_id,避免高频任务挤占低频任务的状态点配额;
- async_scheduler.py: _tick_once 同步置位 job.running,消除短 poll_interval 下下一轮 tick 抢先派发同一 job 的竞态;
- handlers/pipeline_watchdog.py: KG 异常不再被 status=ok 吞掉,KB 成功 + KG 失败时返回 partial failed + 描述性 error,让退避与告警可见;
- registry.py: 清理 _upsert_default_task 冗余三元 + 修正 dispatch 的并发保护注释(lease 仅覆盖 tick,manual 路径不限并发)。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
- app/page.tsx: 根路径重定向接收 searchParams,命中 sessionId 时拼接到 /studio?sessionId=,避免旧书签 /?sessionId=xxx 静默丢失会话;
- (home)/dashboard/_hooks/useSchedulerData.ts: 新增 seenExecRef 去重集合,loadAll 后种入当前已落地执行 id,pushExecution 仅在 execution.id 首次以非 running 状态消费时累加 total_runs / recent,杜绝 SSE 重连重发或冷启动 + SSE 二次计数。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
- app/page.tsx 由仅白名单透传 sessionId 改为透传全部 query
- 用 URLSearchParams 原生编码替代手写 encodeURIComponent,原生支持多值 key
- 修复 E2E `home-chat.spec.ts:725` 第 3 个守卫(外部直达 /?view=archived)
- 根路径不再充当 query 白名单网关,新增 query 维度零修改即可工作

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
- 将 0034_unified_scheduler.py 重命名为 0035_unified_scheduler.py
- revision "0034" → "0035",down_revision "0033" → "0034"
- 链入 origin/feature/1.x.x 已合入的 0034_kg_federated_canonical(PR #554)
- 修复 Backend Unit Tests CI 中 alembic 单 head 自检失败
  (Expected a single Alembic head, got: ['0034', '0034'])

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
@ThreeFish-AI
ThreeFish-AI merged commit 818ef20 into feature/1.x.x May 17, 2026
18 checks passed
@ThreeFish-AI
ThreeFish-AI deleted the ThreeFish-AI/dashboard-heartbeat-scheduler branch May 17, 2026 06:02
ThreeFish-AI added a commit that referenced this pull request Sep 6, 2026
…正 torch 阻断根因;

存量 4 条 Dependabot alert 逐条重新定性,不照单 dismiss——先问「能不能修」。
结论:setuptools 可修(本提交),torch/transformers 三条经实测确认不可修,已在
GitHub 侧 dismiss 为 tolerable_risk。

setuptools #555:由 ignore 改为升级
- 抬 setuptools>=82.0.1 → >=83.0.0(实解 84.0.0),删除 CI 中 PYSEC-2026-3447
  的 ignore 及其 7 行注释(ignore 28→27)
- 依据:① 上一轮隔离验证已证 82↔83 对本项目零功能差异;② negentropy / cognizes
  两 app 早已跑 84.0.0,perceives 停在 82 属不对称;③ pip-audit 实跑对照——保留与
  剔除该 ignore 两种配置匹配数同为 8、均 exit 0,证明它在 84.0.0 下已是死配置
- 修正上一提交「不为不可达低危变更 build 后端版本」的判断:存在零成本修复路径时,
  消因优于记账

torch CVE-2025-3000:注释根因订正(不改依赖)
- 原记「受 marker-pdf/surya/docling-ibm-models/torchvision 四方联动约束」经实测
  不成立——以 constraint-dependencies 解析 torch>=2.13.0 时前三者版本纹丝不动
- 实测真实阻断:torch>=2.13 强制 torchvision 0.25.0→0.29.0(其 0.25.0 硬钉
  torch==2.10.0),而 transformers 受 pillow 冲突钉死 4.57.6,二者在 docling
  engine worker 导入链上不兼容。同一条 parse-pdf:torch 2.10 → Success(757B),
  torch 2.14 → Failed(cannot import name AutoProcessor),连续两次确定性复现
- 据此回退 torch 升级、维持 2.10.0,注释改写为已验证的因果链

验证:uv lock --check 三 app 通过;真实 PDF 走 docling 全链路抽取 Success,与升级前
输出逐字节一致(仅 conversion_time 抖动);pip-audit exit 0(8 ignored);
pytest -m "not slow" 2130 passed,3 个 test_config 失败经基线复跑确认为既存的本机
配置覆盖问题,与本变更无关。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
ThreeFish-AI added a commit that referenced this pull request Sep 6, 2026
沉淀存量 alert 清零过程中的四项实测结论,其中两项推翻既有记载:

- setuptools #555 由 dismiss 改判为升级:记录 pip-audit 双配置对照(保留/剔除
  ignore 匹配数同为 8)作为「该 ignore 已死」的判据,以及「有零成本修复路径时
  消因优于记账」这一对上一轮最小干预判断的修正
- torch #507:旧记「四方联动约束」证伪(constraint 解析下三个 PDF 引擎零位移),
  真实阻断是 torchvision 0.29 与 transformers 4.57.6 在 docling worker 的不兼容
- transformers #678/#499:pillow<11 冲突经 uv lock exit 1 与上游 METADATA 双重证实
- 附带订正:marker-pdf 1.10.2(当前锁定版)本身也声明 Pillow<11,现状已由 override
  压过;准确表述应为「已验证组合换未验证组合」,而非「升 marker 会把 pillow 降级」

两条方法论增补(本轮踩坑所得):
- override-dependencies 会静默压过上游声明,故不能用它验证「某约束是否可行」,
  必须用 constraint-dependencies(冲突即报错)——本轮曾因此得到 transformers 5.x
  「能解析」的假象
- 验证必须走本项目真实调用路径:DocumentConverter 直调在 torch 2.14 下成功,
  只有经 perceives engine worker 才失败,只测上游库会得出完全相反的结论

另记 GitHub 侧收口:dismiss #678/#499/#507,并刷新 #492/#343 两条历史 dismiss 的
陈旧 comment(原载已证伪的 transformers.onnx 口径)。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant