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/PITFALLS.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

27 KiB

Pitfalls Research

Domain: Skill-based Workflow Orchestration (LLM Agent Pipeline) Researched: 2026-03-13 (Original), 2026-03-18 (Anti-Crawler Update) Confidence: HIGH (Pipeline), MEDIUM (Anti-Crawler)


Part 1: Pipeline Architecture Pitfalls (v1.0-1.2)

以下陷阱适用于管道架构本身,已在 v1.0-1.2 中解决。

Pitfall 1: Statelessness = Restart from Scratch

What goes wrong: 系统没有持久化状态,任何失败都需要从头重新执行整个流程。Whisper 识别了 20 分钟,后续步骤失败?重新识别。下载了 2GB 视频,分析失败?重新下载。

Why it happens: 开发者低估了多步骤流程的失败概率。假设每步都成功,或者认为"重新执行一次很快"。实际上,Whisper 识别可能需要 30 分钟,视频下载可能需要 10 分钟,重复执行成本极高。

How to avoid:

  • 每个主要步骤完成后,将状态写入 workflow_state.json
  • 状态包含:步骤名、输入参数、输出路径、完成时间戳
  • 重新执行时,检查状态文件,跳过已完成的步骤
  • 使用幂等设计:重复执行同一链接不会产生重复文件

Warning signs:

  • 没有状态文件或状态文件只在流程结束时写入
  • 重复执行同一任务产生重复输出文件
  • 用户抱怨"又要重新跑一遍"

Phase to address: Phase 1 (PIPE-02: 步骤级状态管理, PIPE-04: 幂等执行保证)


Pitfall 2: Error Propagation Without Context

What goes wrong: 错误在步骤链中传播时丢失了上下文。步骤 6 失败时报告 "AI API 调用失败",但不知道是哪个内容、哪个步骤、什么输入导致的。调试需要从头重现。

Why it happens: 错误处理只关注"失败了",没有保留足够上下文。LLM 调用失败时只记录 "API error",没有记录请求内容、模型参数、重试次数等。

How to avoid:

  • 每个步骤输出结构化 JSON,包含 success、error、context 字段
  • 错误消息必须包含:步骤名、输入摘要、失败原因、可操作建议
  • 使用 correlation ID 追踪整个流程
  • 区分可重试错误(超时、503)和不可重试错误(403、验证失败)

Warning signs:

  • 错误消息只有 "Error" 或 "Failed"
  • 需要问用户"你输入的是什么?"来调试
  • 同样的错误重复出现但不知道根本原因

Phase to address: Phase 1 (PIPE-05: 错误恢复策略, PIPE-07: Schema 验证机制)


Pitfall 3: Assuming Model "Understands Context"

What goes wrong: 主代理期望子代理"理解"整个流程的上下文,但实际上每个子代理只看到自己步骤的输入。kb-deep-analyzer 不知道之前的 Whisper 识别质量如何,只能假设输入是可靠的。

Why it happens: 人类可以自然地维持上下文,开发者误以为 LLM 也能做到。但每次子代理调用都是独立的会话,没有跨步骤的"记忆"。

How to avoid:

  • 显式传递所有需要的上下文,不要假设子代理"知道"
  • 每个步骤的输入 JSON 必须包含该步骤需要的所有信息
  • 使用结构化的输入格式(Schema),而不是自然语言描述
  • 在状态文件中记录关键决策和上下文

Warning signs:

  • 子代理行为不符合预期,"它应该知道..."
  • 需要阅读多个文档才能理解一个步骤的输入要求
  • 步骤之间的"隐式约定"没有文档化

Phase to address: Phase 1 (PIPE-03: 统一 JSON Schema, PIPE-06: 清晰的流程定义)


Pitfall 4: Parallel Chaos (Uncoordinated Parallel Execution)

What goes wrong: 多个子代理同时执行但没有协调机制。结果:文件被覆盖、状态不一致、资源竞争。例如:同时下载和 OCR 处理同一视频导致文件损坏。

Why it happens: 并行看起来更"高效",开发者没有考虑共享资源(文件、状态、API 配额)的并发访问问题。

