feat(detector): 扩展空字段检测到 L2 event_description / L1 scene_summary

断点续跑判据需要 L2/L1 层的 empty_field 检测。零 LLM 成本。
This commit is contained in:
2026-07-09 00:12:55 -04:00
parent f733c13dd1
commit 8182cb86b1
2 changed files with 75 additions and 1 deletions
+24
View File
@@ -42,8 +42,10 @@ def detect_issues(
检查项: 检查项:
- L3: card 必填字段为空(frame_summary / visible_entities / ongoing_actions / spatial_layout - L3: card 必填字段为空(frame_summary / visible_entities / ongoing_actions / spatial_layout
- L3: frame_path 对应文件不存在(需提供 frames_dir) - L3: frame_path 对应文件不存在(需提供 frames_dir)
- L2: event_description 为空
- L2/L1: children 列表为空 - L2/L1: children 列表为空
- L2: 相邻 clips 时间范围不连续(gap > 1秒) - L2: 相邻 clips 时间范围不连续(gap > 1秒)
- L1: scene_summary 为空
参数: 参数:
index: 待检测的 TreeIndex。 index: 待检测的 TreeIndex。
@@ -55,6 +57,17 @@ def detect_issues(
issues: list[NodeIssue] = [] issues: list[NodeIssue] = []
for l1 in index.roots: for l1 in index.roots:
# L1: scene_summary 不为空
if not l1.card.scene_summary:
issues.append(
NodeIssue(
node_id=l1.id,
level=1,
issue_type="empty_field",
details="L1 节点字段为空: scene_summary",
)
)
# L1: children 不为空 # L1: children 不为空
if not l1.children: if not l1.children:
issues.append( issues.append(
@@ -71,6 +84,17 @@ def detect_issues(
_check_time_gaps(l1.children, issues) _check_time_gaps(l1.children, issues)
for l2 in l1.children: for l2 in l1.children:
# L2: event_description 不为空
if not l2.card.event_description:
issues.append(
NodeIssue(
node_id=l2.id,
level=2,
issue_type="empty_field",
details="L2 节点字段为空: event_description",
)
)
# L2: children 不为空 # L2: children 不为空
if not l2.children: if not l2.children:
issues.append( issues.append(
+51 -1
View File
@@ -14,7 +14,7 @@ from app.tree.index import (
L3Node, L3Node,
TreeIndex, TreeIndex,
) )
from app.tree.repair.detector import detect_issues from app.tree.repair.detector import NodeIssue, detect_issues
if TYPE_CHECKING: if TYPE_CHECKING:
from pathlib import Path from pathlib import Path
@@ -185,3 +185,53 @@ class TestDetectIssues:
index = TreeIndex(metadata=IndexMeta("/t.mp4", "video"), roots=[l1]) index = TreeIndex(metadata=IndexMeta("/t.mp4", "video"), roots=[l1])
issues = detect_issues(index) issues = detect_issues(index)
assert any(i.issue_type == "time_gap" for i in issues) assert any(i.issue_type == "time_gap" for i in issues)
def test_detects_l2_empty_event_description(self) -> None:
"""L2 event_description 为空应报 empty_field。"""
l3 = L3Node(
id="l1_0_l2_0_l3_0",
card=L3Card("正常帧", ["实体"], ["动作"], [], "居中", {}),
timestamp=1.0,
)
l2 = L2Node(
id="l1_0_l2_0",
card=L2Card("", [], [], [], [], "", None),
time_range=(0.0, 10.0),
children=[l3],
)
l1 = L1Node(
id="l1_0",
card=L1Card("正常场景", "", [], [], [], [], ""),
time_range=(0.0, 10.0),
children=[l2],
)
index = TreeIndex(metadata=IndexMeta("/t.mp4", "video"), roots=[l1])
issues = detect_issues(index)
l2_empties = [i for i in issues if i.level == 2 and i.issue_type == "empty_field"]
assert len(l2_empties) == 1
assert "event_description" in l2_empties[0].details
def test_detects_l1_empty_scene_summary(self) -> None:
"""L1 scene_summary 为空应报 empty_field。"""
l3 = L3Node(
id="l1_0_l2_0_l3_0",
card=L3Card("正常帧", ["实体"], ["动作"], [], "居中", {}),
timestamp=1.0,
)
l2 = L2Node(
id="l1_0_l2_0",
card=L2Card("正常事件", [], [], [], [], "", None),
time_range=(0.0, 10.0),
children=[l3],
)
l1 = L1Node(
id="l1_0",
card=L1Card("", "", [], [], [], [], ""),
time_range=(0.0, 10.0),
children=[l2],
)
index = TreeIndex(metadata=IndexMeta("/t.mp4", "video"), roots=[l1])
issues = detect_issues(index)
l1_empties = [i for i in issues if i.level == 1 and i.issue_type == "empty_field"]
assert len(l1_empties) == 1
assert "scene_summary" in l1_empties[0].details