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/research/ARCHITECTURE.md
T
starmen1265 2561de6ad7
PR Test / lint (pull_request) Successful in 2s
docs: update research for v1.3 anti-crawler optimization
- Add architecture research for proxy pool, behavior camouflage,
  captcha detection, and error recovery
- Update features, pitfalls, and stack documentation
- Add phase 10 validation checklist

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 20:45:14 +08:00

1440 lines
50 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.
# Architecture Research
**Domain:** Skill-based Workflow Orchestration System
**Researched:** 2026-03-13 (updated 2026-03-18 for v1.3)
**Confidence:** HIGH
---
## v1.3 补充:反爬虫优化架构
**Milestone:** Anti-Crawler Optimization
**Researched:** 2026-03-18
**Focus:** 代理池、行为伪装、验证码检测、错误恢复
---
## Executive Summary
本文档研究如何将反爬虫优化功能集成到现有的 kb-skills 架构中。基于对现有代码结构和反爬虫最佳实践的分析,推荐采用 **模块化的中间件/装饰器模式**,将反爬虫功能作为独立组件注入到现有的 Pipeline 和 Crawler 中。
关键架构决策:
1. **代理池管理** — 新建 `kb_workflow/crawler/proxy_pool.py` 模块,作为独立服务
2. **行为伪装** — 通过装饰器/中间件模式集成到 Playwright 调用
3. **验证码处理** — 检测-暂停机制,不自动解决(避免付费服务依赖)
4. **错误恢复** — 增强现有 `retry.py`,添加 jitter 和状态感知
---
## Current Architecture Overview
```
┌─────────────────────────────────────────────────────────────────────────┐
│ CLI Layer │
│ kb_workflow/cli.py - process/login subcommands │
├─────────────────────────────────────────────────────────────────────────┤
│ Pipeline Layer │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ ParseProcessor │→ │DownloadProcessor│→ │ ASRProcessor │ → ... │
│ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │ │
│ ↓ ↓ ↓ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Platform Adapters (BilibiliAdapter, ZhihuAdapter) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────────────┤
│ Crawler Layer │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ZhihuCrawler (Playwright) - API Interception + Cookie Auth │ │
│ └─────────────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────────────┤
│ Infrastructure │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ CrawlerConfig │ │ KBSettings │ │ WorkflowState│ │
│ └───────────────┘ └───────────────┘ └───────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Recommended New Architecture (v1.3)
```
┌─────────────────────────────────────────────────────────────────────────┐
│ CLI Layer │
│ kb_workflow/cli.py - process/login subcommands │
├─────────────────────────────────────────────────────────────────────────┤
│ Pipeline Layer │
│ [Existing processors - unchanged] │
├─────────────────────────────────────────────────────────────────────────┤
│ Platform Adapters Layer │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ZhihuAdapter / BilibiliAdapter │ │
│ │ (uses @with_anti_detection decorator) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────────────┤
│ Anti-Crawler Layer (NEW) │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ ProxyPool │ │BehaviorCamouflag│ │ CaptchaDetector │ │
│ │ Manager │ │ e │ │ │ │
│ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │ │
│ └────────────────────┴────────────────────┘ │
│ │ │
│ ┌───────────┴───────────┐ │
│ │ @with_anti_detection │ │
│ │ (decorator) │ │
│ └───────────────────────┘ │
├─────────────────────────────────────────────────────────────────────────┤
│ Crawler Layer │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ZhihuCrawler (enhanced with anti-detection hooks) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────────────┤
│ Infrastructure │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │CrawlerConfig │ │AntiCrawlerConf│ │ KBSettings │ │
│ │ (enhanced) │ │ (NEW) │ │ (enhanced) │ │
│ └───────────────┘ └───────────────┘ └───────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Component Design
### 1. Proxy Pool Manager
**Location:** `kb_workflow/crawler/proxy_pool.py`
**Rationale:** 独立模块,与现有 `CrawlerConfig` 分离,因为:
- 代理池是状态性的(需要跟踪健康状态、使用次数)
- 可能被多个 Crawler 共享
- 需要独立的配置源(环境变量、文件、API)
**Design:**
```python
# kb_workflow/crawler/proxy_pool.py
from typing import Optional, List, Dict, Any
from pydantic import BaseModel
import random
from dataclasses import dataclass, field
from datetime import datetime
@dataclass
class ProxyInfo:
"""代理信息"""
url: str
protocol: str = "http" # http, https, socks5
username: Optional[str] = None
password: Optional[str] = None
last_used: Optional[datetime] = None
fail_count: int = 0
success_count: int = 0
is_healthy: bool = True
def to_playwright_format(self) -> Dict[str, Any]:
config = {"server": self.url}
if self.username:
config["username"] = self.username
if self.password:
config["password"] = self.password
return config
class ProxyPoolConfig(BaseModel):
"""代理池配置"""
enabled: bool = False
# 代理列表(支持多个代理)
proxies: List[str] = []
# 轮换策略: round_robin, random, least_used
rotation_strategy: str = "random"
# 健康检查
health_check_enabled: bool = True
max_fail_count: int = 3 # 连续失败 N 次后标记为不健康
# 冷却时间(秒)
cooldown_seconds: float = 0 # 同一代理两次使用之间的最小间隔
@classmethod
def from_env(cls) -> "ProxyPoolConfig":
"""从环境变量加载代理池配置"""
import os
proxies_str = os.environ.get("KB_PROXY_POOL", "")
proxies = [p.strip() for p in proxies_str.split(",") if p.strip()]
return cls(
enabled=len(proxies) > 0,
proxies=proxies,
rotation_strategy=os.environ.get("KB_PROXY_STRATEGY", "random"),
)
class ProxyPoolManager:
"""代理池管理器
职责:
- 管理代理列表
- 轮换策略
- 健康状态跟踪
"""
def __init__(self, config: Optional[ProxyPoolConfig] = None):
self.config = config or ProxyPoolConfig.from_env()
self._proxies: List[ProxyInfo] = []
self._current_index = 0
self._init_proxies()
def _init_proxies(self):
"""初始化代理列表"""
for proxy_url in self.config.proxies:
self._proxies.append(ProxyInfo(url=proxy_url))
def get_proxy(self) -> Optional[ProxyInfo]:
"""获取下一个可用代理"""
healthy_proxies = [p for p in self._proxies if p.is_healthy]
if not healthy_proxies:
return None
if self.config.rotation_strategy == "round_robin":
proxy = healthy_proxies[self._current_index % len(healthy_proxies)]
self._current_index += 1
else: # random
proxy = random.choice(healthy_proxies)
proxy.last_used = datetime.now()
return proxy
def report_success(self, proxy_url: str):
"""报告代理使用成功"""
for p in self._proxies:
if p.url == proxy_url:
p.success_count += 1
p.fail_count = 0 # 重置失败计数
break
def report_failure(self, proxy_url: str):
"""报告代理使用失败"""
for p in self._proxies:
if p.url == proxy_url:
p.fail_count += 1
if p.fail_count >= self.config.max_fail_count:
p.is_healthy = False
break
# Module-level singleton
_proxy_pool: Optional[ProxyPoolManager] = None
def get_proxy_pool() -> ProxyPoolManager:
"""获取代理池管理器单例"""
global _proxy_pool
if _proxy_pool is None:
_proxy_pool = ProxyPoolManager()
return _proxy_pool
```
---
### 2. Behavior Camouflage
**Location:** `kb_workflow/crawler/behavior.py`
**Rationale:** 行为伪装逻辑应该:
- 可以独立测试
- 可以被装饰器或直接调用
- 支持可配置的随机性
**Design:**
```python
# kb_workflow/crawler/behavior.py
import random
import time
from typing import Optional, Callable
from functools import wraps
from pydantic import BaseModel, Field
class BehaviorConfig(BaseModel):
"""行为伪装配置"""
# 请求间隔
min_delay: float = Field(default=1.0, description="最小延迟(秒)")
max_delay: float = Field(default=3.0, description="最大延迟(秒)")
# Jitter 配置(防止模式检测)
jitter_enabled: bool = True
jitter_range: float = Field(default=0.3, description="抖动范围(+/- 比例)")
# 人类模拟
mouse_movement_enabled: bool = False # Playwright 场景下可选
scroll_simulation_enabled: bool = False
# User-Agent 随机化
user_agent_rotation: bool = True
# 常用 User-Agent 池
USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15",
]
class BehaviorCamouflage:
"""行为伪装管理器"""
def __init__(self, config: Optional[BehaviorConfig] = None):
self.config = config or BehaviorConfig()
def get_random_delay(self) -> float:
"""获取随机延迟时间(带 jitter)"""
base_delay = random.uniform(self.config.min_delay, self.config.max_delay)
if self.config.jitter_enabled:
jitter = base_delay * self.config.jitter_range * random.uniform(-1, 1)
base_delay += jitter
return max(0.1, base_delay) # 确保非负
def apply_delay(self):
"""应用随机延迟"""
delay = self.get_random_delay()
time.sleep(delay)
return delay
def get_random_user_agent(self) -> str:
"""获取随机 User-Agent"""
if self.config.user_agent_rotation:
return random.choice(USER_AGENTS)
return USER_AGENTS[0]
def get_playwright_context_options(self) -> dict:
"""获取 Playwright context 选项(包含反检测设置)"""
options = {
"user_agent": self.get_random_user_agent(),
}
return options
def with_behavior_camouflage(config: Optional[BehaviorConfig] = None):
"""装饰器:为函数添加行为伪装
用于同步函数,在调用前添加随机延迟。
Example:
@with_behavior_camouflage()
def fetch_page(url):
return requests.get(url)
"""
camouflage = BehaviorCamouflage(config)
def decorator(func: Callable):
@wraps(func)
def wrapper(*args, **kwargs):
camouflage.apply_delay()
return func(*args, **kwargs)
return wrapper
return decorator
```
---
### 3. CAPTCHA Detection
**Location:** `kb_workflow/crawler/captcha.py`
**Rationale:**
- **不实现自动解决** — 根据项目约束(Out of Scope: 付费打码服务)
- **检测+暂停机制** — 检测到验证码时暂停并通知用户
**Design:**
```python
# kb_workflow/crawler/captcha.py
import re
from typing import Optional, Tuple
from enum import Enum
from dataclasses import dataclass
from pydantic import BaseModel
from kb_workflow.pipeline.errors import KBError, ErrorType
class CaptchaType(str, Enum):
"""验证码类型"""
RECAPTCHA_V2 = "recaptcha_v2"
RECAPTCHA_V3 = "recaptcha_v3"
HCAPTCHA = "hcaptcha"
CLOUDFLARE = "cloudflare"
IMAGE_CAPTCHA = "image_captcha"
SLIDER = "slider"
UNKNOWN = "unknown"
@dataclass
class CaptchaDetectionResult:
"""验证码检测结果"""
detected: bool
captcha_type: Optional[CaptchaType] = None
confidence: float = 0.0
indicators: list = None
def __post_init__(self):
if self.indicators is None:
self.indicators = []
class CaptchaDetector:
"""验证码检测器
职责:
- 检测页面是否包含验证码
- 识别验证码类型
- 不负责解决验证码(需要人工介入)
"""
# 验证码指示器(页面元素/URL/内容)
CAPTCHA_INDICATORS = {
CaptchaType.RECAPTCHA_V2: [
"g-recaptcha",
"recaptcha-checkbox",
"google.com/recaptcha",
],
CaptchaType.RECAPTCHA_V3: [
"grecaptcha.execute",
"recaptcha-v3",
],
CaptchaType.HCAPTCHA: [
"h-captcha",
"hcaptcha.com",
],
CaptchaType.CLOUDFLARE: [
"cloudflare",
"cf-browser-verification",
"challenge-platform",
],
CaptchaType.IMAGE_CAPTCHA: [
"captcha.png",
"verifycode",
"img captcha",
],
CaptchaType.SLIDER: [
"slider",
"slide-verify",
"drag verify",
],
}
def detect_from_html(self, html_content: str) -> CaptchaDetectionResult:
"""从 HTML 内容检测验证码"""
html_lower = html_content.lower()
indicators = []
for captcha_type, patterns in self.CAPTCHA_INDICATORS.items():
for pattern in patterns:
if pattern.lower() in html_lower:
indicators.append(pattern)
return CaptchaDetectionResult(
detected=True,
captcha_type=captcha_type,
confidence=0.8,
indicators=indicators,
)
return CaptchaDetectionResult(detected=False)
def detect_from_response(
self,
status_code: int,
headers: dict,
url: str
) -> CaptchaDetectionResult:
"""从 HTTP 响应检测验证码"""
# 403 通常表示被拦截或需要验证
if status_code == 403:
return CaptchaDetectionResult(
detected=True,
captcha_type=CaptchaType.UNKNOWN,
confidence=0.6,
indicators=["403 Forbidden"],
)
# 检查 URL 重定向
if "captcha" in url.lower() or "verify" in url.lower():
return CaptchaDetectionResult(
detected=True,
captcha_type=CaptchaType.UNKNOWN,
confidence=0.7,
indicators=[f"URL redirect: {url}"],
)
return CaptchaDetectionResult(detected=False)
class CaptchaError(KBError):
"""验证码错误
当检测到验证码时抛出,需要人工介入。
"""
def __init__(
self,
step_name: str,
input_summary: str,
captcha_type: CaptchaType,
suggestion: str = "请手动完成验证码后重试"
):
super().__init__(
message=f"检测到验证码: {captcha_type.value}",
error_type=ErrorType.AUTH,
step_name=step_name,
input_summary=input_summary,
suggestion=suggestion,
)
self.captcha_type = captcha_type
```
---
### 4. Enhanced Retry Mechanism
**Location:** `kb_workflow/pipeline/retry.py` (enhance existing)
**Rationale:**
- 现有实现使用 tenacity,已经很好
- 增强点:添加 jitter、状态感知、验证码感知
**Enhanced Design:**
```python
# kb_workflow/pipeline/retry.py (enhancements)
import random
import logging
from tenacity import (
retry,
stop_after_attempt,
wait_exponential,
retry_if_exception_type,
before_sleep_log,
wait_combine,
wait_random,
)
from .errors import NetworkError, AuthError
logger = logging.getLogger(__name__)
def wait_exponential_jitter(
multiplier: float = 1,
min: float = 2,
max: float = 10,
jitter: float = 0.5
):
"""指数退避 + Jitter
Args:
multiplier: 退避乘数
min: 最小等待时间
max: 最大等待时间
jitter: 随机抖动范围(0-1)
"""
return wait_combine(
wait_exponential(multiplier=multiplier, min=min, max=max),
wait_random(0, jitter)
)
# 增强版重试装饰器(带 jitter)
retry_on_network_error_with_jitter = retry(
stop=stop_after_attempt(3),
wait=wait_exponential_jitter(multiplier=1, min=2, max=10, jitter=0.5),
retry=retry_if_exception_type(NetworkError),
before_sleep=before_sleep_log(logger, logging.WARNING),
reraise=True,
)
"""带 Jitter 的网络错误重试装饰器
在指数退避基础上增加随机抖动,避免多个请求同时重试的"惊群效应"。
"""
# 验证码感知重试(遇到 CaptchaError 不重试)
retry_on_transient_error = retry(
stop=stop_after_attempt(3),
wait=wait_exponential_jitter(multiplier=1, min=2, max=10, jitter=0.5),
retry=(
retry_if_exception_type(NetworkError) &
~retry_if_exception_type(AuthError) # AuthError 不重试
),
before_sleep=before_sleep_log(logger, logging.WARNING),
reraise=True,
)
"""瞬态错误重试装饰器
只重试网络错误,不重试认证错误(包括验证码)。
"""
```
---
### 5. Unified Anti-Detection Decorator
**Location:** `kb_workflow/crawler/anti_detection.py`
**Rationale:**
- 统一入口点,组合所有反爬虫功能
- 便于在 PlatformAdapter 中使用
**Design:**
```python
# kb_workflow/crawler/anti_detection.py
from typing import Optional, Callable, Dict, Any
from functools import wraps
from dataclasses import dataclass
from .proxy_pool import ProxyPoolManager, get_proxy_pool, ProxyInfo
from .behavior import BehaviorCamouflage, BehaviorConfig
from .captcha import CaptchaDetector, CaptchaError, CaptchaType
@dataclass
class AntiDetectionConfig:
"""反检测配置"""
proxy_enabled: bool = True
behavior_camouflage_enabled: bool = True
captcha_detection_enabled: bool = True
behavior_config: Optional[BehaviorConfig] = None
class AntiDetectionContext:
"""反检测上下文
在一次爬取过程中传递的状态信息。
"""
def __init__(self):
self.proxy: Optional[ProxyInfo] = None
self.delays_applied: list = []
self.captcha_detected: bool = False
def with_anti_detection(config: Optional[AntiDetectionConfig] = None):
"""装饰器:集成所有反检测功能
组合使用:
1. 代理池轮换
2. 行为伪装
3. 验证码检测
Example:
@with_anti_detection()
async def fetch_content(self, url: str, context: AntiDetectionContext = None):
# context.proxy 可用于 Playwright
# 延迟已自动应用
...
"""
_config = config or AntiDetectionConfig()
behavior = BehaviorCamouflage(_config.behavior_config)
proxy_pool = get_proxy_pool() if _config.proxy_enabled else None
captcha_detector = CaptchaDetector() if _config.captcha_detection_enabled else None
def decorator(func: Callable):
@wraps(func)
async def async_wrapper(*args, **kwargs):
context = AntiDetectionContext()
# 1. 获取代理
if proxy_pool and proxy_pool.config.enabled:
context.proxy = proxy_pool.get_proxy()
# 2. 应用行为伪装延迟
if _config.behavior_camouflage_enabled:
delay = behavior.apply_delay()
context.delays_applied.append(delay)
# 3. 执行原始函数
try:
result = await func(*args, context=context, **kwargs)
# 报告代理成功
if context.proxy and proxy_pool:
proxy_pool.report_success(context.proxy.url)
return result
except Exception as e:
# 报告代理失败
if context.proxy and proxy_pool:
proxy_pool.report_failure(context.proxy.url)
raise
@wraps(func)
def sync_wrapper(*args, **kwargs):
context = AntiDetectionContext()
# 1. 获取代理
if proxy_pool and proxy_pool.config.enabled:
context.proxy = proxy_pool.get_proxy()
# 2. 应用行为伪装延迟
if _config.behavior_camouflage_enabled:
delay = behavior.apply_delay()
context.delays_applied.append(delay)
# 3. 执行原始函数
try:
result = func(*args, context=context, **kwargs)
if context.proxy and proxy_pool:
proxy_pool.report_success(context.proxy.url)
return result
except Exception as e:
if context.proxy and proxy_pool:
proxy_pool.report_failure(context.proxy.url)
raise
# 根据函数类型选择包装器
import asyncio
if asyncio.iscoroutinefunction(func):
return async_wrapper
return sync_wrapper
return decorator
```
---
## Integration Points
### Integration with ZhihuCrawler
**Modified File:** `kb_workflow/crawler/zhihu_crawler.py`
**Changes:**
```python
# In ZhihuCrawler.__init__:
from .anti_detection import AntiDetectionConfig, get_proxy_pool
def __init__(
self,
headless: bool = True,
timeout: int = 30,
config: Optional[CrawlerConfig] = None,
anti_detection_config: Optional[AntiDetectionConfig] = None
):
self.headless = headless
self.timeout = timeout
self.config = config or get_crawler_config()
self.anti_detection_config = anti_detection_config or AntiDetectionConfig()
# ... rest of init
# In _start_browser_with_proxy:
def _start_browser_with_proxy(self, cookie: str) -> None:
# Use proxy pool if enabled
if self.anti_detection_config.proxy_enabled:
proxy_pool = get_proxy_pool()
if proxy_pool.config.enabled:
proxy = proxy_pool.get_proxy()
if proxy:
self.config.proxy = ProxyConfig(
server=proxy.url,
username=proxy.username,
password=proxy.password
)
# ... rest of method
```
### Integration with ZhihuAdapter
**Modified File:** `kb_workflow/pipeline/platforms/zhihu.py`
**Changes:**
```python
from kb_workflow.crawler.anti_detection import (
with_anti_detection,
AntiDetectionContext,
AntiDetectionConfig
)
class ZhihuAdapter(PlatformAdapter):
def __init__(
self,
headless: bool = True,
timeout: int = 30,
output_dir: Optional[Path] = None,
idempotency_checker: Optional[IdempotencyChecker] = None,
anti_detection_config: Optional[AntiDetectionConfig] = None
):
# ... existing init
self.anti_detection_config = anti_detection_config or AntiDetectionConfig()
@with_anti_detection()
async def fetch_metadata(
self,
content_id: str,
content_type: str,
source_url: str = None,
context: AntiDetectionContext = None # 注入的上下文
) -> Dict[str, Any]:
# Use context.proxy if needed
# ...
```
### Integration with Configuration
**Modified File:** `kb_workflow/pipeline/config.py`
**Changes:**
```python
class KBSettings(BaseSettings):
# ... existing fields
# Anti-crawler settings
proxy_pool: str = Field(
default="",
description="代理池配置(逗号分隔的代理URL)"
)
anti_detection_enabled: bool = Field(
default=True,
description="是否启用反检测功能"
)
```
---
## Data Flow
### Request Flow with Anti-Detection
```
[CLI] process command
|
v
[Pipeline] DownloadProcessor.process()
|
v
[ZhihuAdapter] @with_anti_detection decorator
|
+-> [ProxyPool] get_proxy() -> returns available proxy
|
+-> [BehaviorCamouflage] apply_delay() -> wait random time
|
+-> [ZhihuCrawler] crawl_answer(url, proxy=...)
|
+-> [Playwright] start browser with proxy
|
+-> [CaptchaDetector] detect_from_html() -> check for captcha
| |
| +-> If detected -> raise CaptchaError
|
+-> Return CrawlResult
|
v
[ProxyPool] report_success() / report_failure()
|
v
[Pipeline] continue to next processor
```
### Error Recovery Flow
```
[Request fails]
|
v
[Retry Logic] retry_on_transient_error
|
+-> Is NetworkError? -> Retry with exponential backoff + jitter
|
+-> Is CaptchaError? -> Do NOT retry, surface to user
|
+-> Is AuthError? -> Do NOT retry, trigger QR login
|
v
[After max retries] -> Log error, update state, raise to caller
```
---
## Recommended Build Order
基于依赖关系,建议按以下顺序实现:
### Phase 1: Foundation (No external dependencies)
1. **`kb_workflow/crawler/behavior.py`** — 行为伪装模块
- 独立模块,无外部依赖
- 可独立测试
2. **`kb_workflow/crawler/captcha.py`** — 验证码检测模块
- 独立模块,只需要 HTML 解析
- 定义 CaptchaError 错误类型
### Phase 2: Integration
3. **`kb_workflow/crawler/proxy_pool.py`** — 代理池管理
- 依赖配置系统
- 需要与 CrawlerConfig 集成
4. **`kb_workflow/pipeline/retry.py` (enhance)** — 增强重试机制
- 添加 jitter 支持
- 添加验证码感知
### Phase 3: Unified API
5. **`kb_workflow/crawler/anti_detection.py`** — 统一装饰器
- 组合所有组件
- 提供 `@with_anti_detection` 装饰器
### Phase 4: Platform Integration
6. **`kb_workflow/crawler/zhihu_crawler.py` (modify)** — 集成到 ZhihuCrawler
7. **`kb_workflow/pipeline/platforms/zhihu.py` (modify)** — 集成到 ZhihuAdapter
8. **`kb_workflow/pipeline/config.py` (modify)** — 添加配置项
### Phase 5: Bilibili Support (if needed)
9. **`kb_workflow/pipeline/platforms/bilibili.py` (modify)** — B站反爬适配
---
## Anti-Patterns (v1.3 Specific)
### Anti-Pattern 1: Hardcoding Proxy Settings in Crawler
**What people do:** 在 Crawler 类中硬编码代理配置
```python
# BAD
class ZhihuCrawler:
def __init__(self):
self.proxy = "http://127.0.0.1:7890" # Hardcoded
```
**Why it's wrong:** 无法动态切换代理,无法支持代理池
**Do this instead:** 使用独立的 ProxyPoolManager
---
### Anti-Pattern 2: Retry All Errors Blindly
**What people do:** 对所有错误都进行重试
```python
# BAD
@retry(stop=stop_after_attempt(3))
def crawl():
...
```
**Why it's wrong:** 验证码错误重试会立即再次触发验证码,造成无限循环
**Do this instead:** 使用错误类型过滤
```python
# GOOD
@retry(retry=retry_if_exception_type(NetworkError))
def crawl():
...
```
---
### Anti-Pattern 3: Fixed Delay Intervals
**What people do:** 使用固定延迟
```python
# BAD
time.sleep(2) # Fixed 2 second delay
```
**Why it's wrong:** 固定延迟容易被模式检测识别
**Do this instead:** 使用随机延迟 + jitter
```python
# GOOD
delay = random.uniform(1, 3) + random.uniform(-0.5, 0.5)
time.sleep(delay)
```
---
### Anti-Pattern 4: Captcha Auto-Solve Without Fallback
**What people do:** 依赖第三方验证码解决服务,没有降级方案
**Why it's wrong:** 增加成本,服务不可用时无法工作
**Do this instead:** 检测 + 暂停 + 通知用户(本项目约束)
---
## Scaling Considerations (v1.3)
| Scale | Architecture Adjustments |
|-------|--------------------------|
| 个人使用(当前) | 单进程,内存代理池,本地配置 |
| 小规模(10-100 URL/天) | 添加代理持久化,简单的健康检查 |
| 中规模(100-1000 URL/天) | 考虑使用代理池服务 API,分布式状态存储 |
### Scaling Priorities
1. **First bottleneck:** 代理被封 — 通过代理池轮换解决
2. **Second bottleneck:** 账号被封 — 通过行为伪装和降低频率解决
3. **Third bottleneck:** 验证码频繁 — 考虑暂停机制 + 人工介入
---
## v1.3 Sources
- [ZenRows - How to Avoid Bot Detection with Playwright](https://www.zenrows.com/blog/avoid-playwright-bot-detection) - HIGH confidence
- [Zyte - Best Proxy Providers for Web Scraping 2026](https://www.zyte.com/learn/best-proxy-providers-for-web-scraping/) - HIGH confidence
- [Rebrowser - Python Requests Retry Guide](https://rebrowser.net/blog/python-requests-retry-the-ultimate-guide-to-handling-failed-http-requests-in-python) - HIGH confidence
- [GitHub - backoff Python library](https://github.com/litl/backoff) - HIGH confidence
- [Stack Overflow - How to rotate proxies in Python](https://stackoverflow.com/questions/55872164/how-to-rotate-proxies-on-a-python-requests) - HIGH confidence
- [Medium - Exponential Backoff Decorator](https://medium.com/@suryasekhar/implementing-exponential-backoff-decorator-in-python-26ddf783aea0) - MEDIUM confidence
- [Trickster.dev - Custom Scrapy Middleware for Proxy Pool](https://www.trickster.dev/post/writing-custom-scrapy-middleware-for-proxy-pool-integration/) - HIGH confidence
---
## Original Architecture Research (v1.0)
*Below is the original architecture research from v1.0, preserved for reference.*
---
## Standard Architecture
### System Overview
技能型工作流编排系统采用分层架构,将处理逻辑、状态管理和执行协调分离。基于当前研究和行业最佳实践,推荐以下四层架构:
```
+---------------------------------------------------------------+
| Orchestration Layer |
| [Workflow Coordinator] [State Manager] [Error Handler] |
+---------------------------------------------------------------+
| Skill Definition Layer |
| [SKILL.md specs] [Schema Definitions] [Trigger Rules] |
+---------------------------------------------------------------+
| Execution Layer |
| [Platform Skills] [Processing Skills] [Analysis Skills] |
+---------------------------------------------------------------+
| Infrastructure Layer |
| [File System] [External APIs] [AI Services] [State Store] |
+---------------------------------------------------------------+
```
### Component Responsibilities
| Component | Responsibility | Typical Implementation |
|-----------|----------------|------------------------|
| Workflow Coordinator | 协调多个技能的执行顺序、依赖管理 | kb-workflow 主控模块 |
| State Manager | 步骤级状态持久化、断点续传支持 | workflow_state.json + 原子写入 |
| Error Handler | 错误分类、降级策略、重试逻辑 | 统一错误处理模块 |
| Skill Registry | 技能发现、能力声明、Schema 注册 | SKILL.md + JSON Schema |
| Platform Skills | 平台特定内容获取(B站/知乎) | kb-bilibili-*, kb-zhihu-* |
| Processing Skills | 内容转换(语音识别/OCR) | kb-whisper, kb-funasr, kb-ocr |
| Analysis Skills | AI 深度分析、报告生成 | kb-deep-analyzer |
## Recommended Project Structure
```
kb-skills/
+-- skills/ # 技能模块目录
| +-- core/ # 核心基础设施
| | +-- state_manager.py # 状态管理
| | +-- schema_registry.py # Schema 注册
| | +-- pipeline.py # 管道执行引擎
| | +-- error_handler.py # 错误处理
| |
| +-- platforms/ # 平台技能
| | +-- bilibili/ # B站技能组
| | | +-- SKILL.md
| | | +-- metadata.py
| | | +-- content.py
| | +-- zhihu/ # 知乎技能组
| | +-- SKILL.md
| | +-- metadata.py
| | +-- content.py
| |
| +-- processing/ # 处理技能
| | +-- whisper/ # 语音识别
| | +-- funasr/ # 中文语音识别
| | +-- ocr/ # OCR 处理
| |
| +-- analysis/ # 分析技能
| +-- deep_analyzer/ # 深度分析
|
+-- schemas/ # JSON Schema 定义
| +-- workflow_state.schema.json
| +-- skill_output.schema.json
| +-- platform_metadata.schema.json
|
+-- workflows/ # 工作流定义
| +-- video_pipeline.yaml # 视频处理流程
| +-- article_pipeline.yaml # 文章处理流程
|
+-- cli.py # 统一 CLI 入口
+-- config.py # 配置管理
```
### Structure Rationale
- **skills/core/:** 核心基础设施,所有技能共享的状态管理、Schema 验证和管道执行
- **skills/platforms/:** 按平台组织,每个平台有自己的元数据提取和内容下载技能
- **skills/processing/:** 内容转换技能,独立于平台
- **skills/analysis/:** AI 分析技能,作为管道的最后阶段
- **schemas/:** 统一的 JSON Schema 定义,确保接口一致性
- **workflows/:** 声明式工作流定义,指定技能组合和执行顺序
## Architectural Patterns
### Pattern 1: Pipeline Orchestration (管道编排)
**What:** 将处理流程建模为一系列串联的步骤,每个步骤接收输入、执行处理、产生输出。
**When to use:** 可预测的、重复性的任务,如内容处理管道。
**Trade-offs:**
- 优点:确定性执行、易于调试、支持断点续传
- 缺点:灵活性有限,不适合需要动态决策的场景
**Example:**
```python
# Pipeline definition
class ContentPipeline:
steps = [
("parse", LinkParserSkill),
("metadata", MetadataSkill), # depends on parse
("download", ContentSkill), # depends on metadata
("transcribe", TranscribeSkill), # conditional: only for video
("analyze", AnalyzeSkill), # depends on download + transcribe
]
def run(self, url: str, state: WorkflowState):
for step_name, skill in self.steps:
if state.is_completed(step_name):
continue # Skip completed steps
result = skill.execute(state.get_step_input(step_name))
state.mark_completed(step_name, result)
if not result.success:
return self.handle_error(result.error)
```
### Pattern 2: Step-Level State Management (步骤级状态管理)
**What:** 每个主要步骤完成后立即持久化状态,支持从任意断点恢复执行。
**When to use:** 长时间运行的批处理任务,可能因网络、API 限制等原因中断。
**Trade-offs:**
- 优点:断点续传、执行追踪、幂等性保证
- 缺点:状态管理开销、需要设计状态 Schema
**Example:**
```python
# workflow_state.json
{
"url": "https://www.bilibili.com/video/BV1xx411c7mD",
"platform": "bilibili",
"content_type": "video",
"created_at": "2026-03-13T10:00:00Z",
"updated_at": "2026-03-13T10:15:00Z",
"steps": {
"parse": {
"status": "completed",
"output": {"video_id": "BV1xx411c7mD"},
"completed_at": "2026-03-13T10:00:05Z"
},
"metadata": {
"status": "completed",
"output": {"title": "...", "duration": 1800},
"completed_at": "2026-03-13T10:00:30Z"
},
"download": {
"status": "in_progress",
"output": null,
"started_at": "2026-03-13T10:01:00Z"
}
}
}
```
### Pattern 3: Schema-First Interface Design (Schema 优先接口设计)
**What:** 先定义输入输出的 JSON Schema,再实现技能逻辑。所有技能遵守统一的接口规范。
**When to use:** 多人协作、需要接口稳定性和可测试性的项目。
**Trade-offs:**
- 优点:接口清晰、易于测试、支持自动验证
- 缺点:前期设计成本、Schema 维护负担
**Example:**
```json
// schemas/skill_output.schema.json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["success", "data"],
"properties": {
"success": {"type": "boolean"},
"data": {"type": "object"},
"error": {
"type": "object",
"properties": {
"code": {"type": "string"},
"message": {"type": "string"},
"detail": {"type": "string"}
}
},
"metadata": {
"type": "object",
"properties": {
"duration_ms": {"type": "number"},
"tokens_used": {"type": "number"}
}
}
}
}
```
### Pattern 4: Idempotent Execution (幂等执行)
**What:** 相同输入重复执行产生相同结果,不会产生副作用(如重复文件)。
**When to use:** 任何可能需要重试的处理步骤。
**Trade-offs:**
- 优点:安全重试、简化错误恢复
- 缺点:需要设计去重逻辑
**Example:**
```python
class ContentSkill:
def execute(self, input: dict) -> SkillOutput:
# Generate deterministic filename from input
content_hash = hashlib.sha256(input['url'].encode()).hexdigest()[:12]
output_path = self.get_output_dir() / f"{content_hash}.mp4"
# Check if already exists
if output_path.exists():
return SkillOutput(success=True, data={"path": str(output_path), "cached": True})
# Download content
self.download_content(input['url'], output_path)
return SkillOutput(success=True, data={"path": str(output_path), "cached": False})
```
## Data Flow
### Content Processing Pipeline
```
[URL Input]
|
v
+-------------+ +----------------+
| Link Parser | --> | Platform/Type |
+-------------+ | Detection |
+----------------+
|
+---------------+---------------+
| |
v v
+-------------+ +-------------+
| Bilibili | | Zhihu |
| Pipeline | | Pipeline |
+-------------+ +-------------+
| |
+------+------+ +-------+-------+
| | | |
v v v v
[Metadata] [Download] [Metadata] [Download]
| | | |
+------+------+ +-------+-------+
| |
v v
+-------------+ +-------------+
| Video? | | Article |
| - Whisper | | - Direct |
| - FunASR | | Extract |
| - OCR | +-------------+
+-------------+ |
| |
+---------------+--------------+
|
v
+-----------------+
| Deep Analyzer |
| (AI Analysis) |
+-----------------+
|
v
+-----------------+
| Archive to |
| Knowledge Base |
+-----------------+
```
### State Management Flow
```
[Workflow Start]
|
v
+------------------+
| Load/Init State | <-- workflow_state.json
+------------------+
|
v
+------------------+ +------------------+
| For Each Step | --> | Check Status |
+------------------+ | - completed? skip|
| | - failed? retry |
v | - pending? run |
+------------------+ +------------------+
| Execute Step | |
+------------------+ v
| +------------------+
v | Mark Completed |
+------------------+ | Save State |
| Validate Output | -->| (atomic write) |
+------------------+ +------------------+
|
v
[Next Step / Complete]
```
### Key Data Flows
1. **URL to Metadata:** URL -> Link Parser -> (platform, type, id) -> Platform Metadata Skill -> Metadata JSON
2. **Metadata to Content:** Metadata JSON -> Content Skill -> (video file / article text)
3. **Content to Transcript:** Video File -> Transcribe Skill (Whisper/FunASR) -> Transcript JSON
4. **Content + Transcript to Analysis:** Content + Transcript -> Deep Analyzer -> Analysis Report
## Scaling Considerations
| Scale | Architecture Adjustments |
|-------|--------------------------|
| 1-100 items/day | 当前架构足够,单进程执行 |
| 100-1000 items/day | 添加任务队列,并行处理独立任务 |
| 1000+ items/day | 分布式任务调度,API 请求限流,缓存层 |
### Scaling Priorities
1. **First bottleneck:** 外部 API 限流(B站/知乎)。解决方案:请求队列 + 指数退避重试
2. **Second bottleneck:** AI 分析耗时。解决方案:批处理 + 并行分析
## Anti-Patterns
### Anti-Pattern 1: Global State Mutation
**What people do:** 在多个技能间共享可变全局状态
**Why it's wrong:** 难以追踪状态变化、难以测试、难以恢复
**Do this instead:** 每个步骤从 state 读取输入,将输出写回 state,状态变更通过 state manager 集中管理
### Anti-Pattern 2: Implicit Dependencies
**What people do:** 技能之间通过隐式约定传递数据(如固定文件路径)
**Why it's wrong:** 脆弱的耦合,修改一个技能可能破坏其他技能
**Do this instead:** 在 workflow_state.json 中显式声明依赖关系,通过 state manager 传递数据
### Anti-Pattern 3: Silent Failure
**What people do:** 捕获异常后只打印日志,继续执行
**Why it's wrong:** 下游步骤收到无效输入,产生难以调试的错误
**Do this instead:** 明确的失败处理策略 - 要么终止并记录,要么降级并提供默认值
### Anti-Pattern 4: Non-Idempotent Operations
**What people do:** 每次执行都创建新文件(如 `output_1.mp4`, `output_2.mp4`)
**Why it's wrong:** 重试产生垃圾文件,磁盘占用增长
**Do this instead:** 基于输入生成确定性文件名,已存在则跳过
## Integration Points
### External Services
| Service | Integration Pattern | Notes |
|---------|---------------------|-------|
| Bilibili API | HTTP + Cookie Auth | 需要 BILIBILI_COOKIE 环境变量 |
| Zhihu API | HTTP + Cookie Auth | 需要 ZHIHU_COOKIE 环境变量 |
| AI Provider | OpenAI-compatible API | 需要 AI_API_KEY 环境变量 |
| HuggingFace | API + Local Cache | 需要 HF_TOKEN 环境变量 |
| Git | Command Line | 需要 KB_GIT_USER/EMAIL 配置 |
### Internal Boundaries
| Boundary | Communication | Notes |
|----------|---------------|-------|
| Orchestrator <-> Skills | JSON via stdin/stdout | 标准化输入输出格式 |
| Skills <-> State Manager | File I/O | workflow_state.json |
| Skills <-> External APIs | HTTP | 统一错误处理和重试 |
## Build Order Implications
基于组件依赖关系,建议的构建顺序:
### Phase 1: Foundation (基础设施)
1. **State Manager** - 所有后续组件的基础
2. **Schema Registry** - 定义统一的输入输出格式
3. **Error Handler** - 统一错误处理模式
### Phase 2: Core Skills (核心技能)
4. **Link Parser** - 入口点,无依赖
5. **Platform Metadata Skills** - 依赖 Link Parser 输出
6. **Platform Content Skills** - 依赖 Metadata 输出
### Phase 3: Processing Skills (处理技能)
7. **Whisper/FunASR** - 依赖视频文件
8. **OCR Processor** - 依赖视频截图
### Phase 4: Analysis Skills (分析技能)
9. **Deep Analyzer** - 依赖内容和转录文本
### Phase 5: Orchestration (编排层)
10. **Pipeline Engine** - 组合所有技能
11. **Workflow Definitions** - 声明式流程定义
## Sources
- [AI Architecture Patterns 101: Workflows, Agents, MCPs, and A2A Systems](https://aipmguru.substack.com/p/ai-architecture-patterns-101-workflows) - HIGH confidence, comprehensive pattern overview
- [Data Pipeline Architecture: 5 Design Patterns with Examples - Dagster](https://dagster.io/guides/data-pipeline-architecture-5-design-patterns-with-examples) - HIGH confidence, pipeline patterns
- [Agentic Workflows in 2026: The ultimate guide - Vellum](https://www.vellum.ai/blog/agentic-workflows-emerging-architectures-and-design-patterns) - HIGH confidence, agent architecture patterns
- [State of Open Source Workflow Orchestration Systems 2025](https://www.pracdata.io/p/state-of-workflow-orchestration-ecosystem-2025) - MEDIUM confidence, ecosystem overview
- [ML Pipeline Architecture Design Patterns - Neptune.ai](https://neptune.ai/blog/ml-pipeline-architecture-design-patterns) - MEDIUM confidence, ML-specific patterns
---
*Architecture research for: Skill-based Workflow Orchestration System*
*Original research: 2026-03-13*
*Updated for v1.3 Anti-Crawler: 2026-03-18*