bd40f4965b
Change depends_on from [01-02] to ["02"] for consistency with plan frontmatter format. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
17 KiB
17 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, user_setup, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | user_setup | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 01-foundation | 03 | tdd | 2 |
|
|
true |
|
|
|
Purpose: 确保错误信息包含足够上下文便于调试,配置集中管理便于维护,网络错误自动恢复 Output: kb_pipeline/errors.py, config.py, retry.py + 完整的单元测试
<execution_context> @/home/starmen1265/.claude/get-shit-done/workflows/execute-plan.md @/home/starmen1265/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.mdFrom kb_pipeline/schemas.py:
class StepStatus(str, Enum):
PENDING = "pending"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
SKIPPED = "skipped"
From CONTEXT.md decisions:
- 错误分类: 网络错误 (403/404/500/超时), 验证错误, 系统错误, 认证/配额错误
- 重试策略: 网络错误自动重试3次,指数退避
- 错误信息: 步骤名、输入摘要、错误类型、失败原因、可操作建议
- 配置文件位置: ~/.kb_env
- 配置库: pydantic-settings
From RESEARCH.md Pattern 3 & 4:
# pydantic-settings configuration
from pydantic_settings import BaseSettings, SettingsConfigDict
class KBSettings(BaseSettings):
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="归档目录")
# Structured error handling
class ErrorType(str, Enum):
NETWORK = "network"
VALIDATION = "validation"
SYSTEM = "system"
AUTH = "auth"
class KBError(Exception):
def __init__(self, message, error_type, step_name, input_summary, suggestion, cause=None):
...
2. 实现 kb_pipeline/config.py:
- KBSettings (pydantic-settings BaseSettings)
- get_settings() 单例函数
3. 实现 kb_pipeline/retry.py:
- retry_on_network_error 装饰器 (使用 tenacity)
Task 1: Write failing tests for error handling
tests/test_pipeline/test_errors.py
- Test KBError contains all required fields
- Test KBError.to_dict() returns correct dict
- Test NetworkError includes status_code
- Test each error type has appropriate suggestion
创建 tests/test_pipeline/test_errors.py:
```python
import pytest
from kb_pipeline.errors import (
KBError, NetworkError, ValidationError, SystemError, AuthError, ErrorType
)
class TestKBError:
def test_kberror_contains_all_fields(self):
error = KBError(
message="Test error",
error_type=ErrorType.NETWORK,
step_name="test_step",
input_summary="input_id=123",
suggestion="Check your network"
)
assert error.step_name == "test_step"
assert error.input_summary == "input_id=123"
assert error.suggestion == "Check your network"
def test_kberror_to_dict(self):
error = KBError(...)
result = error.to_dict()
assert "step_name" in result
assert "error_type" in result
assert "suggestion" in result
class TestNetworkError:
def test_network_error_includes_status_code(self):
error = NetworkError(
step_name="fetch",
input_summary="url=http://example.com",
status_code=403
)
assert error.status_code == 403
assert "403" in error.suggestion
class TestErrorHierarchy:
def test_network_error_is_kberror(self):
assert issubclass(NetworkError, KBError)
```
pytest tests/test_pipeline/test_errors.py -v --collect-only 2>/dev/null | grep "test_" | wc -l | xargs -I {} test {} -ge 8
测试文件包含至少 8 个测试用例,所有测试当前失败(RED 阶段)
Task 2: Implement error handling
kb_pipeline/errors.py
- ErrorType enum with NETWORK, VALIDATION, SYSTEM, AUTH
- KBError base class with to_dict method
- NetworkError with status_code and auto-suggestion
- ValidationError for schema failures
- SystemError for file/memory/dependency issues
- AuthError for API quota/token issues
实现 kb_pipeline/errors.py:
```python
from enum import Enum
from typing import Optional, Any
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, cause: Exception = None):
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,
cause=cause
)
self.status_code = status_code
class ValidationError(KBError):
"""验证错误 (Schema 验证失败)"""
def __init__(self, step_name: str, input_summary: str, validation_errors: list = None, cause: Exception = None):
super().__init__(
message="Schema 验证失败",
error_type=ErrorType.VALIDATION,
step_name=step_name,
input_summary=input_summary,
suggestion="检查输入数据格式是否符合要求",
cause=cause
)
self.validation_errors = validation_errors or []
class SystemError(KBError):
"""系统错误 (文件系统/内存/依赖缺失)"""
def __init__(self, step_name: str, input_summary: str, system_message: str = None, cause: Exception = None):
super().__init__(
message=f"系统错误: {system_message or 'unknown'}",
error_type=ErrorType.SYSTEM,
step_name=step_name,
input_summary=input_summary,
suggestion="检查系统资源和依赖是否满足要求",
cause=cause
)
class AuthError(KBError):
"""认证/配额错误 (API配额/Cookie过期/Token失效)"""
def __init__(self, step_name: str, input_summary: str, auth_type: str = None, cause: Exception = None):
super().__init__(
message=f"认证失败: {auth_type or 'unknown'}",
error_type=ErrorType.AUTH,
step_name=step_name,
input_summary=input_summary,
suggestion="检查 API Token 或 Cookie 是否有效",
cause=cause
)
self.auth_type = auth_type
```
注意: SystemError 与 Python 内置同名,考虑命名空间或使用别名
pytest tests/test_pipeline/test_errors.py -v --tb=short
所有测试通过(GREEN 阶段)
Task 3: Write and implement configuration management
kb_pipeline/config.py, tests/test_pipeline/test_config.py
- KBSettings loads from ~/.kb_env
- Required fields: base_path, archive_path
- Optional fields: git_root, git_user, git_email
- Secret fields: hf_token, zhihu_cookie (from env vars)
- get_settings() returns singleton
- Missing required field raises ValidationError
1. 创建 tests/test_pipeline/test_config.py:
- Test KBSettings loads from env file
- Test required fields validation
- Test get_settings returns singleton
2. 实现 kb_pipeline/config.py:
```python
from pydantic import Field, SecretStr
from pydantic_settings import BaseSettings, SettingsConfigDict
from functools import lru_cache
class KBSettings(BaseSettings):
"""KB 工作流配置"""
model_config = SettingsConfigDict(
env_file=str(Path.home() / '.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=SecretStr(""), description="HuggingFace Token")
zhihu_cookie: SecretStr = Field(default=SecretStr(""), description="知乎 Cookie")
@lru_cache
def get_settings() -> KBSettings:
"""获取配置单例"""
return KBSettings()
```
pytest tests/test_pipeline/test_config.py -v --tb=short
所有测试通过,配置管理正常工作
Task 4: Write and implement retry mechanism
kb_pipeline/retry.py, tests/test_pipeline/test_retry.py
- retry_on_network_error decorator retries 3 times
- Uses exponential backoff (2s, 4s, 8s max)
- Only retries on NetworkError
- Logs retry attempts
- Reraises after max attempts
1. 创建 tests/test_pipeline/test_retry.py:
- Test retry succeeds on second attempt
- Test retry exhausts attempts and raises
- Test non-NetworkError is not retried
- Test exponential backoff timing
2. 实现 kb_pipeline/retry.py:
```python
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
)
```
pytest tests/test_pipeline/test_retry.py -v --tb=short
所有测试通过,重试机制正常工作
Task 5: Update package exports
kb_pipeline/__init__.py
- All public APIs exported from package
- Clean import interface
更新 kb_pipeline/__init__.py 导出:
```python
"""KB Pipeline - 工作流管道基础设施"""
from .schemas import (
StepInput, StepOutput, StepResult,
StepStatus, StepRecord, WorkflowState, StepDefinition
)
from .state import WorkflowStateManager
from .errors import (
KBError, NetworkError, ValidationError,
SystemError, AuthError, ErrorType
)
from .config import KBSettings, get_settings
from .retry import retry_on_network_error
__all__ = [
# Schemas
"StepInput", "StepOutput", "StepResult",
"StepStatus", "StepRecord", "WorkflowState", "StepDefinition",
# State
"WorkflowStateManager",
# Errors
"KBError", "NetworkError", "ValidationError",
"SystemError", "AuthError", "ErrorType",
# Config
"KBSettings", "get_settings",
# Retry
"retry_on_network_error",
]
```
python -c "from kb_pipeline import WorkflowStateManager, KBSettings, retry_on_network_error" && echo "OK"
所有公共 API 可从包根导入
1. 运行 `pytest tests/test_pipeline/test_errors.py -v` 全部通过
2. 运行 `pytest tests/test_pipeline/test_config.py -v` 全部通过
3. 运行 `pytest tests/test_pipeline/test_retry.py -v` 全部通过
4. 验证错误 to_dict() 包含所有必要字段
5. 验证配置缺失必需字段时抛出 ValidationError
<success_criteria>
- 结构化错误类型完整,包含上下文信息
- 配置从 ~/.kb_env 加载,缺失必需字段报错
- 网络错误自动重试,指数退避
- 所有测试通过,覆盖率 > 80% </success_criteria>