概要 / TL;DR
修复线上 team-server 在冷启动/高负载下**反复卡死重启**的问题,并把架构改成"重计算与前台隔离",在**万级 skill / 十万级轨迹**下也撑得住。
Fixes the team-server **repeated freeze-and-restart** under cold-start / heavy load, and re-architects heavy compute to run isolated from the web process — holds up at **10k+ skills / 60k+ trajectories**.
> ⚠️ 升级后请盯一下第一波冷启动:探针不再超时、`py-spy` 里不再有 `find_atom_entry_in_any_skill` 热栈即为正常。
根因 / Root cause
现象:`health` / `watcher` / `web` 三个探针**同时** 5s 超时,进程被反复重启。`py-spy` 定位:watcher 热循环 `find_atom_entry_in_any_skill` 对每个未打 `clustered` 标记的 atom **全量重读所有 skill 的 `.candidates.yml`**(O(atoms×skills)),烧满一个核、握死 GIL,饿死跑异步探针的事件循环。embedding 后端其实很快,**并非瓶颈**。
py-spy showed the watcher hot loop re-reading every skill's `.candidates.yml` per un-clustered atom (O(atoms×skills)), pegging one core and starving the async event loop via the GIL. Embedding was not the bottleneck.
主要改动 / Highlights
- **热循环惰性缓存**:每轮 sweep 惰性构建一次 `atom_id→skill` 索引,仅在遇到未打标 atom 时才建;稳态全 `clustered` **零扫盘**(1 万 skill 不再每轮白扫)。O(atoms×skills) → O(skills)。
- **watcher / 画像拆为定时短命子进程**:web 进程只留轻量调度线程 spawn 子进程,重计算全在独立解释器进程,**GIL 与事件循环隔离**;算完即退,不留常驻线程。
- **前端海量 skill 分页**:`/api/v1/dashboard/skills` 加 `limit`/`offset` 分页 + `name` 定向查,前端翻页,1 万 skill **不炸锅**。
- **看板随主 app 挂载**(主进程轻量后无需独立进程);观测端点 `/watcher/status`、`/stats` 改读子进程落盘状态文件。
- **画像同步默认放慢**到 600s(10 分钟);新增 `server.sweep_timeout` / `profile_refresh_interval` / `profile_refresh_timeout` 配置。
升级 / Upgrade
bash
pip install -U xskill → 0.6.21
重启:xskill serve --server(自动挂看板 + 起 sweep/profile 内部调度器)
测试 / Tests
全套 **1573 单测通过**。唯一未过的是 e2e `test_canary_flip_promote_and_install_new_version` —— 系环境问题(该 e2e 跑机器上**已装的 0.6.16 二进制**而非源码,而环境 `websockets 16` 移除了 `WebSocketServerProtocol` 打挂了 0.6.16),与本次改动无关。