抓取引擎的 Feature Flag 与质量评分机制
抓取引擎的 Feature Flag 与质量评分机制
学习目标
- 理解
buildFallbackList算法的完整流程 - 掌握 Feature Flag 优先级求和与阈值过滤机制
- 了解动态特征调整(AddFeatureError / RemoveFeatureError)的循环重试逻辑
前置知识
本章涉及多引擎回退的通用原理,建议先阅读:
下文聚焦 Firecrawl 的具体算法实现。
项目实践
Feature Flag 声明式定义
在 engines/index.ts 中,每个引擎通过一个对象声明其支持的所有特性:
const engineOptions: { [E in Engine]: { features, quality } } = { "fire-engine;chrome-cdp": { features: { actions: true, // 支持页面交互 screenshot: true, // 支持截图 pdf: false, // 不支持 PDF stealthProxy: false, // 不支持隐匿代理 branding: true, // 支持品牌提取 // ... 共 17 种特性 }, quality: 50, }, // ... 其他引擎};所有 Feature Flag 及其优先级定义在 featureFlagOptions 中:
const featureFlagOptions = { actions: { priority: 20 }, // 最高优先级 atsv: { priority: 90 }, // 反反爬解算器 pdf: { priority: 100 }, // PDF 解析 document: { priority: 100 }, // 文档解析 stealthProxy: { priority: 20 }, // 隐匿代理 // ... 其他} as const;buildFallbackList 算法详解
算法分四个步骤:
步骤 1:确定候选引擎列表
根据环境配置和 URL 特征,构建初始引擎列表:
- 如果
FIRE_ENGINE_BETA_URL未配置,所有 Fire Engine 变体不包含在内 - 如果 URL 是 X/Twitter,仅保留
x-twitter引擎 - 如果 URL 是 Wikimedia,随机 50% 保留
wikipedia引擎 - 如果
shouldUseIndex返回 false,移除index和index;documents
步骤 2:计算支持分数
prioritySum = sum(所有请求的 flag 的优先级)priorityThreshold = floor(prioritySum / 2)
for each engine: supportedFlags = engine 支持的 & 请求需要的 flags supportScore = sum(supportedFlags 的优先级) if supportScore >= priorityThreshold: 加入候选这个算法确保候选引擎至少能满足请求一半以上的功能需求。
步骤 3:过滤低质量引擎
if any engine has quality > 0: keep only engines with quality > 0只要存在可用的通用引擎,就不使用专用回退引擎(如 PDF/Document,质量分为负值)。
步骤 4:排序
sort by: supportScore DESC, quality DESC动态特征调整循环
在 scrapeURL 的主循环中,引擎执行可能返回两种动态调整错误:
AddFeatureError 的典型场景:
- 引擎返回 403 → 需要
stealthProxy - 引擎返回 PDF 反爬错误 → 需要先用 stealth 代理预取
RemoveFeatureError 的典型场景:
- 引擎报告某个 flag 配置导致抓取失败(如某个特定代理配置不兼容)
重试追踪器(ScrapeRetryTracker)
每次动态调整后,系统记录到 ScrapeRetryTracker:
retryTracker.record("feature_toggle", error);retryTracker.record("feature_removal", error);retryTracker.record("pdf_antibot", error);retryTracker.record("document_antibot", error);限制配置:
SCRAPE_MAX_ATTEMPTS:最大总尝试次数SCRAPE_MAX_FEATURE_TOGGLES:最大 Feature 添加次数SCRAPE_MAX_FEATURE_REMOVALS:最大 Feature 移除次数
超过限制时抛出 ScrapeRetryLimitError,不再重试。
URL 强制引擎映射
engine-forcing.ts 允许为特定 URL 或域名模式强制使用指定引擎:
特定域名配置 → 强制引擎*.example.com → fire-engine;chrome-cdp这在处理已知需要特定引擎的站点时非常有用。
问题与规避
1. 优先级阈值算法的边界情况
priorityThreshold = floor(prioritySum / 2) 意味着如果一个请求需要 actions(20) + screenshot(10) = 30,阈值为 15。一个只支持 actions(20) 的引擎通过(20 >= 15),但一个只支持 waitFor(1) 的引擎不通过。
陷阱:如果请求需要很多低优先级 flag,prioritySum 很高,但没有任何引擎能满足一半。此时所有引擎都被过滤。
规避:系统中还有 quality > 0 过滤兜底 —— 即使没有引擎通过阈值,至少基础引擎(Fetch)仍可用。
2. forceEngine 绕过所有选择逻辑
当 internalOptions.forceEngine 被设置时,buildFallbackList 跳过所有自动选择,直接使用指定引擎。
陷阱:forceEngine 可能不支持请求需要的功能,但没有回退引擎。
规避:当 forceEngine 是数组时,系统仍然会执行支持分数过滤,确保数组中的引擎至少满足基本需求。
设计取舍
为什么用优先级阈值而非最低支持数?
阈值算法的好处:对于简单请求(不需要任何特殊功能),prioritySum = 0,threshold = 0,所有引擎都通过,由质量分决定使用哪个。对于复杂请求,阈值自动提高,确保只选择能力匹配的引擎。
如果改用”至少支持 N 个 flag”的方案,N 的值对所有请求都是固定的,无法适配简单和复杂请求的差异。