How to avoid:

  • 对于这个项目:串行执行。每个步骤完成后才开始下一个
  • 如果未来需要并行:使用文件锁、状态锁、或任务队列

Phase to address: Phase 1 (设计决策:串行执行模式)


Pitfall 5: "Automating Bad Processes"

What goes wrong: 在流程本身有问题时,自动化只是让错误发生得更快。例如:Whisper 识别质量差(没有后处理),自动化后产出大量低质量文本。

Why it happens: 急于自动化,没有先验证手动流程的正确性。"先跑起来再说"的心态。

How to avoid:

  • 先手动执行完整流程,确保每步输出符合预期
  • 验证流程正确后再自动化
  • 每个步骤有明确的成功标准

Warning signs:

  • 自动化后输出质量下降
  • 需要"后处理"来修复自动化输出

Phase to address: Phase 0 (验证阶段) - 在自动化之前验证


Pitfall 6: Environment Variable Sprawl

What goes wrong: 配置分散在多个地方:~/.kb_env、HF_TOKEN、ZHIHU_COOKIE、环境变量。用户不知道需要配置什么,"在我的机器上能跑"问题频发。

Why it happens: 不同功能由不同时间添加,没有统一的配置管理。

How to avoid:

  • 工作流启动时检查所有必需变量,缺失则报错退出
  • 文档中明确列出所有变量及其用途
  • 可选变量有明确的默认值和回退策略

Warning signs:

  • "在我的机器上能跑"问题
  • 需要检查多个文件才能找到配置
  • 同一变量在不同地方有不同值

Phase to address: Phase 1 (环境变量检查已在 SKILL.md 中定义,需要统一执行)


Pitfall 7: Graceful Degradation That Isn't

What goes wrong: 声称有"降级策略",但实际上降级到无意义的内容。例如:深度分析失败时返回一个模板文本,看起来"成功"了但没有实际价值。

Why it happens: 开发者不想让流程"失败",认为返回一些内容比报错好。但这掩盖了问题,用户得到的是无用的输出。

How to avoid:

  • 关键步骤必须成功,否则整个流程失败 (PROJECT.md 已定义)
  • 区分"必须有"和"最好有"的功能
  • 失败时明确报告失败原因,不要假装成功
  • 降级策略只用于非关键功能(如:OCR 失败时跳过图片分析)

Warning signs:

  • 输出看起来"正常"但没有实际内容
  • 用户发现分析报告是模板文本
  • 成功率统计很高但用户不满意

Phase to address: Phase 1 (SKILL.md 中的"严格失败处理原则"需要严格执行)


Part 2: Anti-Crawler Optimization Pitfalls (v1.3)

以下陷阱适用于 v1.3 反爬虫优化里程碑,重点关注代理轮换、行为伪装、验证码处理、错误恢复等。

Pitfall AC-1: IP 泄漏 - 代理配置不完整导致真实 IP 暴露

What goes wrong: 使用了代理但仍然暴露真实 IP,导致被目标平台封禁。常见于:

  • 只配置了 HTTP 代理但未配置 HTTPS 代理
  • Session 对象未正确绑定代理,部分请求绕过代理
  • DNS 泄漏:DNS 查询未通过代理直接解析
  • WebSocket 连接未走代理

Why it happens: 开发者通常只关注主要请求的代理配置,忽略了:

  1. 系统环境变量可能覆盖代码中的代理设置
  2. requests.Session 默认 trust_env=True,可能使用系统代理而非指定代理
  3. Playwright 的 proxy 配置需要同时覆盖 HTTP/HTTPS

How to avoid:

# Playwright 正确配置
context_options = {
    "proxy": {
        "server": "http://proxy.example.com:8080",
        # 确保同时处理 HTTPS
    }
}

# requests Session 正确配置
session = requests.Session()
session.trust_env = False  # 关键:禁用系统代理干扰
session.proxies = {
    "http": proxy_url,
    "https": proxy_url,  # 必须同时配置
}

Warning signs:

  • 使用代理后仍然收到 403 错误
  • 目标平台显示你的真实地理位置
  • IP 检测 API 返回真实 IP 而非代理 IP

Phase to address: Phase 1 (AC-01: 代理池管理)


Pitfall AC-2: 代理轮换模式过于规律被识别

