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/10-proxy-pool/10-01-PLAN.md
T

195 lines
6.9 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: 10-proxy-pool
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- kb_workflow/crawler/proxy_pool.py
- tests/test_crawler/test_proxy_pool.py
- tests/test_crawler/conftest.py
autonomous: true
requirements:
- PROXY-01
- PROXY-02
user_setup: []
must_haves:
truths:
- "用户可以从 YAML 文件加载多个代理地址"
- "用户可以从环境变量加载单个代理(向后兼容)"
- "每次请求自动选择下一个可用代理(Round-Robin)"
artifacts:
- path: "kb_workflow/crawler/proxy_pool.py"
provides: "ProxyPool, ProxyEntry, ProxyPoolConfig 类"
min_lines: 80
- path: "tests/test_crawler/test_proxy_pool.py"
provides: "代理池单元测试"
contains: "test_load_from_yaml, test_load_from_env, test_round_robin"
key_links:
- from: "ProxyPool.from_yaml()"
to: "~/.kb_proxies.yaml"
via: "PyYAML safe_load"
pattern: "yaml\\.safe_load"
- from: "ProxyPool.get_next_proxy()"
to: "ProxyEntry 列表"
via: "索引指针递增"
pattern: "_current_index.*\\+.*%.*len"
---
<objective>
实现代理池核心功能:配置加载和代理轮换。
Purpose: 建立多代理配置基础设施,支持从 YAML 文件或环境变量加载代理列表,实现 Round-Robin 轮换策略。
Output: proxy_pool.py 模块,包含 ProxyPool、ProxyEntry、ProxyPoolConfig 类。
</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/10-proxy-pool/10-CONTEXT.md
@.planning/phases/10-proxy-pool/10-RESEARCH.md
<interfaces>
<!-- Key interfaces from existing codebase that executor needs -->
From kb_workflow/crawler/config.py:
```python
class ProxyConfig(BaseModel):
server: str
username: Optional[str] = None
password: Optional[str] = None
def to_playwright_format(self) -> Dict[str, Any]:
"""Convert to Playwright proxy format."""
```
From kb_workflow/crawler/config.py:
```python
def get_crawler_config() -> CrawlerConfig:
"""Get crawler configuration singleton."""
```
From tests/test_crawler/conftest.py:
```python
@pytest.fixture
def mock_browser():
"""Mock PlaywrightBrowser instance."""
```
</interfaces>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 0: 创建测试脚手架</name>
<files>tests/test_crawler/test_proxy_pool.py, tests/test_crawler/conftest.py</files>
<behavior>
- Test load from YAML: 加载包含多个代理的 YAML 文件,返回 ProxyPool 实例
- Test load from env: 从 HTTPS_PROXY 环境变量加载单代理,返回 ProxyPool 实例
- Test round-robin: 连续调用 get_next_proxy() 返回按顺序轮换的代理
- Test empty pool: 无代理时 get_next_proxy() 返回 None
- Test single proxy: 单代理时总是返回该代理
</behavior>
<action>
创建 tests/test_crawler/test_proxy_pool.py 测试文件。
测试用例:
1. `test_load_from_yaml`: 使用 tmp_path 创建临时 YAML 文件,验证 ProxyPool.from_yaml() 正确加载代理列表
2. `test_load_from_yaml_not_found`: YAML 文件不存在时返回空 ProxyPool
3. `test_load_from_env`: mock HTTPS_PROXY 环境变量,验证 ProxyPool.from_env_fallback() 正确加载
4. `test_round_robin`: 创建包含 3 个代理的 ProxyPool,连续调用 get_next_proxy() 6 次,验证返回顺序为 A-B-C-A-B-C
5. `test_empty_pool`: 空 ProxyPool 的 get_next_proxy() 返回 None
6. `test_single_proxy`: 单代理 ProxyPool 总是返回该代理
在 conftest.py 中添加 proxy_pool_config fixture(如果需要)。
注意:这些测试应该先失败(RED),因为 ProxyPool 类尚未实现。
</action>
<verify>
<automated>pytest tests/test_crawler/test_proxy_pool.py -x -v 2>&1 | grep -E "(FAILED|ERROR|test_)" || echo "Tests not failing as expected for Wave 0"</automated>
</verify>
<done>测试文件存在,包含 6 个测试用例,运行后显示失败(RED 阶段)</done>
</task>
<task type="auto" tdd="true">
<name>Task 1: 实现 ProxyEntry + ProxyPool 核心类</name>
<files>kb_workflow/crawler/proxy_pool.py, tests/test_crawler/test_proxy_pool.py</files>
<behavior>
- ProxyEntry(url) 创建代理条目,默认 is_available=True, fail_count=0
- ProxyPool(config) 初始化代理池,从 config.proxies 创建 ProxyEntry 列表
- ProxyPool.from_yaml(path) 从 YAML 文件加载代理池配置
- ProxyPool.from_env_fallback() 从环境变量加载单代理
- ProxyPool.get_next_proxy() 返回下一个代理(Round-Robin)
- ProxyPool.has_proxies() 返回是否有配置的代理
</behavior>
<action>
创建 kb_workflow/crawler/proxy_pool.py 模块。
实现以下类(基于 RESEARCH.md Pattern 1 设计):
1. **ProxyEntry (Pydantic BaseModel)**:
- 字段: url: str, fail_count: int = 0, last_fail_time: Optional[datetime] = None, is_available: bool = True
- 方法: record_success(), record_failure()(此 task 仅 stub,Task 2 实现完整逻辑)
2. **ProxyPoolConfig (Pydantic BaseModel)**:
- 字段: proxies: list[str] = [], cooldown_seconds: int = 300, max_fail_count: int = 3
3. **ProxyPool**:
- `__init__(self, config: ProxyPoolConfig)`: 初始化代理池
- `from_yaml(cls, path: Path) -> ProxyPool`: 从 YAML 文件加载
- `from_env_fallback(cls) -> ProxyPool`: 从环境变量加载(向后兼容)
- `get_next_proxy(self) -> Optional[ProxyEntry]`: Round-Robin 轮换
- `has_proxies(self) -> bool`: 检查是否有代理
配置文件格式(~/.kb_proxies.yaml):
```yaml
proxies:
- "http://127.0.0.1:7890"
- "http://user:pass@proxy.example.com:8080"
cooldown_seconds: 300
max_fail_count: 3
```
轮换逻辑:维护 _current_index 指针,每次调用 get_next_proxy() 递增并循环。
运行测试直到全部通过(GREEN 阶段)。
</action>
<verify>
<automated>pytest tests/test_crawler/test_proxy_pool.py -x</automated>
</verify>
<done>
- proxy_pool.py 存在,包含 ProxyEntry, ProxyPoolConfig, ProxyPool 类
- 测试 test_load_from_yaml, test_load_from_env, test_round_robin 通过
- 代码通过 ruff check(如果项目配置了)
</done>
</task>
</tasks>
<verification>
**Wave 1 验证检查:**
1. `pytest tests/test_crawler/test_proxy_pool.py -x` 全部通过
2. ProxyPool 可从 YAML 文件加载多代理
3. ProxyPool 可从环境变量加载单代理(向后兼容)
4. Round-Robin 轮换正常工作
</verification>
<success_criteria>
- [x] 用户可以从 YAML 文件加载多个代理地址(PROXY-01)
- [x] 用户可以从环境变量加载单代理(PROXY-01 向后兼容)
- [x] 每次请求自动选择下一个可用代理(PROXY-02)
- [ ] 代理健康检查(PROXY-03 - Plan 02)
- [ ] 代理失败回退(PROXY-04 - Plan 02)
</success_criteria>
<output>
After completion, create `.planning/phases/10-proxy-pool/10-01-SUMMARY.md`
</output>