# KST × qwen-eye/qwen-ear 融合评估与规划报告

日期：2026-08-17 · 作者：GLM（analysis-only，未改任何代码）
范围：`/data/workspace/kais-shot-timeline`（KST）+ `/data/workspace/kais-hermes-skills/plugins/kais_aigc/`（引擎）+ `/data/workspace/kais-aigc-platform`（KAP 路由侧）

---

## 0. 执行摘要（先给结论）

1. **KST 三条网络路由中两条今日不存在**：`shot-analysis` 已在 KAP 主干落地（`src/routes/production/shot-analysis/index.ts`），但 `character-reid` 与 `audio-analysis` 在 `src/routes/production/` 目录列表中缺席 → step 6/7 今日实际恒走 graceful-degrade。历史真实产出（ep01 的 prompts.json 93 镜全填充）**不是路由产的**，是 GLM-4.6V 云端 + 人工 agent 工作流（`prompt_parts/` 分片 + `prompts/merge_prompts.py`）产的；re-id 由未入库的 `analysis/local_reid.py`（GLM bbox + 颜色直方图聚类）产。`output/*/route_cache/` 目录在全部 3 个 episode 中都不存在——路由链路从未端到端跑通过一支片子。
2. **qwen-eye 是 `scene`/`subject` 空缺的精确补位者**：`call_shot_analysis.py:160-161` 明文注释 `subject ← "" 永不伪造（Phase 7 re-id 处理身份）`、`scene ← "" 永不伪造（未来 Qwen-VL 扩展）`。qwen-eye 本身就是 Qwen VL，且已有现成输入（`frames_5fps/` 1553 张全帧 + `frames.json` 首尾帧 base64）。
3. **qwen-ear 可补 step 7 的完全空洞**（路由不存在、audio_semantic.json 三支片子全部缺席），Demucs stems 恰好已是 16-bit PCM WAV，只需 ffmpeg 按镜切片即可喂 `listen()`。
4. **GPU 结论（实测今日）**：GPU1（3090, 24GB）当前 free=16.8GB（music3 占 6.7GB + 零头），**q3（需 ≥14GB free）现在就能起；q4（20.5GB）/ qwen-ear（21.5GB）需先释放 music3**。KST 的 Demucs/Whisper 与引擎分阶段串行即可，无需真正并发。
5. **推荐首个落地 Phase = Phase 1（qwen-eye 填 scene/subject facets）**：ROI 最高（补文档化空缺 + 零云端成本 + 复用现有帧资产 + q3 可与现状共存），改动面最小（新增 1 个模块 + 1 个管线 flag）。

---

## A. 评估（逐点证据）

### A1. KST 每镜分析数据流与外部依赖清单

#### 管线编排（`run_pipeline.py`）

9 步流水线（docstring `run_pipeline.py:2-65`）。本地 GPU 步骤与网络步骤如下：

