全部文章

重新爬取难题:保持 RAG 管道的新鲜度

您的 RAG 知识库在发布当周就会过时。以下是团队如何在不超出工程预算的情况下,重新爬取数百个垂直源的方法。

挑战

垂直领域 AI 初创公司在第二个月左右都会遇到相同的瓶颈。他们发布了客服 copilot、法律研究助手或合规机器人。第一个 Demo 赢得了客户。随后数据过时,回答开始偏离现实。

我们看到许多团队在构建 AI 端时干净利落,却把数据端作为事后才考虑的事情。数据摄取管道只是在某人笔记本电脑上运行的一个 Python 脚本。它一次性抓取 200 个源 URL,将干净 feather 般的 Markdown 导入向量数据库,然后大家举杯庆祝。六周后,一半的回答都在引用已删除的页面、已废弃的 API,或者在 3 月发布并在 5 月再次更改的产品功能。

解决方案听起来很简单:每周重新抓取每个源。但现实要残酷得多。到 2026 年,大约 60% 的知名网站会阻止 AI 爬虫(高于 2023 年底的 23%),而且这些防护手段不再是愚蠢的 User-Agent 检查。它们会分析会话行为、request 节奏以及握手级别的信号。一个在 1 月份还能正常运行的简陋脚本,到了 3 月份就会静默返回空白页面。

更糟糕的是,一些网站现在会提供焦油坑内容(马尔可夫生成的、读起来像真实散文的垃圾信息),直到污染您的 embeddings。因此,您的工程师每周要花一半的时间来修补爬虫,而不是交付产品。检索质量下降,客户察觉到异常,您雇来构建 AI 的团队变成了爬虫维护工坊。

应对方案

重新抓取难题可以拆分为每次 request 都必须做出的三个具体决策:

  1. 是否渲染? 大多数文档门户网站提供干净的 HTML。但比例越来越高的一部分网站(任何基于 Next.js 构建的网站、任何具有客户端渲染的网站)需要完整的浏览器渲染才能返回有用的内容。
  2. 使用哪个 proxy? 住宅、数据中心、移动、地理定位、特定 ISP。正确的选择因目标而异。
  3. 它真的成功了吗? 返回 200 但 body 为空,或者返回 CAPTCHA HTML 页面,这在 HTTP request 上是成功的,但对抓取来说却是失败的。

像 FourA 这样的平台将其中每一个都作为一等公民来处理。

对于渲染决策,在便宜、快速的情况下调用 Single,对于重度 JS 的目标调用 Browser。调用的 body 结构相同,因此您的数据摄取代码只需根据每个源的标志进行一次分支,而无需处理上百个特定于网站的奇特行为。

对于 proxy 选择,Proxy Finder 会作为每次 Single、Browser 和 Auto 调用的一部分运行。平台会为每次 request 选择一个可用的出口,在 response 的 meta.proxy 中返回其不透明的 id,当您需要固定在同一个出口时,可以在后续调用中重用该 id。您的爬虫不需要自带 proxy 评级算法。(我们在为什么 Proxy 池大小在 2026 年不再重要中写过为什么池大小不再是差异化优势。)

而对于“它真的成功了吗”这个问题,每次 request 都支持一个 validate 块。您可以声明什么算作成功:接受的状态码、必需的 header 值、必须出现或不得出现的 body 字符串。FourA 会返回七种结果之一,并且只有 success 是计费的。未通过您内容规则的 200 响应会被标记为 application_fail,并且绝不会进入您的数据集。

以下是需要 JS 渲染的文档门户网站的重新抓取调用示例。我们让 Auto 进行编排,它会选择合适的产品(Single、Proxy 或 Browser),处理机器人防御,并返回会话三元组,以便下一次重新抓取可以固定在同一个出口:

import requests

r = requests.post(
    "https://api.foura.ai/api/auto",
    headers={"Authorization": "Bearer pk_live_..."},
    json={
        "url": "https://docs.example.com/changelog",
        "validate": {
            "status": {"accept": [200]},
            "data":   {"accept": ["<article"], "fail": ["captcha", "Just a moment"]},
        },
    },
).json()

# r["data"]    — rendered body
# r["meta"]    — { "proxy": "<base36 id>", "cookies": [...], "userAgent": "..." }
# On the next recrawl, pass r["meta"]["proxy"] back as `ignoreProxies: [<id>]` to avoid
# the same exit, or via /api/single with `proxy: <id>` to stick to it.

如果目标触发了 Cloudflare 过渡页,validate.data.fail 规则会捕获它。记录在您使用额度中的结果将是 application_fail。您无需为此付费,并且您的数据摄取代码知道要使用不同的 proxy 进行重试,而不是将 “Just a moment...” 页面输入到 embeddings 中。

对于更广泛的语料库,您可以将相同的模式包装在现有的任务队列中。与我们交流过的团队每天晚上都会与上一次抓取进行 diff 对比,仅对实际发生更改的文档重新进行 embedding,并在几个小时的实际时间内刷新包含 500 个源的语料库。任务队列仍然由您管理。而 proxy 的轮换、渲染决策、成功判定则由我们负责。

结果

一旦基础设施不再是瓶颈,新鲜度循环就会呈现出以下面貌(基于我们在垂直领域 AI 团队中看到的模式所做的说明性场景):

  • 每周重新抓取 500 个源 URL,而不是在发布时一次性抓取 200 个 URL
  • 用于爬虫的工程时间:每周少于 2 小时,低于之前的 1-2 天
  • 检索陈旧度窗口:5-7 天,而不是无限期
  • 向量数据库中的垃圾率接近于零,因为 Cloudflare 过渡页和焦油坑页面在到达您的 embedding 模型之前,就会在 validate 层被拒绝
  • 每个源的成本可预测,因为失败的抓取不会出现在账单中

关键不在于这些有什么神奇之处。关键在于它们很枯燥。而枯燥正是生产环境 AI 所需要的。(有关托管 LLM 提取在何处不再划算的更多信息,请参阅当 LLM 提取在大规模下不再划算时。)

核心要点

大多数构建垂直领域 AI 的团队认为,护城河是 prompt、模型选择或检索算法。其实不然。护城河是新鲜度循环:是那套不起眼的基础设施,它周复一周地保证知识库的真实性。

到 2026 年,在垂直领域 AI 中胜出的团队不会是那些拥有最聪明 prompt 的团队。而是那些让用户根本注意不到数据是否是最新的团队,因为数据永远是最新的。