This repository has been archived on 2026-08-07. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
kb-skills/.planning/phases/02-platform-skills/02-02-PLAN.md
T

370 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: 02-platform-skills
plan: 02
type: execute
wave: 2
depends_on: [02-01]
files_modified:
- kb_pipeline/platforms/__init__.py
- kb_pipeline/platforms/base.py
- kb_pipeline/platforms/bilibili.py
- kb_pipeline/__init__.py
- tests/test_platforms/__init__.py
- tests/test_platforms/conftest.py
- tests/test_platforms/test_bilibili.py
autonomous: true
requirements: [PLAT-01, PLAT-02]
user_setup: []
must_haves:
truths:
- "输入BV号,获取B站视频元数据(标题、作者、时长、描述等)"
- "输入CV号,获取B站专栏元数据和正文内容"
- "元数据保存为结构化JSON文件"
- "内容保存为Markdown文件(YAML Frontmatter + 正文)"
- "403错误触发降级策略(API -> Playwright)"
- "网络错误自动重试3次"
- "失败时记录错误原因到workflow_state.json"
artifacts:
- path: "kb_pipeline/platforms/base.py"
provides: "平台适配器基类"
exports: ["PlatformAdapter"]
- path: "kb_pipeline/platforms/bilibili.py"
provides: "B站平台适配器"
exports: ["BilibiliAdapter"]
min_lines: 100
- path: "tests/test_platforms/test_bilibili.py"
provides: "B站适配器测试"
min_lines: 80
key_links:
- from: "kb_pipeline/platforms/bilibili.py"
to: "kb_pipeline/retry.py"
via: "retry_on_network_error decorator"
pattern: "@retry_on_network_error"
- from: "kb_pipeline/platforms/bilibili.py"
to: "kb_pipeline/errors.py"
via: "NetworkError, AuthError"
pattern: "raise NetworkError"
- from: "kb_pipeline/platforms/bilibili.py"
to: "kb-bilibili-metadata/bilibili_extractor.py"
via: "BilibiliExtractor class"
pattern: "from kb_bilibili_metadata"
- from: "kb_pipeline/platforms/bilibili.py"
to: "kb_pipeline/state.py"
via: "WorkflowStateManager.mark_step_failed"
pattern: "mark_step_failed"
---
<objective>
创建 B站平台适配器,封装视频和专栏的元数据提取与内容获取逻辑,复用现有 BilibiliExtractor 实现。
Purpose: 为 B站视频和专栏提供统一的元数据提取和内容下载接口,支持错误重试、降级策略和状态记录。
Output: PlatformAdapter 基类、BilibiliAdapter 实现类
</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/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/02-platform-skills/02-CONTEXT.md
@.planning/phases/02-platform-skills/02-RESEARCH.md
<interfaces>
<!-- Phase 1 和 Plan 01 提供的接口 -->
From kb_pipeline/link_parser.py (Plan 01):
```python
@dataclass
class ParsedLink:
platform: str # "bilibili" | "zhihu"
content_type: str # "video" | "article" | "answer" | "zhuanlan"
content_id: str # BV号、CV号等
original_url: str
resolved_url: str
class LinkParser:
def parse(self, url: str) -> Optional[ParsedLink]:
```
From kb_pipeline/schemas.py:
```python
class StepInput(BaseModel):
content_id: str
platform: Literal["bilibili", "zhihu"]
class StepOutput(BaseModel):
success: bool
error_message: Optional[str]
completed_at: datetime
class WorkflowState(BaseModel):
content_id: str
platform: str
source_url: str
steps: Dict[str, StepRecord]
def mark_step_failed(self, step_name: str, error_message: str) -> None:
"""标记步骤失败,记录错误信息"""
```
From kb_pipeline/state.py:
```python
class WorkflowStateManager:
def load(self) -> Optional[WorkflowState]: ...
def save(self, state: WorkflowState) -> None: ...
def get_or_create(self, content_id, platform, source_url) -> WorkflowState: ...
```
From kb_pipeline/errors.py:
```python
class NetworkError(KBError):
def __init__(self, step_name, input_summary, status_code=None, cause=None)
class AuthError(KBError):
def __init__(self, step_name, input_summary, auth_type=None, cause=None)
```
From kb_pipeline/retry.py:
```python
retry_on_network_error = retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=2, max=10),
)
```
From kb_pipeline/config.py:
```python
class KBSettings(BaseSettings):
base_path: str
archive_path: str
```
</interfaces>
<existing_code>
<!-- 现有 BilibiliExtractor 的核心方法 -->
From kb-bilibili-metadata/bilibili_extractor.py:
```python
class BilibiliExtractor:
def extract_video_metadata(self, bvid: str) -> Dict[str, Any]:
# 返回 {metadata: {...}, stats: {...}, description: str}
def extract_article_metadata(self, cvid: str) -> Dict[str, Any]:
# 返回 {cvid, title, author, publish_time, ...}
async def _extract_video_by_api(self, bvid: str) -> Dict[str, Any]:
# 使用 httpx 调用 API
DEFAULT_HEADERS = {
'User-Agent': 'Mozilla/5.0 ...',
'Referer': 'https://www.bilibili.com/',
}
```
</existing_code>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: Write interface contracts for PlatformAdapter base class</name>
<files>kb_pipeline/platforms/__init__.py, kb_pipeline/platforms/base.py, tests/test_platforms/__init__.py, tests/test_platforms/conftest.py</files>
<behavior>
- Test 1: PlatformAdapter 是抽象基类,不能直接实例化
- Test 2: 子类必须实现 get_content_type, fetch_metadata, fetch_content, get_output_filename 方法
</behavior>
<action>
1. 创建 kb_pipeline/platforms/__init__.py:
- 导出 PlatformAdapter, BilibiliAdapter
2. 创建 kb_pipeline/platforms/base.py:
```python
from abc import ABC, abstractmethod
from typing import Dict, Any, Optional
from pathlib import Path
class PlatformAdapter(ABC):
"""平台适配器基类"""
@abstractmethod
def get_content_type(self, url: str) -> str:
"""识别内容类型 (video/article/answer/zhuanlan)"""
pass
@abstractmethod
async def fetch_metadata(self, content_id: str, content_type: str) -> Dict[str, Any]:
"""获取元数据"""
pass
@abstractmethod
async def fetch_content(self, content_id: str, content_type: str) -> str:
"""获取正文内容(Markdown格式)"""
pass
@abstractmethod
def get_output_filename(self, content_id: str, content_type: str, suffix: str) -> str:
"""生成输出文件名"""
pass
```
3. 创建测试目录和 fixtures:
- tests/test_platforms/__init__.py (空文件)
- tests/test_platforms/conftest.py: 定义共享 fixtures (mock_api_response, tmp_content_dir)
</action>
<verify>
<automated>pytest tests/test_platforms/ -x -v -k "base"</automated>
</verify>
<done>PlatformAdapter 基类定义完成,子类接口明确</done>
</task>
<task type="auto" tdd="true">
<name>Task 2: Implement BilibiliAdapter for video metadata with state recording</name>
<files>kb_pipeline/platforms/bilibili.py, tests/test_platforms/test_bilibili.py</files>
<behavior>
- Test 1: fetch_metadata("BVxxx", "video") 返回正确的视频元数据
- Test 2: 403错误时触发降级策略(API -> Playwright)
- Test 3: 网络错误自动重试3次
- Test 4: get_output_filename 返回正确格式 "bilibili_BVxxx_metadata.json"
- Test 5: 失败时调用 WorkflowStateManager.mark_step_failed() 记录错误
</behavior>
<action>
创建 kb_pipeline/platforms/bilibili.py:
```python
from kb_pipeline.platforms.base import PlatformAdapter
from kb_pipeline.errors import NetworkError, AuthError
from kb_pipeline.retry import retry_on_network_error
from kb_pipeline.state import WorkflowStateManager
from kb_bilibili_metadata.bilibili_extractor import BilibiliExtractor
class BilibiliAdapter(PlatformAdapter):
PLATFORM = "bilibili"
def __init__(self, headless: bool = True, timeout: int = 30, state_manager: Optional[WorkflowStateManager] = None):
self.extractor = BilibiliExtractor(headless=headless, timeout=timeout)
self.state_manager = state_manager
def get_content_type(self, url: str) -> str:
# 从 URL 识别 video 或 article
@retry_on_network_error
async def fetch_metadata(self, content_id: str, content_type: str) -> Dict[str, Any]:
try:
if content_type == "video":
return self.extractor.extract_video_metadata(content_id)
elif content_type == "article":
return self.extractor.extract_article_metadata(content_id)
raise ValueError(f"Unsupported content type: {content_type}")
except (NetworkError, AuthError) as e:
# 记录失败原因到状态文件
if self.state_manager:
self.state_manager.mark_step_failed("metadata", str(e))
raise
async def fetch_content(self, content_id: str, content_type: str) -> str:
# 视频内容获取留给 Phase 3 (ASR)
# 专栏内容需要额外实现
pass
def get_output_filename(self, content_id: str, content_type: str, suffix: str) -> str:
return f"bilibili_{content_id}_{suffix}"
```
创建 tests/test_platforms/test_bilibili.py:
- 使用 mock 模拟 BilibiliExtractor 的返回值
- 测试视频元数据提取
- 测试错误处理和重试机制
- 测试失败时状态记录到 WorkflowStateManager
</action>
<verify>
<automated>pytest tests/test_platforms/test_bilibili.py::test_video_metadata -x -v</automated>
</verify>
<done>BilibiliAdapter 能正确提取B站视频元数据,支持错误重试和状态记录</done>
</task>
<task type="auto" tdd="true">
<name>Task 3: Implement BilibiliAdapter for article content</name>
<files>kb_pipeline/platforms/bilibili.py, tests/test_platforms/test_bilibili.py</files>
<behavior>
- Test 1: fetch_metadata("cvxxx", "article") 返回正确的专栏元数据
- Test 2: fetch_content("cvxxx", "article") 返回 Markdown 格式的正文
- Test 3: Markdown 包含 YAML Frontmatter (title, author, source_url, platform, content_type, fetched_at)
</behavior>
<action>
扩展 kb_pipeline/platforms/bilibili.py:
1. 实现 fetch_content 方法:
- 使用 BilibiliExtractor 获取专栏内容
- 转换为 Markdown 格式
- 添加 YAML Frontmatter
2. 添加 format_content_markdown 辅助函数:
```python
import yaml
from datetime import datetime
def format_content_markdown(metadata: dict, content: str, platform: str, content_type: str) -> str:
frontmatter = {
"title": metadata.get("title", ""),
"author": metadata.get("author", ""),
"source_url": metadata.get("url", ""),
"platform": platform,
"content_type": content_type,
"fetched_at": datetime.now().isoformat(),
}
return f"---\n{yaml.dump(frontmatter, allow_unicode=True)}---\n\n{content}"
```
3. 扩展测试覆盖专栏场景
</action>
<verify>
<automated>pytest tests/test_platforms/test_bilibili.py::test_article_content -x -v</automated>
</verify>
<done>BilibiliAdapter 能正确提取B站专栏内容和元数据</done>
</task>
<task type="auto">
<name>Task 4: Update package exports</name>
<files>kb_pipeline/__init__.py</files>
<action>
更新 kb_pipeline/__init__.py:
```python
from .platforms import PlatformAdapter, BilibiliAdapter
__all__ = [
# ... existing exports ...
"PlatformAdapter",
"BilibiliAdapter",
]
```
</action>
<verify>
<automated>python -c "from kb_pipeline import BilibiliAdapter; print('OK')"</automated>
</verify>
<done>BilibiliAdapter 可从 kb_pipeline 包根导入</done>
</task>
</tasks>
<verification>
- 所有测试通过: `pytest tests/test_platforms/test_bilibili.py -v`
- 导入测试: `python -c "from kb_pipeline import BilibiliAdapter"`
- 覆盖率: `pytest tests/test_platforms/ --cov=kb_pipeline/platforms --cov-report=term-missing`
</verification>
<success_criteria>
- [ ] PlatformAdapter 基类定义清晰,子类接口明确
- [ ] BilibiliAdapter 正确提取B站视频元数据
- [ ] BilibiliAdapter 正确提取B站专栏内容和元数据
- [ ] 内容保存为 Markdown 格式,包含 YAML Frontmatter
- [ ] 网络错误自动重试3次
- [ ] 403/超时错误触发降级策略,记录失败原因到 workflow_state.json
- [ ] 所有测试通过(至少 8 个测试用例)
</success_criteria>
<output>
After completion, create `.planning/phases/02-platform-skills/02-02-SUMMARY.md`
</output>