| Step | 函数 | 产出 | 外部依赖 | 超时/缓存 |
|---|---|---|---|---|
| 3 Demucs | `step_separate` (`run_pipeline.py:136-152`) | `stems/` + `audio_analysis.json` | `python -m demucs` 子进程 (`audio/separate_stems.py:55-65`)，`--device` 默认 **cuda:1**（`run_pipeline.py:671-673`） | 文件存在即缓存 (`:143`) |
| 4 Whisper | `step_transcribe` (`run_pipeline.py:155-171`) | `transcript.json` | faster-whisper→openai-whisper 双后端 (`audio/transcribe.py:109-123`)；OOM 回退链 cuda:N→另一卡→cpu (`audio/transcribe.py:118-121`) | 文件存在即缓存 |
| 5 运镜语义 | `step_semantic` (`run_pipeline.py:174-243`) | `prompts.json` + `route_cache/shot_analysis/shot_XXX.json` | **网络①**：KAP `POST /api/v1/production/shot-analysis`，默认 `http://127.0.0.1:8000`（`run_pipeline.py:632-635`） | read 960s（`:636-637`，> 路由侧 900s）；mtime+video-stamp 双层缓存（`:205-243`） |
| 6 跨镜 re-id | `step_reid` (`run_pipeline.py:246-324`) | `registry.draft.json` + HITL HTML | **网络②**：KAP `POST /api/v1/production/character-reid`（`:641-644`，注释自认 DEFERRED） | read 960s；per-video 缓存 `route_cache/character_reid/video_<vch>.json`（`call_reid.py:316-318`） |
| 7 音频语义 | `step_audio_semantic` (`run_pipeline.py:327-414`) | `audio_semantic.json` | **网络③**：KAP `POST /api/production/audio-analysis`（**无 /v1/**，`run_pipeline.py:650-654`；`call_audio_analysis.py:121` ROUTE_PATH 单一真源） | read 900s；poisoned-cache 失效（`call_audio_analysis.py:697-712`） |

#### 融合点 1：`analysis/call_shot_analysis.py`（424 行）

- 请求体：`{video, shots, shot_id_range:[N,N], semantic:true, subject:false, grid_n:20, fps:24}`（`:338-346`，semantic MUST 为 true 否则映射失效 `:16`）。
- **facet 映射（CONTEXT D-XX LOCKED）** `compose_facets` (`:125-162`)：
  - `camera ← COMPOSE(shot_scale, camera_primitive, camera_speed, geometry.primitive)`（`:155-156`）
  - `action ← subject_motion`、`lighting ← lighting`、`style ← lens_feel`（`:157-159`）
  - **`subject ← ""`（`:160`）、`scene ← ""`（`:161`）—— 两个 facet 故意留空**，注释点名「未来 Qwen-VL 扩展」。
- 缓存：4-tuple `_cache_key`（vch + route_name + route_version + 文件名隐含 shot_id），`route_cache/shot_analysis/shot_XXX.json`（`:308-359`）。
- graceful-degrade：preflight 失败一次即短路（`:220-243`），空 facets 仍 schema 合法 + warnings sidecar（`:49-52`）。

**KAP 路由侧实况**（`/data/workspace/kais-aigc-platform`）：
- `shot-analysis` **已在主干**（`src/routes/production/shot-analysis/index.ts`）。THIN wrapper：zod 校验 → docker cp 视频 → gold-team v6 task fan-out → 轮询聚合（`:1-22` docstring）。`PER_SHOT_DEADLINE_MS = 900_000`（index.ts:38）。
- 真正干活的是 `scripts/shot-analysis/shot_analysis_driver.py`：ComfyUI 工作流 = `VHS_LoadVideoPath` + `ShotGeometryLK`（几何层）+ **`AILab_QwenVL_Advanced`（语义层，`Qwen3-VL-8B-Instruct` 8-bit）**（`:74-89`）。SEMANTIC_PROMPT 产出 `shot_scale/camera_primitive/camera_speed/subject_motion/lens_feel/lighting` 六字段（`:29-40`）——**没有 scene、没有 subject 字段**。
- 依赖链重量级：KAP(:8000) + gold-team(:8002) + ComfyUI 容器(:8188) + AILab 节点 + 8B 模型权重，全都要活着。

#### 融合点 2：`analysis/call_audio_analysis.py`（909 行）

- 请求体含 `stems_dir`（Demucs 目录直接传给路由做 SER/MIR 输入，`:485-486`）+ `models`（SenseVoice/WhisperX large-v3/MERT，`:501-504`）。
- **路由今日不存在**：`ls src/routes/production/` 无 `audio-analysis` 目录。stub_mode 语义已实现（data.shots=[] → degrade，`:367-374`）。
- 三模态投影 `normalize_audio_semantic`（`:163-316`）：dialogue(text/spk_id/emotion/emotion_confidence/events/words) + sfx(events/description)；reproduction 由 producer 侧 `audio/gen_audio_prompts.py:compose_reproduction` 组装（`call_audio_analysis.py:772-788`）。
- **实况**：三支片子的 `audio_semantic.json` 全部不存在（`ls output/*/audio_semantic.json` 为空）→ CONTRACT-05 byte-identical-absent 恒触发。

#### 融合点 3：re-id 与 turnaround

- `call_reid.py`（457 行）：路由 DEFERRED（`:6-9` docstring 自述「路由今日不存在」）；`ls src/routes/production/` 确认无 `character-reid`。→ step 6 今日恒产空 clusters。
- **`analysis/local_reid.py`（308 行，git 未跟踪 `??`）已是实际替代**：
  - Stage 1：GLM-4.6V 云端 bbox 检测（`detect_characters_frame` `:47-104`，`https://open.bigmodel.cn/api/anthropic/v1/messages`，API key 从 `~/.hermes/config.yaml` providers.zhipu-anthropic 读取 `:38-44`）。
  - Stage 2：**HSV+RGB 512 维颜色直方图 embedding**（`:109-129`，无模型下载）+ AgglomerativeClustering cosine τ=0.35（`:221-227`）。
  - 产出 `registry.draft.json`（method=local_reid_glm46v_hist）+ `characters/` crops。ep01 实跑：7 clusters。
- **`analysis/gen_turnaround_v2.py`（437 行，最近一次 commit `7cb11fb`）**：bbox 扩 20% crop → GLM-4.6V 分析角色外观 → dreamina img2image 多参考帧生成 turnaround（`:213-278`）。v1（`gen_turnaround.py`）用小 crop 单参考，v2 改全帧多参考。
- ep01 `registry.draft.json` 实况：7 clusters，代表 crop + `turnarounds_v2/` 产物存在。

#### 融合点 4：`prompts/merge_prompts.py` + 编排

- `merge_prompts.py`（79 行）：合并 `prompt_parts/part_*.json` → prompts.json，7 字段必填检查（`:16`）。**这是历史 GLM 人工工作流的遗留入口**（ep01 的 prompt_parts 有 part_001-012… 分片，每片 12 镜——典型的 agent 分批产出）。
- `prompts/attach_refs.py`（232 行）：confirmed registry → character_refs/prop_refs + `_recompose` prompt_text 模板（`:158-190`）。
- ep01 prompts.json 实况：93 镜，facets/scene/subject 全填充、无 character_refs（attach_refs 未跑或当时无 confirmed registry）——填充来自 GLM 云端而非路由。

#### 数据流总图（每镜分析的真实现状）