What goes wrong: 虽然使用了代理轮换,但因为轮换间隔固定、顺序可预测,被平台识别为自动化行为。表现为:

  • 每次请求后立即切换 IP
  • 代理使用顺序固定 (A→B→C→A→B→C)
  • 所有代理来自同一 IP 段或同一数据中心

Why it happens: 开发者追求简单实现,使用 itertools.cycle() 或简单的取模轮换,忽略了反爬系统会检测:

  1. IP 变化模式
  2. 请求时间分布
  3. 代理 IP 的 ASN (自治系统号) 分布

How to avoid:

import random

class ProxyPool:
    def get_proxy(self) -> str:
        # 随机选择而非顺序轮换
        return random.choice(self.healthy_proxies)

    def should_rotate(self) -> bool:
        # 随机化轮换时机,而非每次请求都轮换
        return random.random() < self.rotation_probability

Better approach:

  • 使用加权随机选择,健康度高的代理获得更多请求
  • 同一会话内保持 IP 稳定,仅在检测到风险或达到请求阈值时轮换
  • 混合使用住宅代理和数据中心代理

Warning signs:

  • 新代理 IP 很快被封
  • 收到大量 CAPTCHA 挑战
  • 平台返回"请求过于频繁"错误

Phase to address: Phase 1 (AC-01: 代理池管理)


Pitfall AC-3: 验证码检测缺失导致无限阻塞

What goes wrong: 爬虫遇到验证码页面后:

  • 继续发送请求,被持续拒绝
  • 无限重试,消耗所有代理资源
  • 没有任何告警,任务静默失败

Why it happens: 开发者假设请求成功返回 200 就代表正常,忽略了:

  1. 验证码页面可能返回 200 状态码
  2. Cloudflare/DataDome 等服务返回"假"的正常响应
  3. 平台使用 JavaScript 渲染验证码,API 拦截无法检测

How to avoid:

# 验证码检测逻辑
CAPTCHA_INDICATORS = [
    "captcha", "verify", "验证", "人机验证",
    "cf-browser-verification", "challenge-platform",
]

def detect_captcha(html_content: str, url: str) -> bool:
    content_lower = html_content.lower()
    return any(indicator in content_lower for indicator in CAPTCHA_INDICATORS)

# Playwright 中检测
def check_for_captcha(page) -> bool:
    captcha_selectors = [
        "iframe[src*='captcha']",
        ".captcha-container",
        "#captcha",
        "div[class*='verify']",
    ]
    for selector in captcha_selectors:
        if page.locator(selector).count() > 0:
            return True
    return False

Response strategy when detected:

  1. 立即停止当前任务
  2. 记录触发验证码的 URL 和代理
  3. 将该代理标记为"风险"
  4. 通知用户(对于个人知识库场景,等待人工介入是合理的)

Warning signs:

  • 响应内容突然变短或为空
  • 响应 HTML 包含验证码相关关键词
  • 页面跳转到验证码 URL

Phase to address: Phase 2 (AC-02: 请求行为伪装) + Phase 3 (AC-03: 验证码检测)


Pitfall AC-4: 重试策略不区分错误类型

What goes wrong: 对所有错误使用相同的重试策略:

  • 404 Not Found 也在重试
  • 401 Unauthorized 无限重试
  • 429 Rate Limit 重试间隔过短

Why it happens: 使用统一的重试装饰器,未针对不同 HTTP 状态码制定策略。现有代码 retry_on_network_error 只处理 NetworkError,但错误分类不够细致。

How to avoid:

# 错误分类策略
ERROR_STRATEGIES = {
    # 永不重试
    400: "no_retry",      # Bad Request - 修复请求
    401: "no_retry",      # Unauthorized - 需要 Cookie 更新
    403: "switch_proxy",  # Forbidden - 可能是 IP 封禁
    404: "no_retry",      # Not Found - 资源不存在

    # 有限重试
    429: "slow_retry",    # Rate Limit - 长间隔重试
    499: "retry",         # Client Closed - 可重试

    # 正常重试
    500: "retry",         # Internal Server Error
    502: "retry",         # Bad Gateway
    503: "retry",         # Service Unavailable
    504: "retry",         # Gateway Timeout
}

