273 lines
8.7 KiB
Markdown
273 lines
8.7 KiB
Markdown
---
|
||
phase: 14-robustness-cost
|
||
plan: 03
|
||
type: execute
|
||
wave: 1
|
||
depends_on: []
|
||
files_modified:
|
||
- kb_workflow/srt/cache/semantic_cache.py
|
||
- kb_workflow/srt/cache/__init__.py
|
||
autonomous: true
|
||
requirements:
|
||
- COST-02
|
||
must_haves:
|
||
truths:
|
||
- "相同内容的字幕使用缓存结果,避免重复 LLM 调用"
|
||
- "缓存键基于内容哈希(SHA256),精确匹配"
|
||
- "缓存存储在本地文件系统 (.planning/cache/semantic/)"
|
||
artifacts:
|
||
- path: "kb_workflow/srt/cache/semantic_cache.py"
|
||
provides: "SemanticCache 缓存管理器"
|
||
exports: ["SemanticCache", "compute_content_hash"]
|
||
- path: "kb_workflow/srt/cache/__init__.py"
|
||
provides: "缓存模块导出"
|
||
exports: ["SemanticCache"]
|
||
key_links:
|
||
- from: "SemanticCache.get()"
|
||
to: ".planning/cache/semantic/"
|
||
via: "文件系统读取"
|
||
pattern: "Path.read_text"
|
||
- from: "SemanticCache.set()"
|
||
to: ".planning/cache/semantic/"
|
||
via: "文件系统写入"
|
||
pattern: "Path.write_text"
|
||
---
|
||
|
||
<objective>
|
||
实现基于内容哈希的智能缓存,避免重复 LLM 调用,降低成本和延迟。
|
||
|
||
Purpose: 相同内容的字幕直接使用缓存结果,减少 LLM API 调用次数。
|
||
Output: SemanticCache 缓存管理器,支持内容哈希、文件存储、TTL 永久有效。
|
||
</objective>
|
||
|
||
<execution_context>
|
||
@/home/starmen1265/.claude/get-shit-done/workflows/execute-plan.md
|
||
@/home/starmen1265/.claude/get-shit-done/templates/summary.md
|
||
</execution_context>
|
||
|
||
<context>
|
||
@.planning/phases/14-robustness-cost/14-CONTEXT.md
|
||
@.planning/phases/14-robustness-cost/14-RESEARCH.md
|
||
@.planning/phases/14-robustness-cost/14-VALIDATION.md
|
||
@.planning/ROADMAP.md
|
||
@.planning/REQUIREMENTS.md
|
||
@.planning/STATE.md
|
||
|
||
@kb_workflow/srt/semantic_engine.py
|
||
@kb_workflow/srt/corrector.py
|
||
</context>
|
||
|
||
<interfaces>
|
||
<!-- Key contracts needed for cache implementation -->
|
||
|
||
From 14-CONTEXT.md (User Decisions):
|
||
```python
|
||
# 缓存机制
|
||
- 缓存键: 字幕内容哈希(SHA256)— 精确匹配
|
||
- 缓存存储: 本地文件系统 — .planning/cache/semantic/
|
||
- 缓存 TTL: 永久有效 — 手动清理
|
||
- 缓存内容: 完整的 SegmentationResult(JSON 格式)
|
||
```
|
||
|
||
From kb_workflow/srt/semantic_engine.py:
|
||
```python
|
||
class SegmentationResult(BaseModel):
|
||
video_type: str
|
||
segments: List[VideoSegment]
|
||
actual_segment_count: int
|
||
|
||
class VideoSegment(BaseModel):
|
||
title: Optional[str]
|
||
content: str
|
||
start_index: int
|
||
end_index: int
|
||
```
|
||
|
||
关键观察:
|
||
- SegmentationResult 是 Pydantic 模型,可以序列化为 JSON
|
||
- 需要缓存完整的 SegmentationResult 对象
|
||
- 缓存键应该仅基于内容哈希,不包含元数据
|
||
</interfaces>
|
||
|
||
<tasks>
|
||
|
||
<task type="auto" tdd="true">
|
||
<name>Task 1: 创建 SemanticCache 缓存管理器</name>
|
||
<files>kb_workflow/srt/cache/semantic_cache.py, kb_workflow/srt/cache/__init__.py</files>
|
||
<behavior>
|
||
- compute_content_hash(): 计算内容的 SHA256 哈希
|
||
- get(): 从缓存读取 SegmentationResult
|
||
- set(): 保存 SegmentationResult 到缓存
|
||
- 缓存目录不存在时自动创建
|
||
- 缓存文件命名: {hash}.json
|
||
- 缓存内容包含 version 字段(未来兼容性)
|
||
- 缓存命中/未命中记录 debug 日志
|
||
</behavior>
|
||
<action>
|
||
创建 kb_workflow/srt/cache/semantic_cache.py:
|
||
|
||
1. 导入依赖:
|
||
```python
|
||
import hashlib
|
||
import json
|
||
import logging
|
||
from pathlib import Path
|
||
from typing import Optional
|
||
|
||
from kb_workflow.srt.semantic_engine import SegmentationResult
|
||
```
|
||
|
||
2. 实现 compute_content_hash():
|
||
```python
|
||
def compute_content_hash(content: str) -> str:
|
||
"""计算内容的 SHA256 哈希作为缓存键"""
|
||
return hashlib.sha256(content.encode('utf-8')).hexdigest()
|
||
```
|
||
|
||
3. 实现 SemanticCache 类:
|
||
```python
|
||
class SemanticCache:
|
||
"""语义分段结果缓存管理器
|
||
|
||
使用内容哈希(SHA256)作为缓存键,存储在本地文件系统。
|
||
"""
|
||
|
||
CACHE_VERSION = "1.0"
|
||
DEFAULT_CACHE_DIR = Path(".planning/cache/semantic")
|
||
|
||
def __init__(self, cache_dir: Optional[Path] = None):
|
||
"""初始化缓存管理器
|
||
|
||
Args:
|
||
cache_dir: 缓存目录路径,默认为 .planning/cache/semantic/
|
||
"""
|
||
self.cache_dir = cache_dir or self.DEFAULT_CACHE_DIR
|
||
self.logger = logging.getLogger(__name__)
|
||
|
||
def get(self, content: str) -> Optional[SegmentationResult]:
|
||
"""从缓存读取分段结果
|
||
|
||
Args:
|
||
content: 字幕内容
|
||
|
||
Returns:
|
||
缓存的 SegmentationResult,如果不存在则返回 None
|
||
"""
|
||
cache_key = compute_content_hash(content)
|
||
cache_file = self.cache_dir / f"{cache_key}.json"
|
||
|
||
if not cache_file.exists():
|
||
self.logger.debug(f"Cache miss: {cache_key}")
|
||
return None
|
||
|
||
try:
|
||
data = json.loads(cache_file.read_text(encoding='utf-8'))
|
||
|
||
# 验证版本
|
||
if data.get('version') != self.CACHE_VERSION:
|
||
self.logger.warning(
|
||
f"Cache version mismatch: {data.get('version')} != {self.CACHE_VERSION}"
|
||
)
|
||
return None
|
||
|
||
# 移除 version 字段,转换为 SegmentationResult
|
||
data.pop('version', None)
|
||
result = SegmentationResult(**data)
|
||
|
||
self.logger.debug(f"Cache hit: {cache_key}")
|
||
return result
|
||
|
||
except (json.JSONDecodeError, IOError, ValidationError) as e:
|
||
self.logger.warning(f"Failed to load cache: {e}")
|
||
return None
|
||
|
||
def set(self, content: str, result: SegmentationResult) -> None:
|
||
"""保存分段结果到缓存
|
||
|
||
Args:
|
||
content: 字幕内容
|
||
result: 分段结果
|
||
"""
|
||
cache_key = compute_content_hash(content)
|
||
cache_file = self.cache_dir / f"{cache_key}.json"
|
||
|
||
try:
|
||
# 确保目录存在
|
||
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
||
|
||
# 序列化为 JSON,添加 version 字段
|
||
data = result.model_dump()
|
||
data['version'] = self.CACHE_VERSION
|
||
|
||
cache_file.write_text(
|
||
json.dumps(data, ensure_ascii=False, indent=2),
|
||
encoding='utf-8'
|
||
)
|
||
|
||
self.logger.debug(f"Cache saved: {cache_key}")
|
||
|
||
except IOError as e:
|
||
self.logger.warning(f"Failed to save cache: {e}")
|
||
```
|
||
|
||
4. 创建 kb_workflow/srt/cache/__init__.py:
|
||
```python
|
||
"""语义分段缓存模块"""
|
||
|
||
from kb_workflow.srt.cache.semantic_cache import (
|
||
SemanticCache,
|
||
compute_content_hash
|
||
)
|
||
|
||
__all__ = ['SemanticCache', 'compute_content_hash']
|
||
```
|
||
|
||
遵守用户决策:
|
||
- 缓存键: SHA256 内容哈希
|
||
- 缓存存储: 本地文件系统 (.planning/cache/semantic/)
|
||
- 缓存 TTL: 永久有效
|
||
- 缓存内容: 完整的 SegmentationResult
|
||
</action>
|
||
<verify>
|
||
<automated>pytest tests/test_semantic_robustness.py::test_cache_hit -x</automated>
|
||
</verify>
|
||
<done>
|
||
- SemanticCache 类实现完整的缓存读写功能
|
||
- compute_content_hash() 使用 SHA256 计算内容哈希
|
||
- 缓存目录不存在时自动创建
|
||
- 缓存文件包含 version 字段,支持未来兼容性
|
||
- 缓存命中/未命中记录 debug 日志
|
||
- 缓存加载失败时返回 None,不中断流程
|
||
</done>
|
||
</task>
|
||
|
||
</tasks>
|
||
|
||
<verification>
|
||
## 整体验证标准
|
||
|
||
1. **缓存键计算**: 相同内容产生相同的 SHA256 哈希
|
||
2. **缓存命中**: 第二次调用时从缓存读取,不调用 LLM
|
||
3. **缓存未命中**: 首次调用时保存结果到缓存
|
||
4. **版本控制**: 缓存包含 version 字段,支持未来格式变更
|
||
5. **错误处理**: 缓存读写失败时返回 None,不中断流程
|
||
|
||
运行完整测试套件:
|
||
```bash
|
||
python3 -m pytest tests/test_semantic_robustness.py::test_cache_hit -v -x
|
||
```
|
||
</verification>
|
||
|
||
<success_criteria>
|
||
1. 相同内容第二次调用时从缓存读取
|
||
2. 缓存文件存储在 .planning/cache/semantic/ 目录
|
||
3. 缓存文件命名格式: {sha256_hash}.json
|
||
4. 缓存内容包含完整的 SegmentationResult (JSON 格式)
|
||
5. 缓存命中时记录 debug 级别日志
|
||
6. 所有测试通过
|
||
</success_criteria>
|
||
|
||
<output>
|
||
执行完成后创建 `.planning/phases/14-robustness-cost/14-03-SUMMARY.md`
|
||
</output>
|