```
video.mp4
 ├─ ffmpeg 5fps 抽帧 ──→ frames_5fps/*.jpg (ep01: 1553 张, 1920×1080)   ← 未被任何分析步骤消费（仅 local_reid/turnaround 用）
 ├─ ffmpeg 首尾帧 ────→ frames.json (base64, 480px)                      ← 仅 timeline HTML 缩略图用
 ├─ PySceneDetect ───→ shots.json (93 镜)
 ├─ Demucs ─────────→ stems/htdemucs/{vocals,drums,bass,other}.wav (各54MB) + audio_analysis.json
 ├─ Whisper ────────→ transcript.json
 ├─ [网络①shot-analysis: 路由存在但从未跑通] ─→ prompts.json camera/action/lighting/style
 ├─ [网络②character-reid: 路由不存在] ─→ registry.draft.json   ← 实际由 local_reid.py (GLM云端) 产
 ├─ [网络③audio-analysis: 路由不存在] ─→ audio_semantic.json   ← 实际恒缺席
 └─ GLM-4.6V 云端人工工作流 (prompt_parts + merge_prompts) ─→ 历史全量 facets   ← 历史真相
```

### A2. qwen-eye / qwen-ear 引擎契约提取

#### qwen-eye（`plugins/kais_aigc/qwen_eye.py`，308 行）

**API 契约**（OpenAI 兼容，无 KAP envelope 包装——直连 llama-server）：
- 端点：`POST http://127.0.0.1:8125/v1/chat/completions`（`qwen_eye.py:56`），health `GET :8125/health`（`:57`）。
- 请求体：`{model:"qwen3.8", temperature:0.1, max_tokens:N, chat_template_kwargs:{enable_thinking:false}, messages}`（`:238-244`）。
- 图像输入：`{"type":"image_url","image_url":{"url":"data:image/jpeg;base64,..."}}`（`:280-282`）。
- 响应取值：`resp.choices[0].message.content`（`:251-252`）。
- **调用方法**：`observe(image_paths)` 多图观察（`:268-284`）、`observe_single(img, question)` 单图问答（`:286-300`）、`ask(text)` 纯文本判定（`:304-308`）。

**生命周期**（`:151-230`）：
1. `ensure_ready()` 先 health 探测——已健康返回 `(True, False)` 不拥有。
2. Guard 1：GPU1 free VRAM `< 14000MiB`（`_VRAM_MIN_FREE_MIB`，`:67`，GPU index=1 `:68`）→ 直接 `(False, False)` 不启动（nvidia-smi 缺失 fail-open）。
3. KAP allocate `POST :10588/api/production/llm/allocate` `{variantId:"q3", caller}`（`:55,179-186`）→ 失败 fallback `bash /opt/qwen-llm/kap-llm.sh start q3`（`:58,193-198`）。
4. health 轮询 ≤120s（`LLM_START_TIMEOUT_S` `:60`）；Guard 2：`/opt/qwen-llm/server.log` 增量命中 `failed to load model|model loading error` → 立即放弃（`:66-71,128-139,205-211`）。
5. `stop_if_owned()` 只停自己拉起的（`:215-230`）——**预存在 lease 绝不动**。

**两段式判定协议**（skill `video-qc-qwen-vl-judge/SKILL.md:17-20`）：
1. 观察层（VL）：N 帧 → **每图一条 user 消息** → 自然语言观察。
2. 判定层（文本）：观察文本 + SPEC → 维度级 JSON（正则 `\{.*\}` 提取）。

**硬约束 / failure modes**（`qwen_eye.py:25-32` + SKILL.md「三大硬约束」）：
1. **llama.cpp 多图丢弃 bug**：单条 user 消息含 N 图只算 ceil(N/2) 张 → 每图单独一条 user 消息（连续多条 user 合法）。
2. **thinking 吃 token**：不关 enable_thinking 会吃光 max_tokens → 空输出（PARSE_FAIL 根因）。
3. **渲染脉冲 OOM**：q3 需 13.4GB，ComfyUI 渲染期加载必 OOM → 双 guard fail-fast（0.044s 短路 vs 原 120s 盲等）。
4. 单实例 thread-unsafe by design（`:78-80`）；`_call_llm` 失败重试 1 次（`:246-259`）；`LLM_CALL_TIMEOUT_S=3600`（CPU 回退 ~5.5min/调用，`:61`）。
5. 速度：GPU q3 ~25-30s/镜（两段式 5 帧）；任意视频 5 帧模式 ~27s/视频（SKILL.md:47,54-55）。单图 observe_single 延迟**待验证**（估 5-15s）。
6. 质量证据：负对照 8/8 拦截、重复判定 7/7 一致、空间定位赢云端 GLM vision（像素统计裁决，SKILL.md「交叉验证数据点」）。

#### qwen-ear（`plugins/kais_aigc/qwen_ear.py`，225 行）

**API 契约**：
- 端点：`POST :8126/v1/chat/completions`（`qwen_ear.py:47`），health `:8126/health`（`:48`）。
- **音频输入**：`{"type":"input_audio","input_audio":{"data":"<base64>","format":"wav"|"mp3"}}`（`:210-214`；llama.cpp mtmd server-common.cpp:1015 硬约束）。
- `listen(audio_path, question)`（`:196-217`）：后缀守卫只认 `.wav/.mp3`（`:203-205`）+ **大小守卫 ≤8MB**（`:59-60,206-209`）。
- `ask(text)` 文本层（`:221-225`）。
- `EAR_CALL_TIMEOUT_S=300`（~10s 音频推理几秒，`:53`）。

**生命周期**：
- `ensure_ready()`：health → `bash /opt/qwen-ear/kap-ear.sh start`（`:49,129-134`）→ 轮询 ≤300s（**NTFS 冷读 18.6GB 预留**，`:52`）+ server.log 死因检测（`:141-145`）。VRAM 预检在 kap-ear.sh 内部。
- `kap-ear.sh`：门禁 `NEED_MB=21500`（19.9GB 模型 + 余量），280s deadline 条件等待渲染窗口，wait_ready 300s，attempt>1 先 pkill 再 launch（kap-ear.sh start 分支实测源码）。
- `stop_if_owned()` 同语义（`:149-161`）。

