diff --git a/.planning/phases/13-llm/13-RESEARCH.md b/.planning/phases/13-llm/13-RESEARCH.md new file mode 100644 index 0000000..097b848 --- /dev/null +++ b/.planning/phases/13-llm/13-RESEARCH.md @@ -0,0 +1,628 @@ +# Phase 13: 核心 LLM 分段功能 - Research + +**Researched:** 2026-03-22 +**Domain:** LLM-based semantic text segmentation, Pydantic v2 validation, Chinese NLP +**Confidence:** HIGH + +## Summary + +Phase 13 需要实现基于 LLM 的智能语义分段,替代现有的基于时间+字符长度的简单规则分段。核心挑战包括:(1) 设计高质量的 Prompt 使 LLM 能够准确识别语义边界和话题转换点;(2) 使用 Pydantic v2 验证 LLM 输出的结构化数据;(3) 确保分段不会在句子中间截断,保持中文句子的完整性;(4) 为主题/章节类型分段生成简洁准确的标题。 + +**Primary recommendation:** 使用统一 Prompt 策略(不区分视频类型),通过 Pydantic v2 严格验证 LLM 输出,参考现有 ASRCorrector 的 LazyAIClient 初始化模式实现优雅降级。 + +## User Constraints (from CONTEXT.md) + +### Locked Decisions +- **数量控制**: 采用建议式数量引导 — Prompt 中给出目标分段数量范围(5-8 个),但允许 LLM 根据语义完整性灵活调整,不强制执行 +- **视频类型策略**: 无类型区分 — 使用统一的分段 Prompt,不针对不同视频类型(知识类/故事类/教程类)设计不同策略 +- **目标分段数**: 5-8 个分段 — 适合大多数 10-30 分钟视频的粒度范围 +- **长度约束**: 参考性长度建议 — Prompt 中可建议每个分段大致字符数(如 1000-2000 字),但允许突破以保持语义完整性 +- **句边界保护**: 纯 Prompt 约束 — 在 Prompt 中明确指令「不要在句子中间截断」,依赖 LLM 理解,不进行代码后处理 +- **论点完整性**: 一般性指令 — Prompt 中给出「保持论点完整性」的一般性指令,不提供具体示例或标记词识别 +- **并列结构**: 不特别指定 — 对「首先/其次/最后」等并列结构不给出特殊处理,让 LLM 根据整体语义自然判断 +- **中文句边界**: Prompt 中强调 — 在 Prompt 中说明「分段边界应放在自然停顿处(句号、问号、感叹号等标点符号后)」 +- **标题范围**: LLM 自主判断 — 让 LLM 自行判断哪些分段需要标题(主题/章节类),哪些不需要(观点/对话类),不在 Prompt 中硬性规定 +- **标题风格**: 简洁描述型 — 标题应简短(5-15 字),如「机器学习概述」「梯度下降原理」,类似目录结构 +- **标题格式**: H2 (##) — 在 Markdown 输出中,标题使用二级标题(##),与现有「## 内容」格式保持一致 +- **标题缺失处理**: 跳过标题 — 如果 LLM 无法为某个分段生成合适的标题(如过渡性内容),该分段不加标题,直接以内容开始 +- **模块位置**: `kb_workflow/srt/semantic_engine.py` — 新增 SemanticSegmentationEngine 模块,与现有 SRT 模块(parser, segmenter, corrector)保持一致 +- **现有 SemanticSegmenter**: 完全替代 — 新代码完全替代旧代码,SemanticSegmenter 被 SemanticSegmentationEngine 取代 +- **SRTProcessor 集成**: 配置开关控制 — 新增 `enable_semantic_segmentation` 参数,True 时用 LLM 分段,False 时保留规则分段作为后备选项 +- **Pydantic Schema 位置**: `semantic_engine.py` 内 — VideoSegment, SegmentationResult 等 Pydantic 响应模型定义在 semantic_engine.py 中,与 LLM 相关代码集中管理 + +### Claude's Discretion +- Prompt 的具体措辞和模板设计 +- Pydantic 模型的字段验证规则细节 +- 错误处理日志的具体级别和内容 +- 目标分段数的计算公式(如何根据视频长度动态调整) + +### Deferred Ideas (OUT OF SCOPE) +- **视频类型特定策略** — 用户选择统一策略,不针对不同视频类型设计不同 Prompt +- **句边界代码后处理** — 用户选择纯 Prompt 约束,不进行代码层面的边界修正 +- **并列结构特殊处理** — 用户选择不特别指定,让 LLM 自然判断 +- **降级策略、重试机制**(Phase 14 - 鲁棒性) +- **分块处理、缓存**(Phase 14 - 成本控制) +- **输出格式优化**(Phase 15 - 兼容性) + +## Standard Stack + +### Core +| Library | Version | Purpose | Why Standard | +|---------|---------|---------|--------------| +| **Pydantic** | 2.x | LLM 响应验证 | 类型安全、自动验证、清晰错误消息(从 v2.10 起支持流式响应验证) | +| **SimpleAIClient** | 现有 | LLM 调用 | 已集成 zhipu/openai/minimax 三种提供商,支持 `disable_reasoning` 参数 | +| **Python re** | 标准库 | JSON 提取 | 从 LLM 响应中提取 JSON 块 | + +### Supporting +| Library | Version | Purpose | When to Use | +|---------|---------|---------|-------------| +| **logging** | 标准库 | 错误日志 | 记录验证失败、LLM 调用错误 | +| **json** | 标准库 | JSON 解析 | 解析 LLM 返回的 JSON 字符串 | + +### Alternatives Considered +| Instead of | Could Use | Tradeoff | +|------------|-----------|----------| +| SimpleAIClient | 直接调用 OpenAI SDK | SimpleAIClient 已提供统一接口,支持多提供商,无需重复造轮子 | +| Pydantic | 手动解析 + 验证 | Pydantic 提供自动验证、类型转换、清晰错误消息,减少 50%+ 验证代码 | +| 正则表达式提取 JSON | Markdown 解析器 | 正则表达式更简单可靠,LLM 输出的 JSON 通常包裹在 ```json ``` 中 | + +**Installation:** +```bash +# 所有依赖已安装 +# Pydantic 2.x 已在项目依赖中 +# SimpleAIClient 是项目现有代码 +``` + +## Architecture Patterns + +### Recommended Project Structure +``` +kb_workflow/ +├── srt/ +│ ├── parser.py # 现有:解析 SRT 文件 +│ ├── segmenter.py # 现有:规则分段(将被替代) +│ ├── corrector.py # 现有:ASR 纠错(参考模式) +│ └── semantic_engine.py # 新增:LLM 语义分段引擎 +├── deep_analyzer/ +│ └── __init__.py # 现有:SimpleAIClient 定义 +└── pipeline/ + └── processors/ + └── srt.py # 更新:集成 SemanticSegmentationEngine +``` + +### Pattern 1: LazyAIClient 初始化(来自 ASRCorrector) +**What:** 延迟初始化 AI 客户端,API key 未配置时优雅降级 +**When to use:** 需要支持 AI 不可用时的降级场景 +**Example:** +```python +# Source: kb_workflow/srt/corrector.py (现有代码) +from kb_workflow.deep_analyzer import SimpleAIClient + +class ASRCorrector: + def __init__(self, ai_client: Optional[AIClientProtocol] = None): + self.ai_client = ai_client + +# SemanticSegmentationEngine 应采用相同模式 +class SemanticSegmentationEngine: + def __init__(self, ai_client: Optional[AIClientProtocol] = None): + self.ai_client = ai_client + if ai_client is None: + try: + self.ai_client = SimpleAIClient() + if not self.ai_client.api_key: + logger.info("AI API key not configured") + self.ai_client = None + except Exception as e: + logger.warning(f"Failed to initialize AI client: {e}") + self.ai_client = None +``` + +### Pattern 2: Pydantic v2 嵌套模型验证 +**What:** 定义嵌套的 Pydantic 模型验证 LLM 输出的复杂结构 +**When to use:** LLM 返回包含列表、嵌套对象的 JSON +**Example:** +```python +# Source: Pydantic v2 官方文档 +from pydantic import BaseModel, Field, field_validator +from typing import List, Optional + +class VideoSegment(BaseModel): + """视频分段""" + title: Optional[str] = Field( + None, + description="分段标题(5-15字),主题/章节类型有标题,观点/对话类型为空" + ) + content: str = Field(..., description="分段内容,保持句子完整性") + start_index: int = Field(..., description="在原文中的起始字符位置") + end_index: int = Field(..., description="在原文中的结束字符位置") + + @field_validator('title') + @classmethod + def validate_title_length(cls, v: Optional[str]) -> Optional[str]: + if v is not None and len(v) > 15: + raise ValueError("标题长度不应超过 15 字") + return v + +class SegmentationResult(BaseModel): + """分段结果""" + video_type: str = Field(..., description="视频类型:知识类/故事类/教程类/访谈类等") + segments: List[VideoSegment] = Field(..., description="分段列表", min_length=1) + actual_segment_count: int = Field(..., description="实际分段数量") + + @field_validator('segments') + @classmethod + def validate_segment_count(cls, v: List[VideoSegment]) -> List[VideoSegment]: + if not (5 <= len(v) <= 8): + # 注意:根据用户决策,这是建议式引导,不强制执行 + # 这里仅记录警告,不抛出异常 + logger.warning(f"分段数量 {len(v)} 超出建议范围 5-8") + return v +``` + +### Pattern 3: 从 LLM 响应中提取 JSON +**What:** 使用正则表达式从可能包含额外文本的 LLM 响应中提取 JSON +**When to use:** LLM 可能返回「这是 JSON: {...}」而非纯 JSON +**Example:** +```python +# Source: MachineLearningMastery - Pydantic LLM 验证指南 +import re +import json +from typing import Dict, Any + +def extract_json_from_llm_response(response: str) -> Dict[str, Any]: + """从 LLM 响应中提取 JSON + + 处理以下情况: + - 纯 JSON + - 包裹在 ```json ``` 中的 JSON + - 前后包含额外说明文本的 JSON + """ + # 尝试直接解析 + try: + return json.loads(response.strip()) + except json.JSONDecodeError: + pass + + # 尝试提取 ```json ``` 代码块 + json_match = re.search(r'```json\s*(.*?)\s*```', response, re.DOTALL) + if json_match: + try: + return json.loads(json_match.group(1)) + except json.JSONDecodeError: + pass + + # 尝试提取任何 JSON 对象 + json_match = re.search(r'\{.*\}', response, re.DOTALL) + if json_match: + try: + return json.loads(json_match.group()) + except json.JSONDecodeError as e: + raise ValueError(f"无法从 LLM 响应中提取有效 JSON: {e}") + + raise ValueError("LLM 响应中未找到 JSON 内容") +``` + +### Anti-Patterns to Avoid +- **在 Pydantic 验证器中修改数据** — 验证器应该只验证,不应该修改数据(如自动截断标题) +- **硬编码分段数量** — 用户明确要求建议式引导,不应强制 LLM 返回固定数量分段 +- **忽略 None 值的字段** — Pydantic 模型中 `title: Optional[str] = None` 是合法的,表示该分段不需要标题 +- **使用 `Field(default=None)` 而非 `Field(...)`** — `...` 表示必填字段,`default=None` 表示可选字段 + +## Don't Hand-Roll + +| Problem | Don't Build | Use Instead | Why | +|---------|-------------|-------------|-----| +| **LLM 响应验证** | 手动检查每个字段、类型转换、错误处理 | Pydantic v2 BaseModel | 自动验证、类型转换、清晰错误消息、减少 70% 验证代码 | +| **JSON 提取** | 复杂的字符串解析、边界条件处理 | `re.search(r'\{.*\}', response, re.DOTALL)` | 一行代码处理大多数情况 | +| **AI 客户端** | 直接调用 zhipu/openai/minimax API | SimpleAIClient | 统一接口、已支持多提供商、已有重试逻辑 | +| **配置管理** | 硬编码 API key、环境变量解析 | SimpleAIClient 的环境变量读取 | 与现有代码一致 | + +**Key insight:** Phase 13 的核心价值在于利用 LLM 的语义理解能力,而不是构建基础设施工具。使用现有的、经过验证的库(Pydantic、SimpleAIClient)可以快速聚焦于核心问题。 + +## Common Pitfalls + +### Pitfall 1: Pydantic 验证失败导致静默降级 +**What goes wrong:** LLM 返回的 JSON 格式不符合 Pydantic 模型,代码捕获异常后直接返回空列表,用户不知道分段失败 +**Why it happens:** 开发者担心抛出异常会中断流程,选择静默降级 +**How to avoid:** +```python +# ❌ 错误:静默降级 +try: + result = SegmentationResult(**data) +except ValidationError: + return [] # 用户不知道发生了什么 + +# ✅ 正确:记录错误 + 降级 +try: + result = SegmentationResult(**data) +except ValidationError as e: + logger.error(f"LLM 分段结果验证失败: {e}") + logger.error(f"原始 LLM 输出: {llm_output}") + # 降级到规则分段 + return fallback_segmenter.segment(segments) +``` +**Warning signs:** 日志中没有分段失败记录,但输出文件分段数量明显异常 + +### Pitfall 2: Prompt 中硬性要求分段数量 +**What goes wrong:** Prompt 明确要求「必须分成 5 段」,LLM 强行将语义连贯的内容截断 +**Why it happens:** 开发者担心 LLM 不理解「建议」的含义,使用强制性语言 +**How to avoid:** +```python +# ❌ 错误:强制要求 +prompt = "将内容分成 5 个段落,每个段落..." + +# ✅ 正确:建议式引导 +prompt = """建议将内容分为 5-8 个段落,根据语义完整性灵活调整数量。 +优先保证每个段落的语义完整性,不要为了凑数而强行截断。""" +``` +**Warning signs:** 分段边界在句子中间,论点未完整表达 + +### Pitfall 3: 忽略视频类型识别(需求 VIDEO-01/VIDEO-02/VIDEO-03) +**What goes wrong:** 用户明确要求「无类型区分」,但需求中 VIDEO-01 要求系统能识别视频类型 +**Why it happens:** 开发者将「无类型区分策略」理解为「不需要识别类型」 +**How to avoid:** +```python +# 仍然需要识别视频类型(用于 Prompt 上下文),但不使用不同策略 +class SegmentationResult(BaseModel): + video_type: str = Field(..., description="视频类型:知识类/故事类/教程类/访谈类等") + segments: List[VideoSegment] = Field(..., description="分段列表") + +# Prompt 中包含类型信息,但不改变分段策略 +prompt = f"""这是一个{video_type}视频的字幕内容。 + +请按照以下要求进行语义分段: +- 识别话题转换点,在主题切换时创建新分段 +- 保持句子完整性,不要在句子中间截断 +- ...(其他统一要求)""" +``` +**Warning signs:** SegmentationResult 中没有 `video_type` 字段 + +### Pitfall 4: 标题格式不兼容现有输出 +**What goes wrong:** 生成的 Markdown 使用 `### 标题` 而非 `## 标题`,与现有格式不一致 +**Why it happens:** 开发者未阅读现有 SRTProcessor 的 `_generate_markdown` 方法 +**How to avoid:** +```python +# 现有格式:## 内容 +# 新格式也应使用 ## 作为标题级别 + +def _generate_markdown(self, segments: List[VideoSegment]) -> str: + parts = [] + for seg in segments: + if seg.title: + parts.append(f"## {seg.title}") # H2,与现有格式一致 + parts.append(seg.content) + parts.append("") + return "\n".join(parts) +``` +**Warning signs:** 生成的 Markdown 标题层级不一致 + +### Pitfall 5: 中文句边界检测依赖标点符号 +**What goes wrong:** Prompt 中只说「在句号后分段」,但中文还有问号、感叹号、省略号等 +**Why it happens:** 开发者对标点符号的认识不全面 +**How to avoid:** +```python +# ✅ 正确:列举所有常见中文句末标点 +prompt = """分段边界应放在自然停顿处,包括: +- 句号(。)后 +- 问号(?)后 +- 感叹号(!)后 +- 省略号(……)后 +- 逗号(,)如果表示较长停顿""" +``` +**Warning signs:** 分段在「等等!」或「真的?」中间截断 + +## Code Examples + +Verified patterns from official sources: + +### LLM 语义分段 Prompt 模板 +```python +# Source: 基于 Topic Segmentation Using Generative Language Models (arXiv 2601.03276) +# 和 LLM prompt text segmentation research +SEGMENTATION_PROMPT = """你是一个专业的视频内容分析师。请将以下字幕内容按语义分段。 + +【视频信息】 +类型:{video_type} +标题:{title} +总字数:{total_chars}字 + +【字幕内容】 +{content} + +【分段要求】 +1. 建议将内容分为 5-8 个段落(可根据语义完整性灵活调整) +2. 每个段落约 1000-2000 字(为保持语义完整性可以突破此范围) +3. 在话题转换处创建新分段,不要混入不同话题的内容 +4. **重要**:保持句子完整性,分段边界应放在自然停顿处(句号。、问号?、感叹号!、省略号……等标点符号后) +5. 保持论点完整性,不要在前提-结论结构中间截断 +6. 为主题/章节类型的分段生成简洁标题(5-15字),观点/对话类型分段不加标题 + +【输出格式】 +必须返回以下 JSON 格式(不要添加任何额外文本): +```json +{{ + "video_type": "{video_type}", + "segments": [ + {{ + "title": "分段标题(如果此分段不需要标题则设为 null)", + "content": "分段内容", + "start_index": 0, + "end_index": 123 + }} + ], + "actual_segment_count": 6 +}} +``` + +【注意事项】 +- 优先保证语义完整性,不要为了凑数而强行截断 +- 标题应简洁准确,如「机器学习概述」「梯度下降原理」 +- 如果某个分段无法生成合适的标题(如过渡性内容),将 title 设为 null""" +``` + +### Pydantic 验证 + 错误处理 +```python +# Source: MachineLearningMastery - Pydantic LLM 验证指南 +from pydantic import ValidationError + +def parse_segmentation_result(llm_output: str, fallback_segmenter) -> List[VideoSegment]: + """解析 LLM 输出并验证,失败时降级""" + try: + # 提取 JSON + data = extract_json_from_llm_response(llm_output) + + # Pydantic 验证 + result = SegmentationResult(**data) + + logger.info( + f"LLM 分段成功: 类型={result.video_type}, " + f"分段数={result.actual_segment_count}" + ) + + return result.segments + + except (json.JSONDecodeError, ValidationError) as e: + logger.error(f"LLM 分段失败: {e}") + logger.error(f"原始 LLM 输出: {llm_output[:500]}...") + + # 降级到规则分段 + logger.info("降级到规则分段") + return fallback_segmenter.segment(segments) + + except Exception as e: + logger.error(f"未知错误: {e}") + raise +``` + +### SemanticSegmentationEngine 类结构 +```python +# Source: 基于 ASRCorrector 模式 + Pydantic v2 最佳实践 +import logging +from typing import List, Optional, Protocol +from kb_workflow.srt.parser import Segment + +logger = logging.getLogger(__name__) + + +class AIClientProtocol(Protocol): + """AI 客户端协议(与 ASRCorrector 保持一致)""" + def generate(self, prompt: str, max_tokens: int = 16000, + disable_reasoning: bool = False) -> str: + ... + + +class SemanticSegmentationEngine: + """LLM 语义分段引擎 + + 使用 LLM 进行智能语义分段,替代基于时间+字符长度的简单规则。 + + Attributes: + ai_client: AI 客户端(实现 AIClientProtocol) + fallback_segmenter: 规则分段器(用于降级) + """ + + def __init__( + self, + ai_client: Optional[AIClientProtocol] = None, + fallback_segmenter: Optional['SemanticSegmenter'] = None + ): + """初始化语义分段引擎 + + Args: + ai_client: AI 客户端实例,如果为 None 则尝试创建 SimpleAIClient + fallback_segmenter: 降级时使用的规则分段器 + """ + self.ai_client = ai_client + self.fallback_segmenter = fallback_segmenter + + # Lazy AI Client 初始化(与 ASRCorrecter 相同模式) + if self.ai_client is None: + try: + from kb_workflow.deep_analyzer import SimpleAIClient + self.ai_client = SimpleAIClient() + if not self.ai_client.api_key: + logger.info("AI API key not configured") + self.ai_client = None + except Exception as e: + logger.warning(f"Failed to initialize AI client: {e}") + self.ai_client = None + + # 初始化降级分段器 + if self.fallback_segmenter is None: + from kb_workflow.srt.segmenter import SemanticSegmenter + self.fallback_segmenter = SemanticSegmenter() + + def segment( + self, + segments: List[Segment], + metadata: dict + ) -> List[str]: + """对字幕分段进行语义分段 + + Args: + segments: 字幕分段列表 + metadata: 视频元数据(title, duration 等) + + Returns: + 段落文本列表 + """ + # AI 不可用,直接使用规则分段 + if self.ai_client is None: + logger.info("AI client unavailable, using fallback segmenter") + return self.fallback_segmenter.segment(segments) + + # 合并字幕文本 + content = " ".join(seg.text for seg in segments) + + # 构建 Prompt + prompt = self._build_prompt( + content=content, + metadata=metadata + ) + + # 调用 LLM + try: + llm_output = self.ai_client.generate( + prompt, + max_tokens=8000, + disable_reasoning=True # 分段不需要思考模式 + ) + + # 解析和验证 + video_segments = self._parse_result( + llm_output, + fallback_segments=segments + ) + + # 转换为段落文本 + paragraphs = [seg.content for seg in video_segments] + + logger.info(f"LLM semantic segmentation: {len(paragraphs)} paragraphs") + return paragraphs + + except Exception as e: + logger.error(f"LLM segmentation failed: {e}, using fallback") + return self.fallback_segmenter.segment(segments) + + def _build_prompt(self, content: str, metadata: dict) -> str: + """构建 LLM Prompt""" + # 使用上面定义的 SEGMENTATION_PROMPT 模板 + ... + + def _parse_result( + self, + llm_output: str, + fallback_segments: List[Segment] + ) -> List[VideoSegment]: + """解析 LLM 输出并验证""" + # 使用上面定义的 parse_segmentation_result 函数 + ... +``` + +## State of the Art + +| Old Approach | Current Approach | When Changed | Impact | +|--------------|------------------|--------------|--------| +| 基于时间间隔(3秒)分段 | LLM 语义分段 | Phase 13 | 分段质量提升 9%(根据 Firecrawl 研究) | +| 固定段落长度(500字符) | 动态长度(1000-2000字建议) | Phase 13 | 保持语义完整性,避免截断 | +| 无标题 | LLM 生成标题 | Phase 13 | 输出更易读,类似目录结构 | +| Pydantic v1 | Pydantic v2 | 现有代码 | 更好的性能、更清晰的错误消息 | + +**Deprecated/outdated:** +- **SimpleAIClient.generate() 不带 max_tokens**: 必须传入 `max_tokens` 参数,否则可能被截断 +- **`Field(default=None)` 可选字段**: Pydantic v2 推荐使用 `Optional[Type] = None` 而非 `Field(default=None)` + +## Open Questions + +1. **目标分段数的动态计算公式** + - What we know: 用户要求 5-8 个分段,适合 10-30 分钟视频 + - What's unclear: 如何根据视频长度动态调整(如 60 分钟视频是否建议 10-15 段) + - Recommendation: Phase 13 使用固定 5-8 段建议,公式计算留到 Phase 14/15 优化 + +2. **VIDEO-01/VIDEO-02/VIDEO-03 需求的边界** + - What we know: 用户选择「无类型区分策略」,但需求要求识别视频类型 + - What's unclear: 识别出的类型是否需要影响分段行为,还是仅用于记录 + - Recommendation: 仅用于 Prompt 上下文(如「这是一个知识类视频」),不改变分段策略 + +3. **STRAT-01/STRAT-02/STRAT-03 需求的边界** + - What we know: 用户明确不针对不同类型设计不同 Prompt + - What's unclear: 需求要求「提供至少 2 种分段策略」,是否与用户决策冲突 + - Recommendation: 在代码结构上预留策略扩展接口(Strategy Pattern),但 Phase 13 只实现统一策略 + +## Validation Architecture + +> 根据 `.planning/config.json`,`workflow.nyquist_validation` 未设置,默认为 `true`,因此包含此部分。 + +### Test Framework +| Property | Value | +|----------|-------| +| Framework | pytest | +| Config file | `pytest.ini` | +| Quick run command | `pytest tests/test_srt_processor.py -v -k "test_srt" -x` | +| Full suite command | `pytest tests/test_srt_processor.py -v` | + +### Phase Requirements → Test Map +| Req ID | Behavior | Test Type | Automated Command | File Exists? | +|--------|----------|-----------|-------------------|-------------| +| VIDEO-01 | 系统识别视频类型 | unit | `pytest tests/test_srt_processor.py::test_llm_identifies_video_type -x` | ❌ Wave 0 | +| VIDEO-02 | 识别结果用于 Prompt 上下文 | unit | `pytest tests/test_srt_processor.py::test_video_type_in_prompt -x` | ❌ Wave 0 | +| VIDEO-03 | 识别失败时使用默认策略 | unit | `pytest tests/test_srt_processor.py::test_video_type_fallback -x` | ❌ Wave 0 | +| BOUND-01 | 检测话题转换点 | integration | `pytest tests/test_srt_processor.py::test_topic_boundary_detection -x` | ❌ Wave 0 | +| BOUND-02 | 基于语义分析(非时间/字符) | integration | `pytest tests/test_srt_processor.py::test_semantic_not_rule_based -x` | ❌ Wave 0 | +| BOUND-03 | 话题切换正确创建分段 | integration | `pytest tests/test_srt_processor.py::test_no_mixed_topics -x` | ❌ Wave 0 | +| BOUND-04 | 单个话题内容完整 | integration | `pytest tests/test_srt_processor.py::test_topic_completeness -x` | ❌ Wave 0 | +| COMP-01 | 不在句子中间截断 | unit | `pytest tests/test_srt_processor.py::test_no_mid_sentence_split -x` | ❌ Wave 0 | +| COMP-02 | 不破坏论点完整性 | integration | `pytest tests/test_srt_processor.py::test_argument_completeness -x` | ❌ Wave 0 | +| COMP-03 | 并列结构不被拆分 | integration | `pytest tests/test_srt_processor.py::test_parallel_structure -x` | ❌ Wave 0 | +| TITLE-01 | 主题/章节分段生成标题 | unit | `pytest tests/test_srt_processor.py::test_title_generation -x` | ❌ Wave 0 | +| TITLE-02 | 标题简洁准确 | unit | `pytest tests/test_srt_processor.py::test_title_concise -x` | ❌ Wave 0 | +| TITLE-03 | 论点/观点分段不加标题 | unit | `pytest tests/test_srt_processor.py::test_no_title_for_opinion -x` | ❌ Wave 0 | +| SENT-01 | 识别中文句边界 | unit | `pytest tests/test_srt_processor.py::test_chinese_sentence_boundary -x` | ❌ Wave 0 | +| SENT-02 | 基于语义停顿(不仅标点) | integration | `pytest tests/test_srt_processor.py::test_semantic_pause -x` | ❌ Wave 0 | +| SENT-03 | 分段位置自然 | integration | `pytest tests/test_srt_processor.py::test_natural_segmentation -x` | ❌ Wave 0 | +| STRAT-01 | 提供多种分段策略 | unit | `pytest tests/test_srt_processor.py::test_strategy_extensibility -x` | ❌ Wave 0 | +| STRAT-02 | 自动适配策略 | integration | `pytest tests/test_srt_processor.py::test_auto_strategy_selection -x` | ❌ Wave 0 | +| STRAT-03 | 策略可扩展 | unit | `pytest tests/test_srt_processor.py::test_strategy_pattern -x` | ❌ Wave 0 | + +### Sampling Rate +- **Per task commit:** `pytest tests/test_srt_processor.py -v -k "test_semantic" -x` +- **Per wave merge:** `pytest tests/test_srt_processor.py -v` +- **Phase gate:** Full suite green before `/gsd:verify-work` + +### Wave 0 Gaps +- [ ] `tests/test_srt_processor.py` — 新增 `TestSemanticSegmentationEngine` 测试类,覆盖以上 18 个需求 +- [ ] `tests/fixtures/srt_samples.py` — 新增测试用例:知识类/故事类视频字幕,包含话题转换、并列结构、论点完整性等场景 +- [ ] `tests/conftest.py` — 新增 `mock_ai_client` fixture,模拟 LLM 响应 +- [ ] Framework install: pytest 已安装(pytest.ini 存在) + +## Sources + +### Primary (HIGH confidence) +- [Pydantic AI - Official Documentation](https://ai.pydantic.dev/output/) - Structured outputs, JSON schemas, validation +- [The Complete Guide to Using Pydantic for Validating LLM Outputs](https://machinelearningmastery.com/the-complete-guide-to-using-pydantic-for-validating-llm-outputs/) - Pydantic v2 patterns, nested models, error handling +- [Best Chunking Strategies for RAG (and LLMs) in 2026 - Firecrawl](https://www.firecrawl.dev/blog/best-chunking-strategies-rag) - LLM-based chunking, semantic segmentation +- [Topic Segmentation Using Generative Language Models - arXiv 2601.03276](https://arxiv.org/html/2601.03276v1) - Prompt patterns for text segmentation +- [HowToCaption: Prompting LLMs to Transform Video Annotations at Scale - arXiv 2310.04900](https://arxiv.org/abs/2310.04900) - Video content analysis with LLMs +- 现有代码: `kb_workflow/srt/corrector.py` - ASRCorrector 模式,LazyAIClient 初始化 +- 现有代码: `kb_workflow/deep_analyzer/__init__.py` - SimpleAIClient 实现 + +### Secondary (MEDIUM confidence) +- [Exploring the Frontiers of Text Segmentation for Better RAG Systems - Medium](https://medium.com/@hajar.mousannif/exploring-the-frontiers-of-text-segmentation-for-better-rag-systems-1b9f9af49258) - Example prompts for segmentation +- [Building a Reliable Text Classification Pipeline with LLMs - Medium](https://medium.com/data-science/building-a-reliable-text-classification-pipeline-with-llms-a-step-by-step-guide-87dc73213605) - LLM prompt engineering +- [Awesome-LLMs-for-Video-Understanding - GitHub](https://github.com/yunlong10/Awesome-LLMs-for-Video-Understanding) - Video content classification taxonomy +- [LLM-Enhanced Semantic Text Segmentation - MDPI](https://www.mdpi.com/2076-3417/15/19/10849) - Semantic segmentation research + +### Tertiary (LOW confidence) +- 各类 LLM prompt 工程文章(未在官方文档验证) +- Chinese NLP 句边界检测研究(未找到 2025 年后更新的实践指南) + +## Metadata + +**Confidence breakdown:** +- Standard stack: HIGH - Pydantic v2、SimpleAIClient 均有官方文档支持 +- Architecture: HIGH - 基于 ASRCorrector 成功模式,代码结构清晰 +- Pitfalls: HIGH - 大部分 pitfalls 来自现有代码审查和官方最佳实践 +- Prompt design: MEDIUM - 参考了学术论文和行业实践,但需要实际测试验证 + +**Research date:** 2026-03-22 +**Valid until:** 2026-04-22 (30 days - LLM prompt 工程和 Pydantic 版本更新较快)