From 8c383354c59d2ee5b39ded3fad0460257fbdc042 Mon Sep 17 00:00:00 2001 From: iomgaa Date: Thu, 9 Jul 2026 00:54:27 -0400 Subject: [PATCH] =?UTF-8?q?fix(detector):=20=E7=A7=BB=E9=99=A4=20ongoing?= =?UTF-8?q?=5Factions/visible=5Fentities=20=E7=A9=BA=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=20+=20=E6=96=B0=E5=A2=9E=20repair=20sh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 抽检确认这两个字段为空是合法内容状态(静物/黑帧/模糊帧), VLM 重修也修不好,保留会导致断点续跑死循环。 L3 空字段检测缩减为 frame_summary + spatial_layout。 --- app/tree/repair/detector.py | 9 +++------ scripts/repair_trees.sh | 27 +++++++++++++++++++++++++++ tests/unit/test_repair_detector.py | 16 ++++++++-------- 3 files changed, 38 insertions(+), 14 deletions(-) create mode 100755 scripts/repair_trees.sh diff --git a/app/tree/repair/detector.py b/app/tree/repair/detector.py index aba8bb6..169cfae 100644 --- a/app/tree/repair/detector.py +++ b/app/tree/repair/detector.py @@ -40,7 +40,8 @@ def detect_issues( """扫描树,返回所有问题节点列表。 检查项: - - L3: card 必填字段为空(frame_summary / visible_entities / ongoing_actions / spatial_layout) + - L3: card 必填字段为空(frame_summary / spatial_layout) + - 注: visible_entities / ongoing_actions 为空是合法状态(静物/黑帧),不纳入检测 - L3: frame_path 对应文件不存在(需提供 frames_dir) - L2: event_description 为空 - L2/L1: children 列表为空 @@ -108,14 +109,10 @@ def detect_issues( continue for l3 in l2.children: - # L3: 各必填字段不为空 + # L3: 核心必填字段不为空(visible_entities/ongoing_actions 为空是合法状态) empty_fields: list[str] = [] if not l3.card.frame_summary: empty_fields.append("frame_summary") - if not l3.card.visible_entities: - empty_fields.append("visible_entities") - if not l3.card.ongoing_actions: - empty_fields.append("ongoing_actions") if not l3.card.spatial_layout: empty_fields.append("spatial_layout") if empty_fields: diff --git a/scripts/repair_trees.sh b/scripts/repair_trees.sh new file mode 100755 index 0000000..2f20f37 --- /dev/null +++ b/scripts/repair_trees.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# 树修复管线:检测 + VLM 重生成 + 校验 + Q&A 补全 +# +# 用法: +# bash scripts/repair_trees.sh # 默认 16 并发续跑 +# bash scripts/repair_trees.sh --dry-run # 仅检测不修复 +# bash scripts/repair_trees.sh --reaggregate-all # 强制全量重聚合 +# CONCURRENCY=8 bash scripts/repair_trees.sh # 自定义并发数 +# +# 日志输出: +# stderr → 终端实时显示 +# logs/repair_trees.log → 完整日志(自动 rotation 50MB) +# logs/repair_telemetry.db → LLM/VLM 调用遥测 +# logs/repair_progress.json → 断点续跑进度 + +set -euo pipefail + +CONCURRENCY="${CONCURRENCY:-16}" + +conda activate Video-Tree-TRM + +python tools/repair_trees.py \ + --videos-dir store/videos \ + --srt-dir data/Video-MME/subtitle \ + --questions-dir store/questions/benchmarks/Video-MME \ + --concurrency "$CONCURRENCY" \ + "$@" diff --git a/tests/unit/test_repair_detector.py b/tests/unit/test_repair_detector.py index 1780255..e81b347 100644 --- a/tests/unit/test_repair_detector.py +++ b/tests/unit/test_repair_detector.py @@ -119,25 +119,25 @@ class TestDetectIssues: issues = detect_issues(index) assert any(i.issue_type == "no_children" and i.level == 1 for i in issues) - def test_empty_visible_entities(self) -> None: - """visible_entities 为空也触发 empty_field。""" + def test_empty_visible_entities_not_flagged(self) -> None: + """visible_entities 为空是合法状态(静物/黑帧),不触发 empty_field。""" card = L3Card("正常描述", [], ["动作"], [], "居中", {}) l3 = L3Node(id="l1_0_l2_0_l3_0", card=card, timestamp=1.0) l2 = L2Node(id="l1_0_l2_0", card=_card_l2(), time_range=(0.0, 10.0), children=[l3]) l1 = L1Node(id="l1_0", card=_card_l1(), time_range=(0.0, 10.0), children=[l2]) index = TreeIndex(metadata=IndexMeta("/t.mp4", "video"), roots=[l1]) issues = detect_issues(index) - assert any(i.issue_type == "empty_field" and "visible_entities" in i.details for i in issues) + assert not any(i.issue_type == "empty_field" for i in issues) - def test_empty_ongoing_actions(self) -> None: - """ongoing_actions 为空也触发 empty_field。""" + def test_empty_ongoing_actions_not_flagged(self) -> None: + """ongoing_actions 为空是合法状态(静物/黑帧),不触发 empty_field。""" card = L3Card("正常描述", ["实体"], [], [], "居中", {}) l3 = L3Node(id="l1_0_l2_0_l3_0", card=card, timestamp=1.0) l2 = L2Node(id="l1_0_l2_0", card=_card_l2(), time_range=(0.0, 10.0), children=[l3]) l1 = L1Node(id="l1_0", card=_card_l1(), time_range=(0.0, 10.0), children=[l2]) index = TreeIndex(metadata=IndexMeta("/t.mp4", "video"), roots=[l1]) issues = detect_issues(index) - assert any(i.issue_type == "empty_field" and "ongoing_actions" in i.details for i in issues) + assert not any(i.issue_type == "empty_field" for i in issues) def test_empty_spatial_layout(self) -> None: """spatial_layout 为空也触发 empty_field。""" @@ -150,7 +150,7 @@ class TestDetectIssues: assert any(i.issue_type == "empty_field" and "spatial_layout" in i.details for i in issues) def test_multiple_empty_fields_single_issue(self) -> None: - """多个字段同时为空只产生一个 issue,details 列出所有空字段。""" + """多个必填字段同时为空只产生一个 issue,details 列出所有空字段。""" card = L3Card("", [], [], [], "", {}) l3 = L3Node(id="l1_0_l2_0_l3_0", card=card, timestamp=1.0) l2 = L2Node(id="l1_0_l2_0", card=_card_l2(), time_range=(0.0, 10.0), children=[l3]) @@ -159,7 +159,7 @@ class TestDetectIssues: issues = [i for i in detect_issues(index) if i.issue_type == "empty_field"] assert len(issues) == 1 assert "frame_summary" in issues[0].details - assert "visible_entities" in issues[0].details + assert "spatial_layout" in issues[0].details def test_time_gap(self) -> None: l3_a = L3Node(id="l1_0_l2_0_l3_0", card=_card_l3(), timestamp=1.0)