**与 qwen-eye 的 GPU 互斥**（`qwen_ear.py:13-16`）：q3=13.4GB / q4=17.9GB / ear=19.9GB，3090 单卡放不下两个。消费方约定调用序：**先 stop qwen-eye（若拥有）→ 用 ear → 再还**。

**failure modes**：ComfyUI 渲染期 VRAM 不足 → kap-ear.sh 条件等待 280s fail-fast；端口 8126 冲突 → wait_ready 检测 server died；音频只吃 wav/mp3；>8MB 拒绝。

#### 今日实测状态（只读探测，未拉起任何引擎）

- GPU0 = 3060 Ti（8GB，桌面+杂项），GPU1 = 3090（24GB）：used 7.3GB / **free 16.8GB**。
- GPU1 占用者：`/opt/music3-env/bin/python` 6.7GB + python3.13 0.44GB + Xorg 0.12GB。
- `:8125/health` 与 `:8126/health` 均**无响应（引擎未启动）**——本次评估遵守约束未拉起，测后无需 stop。
- KAP `:10588` **在线**（llm/status 返回 code:200），scheduler 显示全部服务 stopped、无 pendingAllocation、无锁。
- **推论**：现状下 q3 立即可起（16.8 ≥ 14.0）；q4（20.5）与 qwen-ear（21.5）**需先释放 music3（6.7GB）**，属「不 kill 非自己拉起的进程」边界——需 Kai 决策或等其自然退出。

### A3. 逐融合点评估

#### FP-1：qwen-eye 替代/补充 shot-analysis 路由（scene/subject facets）

| 维度 | KAP 路由（现状） | qwen-eye 本地 |
|---|---|---|
| 可用性 | 路由在主干，但依赖 KAP+gold-team+ComfyUI+AILab 全链活着；**KST 侧从未跑通一支片子**（route_cache 空） | 引擎脚本+权重已部署（/opt/qwen-llm + /mnt/storage/models）；单依赖 llama-server |
| 模型 | Qwen3-VL-**8B**-Instruct 8-bit（driver :44） | Qwen3.8-**27B** + mmproj-F16（更大更强） |
| 覆盖 facets | camera/action/lighting/style（**无 scene/subject**，driver SEMANTIC_PROMPT :29-40 无此二字段） | scene/subject 天然可产（这正是 VL 观察层输出）+ 也可产 camera 等（但几何层 LK 特征路由侧更强） |
| 延迟 | 单镜 <120s 典型（index.ts:35-36），预算 900s/镜 | 单图 observe_single ~5-15s（待验证）；93 镜 × 2 facets ≈ 15-30min |
| 成本 | 0 元（本地 ComfyUI），但运维重 | 0 元，运维轻（kap-llm.sh 一条命令） |
| 输入 | 需 docker cp 视频 + 容器内解码 | **复用已有 frames_5fps 全帧 jpg / frames.json**，零额外 IO |

**结论：互补而非替代**。路由的几何层（ShotGeometryLK 光流）是 qwen-eye 没有的硬信号；qwen-eye 补文档化留空的 scene（环境/场景描述）与 subject（主体外观描述）。camera/action/lighting/style 维持路由优先。route 挂时以路由为准，scene/subject 恒走本地 VL——两者正交，无冲突。

#### FP-2：qwen-ear 补 audio-analysis 路由（step 7）

- 路由不存在 + audio_semantic.json 恒缺席 → **qwen-ear 是唯一能填的选项**（不引入云端）。
- 输入完美匹配：Demucs stems 已是 16-bit PCM WAV（`audio/separate_stems.py` wave 读取惯例）；按镜 ffmpeg 切 vocals.wav 段（93 镜，平均 3-5s/镜 ≈ 300-800KB@44.1k stereo）——远小于 8MB 上限，无需降采样。
- 能产出：dialogue.emotion/emotion_confidence（SER）、events（BGM/笑声/掌声…SenseVoice 8-event 同类）、sfx.description（NL）——与 `normalize_audio_semantic` 投影字段几乎一一对应（`call_audio_analysis.py:209-297`）。
- 不能产出：words[]（WhisperX 词级对齐——但 KST 已有 faster-whisper 转录，词级本就 experimental）、MERT embedding（MUS-04 乐器识别 v1.3 deferred，schema 本就禁 emit）。
- 代价：**GPU 近独占**（21.5GB 门禁）。music3 占 6.7GB 的今天起不来。这决定它是「渲染窗口任务」而非随手任务。

#### FP-3：qwen-eye 替代 local_reid.py 的 GLM 云端调用

- 现状 Stage 1 每帧一次 GLM-4.6V 云端调用（40 帧采样，`local_reid.py:151-166`），花钱+外网+30s 超时重试。
- qwen-eye `observe_single(frame, "列出所有角色+bbox JSON")` 可本地免费替代；**但 bbox 定位精度 27B q3 量化 vs GLM-4.6V 云端未验证——待验证**（skill 数据证明 qwen-eye 空间定位判断强于云端 vision，但那是判断题不是回归坐标题）。
- embedding 层不动（HSV 直方图已验证对动画角色有效，`local_reid.py:110-114`）。
- 额外收益：qwen-eye 观察文本可直接填 cluster 的 appearance 字段（现在 GLM 也产，但省了云端钱）。