For 429 Rate Limit specifically:

# 尊重 Retry-After 响应头
def handle_429(response):
    retry_after = response.headers.get("Retry-After")
    if retry_after:
        wait_time = int(retry_after)
    else:
        wait_time = 60  # 默认等待 60 秒
    time.sleep(wait_time)

Warning signs:

  • 重试日志显示相同的 401/404 错误重复出现
  • 重试次数很快耗尽但问题未解决
  • 代理池快速耗尽

Phase to address: Phase 4 (AC-04: 错误恢复机制增强)


Pitfall AC-5: 请求间隔过于规律被识别为机器人

What goes wrong: 使用固定间隔(如每 2 秒一个请求)被反爬系统识别:

  • 间隔时间完全相同
  • 请求时间戳呈现明显的周期性
  • 24 小时请求分布不自然(凌晨也高频请求)

Why it happens: 开发者使用 time.sleep(2) 实现简单的限流,但:

  1. 固定间隔是机器人行为的典型特征
  2. 真实用户行为具有随机性
  3. 现有代码 random.uniform(min_delay, max_delay) 是正确方向,但范围可能需要调整

How to avoid:

import random
import time

class BehaviorSimulator:
    def __init__(self):
        self.base_interval = 2.0
        self.jitter_range = 1.0

    def get_next_delay(self) -> float:
        # 基础间隔 + 随机抖动
        jitter = random.uniform(-self.jitter_range, self.jitter_range)
        delay = self.base_interval + jitter

        # 偶尔插入较长暂停(模拟阅读时间)
        if random.random() < 0.1:  # 10% 概率
            delay += random.uniform(5, 15)

        return max(0.5, delay)  # 保证最小间隔

Warning signs:

  • 使用随机间隔后成功率显著提升
  • 平台开始频繁要求验证码

Phase to address: Phase 2 (AC-02: 请求行为伪装)


Pitfall AC-6: 指数退避无 Jitter 导致重试风暴

What goes wrong: 多个爬虫实例同时遇到错误时,因为退避时间相同,会在同一时刻重试,导致"重试风暴"。

Why it happens: 标准指数退避算法:wait = base * 2^attempt,所有实例计算结果相同。

How to avoid:

# 正确:指数退避 + 随机抖动
def get_backoff_with_jitter(attempt: int, base: float = 1.0) -> float:
    exponential = base * (2 ** attempt)
    jitter = random.uniform(0, exponential * 0.5)  # 0-50% 抖动
    return exponential + jitter

# tenacity 配置
from tenacity import wait_exponential_jitter

retry_decorator = retry(
    wait=wait_exponential_jitter(initial=1, max=60, jitter=5),
    stop=stop_after_attempt(3),
)

Warning signs:

  • 服务恢复瞬间流量激增
  • 错误在固定时间间隔后重复出现

Phase to address: Phase 4 (AC-04: 错误恢复机制增强)


Technical Debt Patterns

Pipeline Technical Debt

Shortcut Immediate Benefit Long-term Cost When Acceptable
跳过 Schema 验证 快速开发,减少代码 后续调试困难,接口不兼容问题频发 Never
状态只在内存中 简单实现 无法恢复,每次失败重头开始 仅限一次性任务
硬编码路径 不用配置 迁移困难,环境差异问题 Never
自然语言描述步骤输入 灵活,文档友好 LLM 理解不一致,调试困难 仅限人类阅读的文档
跳过错误处理 节省开发时间 生产环境频繁崩溃 仅限原型/POC
不记录中间结果 节省存储 无法断点续传,无法审计 仅限幂等操作

Anti-Crawler Technical Debt

Shortcut Immediate Benefit Long-term Cost When Acceptable
硬编码单个代理 快速实现 单点故障,被封即全部失败 仅开发测试阶段
固定请求间隔 代码简单 被识别为机器人 永不可接受
忽略 4xx 错误重试 减少代码量 永久性错误无限重试 永不可接受
不验证代理可用性 启动快 运行时大量失败请求 仅 MVP 演示
禁用 SSL 验证 解决证书问题 安全风险,可能被中间人攻击 永不可接受

Integration Gotchas

Pipeline Integration

