docs(14): research phase 14 robustness and cost optimization
This commit is contained in:
@@ -0,0 +1,434 @@
|
||||
# Phase 14: 鲁棒性与成本控制 - Research
|
||||
|
||||
**Research Date:** 2026-03-23
|
||||
**Domain:** LLM 容错处理、缓存优化、文本分块
|
||||
**Confidence:** HIGH
|
||||
|
||||
## Summary
|
||||
|
||||
本阶段专注于提高 LLM 语义分段的稳定性和成本效率。基于用户决策,实现轻量级的重试机制、智能缓存和长文本分块处理。核心原则是"快速失败,优雅降级"——AI 不可用时立即使用规则分段,保证系统可用性。
|
||||
|
||||
**Primary Recommendation:** 复用 ASRCorrector 的分批处理模式和 SimpleAIClient 的重试逻辑,避免引入新依赖,保持代码一致性。
|
||||
|
||||
<user_constraints>
|
||||
## User Constraints (from CONTEXT.md)
|
||||
|
||||
### Locked Decisions
|
||||
|
||||
#### 重试策略
|
||||
- **重试次数**: 重试 1 次 — 失败后立即降级到规则分段,简单快速,避免浪费配额
|
||||
- **重试条件**: 仅可恢复错误触发重试 — 网络错误/超时/限流重试,API 错误(如 400)直接降级
|
||||
- **重试延迟**: 立即重试 — 失败后立即重试,大多数情况下网络抖动是瞬时的
|
||||
- **不可恢复错误**: API 错误(4xx)、解析失败、验证失败等直接降级,不重试
|
||||
|
||||
#### 分块处理
|
||||
- **分块阈值**: 5000 字符(约 10-15 分钟字幕)— 与 ASRCorrector 的 batch_size 一致,保持代码模式统一
|
||||
- **分块方式**: 按句子边界分割 — 在句号、问号、感叹号等标点符号后分割,保持语义完整性
|
||||
- **结果合并**: 直接拼接各分块结果 — 每个分块独立分段后直接拼接,简单快速
|
||||
- **分块策略**: 长字幕按句子边界分成多个 5000 字符以内的块,每块独立调用 LLM 分段
|
||||
|
||||
#### 缓存机制
|
||||
- **缓存键**: 字幕内容哈希(SHA256)— 精确匹配,相同内容一定命中
|
||||
- **缓存存储**: 本地文件系统 — 存储在 `.planning/cache/semantic/` 目录,简单持久化
|
||||
- **缓存 TTL**: 永久有效 — 字幕内容不变,分段结果应该一致,手动清理过期缓存
|
||||
- **缓存内容**: 完整的 SegmentationResult(JSON 格式),包含 video_type 和 segments
|
||||
|
||||
#### 日志详细程度
|
||||
- **降级日志**: 记录原因 + 降级方式 — 如 "API key 未配置,降级到规则分段"
|
||||
- **日志级别**: 区分级别
|
||||
- 降级:warning 级别
|
||||
- 重试:info 级别
|
||||
- 成功:debug 级别
|
||||
- **重试日志**: 记录重试原因和次数
|
||||
- **缓存日志**: 记录缓存命中/未命中,使用 debug 级别
|
||||
|
||||
### Claude's Discretion
|
||||
|
||||
- 具体的重试逻辑实现细节
|
||||
- 缓存文件命名规则和目录结构
|
||||
- 分块算法的具体实现(句子边界检测)
|
||||
- 日志消息的具体措辞和格式
|
||||
|
||||
### Deferred Ideas (OUT OF SCOPE)
|
||||
|
||||
- **缓存清理命令**: 自动清理过期缓存的功能 — 延后到未来版本,当前手动删除缓存文件
|
||||
- **缓存压缩**: 压缩缓存文件以节省空间 — 延后到缓存文件较大时再考虑
|
||||
- **分块智能合并**: 检测相邻分段语义相似度并合并 — 增加复杂度,当前直接拼接足够
|
||||
</user_constraints>
|
||||
|
||||
<phase_requirements>
|
||||
## Phase Requirements
|
||||
|
||||
| ID | Description | Research Support |
|
||||
|----|-------------|-----------------|
|
||||
| ROBUST-01 | LLM 调用失败时自动降级到简单规则分段 | 重试策略实现,异常时调用 fallback_segmenter |
|
||||
| ROBUST-02 | LLM 响应解析失败时自动重试或降级,有日志记录 | 区分可恢复/不可恢复错误,解析失败直接降级 |
|
||||
| ROBUST-03 | AI 不可用时自动使用简单规则分段 | Lazy AI Client 初始化,None 时直接使用规则分段 |
|
||||
| ROBUST-04 | 降级过程有日志记录,便于问题诊断 | 分级日志:降级 warning,重试 info,成功 debug |
|
||||
| COST-01 | 长视频字幕自动分块处理,避免上下文窗口溢出 | 复用 ASRCorrector 分批模式,5000 字符阈值 |
|
||||
| COST-02 | 相同内容使用缓存结果,避免重复 LLM 调用 | SHA256 哈希缓存键,本地文件系统存储 |
|
||||
| COST-03 | 提供配置开关,用户可选择是否启用 LLM 分段 | SRTProcessor 已有 enable_semantic_segmentation 参数 |
|
||||
</phase_requirements>
|
||||
|
||||
## Standard Stack
|
||||
|
||||
### Core
|
||||
| Library | Version | Purpose | Why Standard |
|
||||
|---------|---------|---------|--------------|
|
||||
| hashlib (built-in) | - | 内容哈希计算 | Python 标准库,无需额外依赖 |
|
||||
| logging (built-in) | - | 分级日志记录 | Python 标准库,已在项目中使用 |
|
||||
| json (built-in) | - | 缓存序列化 | Python 标准库,已在项目中使用 |
|
||||
| pathlib (built-in) | - | 路径操作 | Python 标准库,跨平台兼容 |
|
||||
|
||||
### Supporting
|
||||
| Library | Version | Purpose | When to Use |
|
||||
|---------|---------|---------|-------------|
|
||||
| pydantic | v2 | 响应验证 | 已在项目中使用,验证 SegmentationResult |
|
||||
| requests | 2.32.5+ | HTTP 请求 | 已通过 SimpleAIClient 隐式依赖 |
|
||||
|
||||
### Alternatives Considered
|
||||
| Instead of | Could Use | Tradeoff |
|
||||
|------------|-----------|----------|
|
||||
| 简单重试循环 | tenacity 库 | tenacity 功能丰富但增加依赖,简单场景手写即可 |
|
||||
| 文件系统缓存 | redis/sqlite | Redis 需要额外服务,sqlite 增加复杂度,文件系统最简单 |
|
||||
| 手动分块 | langchain TextSplitter | langchain 体积大,ASRCorrector 模式已足够 |
|
||||
|
||||
**Installation:**
|
||||
```bash
|
||||
# 无需安装新依赖,全部使用 Python 标准库或已有依赖
|
||||
```
|
||||
|
||||
## Architecture Patterns
|
||||
|
||||
### Recommended Project Structure
|
||||
```
|
||||
kb_workflow/srt/
|
||||
├── semantic_engine.py # 现有文件,添加重试、分块、缓存
|
||||
├── cache/ # 新增目录
|
||||
│ ├── __init__.py
|
||||
│ └── semantic_cache.py # 缓存管理器
|
||||
└── corrector.py # 参考分批处理模式(已有)
|
||||
```
|
||||
|
||||
### Pattern 1: Lazy Initialization with Fallback(已有模式)
|
||||
**What:** 延迟初始化 AI Client,失败时使用规则分段
|
||||
**When to use:** AI 依赖可能不可用的场景
|
||||
**Example:**
|
||||
```python
|
||||
# Source: kb_workflow/srt/semantic_engine.py:99-114
|
||||
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
|
||||
```
|
||||
|
||||
### Pattern 2: Batch Processing with Sentence Boundary Detection(复用模式)
|
||||
**What:** 按句子边界分割长文本,每批独立处理
|
||||
**When to use:** 文本超过 LLM 上下文窗口
|
||||
**Example:**
|
||||
```python
|
||||
# Source: kb_workflow/srt/corrector.py:125-136
|
||||
# 按句子分割(简单分割,基于句号、问号、感叹号)
|
||||
sentences = []
|
||||
current = ""
|
||||
for char in text:
|
||||
current += char
|
||||
if char in '。!?\n':
|
||||
sentences.append(current)
|
||||
current = ""
|
||||
|
||||
# 添加最后一个不完整的句子
|
||||
if current:
|
||||
sentences.append(current)
|
||||
```
|
||||
|
||||
### Pattern 3: Content-Based Hashing(新增)
|
||||
**What:** 使用 SHA256 哈希内容作为缓存键
|
||||
**When to use:** 需要精确匹配相同内容
|
||||
**Example:**
|
||||
```python
|
||||
import hashlib
|
||||
|
||||
def compute_content_hash(content: str) -> str:
|
||||
"""计算内容的 SHA256 哈希"""
|
||||
return hashlib.sha256(content.encode('utf-8')).hexdigest()
|
||||
```
|
||||
|
||||
### Pattern 4: Retry with Distinguishable Errors(新增)
|
||||
**What:** 区分可恢复和不可恢复错误,仅对可恢复错误重试
|
||||
**When to use:** API 调用可能因临时故障失败
|
||||
**Example:**
|
||||
```python
|
||||
import requests
|
||||
from requests.exceptions import ConnectionError, Timeout, HTTPError
|
||||
|
||||
def is_recoverable_error(error: Exception) -> bool:
|
||||
"""判断是否为可恢复错误"""
|
||||
if isinstance(error, (ConnectionError, Timeout)):
|
||||
return True
|
||||
if isinstance(error, HTTPError):
|
||||
status_code = error.response.status_code
|
||||
# 网络错误、超时、限流重试
|
||||
return status_code in (429, 500, 502, 503, 504)
|
||||
return False
|
||||
|
||||
def call_llm_with_retry(prompt: str, max_retries: int = 1):
|
||||
"""带重试的 LLM 调用"""
|
||||
for attempt in range(max_retries + 1):
|
||||
try:
|
||||
return llm_client.generate(prompt)
|
||||
except Exception as e:
|
||||
if attempt < max_retries and is_recoverable_error(e):
|
||||
logger.info(f"LLM call failed (attempt {attempt + 1}): {e}, retrying...")
|
||||
continue
|
||||
# 最后一次失败或不可恢复错误,抛出异常
|
||||
raise
|
||||
```
|
||||
|
||||
### Anti-Patterns to Avoid
|
||||
- **不区分错误类型的盲目重试**: 所有错误都重试会导致浪费配额,应区分可恢复/不可恢复错误
|
||||
- **固定延迟重试**: 应立即重试(网络抖动是瞬时的),不要使用指数退避
|
||||
- **缓存键包含可变元数据**: 缓存键应仅基于内容哈希,不应包含视频 ID 或时间戳
|
||||
- **过度复杂的分块合并**: 直接拼接各分块结果即可,无需检测语义相似度
|
||||
|
||||
## Don't Hand-Roll
|
||||
|
||||
| Problem | Don't Build | Use Instead | Why |
|
||||
|---------|-------------|-------------|-----|
|
||||
| 内容哈希 | 自定义哈希算法 | hashlib.sha256() | 标准库函数,安全可靠 |
|
||||
| 日志分级 | print 语句 | logging 模块 | 已在项目中使用,支持分级和输出控制 |
|
||||
| JSON 序列化 | 手动拼接字符串 | json.dump()/json.load() | 标准库函数,处理转义和格式 |
|
||||
| 路径操作 | 字符串拼接 | pathlib.Path | 跨平台兼容,自动处理路径分隔符 |
|
||||
| 重试装饰器 | 复杂的装饰器 | 简单 for 循环 | 简单场景(1 次重试)无需额外抽象 |
|
||||
|
||||
**Key insight:** Python 标准库提供了所需的全部基础设施,无需引入新依赖。重试、缓存、分块都是简单模式,过度工程化会增加维护成本。
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
### Pitfall 1: 不区分可恢复/不可恢复错误
|
||||
**What goes wrong:** 所有 LLM 调用失败都重试,包括 API 错误(如 400 Bad Request),导致浪费配额和时间
|
||||
**Why it happens:** 简单的 try-except 捕获所有异常,统一重试
|
||||
**How to avoid:** 使用 `is_recoverable_error()` 函数判断错误类型,仅对网络错误、超时、限流进行重试
|
||||
**Warning signs:** 看到 "重试 400 错误" 的日志
|
||||
|
||||
### Pitfall 2: 缓存键冲突
|
||||
**What goes wrong:** 不同内容的哈希意外相同(哈希碰撞),导致返回错误的缓存结果
|
||||
**Why it happens:** 使用弱哈希算法或包含可变元数据(如时间戳)
|
||||
**How to avoid:** 使用 SHA256 强哈希,缓存键仅基于内容
|
||||
**Warning signs:** 缓存命中后分段结果与内容不符
|
||||
|
||||
### Pitfall 3: 分块边界破坏语义
|
||||
**What goes wrong:** 在句子或段落中间分割,破坏内容完整性
|
||||
**Why it happens:** 仅按字符数分割,不考虑句子边界
|
||||
**How to avoid:** 复用 ASRCorrector 的分批模式,在标点符号(。!?\n)后分割
|
||||
**Warning signs:** 分块后某些分段以不完整的句子开头
|
||||
|
||||
### Pitfall 4: 缓存目录不存在导致写入失败
|
||||
**What goes wrong:** 首次运行时缓存目录不存在,导致文件写入失败
|
||||
**Why it happens:** 假设目录已存在,未创建父目录
|
||||
**How to avoid:** 使用 `pathlib.Path.mkdir(parents=True, exist_ok=True)` 创建目录
|
||||
**Warning signs:** FileNotFoundError 或 PermissionError
|
||||
|
||||
### Pitfall 5: 降级日志信息不足
|
||||
**What goes wrong:** 降级时仅记录 "降级到规则分段",无法诊断具体原因
|
||||
**Why it happens:** 捕获异常后未记录原始错误信息
|
||||
**How to avoid:** 记录具体错误类型和消息,如 "ConnectionError: 请求超时,降级到规则分段"
|
||||
**Warning signs:** 无法定位降级原因
|
||||
|
||||
## Code Examples
|
||||
|
||||
Verified patterns from official sources:
|
||||
|
||||
### 可恢复错误检测
|
||||
```python
|
||||
# Source: WebSearch 2026 - Python Retry Best Practices
|
||||
from requests.exceptions import ConnectionError, Timeout, HTTPError
|
||||
|
||||
def is_recoverable_error(error: Exception) -> bool:
|
||||
"""判断是否为可恢复错误
|
||||
|
||||
可恢复错误:网络错误、超时、限流
|
||||
不可恢复错误:API 错误(4xx)、解析失败、验证失败
|
||||
"""
|
||||
if isinstance(error, (ConnectionError, Timeout)):
|
||||
return True
|
||||
if isinstance(error, HTTPError):
|
||||
status_code = error.response.status_code
|
||||
# 429 Too Many Requests, 5xx Server Error
|
||||
return status_code in (429, 500, 502, 503, 504)
|
||||
return False
|
||||
```
|
||||
|
||||
### 内容哈希缓存键
|
||||
```python
|
||||
# Source: Python standard library documentation
|
||||
import hashlib
|
||||
|
||||
def compute_cache_key(content: str) -> str:
|
||||
"""计算内容的 SHA256 哈希作为缓存键"""
|
||||
return hashlib.sha256(content.encode('utf-8')).hexdigest()
|
||||
```
|
||||
|
||||
### 缓存读写
|
||||
```python
|
||||
# Source: Python standard library documentation
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
def load_cache(cache_key: str, cache_dir: Path) -> dict | None:
|
||||
"""从文件加载缓存"""
|
||||
cache_file = cache_dir / f"{cache_key}.json"
|
||||
if not cache_file.exists():
|
||||
return None
|
||||
try:
|
||||
return json.loads(cache_file.read_text(encoding='utf-8'))
|
||||
except (json.JSONDecodeError, IOError) as e:
|
||||
logger.warning(f"Failed to load cache: {e}")
|
||||
return None
|
||||
|
||||
def save_cache(cache_key: str, data: dict, cache_dir: Path):
|
||||
"""保存缓存到文件"""
|
||||
cache_dir.mkdir(parents=True, exist_ok=True)
|
||||
cache_file = cache_dir / f"{cache_key}.json"
|
||||
try:
|
||||
cache_file.write_text(json.dumps(data, ensure_ascii=False), encoding='utf-8')
|
||||
except IOError as e:
|
||||
logger.warning(f"Failed to save cache: {e}")
|
||||
```
|
||||
|
||||
### 按句子边界分割
|
||||
```python
|
||||
# Source: kb_workflow/srt/corrector.py:125-136
|
||||
def split_by_sentences(text: str, max_length: int = 5000) -> list[str]:
|
||||
"""按句子边界分割文本
|
||||
|
||||
在句号、问号、感叹号后分割,保持语义完整性。
|
||||
"""
|
||||
sentences = []
|
||||
current = ""
|
||||
for char in text:
|
||||
current += char
|
||||
if char in '。!?\n':
|
||||
sentences.append(current)
|
||||
current = ""
|
||||
|
||||
if current:
|
||||
sentences.append(current)
|
||||
|
||||
# 按最大长度分批
|
||||
batches = []
|
||||
current_batch = []
|
||||
current_length = 0
|
||||
|
||||
for sentence in sentences:
|
||||
if current_length + len(sentence) > max_length:
|
||||
if current_batch:
|
||||
batches.append(''.join(current_batch))
|
||||
current_batch = [sentence]
|
||||
current_length = len(sentence)
|
||||
else:
|
||||
current_batch.append(sentence)
|
||||
current_length += len(sentence)
|
||||
|
||||
if current_batch:
|
||||
batches.append(''.join(current_batch))
|
||||
|
||||
return batches
|
||||
```
|
||||
|
||||
## State of the Art
|
||||
|
||||
| Old Approach | Current Approach | When Changed | Impact |
|
||||
|--------------|------------------|--------------|--------|
|
||||
| 盲目重试所有错误 | 区分可恢复/不可恢复错误 | 2026-03-23 (Phase 14) | 减少配额浪费,提高响应速度 |
|
||||
| 无缓存 | SHA256 内容哈希缓存 | 2026-03-23 (Phase 14) | 相同内容避免重复 LLM 调用 |
|
||||
| 长文本直接调用 | 分块处理(5000字符) | 2026-03-23 (Phase 14) | 避免上下文窗口溢出 |
|
||||
| 简单 print 日志 | 分级 logging | 已在项目中使用 | 便于问题诊断和日志过滤 |
|
||||
|
||||
**Deprecated/outdated:**
|
||||
- **无区分的重试**: 所有错误都重试的模式已被淘汰,应根据错误类型决定是否重试
|
||||
- **基于时间戳的缓存**: 缓存键包含时间戳会导致相同内容缓存不命中,应仅基于内容哈希
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **分块后的分段索引如何计算?**
|
||||
- What we know: 每个分块独立分段,结果直接拼接
|
||||
- What's unclear: VideoSegment.start_index 和 end_index 是相对于原文还是分块?
|
||||
- Recommendation: 相对于原文,需要在分块时记录每个分块的起始偏移量,分段后累加偏移量
|
||||
|
||||
2. **缓存文件是否需要版本控制?**
|
||||
- What we know: 缓存永久有效,手动清理
|
||||
- What's unclear: 如果 SegmentationResult 结构变化,旧缓存会加载失败
|
||||
- Recommendation: 在缓存 JSON 中添加 version 字段,加载时验证版本
|
||||
|
||||
3. **分块处理的顺序是否影响结果?**
|
||||
- What we know: 每个分块独立调用 LLM,结果直接拼接
|
||||
- What's unclear: 分块间是否有上下文依赖(如跨分块的论点)
|
||||
- Recommendation: 假设分块间独立,直接拼接。如果出现问题,在 Phase 15 考虑智能合并
|
||||
|
||||
## Validation Architecture
|
||||
|
||||
### Test Framework
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Framework | pytest (python3 -m pytest) |
|
||||
| Config file | pytest.ini (已存在) |
|
||||
| Quick run command | `python3 -m pytest tests/test_semantic_robustness.py -v -x` |
|
||||
| Full suite command | `python3 -m pytest tests/ -v` |
|
||||
|
||||
### Phase Requirements → Test Map
|
||||
| Req ID | Behavior | Test Type | Automated Command | File Exists? |
|
||||
|--------|----------|-----------|-------------------|-------------|
|
||||
| ROBUST-01 | LLM 调用失败时自动降级 | unit | `pytest tests/test_semantic_robustness.py::test_llm_failure_fallback -x` | ❌ Wave 0 |
|
||||
| ROBUST-02 | 解析失败时重试或降级 | unit | `pytest tests/test_semantic_robustness.py::test_parse_failure_retry -x` | ❌ Wave 0 |
|
||||
| ROBUST-03 | AI 不可用时使用规则分段 | unit | `pytest tests/test_semantic_robustness.py::test_ai_unavailable_fallback -x` | ❌ Wave 0 |
|
||||
| ROBUST-04 | 降级日志记录 | unit | `pytest tests/test_semantic_robustness.py::test_fallback_logging -x` | ❌ Wave 0 |
|
||||
| COST-01 | 长文本分块处理 | unit | `pytest tests/test_semantic_robustness.py::test_long_text_chunking -x` | ❌ Wave 0 |
|
||||
| COST-02 | 缓存命中避免重复调用 | unit | `pytest tests/test_semantic_robustness.py::test_cache_hit -x` | ❌ Wave 0 |
|
||||
| COST-03 | 配置开关控制 | integration | `pytest tests/test_srt_processor.py::test_semantic_segmentation_toggle -x` | ❌ Wave 0 |
|
||||
|
||||
### Sampling Rate
|
||||
- **Per task commit:** `python3 -m pytest tests/test_semantic_robustness.py -v -x`
|
||||
- **Per wave merge:** `python3 -m pytest tests/ -v`
|
||||
- **Phase gate:** Full suite green before `/gsd:verify-work`
|
||||
|
||||
### Wave 0 Gaps
|
||||
- [ ] `tests/test_semantic_robustness.py` — 覆盖 ROBUST-01, ROBUST-02, ROBUST-03, ROBUST-04, COST-01, COST-02
|
||||
- [ ] `tests/test_srt_processor.py` — 覆盖 COST-03(配置开关)
|
||||
- [ ] `tests/conftest.py` — 共享 fixtures(mock LLM client, 样本字幕数据)
|
||||
- [ ] 框架安装: 无需安装,pytest 已配置
|
||||
|
||||
**Mock Strategy:** 使用 unittest.mock.patch 模拟 SimpleAIClient.generate(),测试不同失败场景(网络错误、解析失败、超时等)
|
||||
|
||||
## Sources
|
||||
|
||||
### Primary (HIGH confidence)
|
||||
- **kb_workflow/srt/semantic_engine.py** — 现有代码模式,UnifiedSegmentationStrategy, Lazy AI Client 初始化
|
||||
- **kb_workflow/srt/corrector.py** — 分批处理模式(_correct_long_text),按句子分割
|
||||
- **kb_workflow/deep_analyzer/__init__.py** — SimpleAIClient 重试逻辑(max_retries=3)
|
||||
- **Python Standard Library** — hashlib, logging, json, pathlib
|
||||
|
||||
### Secondary (MEDIUM confidence)
|
||||
- [Tenacity Official Repository](https://github.com/jd/tenacity) — 重试库参考(最终决定手写简单重试)
|
||||
- [Python Retry Best Practices with Tenacity](https://medium.com/@isuru-perera/how-i-stopped-worrying-about-api-failures-python-retry-with-tenacity-03cf839b4ceb) — 重试最佳实践
|
||||
- [How to Retry Failed Python Requests in 2026](https://oxylabs.io/blog/python-requests-retry) — 可恢复错误类型
|
||||
|
||||
### Tertiary (LOW confidence)
|
||||
- [nnsplit - Semantic Text Segmentation](https://github.com/xinmeiyang/nnsplit) — 句子边界检测参考(最终决定复用 ASRCorrector 模式)
|
||||
- [PySBD: Pragmatic Sentence Boundary Disambiguation](https://www.researchgate.net/publication/347234862_PySBD_Pragmatic_Sentence_Boundary_Disambiguation) — 句子边界检测研究(最终决定使用简单标点符号分割)
|
||||
|
||||
## Metadata
|
||||
|
||||
**Confidence breakdown:**
|
||||
- Standard stack: HIGH - 全部使用 Python 标准库或已有依赖
|
||||
- Architecture: HIGH - 基于现有代码模式(semantic_engine.py, corrector.py)
|
||||
- Pitfalls: HIGH - 基于用户决策和常见错误模式
|
||||
|
||||
**Research date:** 2026-03-23
|
||||
**Valid until:** 2026-04-23 (30 days, 稳定技术栈)
|
||||
Reference in New Issue
Block a user