#### FP-4：turnaround 分析层

- `gen_turnaround_v2.py` 的 `analyze_character_crops`（`:74-141`）是 GLM 云端调用，产出 species/body_color/…/turnaround_prompt。
- qwen-eye 可替代该分析调用（多 crop = 多条 user 消息，observe 协议现成）；dreamina img2image 生成层保持不变（本地无生图能力）。
- ROI 中等：turnaround 是离线增强产物，不在 pipeline 主链。

### A4. GPU 调度编排方案（3090 单卡跑一部片子）

**现状算术**（今日实测）：24GB 总量，music3 常驻 6.7GB → free 16.8GB。
- q3 = 13.4GB（+1.6GB 脉冲余量 → 门禁 14GB free）：**可与 music3 共存**（16.8-13.4=3.4GB 余）。
- q4 = 17.9GB（门禁 20.5GB）、qwen-ear = 19.9GB（门禁 21.5GB）：**均不可共存**，需 music3 退出或 Kai 批准清理。

**KST 端到端跑片显存编排（建议的阶段化序列）**：

```
[阶段 A] 检测+分离+转录（GPU1）
  PySceneDetect(CPU) → Demucs(~3-4GB) → Whisper large-v3 fp16(~5GB)
  ▲ 三者串行，峰值 ~5GB，与 music3 共存无压力
  ▼ 完成后进程退出，VRAM 自然释放（Demucs/Whisper 是子进程，非常驻）

[阶段 B] qwen-eye q3 窗口（GPU1, 13.4GB）
  ensure_ready(预检≥14GB) → scene/subject facets + re-id bbox 检测
  → stop_if_owned() 释放
  ▲ q3 与 music3(6.7GB) 可共存：16.8-13.4=3.4GB 余量
  ▲ 渲染脉冲风险：若 ComfyUI 同时渲染（今日 comfyui-primary=stopped，无风险）
    → Guard 1 fail-fast 0.044s 短路，step 降级空 facets（不阻塞管线）

[阶段 C]（可选）qwen-ear 独占窗口（GPU1, 19.9GB，门禁 21.5GB）
  前置条件：music3 已退出 或 3090 基本清空
  kap-ear.sh 自带 280s 条件等待——渲染窗口自动等
  → per-shot wav 切片 + listen() 填 audio_semantic
  → stop_if_owned() 释放
  ▲ 若窗口拿不到：graceful-degrade 保持 CONTRACT-05 byte-identical-absent（现行为）

[阶段 D] HTML/export（CPU）
```

**关键设计点**：
1. **每阶段引擎生命周期封闭在 stage 子进程内**（KST 惯例：stage = 独立 CLI 脚本，`CLAUDE.md` Pattern）——ensure_ready 与 stop_if_owned 同进程配对，崩溃也不会泄漏 13.4GB 占用。
2. **顺序陷阱**（skill 明示）：VRAM 预检必须在 health 之后——健康的 q3 自占 13.4GB 会让 free≈10GB，先预检会误杀健康 server（SKILL.md「三大硬约束」第 3 条）。引擎客户端已内置正确顺序（`qwen_eye.py:163-174`）。
3. **与 Demucs/Whisper 的互斥天然成立**：KST 步骤串行（step 3/4 完成后才到 step 5/6/7），不会同时抢卡。唯一外生竞争者是 ComfyUI 渲染脉冲与 music3——前者由 fail-fast 兜底，后者需人工协调。
4. **渲染窗口等待**：kap-ear.sh 已内置「条件等待 VRAM 窗口 280s」；KST 侧无需重复实现，只需容忍 ensure_ready 失败并降级。

### A5. 风险清单

