495 lines
18 KiB
Markdown
495 lines
18 KiB
Markdown
# Technology Stack - LLM Semantic Segmentation for SRT Subtitles
|
|
|
|
**Project:** kb-skills v1.6 - Semantic Segmentation Improvement
|
|
**Researched:** 2026-03-21
|
|
**Focus:** Stack additions for LLM-based semantic segmentation of B站 SRT subtitles
|
|
|
|
## Executive Summary
|
|
|
|
**无需新的外部依赖** - 基于现有SimpleAIClient扩展,使用Pydantic v2进行结构化输出验证。
|
|
|
|
核心架构:
|
|
- **Prompt Engineering**: 两阶段分析(视频类型分类 → 主题分段)
|
|
- **Response Parsing**: Pydantic v2 models with fallback
|
|
- **Integration**: 扩展现有SemanticSegmenter,保持向后兼容
|
|
|
|
---
|
|
|
|
## Required Libraries (None New)
|
|
|
|
### Existing Stack (Already Available)
|
|
| Library | Version | Purpose | Why |
|
|
|---------|---------|---------|-----|
|
|
| Pydantic | v2 | Schema validation | Already in use, perfect for LLM output validation |
|
|
| SimpleAIClient | Custom | LLM calls | Existing integration, supports zhipu/openai/minimax |
|
|
| Python | 3.12+ | Type hints & modern features | Match existing requirement |
|
|
|
|
### NO New Dependencies Required
|
|
All functionality can be built using existing stack. The Pydantic v2 already available provides robust JSON parsing and validation capabilities needed for LLM response handling.
|
|
|
|
---
|
|
|
|
## LLM Prompt Engineering Approach
|
|
|
|
### Two-Stage Analysis Strategy
|
|
|
|
Based on research from [Chapter-Llama (CVPR 2025)](https://arxiv.org/html/2504.00072v1) and [Automate Video Chaptering with LLMs](https://towardsdatascience.com/automate-video-chaptering-with-llms-and-tf-idf-f6569fd4d32b/), a two-stage approach provides better accuracy than single-shot analysis:
|
|
|
|
#### Stage 1: Video Type Detection
|
|
**Purpose:** Classify video type to apply appropriate segmentation strategy
|
|
|
|
**Prompt Template:**
|
|
```python
|
|
VIDEO_TYPE_PROMPT = """你是一个视频内容分析专家。请根据以下字幕内容,判断视频类型。
|
|
|
|
【字幕内容】(前5000字符)
|
|
{text_preview}
|
|
|
|
【视频信息】
|
|
标题:{title}
|
|
时长:{duration_str}
|
|
|
|
【任务】判断视频类型,从以下类别中选择最匹配的一个:
|
|
- tutorial: 教程类(步骤清晰、教学导向)
|
|
- knowledge: 知识类(讲解概念、理论分析)
|
|
- story: 故事类(叙事、情节发展)
|
|
- interview: 访谈类(对话、问答)
|
|
- commentary: 评论类(观点表达、分析讨论)
|
|
- mixed: 混合类型(包含多种元素)
|
|
|
|
【输出格式】只输出类型代码(如 tutorial),不要解释。"""
|
|
```
|
|
|
|
**Response Format:** Plain text type code (single word)
|
|
|
|
**Why Simple Output:**
|
|
- Reduces parsing complexity
|
|
- Lower error rate
|
|
- Faster processing
|
|
- Can be cached for similar content
|
|
|
|
#### Stage 2: Semantic Segmentation
|
|
**Purpose:** Identify topic boundaries and generate chapter titles
|
|
|
|
**Prompt Template:**
|
|
```python
|
|
SEMANTIC_SEGMENTATION_PROMPT = """你是一个视频内容结构化专家。请将以下字幕按语义主题分段。
|
|
|
|
【字幕内容】
|
|
{full_text}
|
|
|
|
【视频类型】:{video_type}
|
|
【视频信息】
|
|
标题:{title}
|
|
总时长:{duration_str}
|
|
|
|
【任务】
|
|
1. 分析字幕内容,识别主题变化边界
|
|
2. 将内容分为{target_segments}个段落
|
|
3. 为每个段落生成简洁标题(不超过15字)
|
|
|
|
【分段原则】
|
|
- 保持论点完整性,不要在句子中间截断
|
|
- 在话题转换处分段
|
|
- 考虑{video_type}的特点:
|
|
* tutorial: 按步骤/章节分段
|
|
* knowledge: 按概念/论点分段
|
|
* story: 按情节发展分段
|
|
* interview: 按话题/问答分段
|
|
* commentary: 按观点段落分段
|
|
* mixed: 综合以上策略
|
|
|
|
【输出格式】必须严格遵循以下JSON格式:
|
|
{{
|
|
"segments": [
|
|
{{
|
|
"title": "段落标题",
|
|
"start_index": 0,
|
|
"end_index": 150,
|
|
"summary": "段落主题摘要(30字以内)"
|
|
}}
|
|
],
|
|
"overall_summary": "整体内容摘要(100字以内)"
|
|
}}
|
|
|
|
注意:
|
|
- start_index 和 end_index 是字符在原文中的位置(从0开始)
|
|
- 确保segments覆盖全部内容,不遗漏不重复
|
|
- 只输出JSON,不要包含其他内容"""
|
|
```
|
|
|
|
**Response Format:** Structured JSON with Pydantic validation
|
|
|
|
**Why JSON Output:**
|
|
- Machine-readable for precise mapping
|
|
- Supports Pydantic validation
|
|
- Enables error recovery
|
|
- Matches existing workflow patterns
|
|
|
|
---
|
|
|
|
## Response Format Specifications
|
|
|
|
### Pydantic Schemas for LLM Output
|
|
|
|
Based on best practices from [Pydantic validation guides](https://machinelearningmastery.com/the-complete-guide-to-using-pydantic-for-validating-llm-outputs/):
|
|
|
|
```python
|
|
from pydantic import BaseModel, Field, field_validator
|
|
from typing import List
|
|
|
|
class VideoSegment(BaseModel):
|
|
"""Single semantic segment"""
|
|
|
|
title: str = Field(..., min_length=1, max_length=50, description="Segment title")
|
|
start_index: int = Field(..., ge=0, description="Start character position in text")
|
|
end_index: int = Field(..., ge=0, description="End character position in text")
|
|
summary: str = Field(..., min_length=1, max_length=100, description="Segment summary")
|
|
|
|
@field_validator('end_index')
|
|
@classmethod
|
|
def validate_bounds(cls, v, info):
|
|
"""Ensure end_index > start_index"""
|
|
if 'start_index' in info.data and v <= info.data['start_index']:
|
|
raise ValueError('end_index must be greater than start_index')
|
|
return v
|
|
|
|
class SegmentationResult(BaseModel):
|
|
"""Complete segmentation result from LLM"""
|
|
|
|
segments: List[VideoSegment] = Field(..., min_length=1, description="List of segments")
|
|
overall_summary: str = Field(..., min_length=1, max_length=200, description="Overall summary")
|
|
|
|
@field_validator('segments')
|
|
@classmethod
|
|
def validate_segments(cls, v):
|
|
"""Ensure segments are sorted and non-overlapping"""
|
|
if not v:
|
|
raise ValueError('segments cannot be empty')
|
|
|
|
# Sort by start_index
|
|
sorted_segments = sorted(v, key=lambda x: x.start_index)
|
|
|
|
# Check for overlaps
|
|
for i in range(len(sorted_segments) - 1):
|
|
if sorted_segments[i].end_index > sorted_segments[i + 1].start_index:
|
|
raise ValueError(f'Segments {i} and {i+1} overlap')
|
|
|
|
return sorted_segments
|
|
```
|
|
|
|
### Response Parsing Strategy
|
|
|
|
**Three-tier fallback strategy** (inspired by [reliable LLM outputs](https://www.freecodecamp.org/news/how-to-keep-llm-outputs-predictable-using-pydantic-validation/)):
|
|
|
|
```python
|
|
def parse_llm_response(response_text: str, total_length: int) -> SegmentationResult:
|
|
"""Parse LLM response with multiple fallback strategies"""
|
|
|
|
# Strategy 1: Direct JSON parsing
|
|
try:
|
|
return SegmentationResult.model_validate_json(response_text)
|
|
except Exception as e:
|
|
logger.warning(f"Direct JSON parsing failed: {e}")
|
|
|
|
# Strategy 2: Extract JSON from markdown code blocks
|
|
try:
|
|
import re
|
|
json_match = re.search(r'```json\s*(.*?)\s*```', response_text, re.DOTALL)
|
|
if json_match:
|
|
return SegmentationResult.model_validate_json(json_match.group(1))
|
|
except Exception as e:
|
|
logger.warning(f"Markdown JSON extraction failed: {e}")
|
|
|
|
# Strategy 3: Heuristic fallback - simple equal-length segments
|
|
logger.warning("All parsing strategies failed, using heuristic fallback")
|
|
return _generate_fallback_segments(total_length)
|
|
```
|
|
|
|
---
|
|
|
|
## Integration with Existing SimpleAIClient
|
|
|
|
### Architecture Pattern
|
|
|
|
Based on existing `ASRCorrector` pattern in `kb_workflow/srt/corrector.py`:
|
|
|
|
```python
|
|
from typing import Optional, Protocol
|
|
from kb_workflow.deep_analyzer import SimpleAIClient
|
|
|
|
class AIClientProtocol(Protocol):
|
|
"""AI Client Protocol (already exists)"""
|
|
def generate(self, prompt: str, max_tokens: int = 16000, disable_reasoning: bool = None) -> str:
|
|
...
|
|
|
|
class SemanticSegmentationEngine:
|
|
"""LLM-powered semantic segmentation engine"""
|
|
|
|
def __init__(
|
|
self,
|
|
ai_client: Optional[AIClientProtocol] = None,
|
|
enable_fallback: bool = True
|
|
):
|
|
self.ai_client = ai_client
|
|
self.enable_fallback = enable_fallback
|
|
|
|
def detect_video_type(self, text_preview: str, metadata: dict) -> str:
|
|
"""Stage 1: Detect video type"""
|
|
if self.ai_client is None:
|
|
return "mixed" # Default fallback
|
|
|
|
prompt = VIDEO_TYPE_PROMPT.format(
|
|
text_preview=text_preview[:5000],
|
|
title=metadata.get('title', ''),
|
|
duration_str=self._format_duration(metadata.get('duration', 0))
|
|
)
|
|
|
|
try:
|
|
response = self.ai_client.generate(prompt, max_tokens=100, disable_reasoning=True)
|
|
return response.strip().lower()
|
|
except Exception as e:
|
|
logger.warning(f"Video type detection failed: {e}")
|
|
return "mixed"
|
|
|
|
def segment_semantically(
|
|
self,
|
|
full_text: str,
|
|
video_type: str,
|
|
metadata: dict,
|
|
target_segments: int = 5
|
|
) -> SegmentationResult:
|
|
"""Stage 2: Perform semantic segmentation"""
|
|
if self.ai_client is None:
|
|
return self._generate_fallback_segments(len(full_text))
|
|
|
|
prompt = SEMANTIC_SEGMENTATION_PROMPT.format(
|
|
full_text=full_text,
|
|
video_type=video_type,
|
|
title=metadata.get('title', ''),
|
|
duration_str=self._format_duration(metadata.get('duration', 0)),
|
|
target_segments=target_segments
|
|
)
|
|
|
|
try:
|
|
response = self.ai_client.generate(prompt, max_tokens=4000, disable_reasoning=False)
|
|
return parse_llm_response(response, len(full_text))
|
|
except Exception as e:
|
|
logger.warning(f"Semantic segmentation failed: {e}")
|
|
if self.enable_fallback:
|
|
return self._generate_fallback_segments(len(full_text))
|
|
raise
|
|
```
|
|
|
|
### Integration Point in SRTProcessor
|
|
|
|
Minimal changes to existing `kb_workflow/pipeline/processors/srt.py`:
|
|
|
|
```python
|
|
class SRTProcessor(BaseProcessor):
|
|
def __init__(
|
|
self,
|
|
state_manager: Optional[WorkflowStateManager] = None,
|
|
enable_correction: bool = True,
|
|
enable_semantic_segmentation: bool = True # NEW parameter
|
|
):
|
|
super().__init__(state_manager)
|
|
self.enable_correction = enable_correction
|
|
self.enable_semantic_segmentation = enable_semantic_segmentation
|
|
self.segmenter = SemanticSegmenter()
|
|
self.corrector = None
|
|
self.semantic_engine = None # NEW
|
|
|
|
# Lazy initialization
|
|
if enable_semantic_segmentation:
|
|
try:
|
|
from kb_workflow.srt.semantic_engine import SemanticSegmentationEngine
|
|
from kb_workflow.deep_analyzer import SimpleAIClient
|
|
|
|
ai_client = SimpleAIClient()
|
|
if ai_client.api_key:
|
|
self.semantic_engine = SemanticSegmentationEngine(ai_client=ai_client)
|
|
logger.info("Semantic segmentation engine initialized")
|
|
except Exception as e:
|
|
logger.warning(f"Failed to initialize semantic engine: {e}")
|
|
|
|
async def process(self, input_data: SRTInput) -> SRTOutput:
|
|
# ... existing parsing code ...
|
|
|
|
# Step 2: Enhanced semantic segmentation
|
|
if self.semantic_engine and self.enable_semantic_segmentation:
|
|
logger.info("Using LLM-based semantic segmentation")
|
|
paragraphs = self._semantic_segment_with_llm(segments, metadata)
|
|
else:
|
|
logger.info("Using rule-based segmentation")
|
|
paragraphs = self.segmenter.segment(segments)
|
|
|
|
# ... rest of existing pipeline ...
|
|
|
|
def _semantic_segment_with_llm(
|
|
self,
|
|
segments: List[Segment],
|
|
metadata: dict
|
|
) -> List[str]:
|
|
"""Use LLM for intelligent segmentation"""
|
|
# Combine segments into full text
|
|
full_text = ' '.join(seg.text for seg in segments)
|
|
|
|
# Stage 1: Detect video type
|
|
video_type = self.semantic_engine.detect_video_type(full_text, metadata)
|
|
|
|
# Stage 2: Semantic segmentation
|
|
result = self.semantic_engine.segment_semantically(
|
|
full_text=full_text,
|
|
video_type=video_type,
|
|
metadata=metadata,
|
|
target_segments=self._calculate_target_segments(len(full_text))
|
|
)
|
|
|
|
# Extract paragraph texts based on indices
|
|
paragraphs = [
|
|
full_text[seg.start_index:seg.end_index].strip()
|
|
for seg in result.segments
|
|
]
|
|
|
|
return paragraphs
|
|
```
|
|
|
|
---
|
|
|
|
## What NOT to Add (Scope Boundaries)
|
|
|
|
### Explicitly Out of Scope
|
|
|
|
| Feature | Why NOT to Add | Alternative |
|
|
|---------|----------------|-------------|
|
|
| **LangChain** | Over-engineering for single LLM call | Direct SimpleAIClient usage |
|
|
| **New LLM providers** | Existing 3 providers sufficient | Extend zhipu/openai/minimax |
|
|
| **Vector embeddings** | Not needed for segmentation | Plain text analysis |
|
|
| **Chunking strategies** | LLM handles full context | Single-pass analysis |
|
|
| **Streaming responses** | Batch processing is fine | Await full response |
|
|
| **Multi-modal analysis** | Only text (SRT) available | Text-only prompts |
|
|
| **Dedicated segmentation models** | LLM is more flexible | Prompt engineering |
|
|
| **Database storage** | Not needed for pipeline | In-memory processing |
|
|
| **Real-time processing** | Batch is acceptable | Async processing |
|
|
| **Custom tokenizer** | Not needed for this use case | Character-based indexing |
|
|
| **Caching layer** | Adds complexity | Simple in-memory memoization |
|
|
|
|
### Architecture Anti-Patterns to Avoid
|
|
|
|
**From [video chaptering research](https://medium.com/codex/ai-based-video-analyzer-for-automated-chaptering-and-summarization-2ca65f35b687):**
|
|
|
|
1. **❌ Single-shot segmentation**
|
|
- Problem: Too complex for one prompt
|
|
- Solution: Two-stage approach (type → segment)
|
|
|
|
2. **❌ Fixed segment count**
|
|
- Problem: Ignores content structure
|
|
- Solution: Adaptive target based on length
|
|
|
|
3. **❌ Pure time-based segmentation**
|
|
- Problem: Breaks semantic coherence
|
|
- Solution: Content-aware boundaries
|
|
|
|
4. **❌ JSON parsing without validation**
|
|
- Problem: Malformed LLM output
|
|
- Solution: Pydantic + fallback strategies
|
|
|
|
---
|
|
|
|
## Implementation Priority
|
|
|
|
### Phase 1: Core Segmentation (MVP)
|
|
1. Video type detection prompt
|
|
2. Semantic segmentation prompt
|
|
3. Pydantic response models
|
|
4. Basic integration with SRTProcessor
|
|
|
|
### Phase 2: Robustness (Post-MVP)
|
|
1. Fallback strategies
|
|
2. Error recovery
|
|
3. Logging and debugging
|
|
4. Performance optimization
|
|
|
|
### Phase 3: Enhancement (Future)
|
|
1. Caching for similar content
|
|
2. Adaptive segment count
|
|
3. User feedback integration
|
|
4. A/B testing framework
|
|
|
|
---
|
|
|
|
## Configuration Strategy
|
|
|
|
### Environment Variables (No New Variables Needed)
|
|
|
|
Reuse existing AI configuration:
|
|
```bash
|
|
# Already in use
|
|
AI_API_KEY=your_key
|
|
AI_PROVIDER=zhipu # or openai, minimax
|
|
AI_MODEL=glm-5
|
|
AI_BASE_URL=https://open.bigmodel.cn/api/coding/paas/v4
|
|
```
|
|
|
|
### Optional Parameters (YAML Config)
|
|
|
|
Add to existing `.kb_env.yaml`:
|
|
```yaml
|
|
semantic_segmentation:
|
|
enabled: true
|
|
enable_fallback: true
|
|
default_video_type: mixed
|
|
min_segment_length: 200 # characters
|
|
max_segment_length: 2000 # characters
|
|
target_segment_count: 5
|
|
```
|
|
|
|
---
|
|
|
|
## Testing Strategy
|
|
|
|
### Unit Tests
|
|
- Pydantic model validation
|
|
- Response parsing strategies
|
|
- Fallback mechanisms
|
|
|
|
### Integration Tests
|
|
- End-to-end SRT processing
|
|
- Error recovery paths
|
|
- Performance benchmarks
|
|
|
|
### Manual Testing
|
|
- Real B站 videos
|
|
- Different video types
|
|
- Edge cases (very short/long content)
|
|
|
|
---
|
|
|
|
## Sources
|
|
|
|
### Prompt Engineering & Segmentation
|
|
- [Chapter-Llama: Efficient Chaptering in Hour-Long Videos with LLMs (CVPR 2025)](https://arxiv.org/html/2504.00072v1) - HIGH confidence, academic research on LLM-based video chaptering
|
|
- [Automate Video Chaptering with LLMs and TF-IDF](https://towardsdatascience.com/automate-video-chaptering-with-llms-and-tf-idf-f6569fd4d32b/) - MEDIUM confidence, practical implementation guide
|
|
- [AI-Based Video Analyzer for Automated Chaptering and Summarization](https://medium.com/codex/ai-based-video-analyzer-for-automated-chaptering-and-summarization-2ca65f35b687) - MEDIUM confidence, real-world application
|
|
- [Azure AI Video Indexer with LLM Prompts](https://learn.microsoft.com/en-us/azure/azure-video-indexer/prompt-overview) - HIGH confidence, official documentation
|
|
|
|
### Pydantic Validation
|
|
- [The Complete Guide to Using Pydantic for Validating LLM Outputs](https://machinelearningmastery.com/the-complete-guide-to-using-pydantic-for-validating-llm-outputs/) - HIGH confidence, comprehensive guide
|
|
- [A Practical Guide on Structuring LLM Outputs with Pydantic](https://nuno-bispo.medium.com/a-practical-guide-on-structuring-llm-outputs-with-pydantic-68f9f363e26e) - HIGH confidence, practical examples
|
|
- [Building Reliable LLM Applications with Pydantic Validation](https://pub.towardsai.net/building-reliable-llm-applications-with-pydantic-validation-7241a93cebb3) - MEDIUM confidence, reliability patterns
|
|
|
|
### LLM Best Practices
|
|
- [The Ultimate Guide to Prompt Engineering in 2026](https://www.lakera.ai/blog/prompt-engineering-guide) - HIGH confidence, current best practices
|
|
- [Context Engineering for Video Understanding](https://www.twelvelabs.io/blog/context-engineering-for-video-understanding) - MEDIUM confidence, video-specific context design
|
|
- [Harnessing LLMs for Automated Video Content Analysis](https://arxiv.org/html/2406.19528v3) - HIGH confidence, academic research
|
|
|
|
### Video Analysis
|
|
- [Learning Video LLM with Streaming Speech Transcription at Scale (CVPR 2025)](https://cvpr.thecvf.com/virtual/2025/poster/33157) - HIGH confidence, 2025 research
|
|
- [Long Video Summarization via Key Moment Extraction (2026)](https://aclanthology.org/2026.iwsds-1.22.pdf) - HIGH confidence, 2026 research
|
|
|
|
### Confidence Notes
|
|
- HIGH: Academic papers, official documentation, peer-reviewed sources
|
|
- MEDIUM: Industry blogs, practical tutorials, real-world implementations
|
|
- LOW: Single-source claims, unverified practices (avoided in this document)
|