Integration Common Mistake Correct Approach
B站 API 没有处理 403/超时 实现重试机制,区分可重试和不可重试错误
Whisper 假设输出完美 后处理清理(数字格式、标点、分段)
AI API (GLM-5) 单次调用失败即退出 指数退避重试 (1s → 2s → 4s → 8s)
HuggingFace 没有 HF_TOKEN 仍尝试说话者分离 检测 token 存在,无则降级到普通识别
知乎 Cookie 过期导致失败 明确错误消息,提示用户更新 Cookie
Git 归档 没有配置用户信息 检查 KB_GIT_USER/EMAIL,缺失则跳过 Git

Anti-Crawler Integration (v1.3)

Integration Point Common Mistake Correct Approach
ZhihuCrawler 代理配置 只设置 proxy_required=True 但不处理代理缺失 默认 False,显式配置时才强制要求
CrawlerConfig.from_env() 假设环境变量一定存在 提供默认值和清晰的错误提示
retry_on_network_error 装饰器 对所有函数使用同一重试策略 按错误类型区分策略
Playwright 浏览器管理 每次请求创建新浏览器 复用浏览器实例,仅在必要时重建
Cookie 管理 硬编码 Cookie 有效期 实现过期检测和自动刷新

Performance Traps

Pipeline Performance

Trap Symptoms Prevention When It Breaks
内存中累积所有中间结果 内存占用持续增长 每步完成后写入文件,释放内存 处理 1 小时以上视频
同步等待 Whisper 完成 用户长时间无反馈 定时汇报进度(每 2-3 分钟) 超过 10 分钟的任务
单次下载整个视频 网络超时,下载失败 分块下载,支持断点续传 大于 500MB 的视频
无限制重试 无限循环,资源浪费 最大重试次数 + 总超时时间 任何外部服务调用

Anti-Crawler Performance

Trap Symptoms Prevention When It Breaks
每次请求验证代理 请求延迟显著增加 后台异步验证,缓存结果 >100 代理
过于频繁的代理轮换 代理池快速耗尽 基于会话而非请求轮换 >10 QPS
无限制的重试 任务永远不结束 设置最大重试次数和总超时 任何规模
同步等待验证码 线程阻塞 异步检测 + 用户通知 任何规模

Security Mistakes

Pipeline Security

Mistake Risk Prevention
API Key 硬编码 泄露到 Git 仓库 使用环境变量,.gitignore 排除配置文件
Cookie 明文存储 会话劫持 提醒用户 Cookie 敏感性,不提交到 Git

Anti-Crawler Security

Mistake Risk Prevention
代理凭证明文存储 凭证泄露 使用环境变量,支持 Secret 类型
日志输出完整 Cookie 会话劫持 日志脱敏,仅显示前几位
忽略 HTTPS 证书验证 中间人攻击 永不跳过证书验证
代理 URL 包含密码 密码泄露 分离 server/username/password

Platform-Specific Pitfalls

知乎 (Zhihu) 反爬特性

特性 说明 应对策略
x-zse-96 签名 动态签名验证 使用 Playwright 浏览器自动处理(现有方案正确)
Cookie 过期检测 z_c0 Cookie 有效期约 7 天 实现过期检测,触发重新登录
登录状态验证 未登录时重定向到登录页 检测重定向,触发 Cookie 刷新
请求频率敏感 短时间大量请求触发验证码 严格控制请求间隔(2-5秒)
IP 地理位置检测 异常地理位置可能触发验证 使用国内代理

B站 (Bilibili) 反爬特性

特性 说明 应对策略
滑动验证码 高频访问触发 降低频率,使用代理轮换
多维度检测 User-Agent、Cookie、IP 综合判断 保持指纹一致性
登录必要性 部分内容需要登录 Cookie 持久化管理
API 限流 单 IP 请求频率限制 代理轮换 + 请求间隔

"Looks Done But Isn't" Checklist

Pipeline Checklist (v1.0-1.2)

  • 状态管理: 是否每个步骤完成后写入状态?中断后能否恢复?
  • 错误上下文: 错误消息是否包含步骤名、输入摘要、可操作建议?
  • Schema 验证: 每个步骤输入/输出是否通过 Pydantic 验证?
  • 幂等性: 重复执行同一链接是否产生相同结果?
  • 环境变量: 启动时是否检查所有必需变量?