| # | 风险 | 证据/出处 | 缓解 |
|---|---|---|---|
| R1 | **KAP 500 系 envelope 坑**：KAP error() 走 HTTP 200 + `{code:500}` 或反代 HTML 伪 200 | `call_shot_analysis.py:182-200` 已有 CR-02 三层守卫（非 JSON body / envelope 非 dict / code≠200） | 引擎直连 llama-server **无 KAP envelope**（OpenAI 原生格式），此坑不适用于 qwen-eye/ear 调用本身；仅 allocate 走 KAP（失败 fallback 脚本，`qwen_eye.py:189-198`） |
| R2 | **:8125/:8126 端口生命周期**：引擎起后不 stop 会常驻占 13.4/19.9GB，阻塞后续 ComfyUI 渲染 | `qwen_eye.py:215-230` stop_if_owned；skill「用完即停」 | KST stage 子进程内配对 ensure/stop；SIGKILL 防泄漏（atexit/finally 双保险） |
| R3 | **NTFS 冷读慢启动**：q4 108-180s、ear 18.6GB 需 300s；健康超时 < 加载时间 = 假阴性误判 | skill qwen38-27b「坑 4」；`qwen_ear.py:52` | 用引擎自带的分档超时（q3=120s/ear=300s），勿自设更短轮询；失败先查 server.log 有无 cudaMalloc（无 error 只有超时 = 假阴性） |
| R4 | **音频只吃 wav/mp3 + 8MB 上限** | `qwen_ear.py:203-209` | per-shot ffmpeg 切 vocals.wav（`-ss start -to end -ac 1 -ar 16000` 可再瘦身）；flac/ogg 必须先转 |
| R5 | **多图丢弃 bug**：单消息 N 图只算 ceil(N/2) | `qwen_eye.py:25-28`；SKILL.md 硬约束 1 | 严格每图一条 user 消息（observe 已实现）；验证法：对比 usage.input_tokens |
| R6 | **thinking 吃 token** | `qwen_eye.py:30-32` | 恒传 `enable_thinking:false`（客户端已内置）；max_tokens ≥800 |
| R7 | **跨仓库依赖**：KST import plugins/kais_aigc 会引入路径耦合（kais-hermes-skills 检出位置不定） | KST 是独立仓库（CLAUDE.md「松耦合」约束） | **复制引擎客户端**（~150 行）入 KST `analysis/engine_clients/`，pin 引擎版本常量；见 B 规划 Phase 1 |
| R8 | **thread-unsafe**：引擎单实例设计 | `qwen_eye.py:78-80` | KST stage 本就单线程串行，天然满足；勿在 stage 内并发调用 |
| R9 | **music3 常驻 6.7GB**：q4/ear 起不来的现实阻塞 | 今日 nvidia-smi 实测 | q3 路线不受影响；ear 路线需 Kai 协调（不 kill 非自己拉起的进程规程，tts-gpu-vram-contention skill） |
| R10 | **bbox 回归精度未验证**（FP-3）：27B q3 量化做坐标回归 vs GLM-4.6V | 无对照数据 | Phase 2 前置 A/B 验收（同帧 20 张对比 IoU） |
| R11 | **引擎 token 限制**：q3 ctx=16K 单 slot（kap-llm.sh tier_ctx）；长观察文本+判定可能顶满 | kap-llm.sh 源码 tier_ctx | 单镜观察控制帧数（≤3 帧/call）；超长走分段 |
| R12 | **KAP allocate 与直接 start 的端口竞态** | SKILL.md Pitfalls「server 端口竞态」 | 引擎客户端已按 allocate→fallback 顺序；KST 侧不自作主张直接 bash start |

---

## B. 规划（分阶段实施方案）

### 总原则

1. **不碰核心算法**（CLAUDE.md 约束）：分镜检测/转录/分离是 validated 基线，本规划只改「分析层的外部依赖来源」。
2. **镜像 call_shot_analysis.py 的成熟模式**：graceful-degrade + route_cache（改名为 `local_engine_cache`）+ warnings sidecar + schema 写前校验 + 原子写。四个 call_*.py 已把这套模式打磨过 6 轮 review（CR-01/CR-02/WR-01…WR-05），照抄结构。
3. **引擎客户端复制不 import**（R7）：KST 独立仓库不依赖 kais-hermes-skills 检出路径。复制 `QwenEye`/`QwenEar` 的精简版（去 logging、保留 lifecycle+call 协议）入 `analysis/engine_clients/qwen_eye_client.py`，头部注释标注上游 commit 与复制日期，约定「引擎行为变更时手工同步」。
4. **KMC 一致性约定**（P04/P07/P11c 同款）：caller identity（`kst:<stage>`）、单实例串行、`stop_if_owned`、VRAM 预检后置于 health、每图一条 user 消息、`enable_thinking:false`、`ENGINE_NAME` 写进 payload/warnings 便于追溯。

### Phase 0：现状锚定 + 冒烟基线（半天，零代码风险）

**目标**：把「现有链路跑 1 镜」的耗时/质量记录成可对比的基线，后续每个 Phase 用同一把尺子验收。

步骤：
1. **资产盘点文档**：ep01（93 镜）现有 prompts.json（GLM 云端历史产物）抽 10 镜做金标准样本表（scene/subject/camera 人工评分 1-5）。
2. **路由侧冒烟**（可选，KAP 需在线）：对 shot 1 发一次真实 `POST :8000/api/v1/production/shot-analysis`（body 按 `call_shot_analysis.py:338-346`），记录：链路是否活着、单镜耗时、返回 facets 质量。若 gold-team/ComfyUI 不在 → 记「路由不可用」，本身即基线数据。
3. **qwen-eye 单图延迟冒烟**：拉起 q3（当前 free 16.8GB 满足门禁），对 ep01 shot 1 的首/尾/中 3 帧跑 `observe_single` × 3 + 1 次 scene 提问，记录每调用延迟与输出质量；**测完立即 `kap-llm.sh stop` 并 nvidia-smi 验证 VRAM 归还**。
4. **GLM 云端成本锚点**：从 `~/.hermes/config.yaml` 账单或 local_reid 实跑记录（40 帧调用的 token 数）记一笔，作 qwen-eye 替代的省钱基数。

**验收标准**：
- `docs/baseline-local-engines.md`（或 milestones/ 下）含：路由可达性结论、单镜耗时表（路由 vs qwen-eye vs GLM 云端）、10 镜金标准样本表、GPU 基线快照（free/占用者清单）。
- 引擎测后 VRAM 归零差值 < 100MiB（stop_if_owned 有效性证明）。

### Phase 1：qwen-eye 填 scene/subject facets（首个落地，ROI 最高）

**目标**：`prompts.json` 的 scene facet 从恒空 → 本地 VL 填充；subject 提供可选填充（re-id confirmed 前 subject 仍可给「外观描述」而非身份——注意与 `call_shot_analysis.py:160`「Phase 7 re-id 处理身份」的边界：subject 填**外观描述文本**不填角色 ID，不越权）。

