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 校验 → 返回结构化 JSONTransformers 在 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 限制:
- 精确计数:使用 Tiktoken 对目标模型进行 Token 编码
- 渐进裁剪:如果 Token 超标,按
maxTokens * 3字符为起点逐步裁剪 - 循环验证:每次裁剪后重新编码,确保最终不超过限制
- 降级策略:如果 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 计数是估算。