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/01-foundation/01-RESEARCH.md
T

631 lines
26 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 1: Foundation - Research
**Researched:** 2026-03-13
**Domain:** Python workflow infrastructure - state management, schema validation, configuration, error handling
**Confidence:** HIGH
## Summary
Phase 1 建立工作流基础设施的核心组件:状态管理器、Schema 注册表、错误处理器和配置管理器。这些组件遵循 Python 生态系统的最佳实践,使用 Pydantic v2 进行数据验证,pydantic-settings 进行配置管理,以及原子文件写入模式保证状态持久化的可靠性。
**Primary recommendation:** 使用 Pydantic v2 BaseModel 定义所有 Schema,pydantic-settings 管理配置,复用 gsd-openclaw/state_manager.py 的原子写入模式,定义统一的错误类型层次结构。
<user_constraints>
## User Constraints (from CONTEXT.md)
### Locked Decisions
#### 状态管理
- **状态粒度**: 步骤级状态 — 符合现有子技能映射 (metadata→content→whisper→analyzer)
- **状态关联**: 按内容ID关联 — 如 BV123xxx,重复链接自动识别
- **幂等执行**: 自动跳过已完成步骤 — 重新执行同一链接时从中断点继续
- **备份机制**: 需要备份 — 原子写入 + .bak 轮转,崩溃后可恢复
- **存储位置**: 状态文件存放在内容目录下 — 方便关联查看
- **历史记录**: 只保留最新状态 — 不追加历史执行记录
#### Schema 验证
- **验证时机**: 立即验证 — 步骤完成后立即验证,失败则阻止进入下一步
- **Schema 定义位置**: 集中定义 — 所有步骤的 Schema 集中在一个模块
- **验证范围**: 验证输入和输出 — 步骤执行前验证输入,执行后验证输出
- **Schema 格式**: Pydantic 模型 — 自带验证和序列化,类型安全
#### 错误处理
- **错误分类**: 支持四种错误类型
- 网络错误 (403/404/500/超时)
- 验证错误 (Schema 验证失败)
- 系统错误 (文件系统/内存/依赖缺失)
- 认证/配额错误 (API配额/Cookie过期/Token失效)
- **重试策略**: 自动重试 — 网络错误自动重试3次,指数退避
- **错误信息**: 完整上下文 — 步骤名、输入摘要、错误类型、失败原因、可操作建议
- **降级策略**: 定义降级链 — 如官方字幕失败后尝试语音识别
#### 配置管理
- **配置文件位置**: ~/.kb_env — 延续现有习惯
- **验证时机**: 启动时验证 — 缺失必需变量则报错退出
- **敏感信息处理**: 环境变量存敏感 — Cookie/Token 用环境变量,配置文件只存非敏感配置
- **配置库**: pydantic-settings — 自动验证和类型转换
### Claude's Discretion
无特别指定 — 在锁定决策框架内做技术实现选择。
### Deferred Ideas (OUT OF SCOPE)
None — discussion stayed within phase scope
</user_constraints>
<phase_requirements>
## Phase Requirements
| ID | Description | Research Support |
|----|-------------|-----------------|
| PIPE-01 | 统一的管道模型 - 每个步骤是独立管道,定义清晰的输入→处理→输出 | Pydantic BaseModel 定义 StepInput/StepOutput |
| PIPE-02 | 步骤级状态管理 - 主要步骤完成后记录状态到 workflow_state.json | 原子写入 + 备份轮转模式 (参考 gsd-openclaw/state_manager.py) |
| PIPE-03 | 统一 JSON Schema - 所有子技能遵守统一的 StepInput/StepOutput 格式规范 | Pydantic v2 model_dump() / model_validate() |
| REL-01 | 幂等执行保证 - 重复执行同一链接不会产生重复文件或重复处理 | WorkflowState.is_step_completed() 检查 |
| REL-02 | 错误恢复策略 - 403/超时/网络错误有明确的降级策略和重试机制 | FallbackChain + retry_with_backoff() |
| REL-03 | 清晰的流程定义 - 每个步骤的前置条件、输出格式、成功标准明确 | StepDefinition + StepResult Pydantic 模型 |
| VAL-01 | Schema 验证机制 - 每个步骤输出通过 Pydantic Schema 验证,失败则阻止进入下一步 | StepOutput 基类 + model_validate() |
| MNT-02 | 统一配置管理 - 环境变量集中在 ~/.kb_env,使用 pydantic-settings 管理 | pydantic-settings BaseSettings + env_file |
| MNT-03 | 结构化错误处理 - 错误包含上下文信息,便于调试和日志追踪 | KBError 层次结构 + StructuredErrorContext |
</phase_requirements>
## Standard Stack
### Core
| Library | Version | Purpose | Why Standard |
|---------|---------|---------|--------------|
| pydantic | 2.x | 数据验证和 Schema 定义 | Python 生态类型安全验证的事实标准,v2 性能提升 5-50x |
| pydantic-settings | 2.x | 配置管理 | 官方配置库,自动环境变量映射和验证 |
| python | 3.12+ | 运行时 | 项目约束,支持最新类型语法 |
### Supporting
| Library | Version | Purpose | When to Use |
|---------|---------|---------|-------------|
| pytest | 7.0+ | 测试框架 | 所有单元测试和集成测试 |
| tenacity | 8.x | 重试机制 | 网络错误自动重试,指数退避 |
### Alternatives Considered
| Instead of | Could Use | Tradeoff |
|------------|-----------|----------|
| pydantic | dataclasses + 手动验证 | 失去自动验证、序列化、JSON Schema 生成 |
| pydantic-settings | dynaconf | dynaconf 更复杂,pydantic-settings 更轻量且与 Pydantic 无缝集成 |
| tenacity | 手写重试逻辑 | tenacity 提供成熟的指数退避和钩子机制 |
**Installation:**
```bash
pip install pydantic>=2.0 pydantic-settings>=2.0 pytest>=7.0 tenacity>=8.0
```
## Architecture Patterns
### Recommended Project Structure
```
kb-skills/
├── kb_workflow/ # 现有工作流工具
│ └── kb_workflow.py
├── kb_pipeline/ # 新建:核心管道基础设施
│ ├── __init__.py
│ ├── state.py # WorkflowStateManager
│ ├── schemas.py # StepInput, StepOutput, StepResult
│ ├── errors.py # KBError 层次结构
│ ├── config.py # KBSettings (pydantic-settings)
│ └── retry.py # retry_with_backoff
└── tests/
└── test_pipeline/
├── test_state.py
├── test_schemas.py
├── test_errors.py
└── test_config.py
```
### Pattern 1: Atomic State Persistence
**What:** 原子写入状态文件,防止崩溃时数据损坏
**When to use:** 所有 workflow_state.json 写入操作
**Example:**
```python
# Source: gsd-openclaw/src/gsd_openclaw/state_manager.py (已验证)
import json
import os
import tempfile
from pathlib import Path
def save_state_atomically(state: dict, state_path: Path) -> None:
"""Atomic state save with backup rotation."""
backup_path = state_path.with_suffix('.json.bak')
# 1. Create parent directories if missing
state_path.parent.mkdir(parents=True, exist_ok=True)
# 2. Rotate backup: if state.json exists, move it to state.json.bak
if state_path.exists():
if backup_path.exists():
backup_path.unlink()
state_path.rename(backup_path)
# 3. Write to temp file in same directory (for atomic rename)
fd, temp_path = tempfile.mkstemp(dir=state_path.parent, suffix='.tmp')
try:
with os.fdopen(fd, 'w', encoding='utf-8') as f:
json.dump(state, f, indent=2, ensure_ascii=False)
# 4. Atomic replace
os.replace(temp_path, state_path)
except Exception:
if os.path.exists(temp_path):
os.unlink(temp_path)
raise
```
### Pattern 2: Pydantic Schema Registry
**What:** 集中定义所有步骤的输入输出 Schema
**When to use:** 定义新步骤或验证步骤输出
**Example:**
```python
# Source: https://docs.pydantic.dev/latest/concepts/models/
from pydantic import BaseModel, Field
from typing import Optional, Literal
from datetime import datetime
class StepInput(BaseModel):
"""所有步骤输入的基类"""
content_id: str = Field(..., description="内容唯一标识,如 BV号")
platform: Literal["bilibili", "zhihu"] = Field(..., description="平台名称")
class StepOutput(BaseModel):
"""所有步骤输出的基类"""
success: bool = Field(..., description="步骤是否成功")
error_message: Optional[str] = Field(None, description="错误信息")
completed_at: datetime = Field(default_factory=datetime.now)
class Config:
# Pydantic v2 配置
json_schema_extra = {
"example": {
"success": True,
"error_message": None,
"completed_at": "2026-03-13T10:00:00"
}
}
class MetadataStepOutput(StepOutput):
"""元数据步骤输出"""
title: Optional[str] = None
author: Optional[str] = None
duration_seconds: Optional[int] = None
```
### Pattern 3: pydantic-settings Configuration
**What:** 使用 pydantic-settings 管理环境变量配置
**When to use:** 应用启动时加载配置
**Example:**
```python
# Source: https://docs.pydantic.dev/latest/concepts/pydantic_settings/
from pydantic import Field, SecretStr
from pydantic_settings import BaseSettings, SettingsConfigDict
class KBSettings(BaseSettings):
"""KB 工作流配置"""
model_config = SettingsConfigDict(
env_file='~/.kb_env',
env_file_encoding='utf-8',
env_prefix='KB_',
extra='ignore' # 兼容旧配置文件
)
# 必需配置
base_path: str = Field(..., description="知识库根目录")
archive_path: str = Field(..., description="归档目录")
# 可选配置
git_root: str = Field(default="", description="Git 仓库根目录")
git_user: str = Field(default="", description="Git 用户名")
git_email: str = Field(default="", description="Git 邮箱")
# 敏感信息(从环境变量读取,不存配置文件)
hf_token: SecretStr = Field(default="", description="HuggingFace Token")
zhihu_cookie: SecretStr = Field(default="", description="知乎 Cookie")
# 使用
settings = KBSettings() # 自动从 ~/.kb_env 和环境变量加载
```
### Pattern 4: Structured Error Handling
**What:** 结构化错误类型,包含完整上下文
**When to use:** 所有步骤失败时抛出
**Example:**
```python
from enum import Enum
from typing import Optional, Any
from pydantic import BaseModel
class ErrorType(str, Enum):
NETWORK = "network"
VALIDATION = "validation"
SYSTEM = "system"
AUTH = "auth"
class KBError(Exception):
"""KB 工作流基础错误"""
def __init__(
self,
message: str,
error_type: ErrorType,
step_name: str,
input_summary: str,
suggestion: str,
cause: Optional[Exception] = None
):
super().__init__(message)
self.error_type = error_type
self.step_name = step_name
self.input_summary = input_summary
self.suggestion = suggestion
self.cause = cause
def to_dict(self) -> dict:
return {
"error_type": self.error_type.value,
"step_name": self.step_name,
"input_summary": self.input_summary,
"message": str(self),
"suggestion": self.suggestion
}
class NetworkError(KBError):
"""网络错误 (403/404/500/超时)"""
def __init__(self, step_name: str, input_summary: str, status_code: int = None, **kwargs):
suggestion = f"检查网络连接,状态码: {status_code}" if status_code else "检查网络连接"
super().__init__(
message=f"网络请求失败: {status_code or 'unknown'}",
error_type=ErrorType.NETWORK,
step_name=step_name,
input_summary=input_summary,
suggestion=suggestion,
**kwargs
)
self.status_code = status_code
```
### Anti-Patterns to Avoid
- **直接写入状态文件无原子保证**: 崩溃时可能导致文件损坏或内容丢失
- **散落的配置读取**: `os.environ.get()` 分散在多个文件,难以追踪和测试
- **裸 Exception 捕获**: 丢失错误类型信息,无法实现针对性恢复
- **Schema 定义分散**: 每个子技能自己定义格式,难以统一验证
## Don't Hand-Roll
| Problem | Don't Build | Use Instead | Why |
|---------|-------------|-------------|-----|
| 配置管理 | 手写 env 文件解析 | pydantic-settings | 自动类型转换、验证、环境变量映射 |
| 数据验证 | 手写 if-else 检查 | Pydantic BaseModel | 类型安全、自动序列化、JSON Schema 生成 |
| 重试逻辑 | while 循环 + sleep | tenacity | 成熟的指数退避、钩子、停止条件 |
| 原子写入 | 手写临时文件逻辑 | 参考 state_manager.py 模式 | 边界情况处理(权限、磁盘满、崩溃) |
**Key insight:** 配置和验证是 Python 生态中已解决的问题,使用 Pydantic 生态可以获得类型安全、IDE 支持和自动文档生成。
## Common Pitfalls
### Pitfall 1: 状态文件损坏
**What goes wrong:** 写入状态文件时崩溃,导致文件内容不完整或为空
**Why it happens:** 直接写入目标文件,没有使用原子替换
**How to avoid:** 使用 write-to-temp-then-rename 模式,确保写入是原子的
**Warning signs:** workflow_state.json 为空或包含截断的 JSON
### Pitfall 2: 配置分散难以调试
**What goes wrong:** 环境变量散落在多个文件,不知道哪个变量在哪使用
**Why it happens:** 每个模块直接调用 `os.environ.get()`
**How to avoid:** 使用 pydantic-settings 集中定义所有配置,启动时一次性验证
**Warning signs:** 同一变量在多处读取,默认值不一致
### Pitfall 3: Schema 演进导致兼容性问题
**What goes wrong:** 添加新字段后旧的状态文件无法加载
**Why it happens:** Schema 变更没有向后兼容性考虑
**How to avoid:**
- 使用 Optional 类型和默认值
- 使用 schema_version 字段标记版本
- load_state 时处理旧版本数据
### Pitfall 4: 错误信息缺少上下文
**What goes wrong:** 错误只有 "Network error",不知道是哪个步骤、哪个输入
**Why it happens:** 只抛出简单的 Exception 或使用通用错误消息
**How to avoid:** 使用结构化错误类,强制包含 step_name, input_summary, suggestion
## Code Examples
### WorkflowState 完整定义
```python
# Source: 基于 gsd-openclaw/state_manager.py 和 CONTEXT.md 决策
from pydantic import BaseModel, Field
from typing import Optional, Dict, List
from datetime import datetime
from enum import Enum
class StepStatus(str, Enum):
PENDING = "pending"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
SKIPPED = "skipped"
class StepRecord(BaseModel):
"""单个步骤的执行记录"""
step_name: str
status: StepStatus
started_at: Optional[datetime] = None
completed_at: Optional[datetime] = None
error_message: Optional[str] = None
output_summary: Optional[str] = None
class WorkflowState(BaseModel):
"""工作流状态 - 存储在 workflow_state.json"""
schema_version: str = Field(default="1.0")
content_id: str = Field(..., description="内容唯一标识")
platform: str = Field(..., description="平台名称")
source_url: str = Field(..., description="原始链接")
created_at: datetime = Field(default_factory=datetime.now)
updated_at: datetime = Field(default_factory=datetime.now)
steps: Dict[str, StepRecord] = Field(default_factory=dict)
def is_step_completed(self, step_name: str) -> bool:
"""检查步骤是否已完成"""
record = self.steps.get(step_name)
return record is not None and record.status == StepStatus.COMPLETED
def mark_step_started(self, step_name: str) -> None:
"""标记步骤开始"""
self.steps[step_name] = StepRecord(
step_name=step_name,
status=StepStatus.RUNNING,
started_at=datetime.now()
)
self.updated_at = datetime.now()
def mark_step_completed(self, step_name: str, output_summary: str = None) -> None:
"""标记步骤完成"""
if step_name in self.steps:
self.steps[step_name].status = StepStatus.COMPLETED
self.steps[step_name].completed_at = datetime.now()
self.steps[step_name].output_summary = output_summary
else:
self.steps[step_name] = StepRecord(
step_name=step_name,
status=StepStatus.COMPLETED,
completed_at=datetime.now(),
output_summary=output_summary
)
self.updated_at = datetime.now()
def mark_step_failed(self, step_name: str, error_message: str) -> None:
"""标记步骤失败"""
if step_name in self.steps:
self.steps[step_name].status = StepStatus.FAILED
self.steps[step_name].error_message = error_message
self.steps[step_name].completed_at = datetime.now()
else:
self.steps[step_name] = StepRecord(
step_name=step_name,
status=StepStatus.FAILED,
error_message=error_message,
completed_at=datetime.now()
)
self.updated_at = datetime.now()
```
### WorkflowStateManager
```python
# Source: 基于 gsd-openclaw/state_manager.py
import json
from pathlib import Path
from typing import Optional
from .schemas import WorkflowState
class WorkflowStateManager:
"""工作流状态管理器"""
STATE_FILENAME = "workflow_state.json"
def __init__(self, content_dir: Path):
"""
Args:
content_dir: 内容目录路径,状态文件将存放在此目录下
"""
self.content_dir = Path(content_dir)
self.state_path = self.content_dir / self.STATE_FILENAME
def load(self) -> Optional[WorkflowState]:
"""加载状态,如果不存在返回 None"""
if not self.state_path.exists():
# 尝试加载备份
backup_path = self.state_path.with_suffix('.json.bak')
if backup_path.exists():
try:
with open(backup_path, 'r', encoding='utf-8') as f:
data = json.load(f)
return WorkflowState.model_validate(data)
except (json.JSONDecodeError, ValueError):
pass
return None
try:
with open(self.state_path, 'r', encoding='utf-8') as f:
data = json.load(f)
return WorkflowState.model_validate(data)
except (json.JSONDecodeError, ValueError) as e:
# 主文件损坏,尝试备份
backup_path = self.state_path.with_suffix('.json.bak')
if backup_path.exists():
with open(backup_path, 'r', encoding='utf-8') as f:
data = json.load(f)
return WorkflowState.model_validate(data)
raise
def save(self, state: WorkflowState) -> None:
"""原子保存状态"""
import os
import tempfile
backup_path = self.state_path.with_suffix('.json.bak')
# 确保目录存在
self.content_dir.mkdir(parents=True, exist_ok=True)
# 轮转备份
if self.state_path.exists():
if backup_path.exists():
backup_path.unlink()
self.state_path.rename(backup_path)
# 原子写入
fd, temp_path = tempfile.mkstemp(dir=self.content_dir, suffix='.tmp')
try:
with os.fdopen(fd, 'w', encoding='utf-8') as f:
f.write(state.model_dump_json(indent=2))
os.replace(temp_path, self.state_path)
except Exception:
if os.path.exists(temp_path):
os.unlink(temp_path)
raise
def get_or_create(self, content_id: str, platform: str, source_url: str) -> WorkflowState:
"""获取现有状态或创建新状态"""
state = self.load()
if state is None:
state = WorkflowState(
content_id=content_id,
platform=platform,
source_url=source_url
)
return state
```
### Retry with Exponential Backoff
```python
# Source: tenacity 最佳实践
from tenacity import (
retry,
stop_after_attempt,
wait_exponential,
retry_if_exception_type,
before_sleep_log
)
import logging
from .errors import NetworkError
logger = logging.getLogger(__name__)
retry_on_network_error = retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=2, max=10),
retry=retry_if_exception_type(NetworkError),
before_sleep=before_sleep_log(logger, logging.WARNING),
reraise=True
)
# 使用
@retry_on_network_error
def fetch_with_retry(url: str) -> bytes:
"""带重试的网络请求"""
import requests
try:
response = requests.get(url, timeout=30)
response.raise_for_status()
return response.content
except requests.RequestException as e:
raise NetworkError(
step_name="fetch",
input_summary=f"url={url}",
status_code=getattr(e.response, 'status_code', None) if hasattr(e, 'response') else None,
cause=e
)
```
## State of the Art
| Old Approach | Current Approach | When Changed | Impact |
|--------------|------------------|--------------|--------|
| Pydantic v1 Config class | Pydantic v2 model_config | Pydantic v2 (2023) | 更清晰的配置语法,性能提升 |
| 手写 env 文件解析 | pydantic-settings | pydantic-settings 2.0 (2023) | 自动验证、类型转换、嵌套模型支持 |
| 手写重试逻辑 | tenacity 库 | 成熟实践 | 指数退避、钩子、条件重试 |
| dataclass + 手动验证 | Pydantic BaseModel | Pydantic v2 (2023) | 性能提升 5-50x,更丰富的验证器 |
**Deprecated/outdated:**
- Pydantic v1 `class Config:` 语法: 使用 `model_config = ConfigDict(...)` 替代
- python-dotenv 单独使用: pydantic-settings 已内置 dotenv 支持
## Open Questions
1. **降级链如何配置化?**
- What we know: 需要支持 "官方字幕 → Whisper → FunASR" 这样的降级链
- What's unclear: 降级链是硬编码还是可配置?配置格式是什么?
- Recommendation: Phase 1 实现简单的 FallbackChain 类,降级顺序通过代码定义;Phase 2+ 考虑配置文件支持
2. **状态文件的并发访问?**
- What we know: 当前设计假设单进程执行
- What's unclear: 是否需要支持多进程同时处理不同内容?
- Recommendation: Phase 1 不处理并发,使用文件锁是后续优化点
## Validation Architecture
### Test Framework
| Property | Value |
|----------|-------|
| Framework | pytest 7.x |
| Config file | pyproject.toml (参考 gsd-openclaw) |
| Quick run command | `pytest tests/test_pipeline/ -v --tb=short` |
| Full suite command | `pytest tests/ -v --tb=short` |
### Phase Requirements → Test Map
| Req ID | Behavior | Test Type | Automated Command | File Exists? |
|--------|----------|-----------|-------------------|-------------|
| PIPE-01 | StepInput/StepOutput Schema 定义 | unit | `pytest tests/test_pipeline/test_schemas.py -v` | Wave 0 |
| PIPE-02 | 原子状态保存和备份轮转 | unit | `pytest tests/test_pipeline/test_state.py::TestAtomicWrite -v` | Wave 0 |
| PIPE-03 | JSON Schema 导出和验证 | unit | `pytest tests/test_pipeline/test_schemas.py::TestJSONSchema -v` | Wave 0 |
| REL-01 | is_step_completed 幂等检查 | unit | `pytest tests/test_pipeline/test_state.py::TestIdempotency -v` | Wave 0 |
| REL-02 | 网络错误重试 | unit | `pytest tests/test_pipeline/test_retry.py -v` | Wave 0 |
| REL-03 | StepDefinition 验证 | unit | `pytest tests/test_pipeline/test_schemas.py::TestStepDefinition -v` | Wave 0 |
| VAL-01 | Pydantic Schema 验证 | unit | `pytest tests/test_pipeline/test_schemas.py::TestValidation -v` | Wave 0 |
| MNT-02 | pydantic-settings 配置加载 | unit | `pytest tests/test_pipeline/test_config.py -v` | Wave 0 |
| MNT-03 | 结构化错误上下文 | unit | `pytest tests/test_pipeline/test_errors.py -v` | Wave 0 |
### Sampling Rate
- **Per task commit:** `pytest tests/test_pipeline/ -v --tb=short -x`
- **Per wave merge:** `pytest tests/ -v --tb=short`
- **Phase gate:** Full suite green before `/gsd:verify-work`
### Wave 0 Gaps
- [ ] `tests/test_pipeline/test_state.py` — 测试 WorkflowStateManager
- [ ] `tests/test_pipeline/test_schemas.py` — 测试 StepInput/StepOutput/StepResult
- [ ] `tests/test_pipeline/test_errors.py` — 测试 KBError 层次结构
- [ ] `tests/test_pipeline/test_config.py` — 测试 KBSettings
- [ ] `tests/test_pipeline/test_retry.py` — 测试重试逻辑
- [ ] `tests/test_pipeline/conftest.py` — 共享 fixtures
- [ ] `kb_pipeline/__init__.py` — 包初始化
## Sources
### Primary (HIGH confidence)
- [Pydantic Models Documentation](https://docs.pydantic.dev/latest/concepts/models/) - BaseModel, validation, serialization
- [Pydantic Settings Documentation](https://docs.pydantic.dev/latest/concepts/pydantic_settings/) - BaseSettings, env_file, secrets
- [gsd-openclaw/state_manager.py](/home/starmen1265/work/kb-skills/gsd-openclaw/src/gsd_openclaw/state_manager.py) - 原子写入、备份轮转参考实现
- [gsd-openclaw/tests/test_state_manager.py](/home/starmen1265/work/kb-skills/gsd-openclaw/tests/test_state_manager.py) - 状态管理测试模式参考
### Secondary (MEDIUM confidence)
- [kb-workflow/kb_workflow.py](/home/starmen1265/work/kb-skills/kb-workflow/kb_workflow.py) - 现有目录管理和查重逻辑
### Tertiary (LOW confidence)
- 无 — 所有关键信息已从 Primary 源验证
## Metadata
**Confidence breakdown:**
- Standard stack: HIGH - Pydantic 是 Python 生态成熟方案,文档完整
- Architecture: HIGH - 复用 gsd-openclaw 已验证的模式,符合项目决策
- Pitfalls: HIGH - 基于实际代码审查和已知问题
**Research date:** 2026-03-13
**Valid until:** 2026-04-13 (30 days - Pydantic 生态稳定)