改动面：
1. **新增 `analysis/engine_clients/qwen_eye_client.py`**（~150 行）：复制 QwenEye 精简版 + `CALLER = "kst:vision_facets"`。
2. **新增 `analysis/local_vision_facets.py`**（仿 `call_shot_analysis.py` 骨架，~300 行）：
   - 输入：`--shots`、`--frames-dir`（frames_5fps）或 `--frames`（frames.json 解码）、`--work-dir`、`--output prompts.json`。
   - 逻辑：读现有 prompts.json（step 5 产物，可能空 facets）→ 对 scene 为空的镜：取该镜时窗内 1-3 帧（首/中/尾）→ `observe_single(frame, SCENE_PROMPT)` → 正则提 JSON/短文本 → 填 scene。subject 同理（可选 flag）。
   - **local_engine_cache**：`route_cache/local_vision/shot_XXX.json`，`_cache_key = (video_content_hash, engine_name, engine_version, prompt_version)`——镜像 4-tuple 惯例（`call_shot_analysis.py:327-334` WR-04）。
   - graceful-degrade：ensure_ready 失败 → 全部 scene 保持 ""，warnings sidecar 记 `[vision] engine unavailable (VRAM/startup)`，exit 0，schema 仍合法。
   - 引擎生命周期：main() 内 try/finally 包 ensure_ready…stop_if_owned。
3. **`run_pipeline.py` 接线**：step 5 之后插 pre-step（仿 attach_refs 的「无编号 pre-step」先例，`run_pipeline.py:436-442`——避免 step counter bump 破坏 grep 惯例）；新 flag `--local-vision`（默认 on，`--no-local-vision` 关）。
4. **schema**：prompts.schema.json 的 scene/subject 本就是 type:string 无 minLength（`call_shot_analysis.py:50-51` 依赖此事实）——**零 schema 改动**。

**验收标准**：
- ep01 重跑（--skip-detect/--skip-separate/--skip-transcribe 复用缓存）：≥80% 镜 scene 非空；失败镜（若引擎挂）有显式 warning 而非静默。
- 抽 10 镜 scene 人工评分 ≥ 基线金标准表的平均分 -0.5（不劣于 GLM 历史产物的可接受偏差）。
- prompts.json 过 Draft202012Validator（fails loud 惯例）；timeline.html 渲染 scene chip 正常。
- 跑完全程 nvidia-smi GPU1 free 恢复基线（引擎无泄漏）。
- 单镜端到端耗时记录（目标：scene 填充 < 30s/镜，93 镜 < 50min）。

### Phase 2：local_reid.py 转正 + qwen-eye 替代 GLM 云端 bbox

**目标**：把未入库的 local_reid.py 收编进管线（git add + schema 对齐 registry.schema.json），Stage 1 的 GLM 云端调用换 qwen-eye。

改动面：
1. `local_reid.py` 入库 `analysis/`，修 docstring/CLI 对齐项目惯例（Chinese help、--work-dir 等）；输出对齐 `registry.schema.json#clusters[]`（现在的 shape 有 name/species/appearance 等额外字段——registry.schema additionalProperties:false 会拒，需投影到 schema-allowed 字段或 bump schema，**需一次 schema 决策**）。
2. bbox 检测层抽成 `detect_characters_frame(engine, img)` 双后端：qwen-eye（`observe_single(frame, BBOX_PROMPT)`）优先，GLM 云端 fallback（保 local_reid 现行为为降级路）。caller=`kst:reid`。
3. `run_pipeline.py:step_reid` 增加本地后端选择：`--reid-backend {route,local,auto}`（auto = local 优先，route fallback——**路由今日不存在，auto 实际恒 local**）。
4. A/B 验收（R10）：同 20 帧 GLM vs qwen-eye bbox IoU 对比表；qwen-eye 平均 IoU ≥ GLM-0.1 才切默认，否则 GLM 保持默认、qwen-eye 做省钱选项。

**验收标准**：
- ep01 重跑 cluster 数与现 registry.draft.json（7 clusters）一致 ±1；HITL registry_review.html 照常生成。
- 40 帧检测零云端调用（默认路径）；VRAM 归还验证。
- registry.draft.json 过 schema 校验；`registry/apply_edits.py` 下游不受破坏。

### Phase 3：qwen-ear 填 audio_semantic（渲染窗口任务）

**目标**：step 7 从恒 degrade → 本地音频语义（dialogue.emotion/events + sfx），CONTRACT-05 语义保留（零数据时仍不写文件）。

改动面：
1. `analysis/engine_clients/qwen_ear_client.py`（复制精简版，caller=`kst:audio_semantic`）。
2. **新增 `analysis/local_audio_semantic.py`**：
   - per-shot 切片：ffmpeg `-ss <start> -to <end> -i stems/.../vocals.wav -ac 1 -ar 16000 tmp_shot.wav`（mono 16k 5s ≈ 160KB，远低于 8MB）；sfx 层可加切 `other.wav`+`drums.wav` 混合段（待验证是否比纯 vocals 更利于事件检测）。
   - `listen(wav, EAR_PROMPT)`：产出 `{emotion, emotion_confidence, events[], sfx_description}` JSON。
   - 投影进 `normalize_audio_semantic` 同款 shape（直接 import 其投影函数或复制）→ 走既有 `compose_reproduction` 组 reproduction 层（**零 composer 改动**）。
   - 缓存 `route_cache/local_ear/shot_XXX.json` 4-tuple；graceful-degrade = 引擎起不来（VRAM 窗口拿不到）→ warnings + 不写 audio_semantic.json（byte-identical-absent 保留）。