Anti-Crawler Checklist (v1.3)

  • 代理池: 是否验证代理实际可用性?是否测试代理 IP 检测?
  • 请求间隔: 是否添加随机抖动?是否模拟人类行为?
  • 验证码检测: 是否检测所有常见验证码类型?是否有处理流程?
  • 错误恢复: 是否区分可重试/不可重试错误?是否有最大重试限制?
  • Cookie 管理: 是否检测过期?是否支持自动刷新?
  • 日志脱敏: 是否避免在日志中输出完整 Cookie/密码?
  • 集成测试: 是否测试与现有 Pipeline 的兼容性?

Recovery Strategies

Pipeline Recovery

Pitfall Recovery Cost Recovery Steps
Statelessness HIGH 重新实现状态管理,迁移现有数据
Error Context Lost MEDIUM 增强错误处理,更新所有步骤
Schema Mismatch MEDIUM 更新 Schema,迁移现有数据

Anti-Crawler Recovery

Pitfall Recovery Cost Recovery Steps
IP 泄漏 HIGH 1. 停止所有爬虫 2. 更换代理池 3. 等待封禁解除
代理轮换被识别 MEDIUM 1. 更换代理提供商 2. 调整轮换策略 3. 增加请求间隔
验证码检测缺失 LOW 1. 添加检测逻辑 2. 清理被污染的代理 3. 重新开始任务
重试策略错误 LOW 1. 修改错误分类 2. 清理重试队列 3. 重新执行
Cookie 过期 LOW 1. 触发重新登录 2. 更新 Cookie 3. 继续任务

Pitfall-to-Phase Mapping

Pipeline Phases (v1.0-1.2)

Pitfall Prevention Phase Verification
Statelessness = Restart from Scratch Phase 1 (PIPE-02, PIPE-04) 中断执行后可以从断点继续
Error Propagation Without Context Phase 1 (PIPE-05, PIPE-07) 错误消息包含完整上下文和可操作建议
Assuming Model "Understands Context" Phase 1 (PIPE-03, PIPE-06) 每个步骤输入 JSON 自包含所有需要信息
Parallel Chaos Phase 1 (设计文档) 明确文档:串行执行,无并发
"Automating Bad Processes" Phase 0 (验证阶段) 手动执行完整流程,验证每步输出
Environment Variable Sprawl Phase 1 (环境检查) 启动时验证所有必需变量,缺失则报错
Graceful Degradation That Isn't Phase 1 (失败处理原则) 关键步骤失败 = 整体失败,不假装成功

Anti-Crawler Phases (v1.3)

Pitfall Prevention Phase Verification
IP 泄漏 Phase 1 (AC-01: 代理池管理) 使用 IP 检测 API 验证代理生效
代理轮换规律 Phase 1 (AC-01: 代理池管理) 分析日志确认轮换随机性
验证码检测缺失 Phase 3 (AC-03: 验证码检测) 人工触发验证码测试检测逻辑
重试策略错误 Phase 4 (AC-04: 错误恢复) 模拟各类错误验证处理正确
请求间隔规律 Phase 2 (AC-02: 行为伪装) 统计分析请求时间分布
退避无 Jitter Phase 4 (AC-04: 错误恢复) 多实例并发测试验证无风暴
平台特性适配 Phase 5 (AC-05: 知乎) + Phase 6 (AC-06: B站) 实际平台测试

Sources

Pipeline Sources (v1.0-1.2)

Anti-Crawler Sources (v1.3)

高可信度来源 (官方文档/权威技术文章):

中等可信度来源 (技术博客/社区):

平台特性 (社区经验):

现有代码分析:

  • kb_workflow/crawler/zhihu_crawler.py - 现有知乎爬虫实现
  • kb_workflow/crawler/config.py - 现有配置结构
  • kb_workflow/pipeline/retry.py - 现有重试机制

Pitfalls research for: Skill-based Workflow Orchestration + Anti-Crawler Optimization Researched: 2026-03-13 (Pipeline), 2026-03-18 (Anti-Crawler)