Files
Video-Tree-TRM5/research-wiki/designs/2026-07-11-batch-tree-build-design.md
T

94 lines
4.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 卡号除外)
### 两层并发参数
```mermaid
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 审查修正后):
1. **公开异步入口**:现有 `build()` 是同步壳(内部 `asyncio.run(self._build_async(...))`),在异步批量编排里调用会触发"事件循环嵌套"运行时错误。将 `_build_async` 提升为公开 `build_async()` 供批量工具调用;同步 `build()` 保留原样(内部改为调 `build_async`),单视频调用方零影响。
2. **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. 验证
1. 单测:Semaphore 注入后单视频行为不变(默认自建路径)
2. 集成:3-4 个短视频小批量构建,验证跨视频并行、progress 跳过、全局 API 信号量生效(遥测里在途调用数 ≤ api_concurrency
3. 中断-恢复测试:构建中 Ctrl+C 后重跑,已完成视频跳过、未完成视频从段级断点续跑
## 7. 被否方案
- **单一视频级 Semaphorerepair_trees 原样照搬)**:建树内部并发大,总 API 并发 = 视频数 × 内部并发,不可控
- **仅共享全局 API Semaphore、视频数不限**:任意多视频同时提帧会压垮磁盘 IO/CPU