3. 编排位置：step 7 内部先试路由（不存在恒 fail）→ fallback local_ear；或直接 `--audio-backend {route,local,auto}`。
4. **GPU 前置检查**：stage 开始时 nvidia-smi free ≥21.5GB 才尝试（kap-ear.sh 自带等待，KST 侧给 `--ear-wait` 上限 flag，默认 0 = 不等立即降级——避免管线被 280s 卡住）。

**验收标准**：
- 前置条件满足时跑 ep01：≥60% 含对白镜获得非空 dialogue.emotion；audio_semantic.json 过 schema；timeline.html 音乐/音效 chip 渲染。
- 抽 10 镜人工听音频对照 emotion 标签，准确率 ≥60%（SenseVoice 云端同类任务的合理下限参照）。
- VRAM 窗口不足时：管线不阻塞超 `--ear-wait` 秒，降级路径 warnings 明确。
- 引擎 stop 后 VRAM 归还。

### Phase 4（可选增强）：turnaround 分析层本地化 + 路由退役决策

1. `gen_turnaround_v2.py` 的 `analyze_character_crops` 增加 qwen-eye 后端（多 crop = 多条 user 消息调 observe）。
2. **KAP 路由 fallback vs 退役建议**（Phase 1-3 落地后的终局）：
   - **shot-analysis 路由：保留为 fallback，不退役**。理由：(a) 几何层（ShotGeometryLK 光流）是 qwen-eye 没有的硬信号，camera facet 质量依赖它；(b) 路由已在主干维护，沉没成本已付；(c) KST 主 viewport 的 camera/action/lighting/style 继续路由优先，本地 VL 只补 scene/subject——正交互补。降级序建议：`route → local qwen-eye → 空 facets`。
   - **character-reid / audio-analysis 路由：不建设，等效退役**。理由：路由从未存在（DEFERRED/stub），本地引擎已能覆盖 KST 需求的 80%+；若未来 KMC 侧也需要，再以 KAP 服务形式把 qwen-eye/ear 包一层（llm allocate 路由已带 engine 字段先例，`llm/index.ts:40-45`）。
   - 一致性：KST 与 KMC 消费同一引擎客户端约定（caller identity / thread-unsafe / stop_if_owned），未来若引擎升级（q4 双 slot、更大 ctx），两仓库同步改客户端版本常量即可。

### 里程碑与依赖关系

```
Phase 0 (半天) ──→ Phase 1 (1-2天) ──→ Phase 2 (1-2天) ──→ Phase 4 (可选)
                          │
                          └──→ Phase 3 (2-3天, 需 GPU 窗口协调)
```
Phase 1/2 共用 q3（可与 music3 共存，随时可做）；Phase 3 需 3090 近独占（music3 退出窗口），建议安排在渲染间隙。

---

## 附录：证据文件索引

| 主题 | 文件:行号 |
|---|---|
| step_semantic 路由调用与缓存 | run_pipeline.py:174-243, 632-637 |
| scene/subject 留空注释 | call_shot_analysis.py:160-161 |
| 4-tuple cache key | call_shot_analysis.py:327-334 |
| audio 路由 NO /v1/ mount | call_audio_analysis.py:118-121 |
| CONTRACT-05 byte-identical-absent | call_audio_analysis.py:811-829 |
| re-id 路由 DEFERRED | call_reid.py:6-9; run_pipeline.py:641-644 |
| GLM 云端 bbox（re-id 现行） | local_reid.py:47-104, 38-44 |
| 颜色直方图 embedding | local_reid.py:109-129 |
| turnaround v2 GLM+dreamina | gen_turnaround_v2.py:74-141, 213-278 |
| qwen-eye API/生命周期/守卫 | qwen_eye.py:55-71, 151-230, 234-260, 268-308 |
| qwen-ear API/音频约束 | qwen_ear.py:47-60, 118-147, 196-225 |
| kap-ear.sh 门禁 21500MB/280s | /opt/qwen-ear/kap-ear.sh start 分支 |
| KAP shot-analysis 路由 900s | kais-aigc-platform/src/routes/production/shot-analysis/index.ts:38 |
| 路由语义层 Qwen3-VL-8B | kais-aigc-platform/scripts/shot-analysis/shot_analysis_driver.py:29-40, 74-89 |
| 路由目录实况（reid/audio 缺席） | ls kais-aigc-platform/src/routes/production/ |
| 引擎技能（两段式/硬约束/验证数据） | ~/.hermes/skills/kais-movie-pipeline/video-qc-qwen-vl-judge/SKILL.md |
| 引擎架构 skill | ~/.hermes/skills/qwen-eye-vision-engine/SKILL.md |
| qwen38 调度坑 1-4 | ~/.hermes/skills/infrastructure/qwen38-27b-kap-ondemand-scheduling/SKILL.md |
| qwen-ear 部署 VRAM 排查 | ~/.hermes/skills/kais-audio/tts-gpu-vram-contention/SKILL.md:140-177 |
| 引擎单测 | plugins/kais_aigc/tests/test_qwen_eye.py (16), test_qwen_ear.py (10) |
| 今日 GPU 实测 | nvidia-smi: GPU1 used 7329 / free 16796 MiB; :8125/:8126 down; :10588 up |
