跳转到内容

LLM 结构化数据提取

LLM 结构化数据提取

学习目标

  • 理解 Firecrawl 的 Transformer 管线架构
  • 掌握智能模型选择策略(Schema 驱动的模型路由)
  • 了解 Token 限制与成本控制机制

前置知识

本章涉及 LLM 结构化提取的通用原理,建议先阅读:

下文聚焦 Firecrawl 的具体实现方案。


项目实践

Transformer 管线架构

Firecrawl 的抓取管线(scrape pipeline)在拿到引擎返回的 HTML 后,经过一系列 Transformer 处理才输出最终 Document。LLM 提取是其中的一个 Transformer:

引擎返回 HTML
htmlTransform(清理无关元素)
parseMarkdown(HTML → Markdown)
trimToTokenLimit(Token 裁剪)
generateCompletions(LLM 调用)
Schema 校验 → 返回结构化 JSON

Transformers 在 transformers/index.ts 中按顺序注册,包括:

  • json:LLM 结构化提取
  • summary:页面摘要生成
  • query:自然语言查询页面内容
  • question:问答式提取
  • highlights:关键信息高亮

智能模型选择

Firecrawl 不总是使用最强的模型,而是根据 Schema 复杂度自动选择:

Schema 分析
是否有 $defs / $ref(递归引用)?
↓ 是 ↓ 否
gpt-4.1 gpt-4o-mini
(处理复杂 Schema) (处理简单 Schema)

这个设计基于一个观察:递归 Schema(如嵌套的树形结构)需要更强的推理能力,而扁平 Schema 用小模型即可处理,成本差异约 10 倍。

Token 限制与成本控制

trimToTokenLimit 函数确保 HTML 内容不超过模型的最大 Token 限制:

  1. 精确计数:使用 Tiktoken 对目标模型进行 Token 编码
  2. 渐进裁剪:如果 Token 超标,按 maxTokens * 3 字符为起点逐步裁剪
  3. 循环验证:每次裁剪后重新编码,确保最终不超过限制
  4. 降级策略:如果 Tiktoken 编码失败,回退到 2.8 chars/token 的经验估算

成本计算使用 modelPrices 价格表,按输入/输出 Token 分别计费,计入 CostTracking 对象。

LLM Refusal 处理

当 LLM 拒绝提取(如安全策略触发或内容无法匹配 Schema)时,系统抛出 LLMRefusalError

  • 该错误不会被引擎重试机制捕获(不属于引擎层面的失败)
  • 而是直接向上冒泡到 scrapeURLLoop 的错误处理分支
  • 日志记录 refusal 详情,返回给用户明确错误信息

重试策略

当 LLM 返回的 JSON 不匹配 Schema 时,系统使用备用模型重试:

主模型: gpt-4o-mini (或 gpt-4.1)
重试模型: gpt-4.1-mini

重试模型比主模型更强(4.1-mini > 4o-mini),因为首次失败可能需要更强的推理能力来修复。

问题与规避

1. 递归 Schema 导致的生成失败

深层嵌套的递归 Schema(如无限层级的目录树)可能导致 generateObject 无法生成有效结果。

规避:自动检测到递归 Schema 时升级为 gpt-4.1,但即便如此,极端情况仍可能失败。建议在 Schema 设计中限制嵌套深度。

2. 大页面 Token 裁剪导致数据丢失

当页面 HTML 超过 Token 限制时,trimToTokenLimit 会从尾部截断。如果关键数据恰好在页面底部(如表格的分页),可能被裁剪掉。

规避:使用 onlyMainContent: true 先清理非内容区域,减少原始 HTML 大小。

3. 模型价格变动

modelPrices 是硬编码的价格表,如果 OpenAI 等厂商调整价格,成本计算会不准确。

规避:需要定期更新 model-prices.ts 文件。这是硬编码方案的固有缺陷。

设计取舍

为什么用 Vercel AI SDK 而非直接调用 OpenAI API?

Firecrawl 使用 ai 包(Vercel AI SDK)的 generateObject 函数,而非直接调用 OpenAI API。好处:

  • 模型无关:同一套代码可以切换 OpenAI、Anthropic、Google 等不同模型
  • Schema 校验内置generateObject 自动将 JSON Schema 转换为模型的 response format
  • 统一接口:不同 Provider 的流式、重试、错误处理统一抽象

代价是增加了一层依赖抽象,调试时需要理解 AI SDK 的内部行为。

为什么用 Tiktoken 而非模型自身的 Token 计数器?

Tiktoken 在本地计算 Token 数量,不需要调用 API。这对于裁剪决策是必要的 —— 不能在每次裁剪判断时都调用一次 LLM 来数 Token。

代价是 Tiktoken 只支持部分模型(OpenAI 系列),对其他模型(Gemini)的 Token 计数是估算。

参考来源