test(13-03): add content completeness protection tests
- 添加 TestSentenceBoundaryProtection 测试类(COMP-01) - test_sentence_boundary_protection: 验证分段边界在标点符号后 - test_sentence_boundary_in_prompt: 验证 Prompt 包含句边界指令 - 添加 TestArgumentCompleteness 测试类(COMP-02) - test_argument_completeness: 验证论点结构完整性 - test_argument_completeness_in_prompt: 验证 Prompt 包含论点完整性指令 - 添加 TestParallelStructurePreservation 测试类(COMP-03) - test_parallel_structure_preserved: 验证并列结构不被错误拆分 - test_no_special_handling_for_parallel_in_prompt: 验证遵守用户决策(不特别指定并列结构处理) 所有测试通过(29/29),semantic_engine.py 已包含所需的内容完整性保护指令: - 第 337 行:保持句子完整性,列举所有中文句末标点(。?!……) - 第 340 行:保持论点完整性,不在前提-结论结构中间截断 - 不包含并列结构特殊处理(符合用户决策) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -513,3 +513,217 @@ class TestPydanticValidationExtended:
|
||||
|
||||
result = SegmentationResult(**data)
|
||||
assert result.segments[0].title is None
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Test Content Completeness Protection (COMP-01, COMP-02, COMP-03)
|
||||
# =============================================================================
|
||||
|
||||
class TestSentenceBoundaryProtection:
|
||||
"""测试句边界保护功能 - COMP-01: 不在句子中间截断"""
|
||||
|
||||
def test_sentence_boundary_protection(self):
|
||||
"""Test: 分段边界应在标点符号后,不在句子中间截断"""
|
||||
from kb_workflow.srt.parser import Segment
|
||||
|
||||
# Sample segments with clear sentence boundaries
|
||||
segments = [
|
||||
Segment(id=1, start_time=0.0, end_time=3.0, text="机器学习是人工智能的一个分支。"),
|
||||
Segment(id=2, start_time=3.0, end_time=6.0, text="它使用算法来解析数据、从中学习。"),
|
||||
Segment(id=3, start_time=6.0, end_time=9.0, text="机器学习的目标是让计算机能够自动学习!"),
|
||||
]
|
||||
|
||||
# Mock AI client
|
||||
mock_client = Mock()
|
||||
mock_client.api_key = "test-key"
|
||||
mock_response = """```json
|
||||
{
|
||||
"video_type": "知识类",
|
||||
"segments": [
|
||||
{
|
||||
"title": "机器学习概述",
|
||||
"content": "机器学习是人工智能的一个分支。它使用算法来解析数据、从中学习。",
|
||||
"start_index": 0,
|
||||
"end_index": 35
|
||||
},
|
||||
{
|
||||
"title": null,
|
||||
"content": "机器学习的目标是让计算机能够自动学习!",
|
||||
"start_index": 36,
|
||||
"end_index": 55
|
||||
}
|
||||
],
|
||||
"actual_segment_count": 2
|
||||
}
|
||||
```"""
|
||||
mock_client.generate.return_value = mock_response
|
||||
|
||||
# Create engine and segment
|
||||
engine = SemanticSegmentationEngine(ai_client=mock_client)
|
||||
metadata = {"title": "机器学习介绍", "duration": 9.0}
|
||||
paragraphs = engine.segment(segments, metadata)
|
||||
|
||||
# Verify segmentation occurred
|
||||
assert len(paragraphs) == 2
|
||||
|
||||
# Verify sentence boundaries - each paragraph should end with punctuation
|
||||
import re
|
||||
sentence_endings = r'[。?!……]$'
|
||||
|
||||
for para in paragraphs:
|
||||
para_stripped = para.strip()
|
||||
assert re.search(sentence_endings, para_stripped), \
|
||||
f"段落应以标点符号结尾: {para[:50]}..."
|
||||
|
||||
def test_sentence_boundary_in_prompt(self):
|
||||
"""Test: SEGMENTATION_PROMPT 包含句边界保护指令"""
|
||||
engine = SemanticSegmentationEngine()
|
||||
|
||||
# Get the prompt template
|
||||
content = "测试内容"
|
||||
metadata = {"title": "测试", "duration": 10.0, "video_type": "知识类"}
|
||||
prompt = engine._build_prompt(content, metadata)
|
||||
|
||||
# Verify prompt contains sentence boundary instructions
|
||||
assert "句号" in prompt or "。" in prompt
|
||||
assert "问号" in prompt or "?" in prompt
|
||||
assert "感叹号" in prompt or "!" in prompt
|
||||
assert "省略号" in prompt or "……" in prompt
|
||||
assert "不要在句子中间截断" in prompt or "保持句子完整性" in prompt
|
||||
|
||||
|
||||
class TestArgumentCompleteness:
|
||||
"""测试论点完整性保护 - COMP-02: 不破坏论点完整性"""
|
||||
|
||||
def test_argument_completeness(self):
|
||||
"""Test: 论点结构应保持完整,前提-结论不被拆分"""
|
||||
from kb_workflow.srt.parser import Segment
|
||||
|
||||
# Sample segments with premise-conclusion structures
|
||||
segments = [
|
||||
Segment(id=1, start_time=0.0, end_time=4.0, text="首先,让我们理解什么是深度学习。"),
|
||||
Segment(id=2, start_time=4.0, end_time=8.0, text="深度学习是机器学习的一个子集。"),
|
||||
Segment(id=3, start_time=8.0, end_time=12.0, text="因此,深度学习能够处理更复杂的数据模式。"),
|
||||
]
|
||||
|
||||
# Mock AI client
|
||||
mock_client = Mock()
|
||||
mock_client.api_key = "test-key"
|
||||
mock_response = """```json
|
||||
{
|
||||
"video_type": "知识类",
|
||||
"segments": [
|
||||
{
|
||||
"title": "深度学习简介",
|
||||
"content": "首先,让我们理解什么是深度学习。深度学习是机器学习的一个子集。因此,深度学习能够处理更复杂的数据模式。",
|
||||
"start_index": 0,
|
||||
"end_index": 50
|
||||
}
|
||||
],
|
||||
"actual_segment_count": 1
|
||||
}
|
||||
```"""
|
||||
mock_client.generate.return_value = mock_response
|
||||
|
||||
# Create engine and segment
|
||||
engine = SemanticSegmentationEngine(ai_client=mock_client)
|
||||
metadata = {"title": "深度学习介绍", "duration": 12.0}
|
||||
paragraphs = engine.segment(segments, metadata)
|
||||
|
||||
# Verify segmentation occurred
|
||||
assert len(paragraphs) == 1
|
||||
|
||||
# Verify argument completeness - premise and conclusion together
|
||||
first_para = paragraphs[0]
|
||||
assert "首先" in first_para
|
||||
assert "因此" in first_para
|
||||
|
||||
def test_argument_completeness_in_prompt(self):
|
||||
"""Test: SEGMENTATION_PROMPT 包含论点完整性指令"""
|
||||
engine = SemanticSegmentationEngine()
|
||||
|
||||
# Get the prompt template
|
||||
content = "测试内容"
|
||||
metadata = {"title": "测试", "duration": 10.0, "video_type": "知识类"}
|
||||
prompt = engine._build_prompt(content, metadata)
|
||||
|
||||
# Verify prompt contains argument completeness instructions
|
||||
assert "论点完整性" in prompt or "保持论点" in prompt
|
||||
assert "前提" in prompt or "结论" in prompt or "不要截断" in prompt
|
||||
|
||||
|
||||
class TestParallelStructurePreservation:
|
||||
"""测试并列结构保护 - COMP-03: 并列结构不被错误拆分"""
|
||||
|
||||
def test_parallel_structure_preserved(self):
|
||||
"""Test: 并列结构(首先/其次/最后)应保持完整"""
|
||||
from kb_workflow.srt.parser import Segment
|
||||
|
||||
# Sample segments with parallel structures
|
||||
segments = [
|
||||
Segment(id=1, start_time=0.0, end_time=4.0, text="首先,我们需要收集数据。"),
|
||||
Segment(id=2, start_time=4.0, end_time=8.0, text="其次,我们清洗和预处理数据。"),
|
||||
Segment(id=3, start_time=8.0, end_time=12.0, text="最后,我们训练模型并进行评估。"),
|
||||
]
|
||||
|
||||
# Mock AI client
|
||||
mock_client = Mock()
|
||||
mock_client.api_key = "test-key"
|
||||
mock_response = """```json
|
||||
{
|
||||
"video_type": "教程类",
|
||||
"segments": [
|
||||
{
|
||||
"title": "数据收集",
|
||||
"content": "首先,我们需要收集数据。",
|
||||
"start_index": 0,
|
||||
"end_index": 14
|
||||
},
|
||||
{
|
||||
"title": "数据预处理",
|
||||
"content": "其次,我们清洗和预处理数据。",
|
||||
"start_index": 15,
|
||||
"end_index": 30
|
||||
},
|
||||
{
|
||||
"title": "模型训练",
|
||||
"content": "最后,我们训练模型并进行评估。",
|
||||
"start_index": 31,
|
||||
"end_index": 45
|
||||
}
|
||||
],
|
||||
"actual_segment_count": 3
|
||||
}
|
||||
```"""
|
||||
mock_client.generate.return_value = mock_response
|
||||
|
||||
# Create engine and segment
|
||||
engine = SemanticSegmentationEngine(ai_client=mock_client)
|
||||
metadata = {"title": "数据处理流程", "duration": 12.0}
|
||||
paragraphs = engine.segment(segments, metadata)
|
||||
|
||||
# Verify segmentation occurred
|
||||
assert len(paragraphs) == 3
|
||||
|
||||
# Verify each parallel point is complete in its segment
|
||||
assert "首先" in paragraphs[0]
|
||||
assert "其次" in paragraphs[1]
|
||||
assert "最后" in paragraphs[2]
|
||||
|
||||
def test_no_special_handling_for_parallel_in_prompt(self):
|
||||
"""Test: SEGMENTATION_PROMPT 不包含并列结构特殊处理
|
||||
|
||||
遵守用户决策:让 LLM 自然判断
|
||||
"""
|
||||
engine = SemanticSegmentationEngine()
|
||||
|
||||
# Get the prompt template
|
||||
content = "测试内容"
|
||||
metadata = {"title": "测试", "duration": 10.0, "video_type": "知识类"}
|
||||
prompt = engine._build_prompt(content, metadata)
|
||||
|
||||
# Verify prompt does NOT have specific instructions for parallel structures
|
||||
# (per user decision to let LLM naturally judge)
|
||||
# The prompt should not have special handling like "保持并列结构完整"
|
||||
assert "保持并列结构完整" not in prompt
|
||||
assert "并列结构不拆分" not in prompt
|
||||
|
||||
Reference in New Issue
Block a user