4.6 KiB
4.6 KiB
Spec-2:建树批量并行入口
- 日期: 2026-07-11
- 状态: 已批准(用户确认两层参数推荐方案)
- 系列: Spec-1/2/3 三件套之一,见 [2026-07-11-agent-runtime-fixes-design.md]、[2026-07-11-question-gen-v2-design.md]
1. 问题
TRM5 只有单视频建树(app/tree/video_builder.py,内部 Semaphore(16) 限 VLM/LLM 调用)与修复/迁移工具,没有多视频批量构建入口——批量建树只能视频间串行,非 API 阶段(ffmpeg 帧提取、图像编码、IO)与 API 阶段无法跨视频重叠,太慢。
2. 并发语义调研结论(项目惯例)
| 位置 | 并行单元 | 惯例 |
|---|---|---|
app/harness/inference.py |
题目 | 一个 asyncio.Semaphore + gather,任务级 |
tools/repair_trees.py |
视频 | 视频级 Semaphore + gather + progress.json + 熔断阈值随并发缩放 |
app/tree/video_builder.py |
API 调用 | Semaphore 作为参数在协程链中显式传递 |
建树是唯一任务内部本身有大并发的场景 → 视频级与 API 级信号量必须分开,否则 16×16=256 API 并发打爆端点与熔断器。
3. 设计
入口形态(遵循项目结构规范)
tools/build_trees.py:独立工具(不被其他模块 import),复刻repair_trees.py的编排模式scripts/build_trees.sh:自包含实验记录,写死参数、零参数复现(GPU 卡号除外)
两层并发参数
graph LR
subgraph tools/build_trees.py
V[视频级 Semaphore<br/>video_concurrency=16] --> B1[VideoTreeBuilder 视频A]
V --> B2[VideoTreeBuilder 视频B]
V --> B3[...]
end
B1 --> API[全局共享 Semaphore<br/>api_concurrency=16]
B2 --> API
B3 --> API
API --> E[VLM/LLM 端点]
| 参数 | 默认 | 语义 |
|---|---|---|
--video-concurrency |
16 | 同时在建的视频数;吞吐提升来自非 API 阶段跨视频重叠 |
--api-concurrency |
16 | 全局在途 VLM/LLM 调用上限,跨所有视频共享一个 Semaphore 实例——端点压力与今天单视频建树完全一致 |
熔断阈值按 repair_trees 惯例缩放:max(cfg_threshold, api_concurrency * 2)。
配置归属(D7 规则,Codex 审查补充):
| 参数 | 归属 | 理由 |
|---|---|---|
api_concurrency |
工程配置 .env(TREE_BUILD_API_CONCURRENCY=16) |
端点保护参数,少变、随部署环境定 |
video_concurrency |
sh 脚本写死(默认 16)+ CLI 单次覆盖 | 单机吞吐参数,随硬件观测调整,不进科研 YAML(不会被实验扫动) |
builder 改动(唯一的存量修改)
两处(Codex 审查修正后):
- 公开异步入口:现有
build()是同步壳(内部asyncio.run(self._build_async(...))),在异步批量编排里调用会触发"事件循环嵌套"运行时错误。将_build_async提升为公开build_async()供批量工具调用;同步build()保留原样(内部改为调build_async),单视频调用方零影响。 - Semaphore 注入:内部 Semaphore 改为构造器可选参数(不传则自建,行为零变化)。builder 内部协程链本就显式传递 Semaphore,改动面极小。
断点续跑
- 视频级:
progress.json(复用 repair_trees 的save_progress模式);tree.json 存在且完整性校验通过的视频自动跳过 - 视频内:现有段级恢复(核心算法 #3)不动
输入输出
- 输入:
--videos-dir(视频文件 + 可选同名 SRT) - 输出:
store/videos/<video_id>/tree.json;帧持久化沿用现有 cache 机制
4. 风险与观测
- 16 路并行 ffmpeg/cv2 解码可能压满 CPU/磁盘 → 实现时输出速率日志(视频/分钟,复刻 repair_trees),观测后再调 video_concurrency
- 日志遵循"禁止缓存、立即输出"(CLAUDE.md §2.1)
5. 不做什么(YAGNI)
- 不做分布式/多机;不做动态并发自适应
- 不改单视频建树算法(核心算法 #1/#2/#3 保真,仅信号量注入)
6. 验证
- 单测:Semaphore 注入后单视频行为不变(默认自建路径)
- 集成:3-4 个短视频小批量构建,验证跨视频并行、progress 跳过、全局 API 信号量生效(遥测里在途调用数 ≤ api_concurrency)
- 中断-恢复测试:构建中 Ctrl+C 后重跑,已完成视频跳过、未完成视频从段级断点续跑
7. 被否方案
- 单一视频级 Semaphore(repair_trees 原样照搬):建树内部并发大,总 API 并发 = 视频数 × 内部并发,不可控
- 仅共享全局 API Semaphore、视频数不限:任意多视频同时提帧会压垮磁盘 IO/CPU