RAG 为什么用 R2 而非爬虫:数据噪音与召回质量

AI 工程64 阅读约 4 分钟

RAG(检索增强生成)的效果,七成取决于喂进去的数据干不干净。我搭博客 AI 助手时,在「数据源」这一步面临两个选择,最后的决定影响了整个召回质量。

两种数据源

Cloudflare AI Search(以及大多数 RAG 方案)支持两类数据源:

1. 爬虫(Sitemap / Crawler):给它一个网站,它自动抓页面。上手最快,但有致命问题:

  • 抓的是 HTML,且常常只有首屏内容,对分段渲染的长文章无能为力;
  • 更要命的是,爬虫分不清正文和噪音——导航栏、侧边栏、评论区、页脚、甚至 AI 摘要框,全被一锅端进去。

2. R2 存储桶(主动维护 Markdown):自己把干净的 Markdown 文件放进对象存储当数据源。要自己搭同步管道,但内容 100% 可控。

我选了 R2 方案。

为什么噪音是致命的

RAG 的核心是把文本切块、向量化,存进向量库,检索时按语义相似度召回。问题在于:噪音也会被向量化。

想象每篇文章页都带着相同的导航文字「首页 / 归档 / 关于 / 友链」、相同的页脚、一堆评论。这些重复的、与正文无关的内容被切块、嵌入后,会在向量空间里形成一片「噪音云」。后果是:

  • 用户问一个问题,召回的可能是某篇文章的评论导航,而不是正文;
  • 大量重复样板内容拉低了正文 chunk 的区分度,相似度排序失真;
  • 长文被首屏截断,关键内容根本没进库。

噪音污染向量空间——这是爬虫方案最隐蔽也最难补救的问题。

R2 方案的做法

我让博客后端在内容变更时,主动把文章转成干净的 Markdown推送过来,写进 R2。每个文件带一段 Frontmatter,给模型结构化的元数据上下文:

---
id: 42
title: Redis 归档缓存的事件驱动失效
url: https://jiawen.live/article/42
created_at: 2026-03-12
---

正文(纯 Markdown,无任何 UI 噪音)...

这样 AI Search 索引到的:

  • 是纯正文,没有导航/评论/侧边栏;
  • 是完整长文,不被首屏截断;
  • 带结构化元数据,召回时能直接拿到文章 id、标题、URL,方便引用和「取全文」的后续工具调用。

检索时配上 query 改写和重排序,进一步提升相关性:

.search({
  messages: [{ role: 'user', content: query }],
  ai_search_options: {
    retrieval: { max_num_results: 4 },
    query_rewrite: { enabled: true },
    reranking: { enabled: true, model: '@cf/baai/bge-reranker-base' },
  },
})

代价与权衡

R2 方案不是免费的——你得自己搭「内容变更 → 同步到 R2」的管道(我用带 HMAC 签名的 Webhook 做)。但这点工程量换来的是对数据质量的完全掌控,对 RAG 来说太值了:

爬虫 R2 Markdown
上手 极快 要搭同步管道
内容纯净度 混杂噪音 100% 可控
长文支持 常被截断 完整
元数据 Frontmatter 结构化
召回质量 受噪音拖累

小结

RAG 的质量上限由数据质量决定。爬虫省事,但抓来的 HTML 混着导航、评论、侧边栏,这些噪音被向量化后会污染整个检索空间。主动维护干净的 Markdown(放 R2)虽然要搭一条同步管道,却换来纯正文、完整长文、结构化元数据——召回质量完全不是一个量级。在 RAG 里,喂什么比模型多大更重要。

相关文章

评论 (3)

B

breeze

向量库选型有推荐吗?自建还是托管?

N

Nova

写得真清楚,收藏了 🙏

G

GG_Bond

思路很顺,一口气读完了