← RAG 系列

RAG Practice 03 · NAIVE RAG · DOCUMENT QA · 可阅读

RAG 答不全的时候,先想什么

用最基础的 RAG,让 Cloudflare 的英文技术文档回答中文问题;顺着一个答不全的真实问题,把六种常见的检索改动逐个试一遍。

做到了
  • 有证据时能带引用地回答,语料里没有时拒答而不是编(Q5、Q9a)。
  • 定位了一个典型失败:Q7(环境变量、secret、普通配置怎么设,本地和线上有何区别)的 Top-10 没有一条跑题,却只覆盖回答所需 4 块材料里的 1/4。
  • 让系统先承认证据不够、再给出更窄的问题:缺的「线上怎么设 secret」从第 36 名带到第 2 名,并得到一个有引用的回答。
没做到
  • Top-k、去重、reranker、拆问题、改写,没有一个让 Q7 缺的证据进入 Top-10。
  • 换 embedding:nomic 在 Q7 上补到 3/4,但「普通变量」那块掉到第 134 名,整个 benchmark 上也不如 bge-m3。
  • 「普通变量」和「secret 会加密」两块,在 bge-m3 baseline 上连 recovery 之后也没进 Top-10。
还要回答
这些机制为什么都帮不上?失败到底发生在哪一层?检索不够的时候,系统还能怎么帮用户?

Cloudflare Docs · English · 26 页 snapshot中文提问(英文仅作诊断)Document Q&A,回答带引用baseline 固定:按结构切分 · bge-m3 · Top-5中级 · 通读约 20 分钟,只看结论读 07;出发点与完整范围

先看这套 baseline 怎么工作,再看 Q7 怎么答不全 → 01

出发点、难度、做了和没做什么(可跳过)

导读 · 出发点

为什么做这个实验,写给谁

出发点
多数 RAG 教程按技术讲:先切分,再 embedding,再 rerank……每一步看上去都有用。这里反过来:先搭一套小而完整的 RAG,在真实文档上问真实的问题,再顺着一个真实的失败查下去,看每个机制到底对应哪一种 failure。
面向谁
在做、或准备做「基于文档回答问题」的工程师和产品同学;已经跑过基础 RAG,遇到过「检索到的都相关,回答却不完整」的人。
能学到什么
  • 一套真实的 baseline 是怎么工作的
  • 「相关」和「足够回答」不是一回事
  • 「语料里没有证据」和「检索没找到」怎么区分
  • Top-k、去重、reranker、拆问题、改写、换 embedding 各自对应什么 failure,什么时候没用
  • 检索覆盖不了的时候,系统还能怎么帮用户继续
不适合谁
想要从零搭建的安装教程;想找「最佳 RAG 配置」或模型排行榜;想了解 Graph RAG、agent、微调;需要一份可以直接上线的生产指南。

导读 · 这一篇的定位

它是什么,要读多久

性质
一份真实工程实验的排查记录,不是教程,也不是 Cloudflare 官方指南。结论基于这份 snapshot(26 页,取于 2026-10-04)、这套 benchmark 和几个 case。问题用中文问,文档是英文,目标是每一句回答都有证据支撑。
难度
中级。需要大概知道 chunk、embedding、向量检索是什么(01 里各用一句话解释);不需要会写代码。
阅读时间
通读约 20 分钟;只想看结论,读 07 约 2 分钟。
由浅入深
  • 01–03 基础:一套 baseline,一个成功的 case,一个没有证据的 case
  • 04–05 进阶:一个答不全的问题,和六个依次排除的假设
  • 06 综合:检索覆盖不了的时候怎么办
  • 07 结论、08 复现笔记
怎么读
顺着读最顺。已经熟悉基础的话,可以从 04 开始;只想看结论读 07;想自己复现读 08。

导读 · 范围

实现了什么,没实现什么

做了

  • 一份 Cloudflare 官方文档 snapshot:26 页、336 个 chunk,固定版本
  • 按结构切分,bge-m3 embedding,平铺 cosine 检索,Top-5 作证据
  • qwen3.8-flash 生成带引用的回答,校验引用;证据不够就拒答
  • 一套 benchmark:9 道题,每道有明确的证据要求
  • 对一个检索失败(Q7)的六类排查,以及一次 recovery

没做

  • 不是产品:没有界面,没有线上部署
  • 没有向量数据库,没有 BM25 / 混合检索,没有 metadata 过滤
  • 没有微调;reranker、改写、去重只是实验,没有进入 baseline
  • 评估规模很小:9 道题,Q7 只是其中一道;每个涉及模型的实验只调用了一次
  • 没有评估生成质量,也没有比较不同的生成模型
  • 没有解决 Q7 的全部:普通变量与 secret 的区别(C1/C2)仍然找不回来

01 · Baseline · 基础

一套刻意保持简单的 RAG

后面要查的是 failure,不是调参,所以每一环都尽量简单。读完这一节,你只需要记住这条线:

  1. 官方文档
  2. 按结构切成 chunk
  3. embedding
  4. cosine 检索
  5. Top-5 作为证据
  6. Qwen 生成
  7. 引用校验
语料
26 页 Cloudflare 官方文档(Pages、Workers、R2、D1、Wrangler、绑定、变量与 secret、限额与价格),snapshot v1-2026-10-04
切分
336 个 chunk,其中 334 个有正文可以 embed(另外 2 个只有标题);中位长度 827 字符
检索
bge-m3(1024 维)+ 平铺 cosine,取 Top-5 交给生成
生成
qwen3.8-flash:只能用给它的证据,回答要引用 chunk,并逐字引用原文;系统校验引用是否真的存在
够不够
证据够不够答,由一份手写的「证据清单」判断,不够就拒答、不调用模型(这份清单只为单个 case 写过,不通用)
第一次见这些词?
chunk
文档切出来的一小段,是检索和引用的最小单位
embedding
把问题和 chunk 都映射成向量,用距离找语义相近的内容
cosine
两个向量方向有多接近的分数,越高越相似
Top-k
按分数取前 k 个 chunk 当作证据
引用校验
回答里每个引用都必须对应一个真的取回的 chunk,引用的原文必须逐字出现在这个 chunk 里

chunk 怎么切、用了哪些模型、环境是什么,放在最后的「复现笔记」,这里不展开。

02 · 正常工作的样子 · 基础

一个成功的 case:Q5

问题怎么给 Pages Functions 配置 R2 bucket 绑定,并在代码里访问它?

先看它正常工作时是什么样,这样后面出现的失败,你才知道不是整套系统没跑起来。

Q5 的 Top-5(bge-m3,cosine)
#文档 · 位置cosine被引用
1D10 · Bindings › R2 buckets0.785是
2D10 · Bindings › R2 buckets › Interact with your R2 buckets locally0.707是
3D08 · Configuration › Bindings › R2 buckets0.658是
4D23 · Configuration › Bindings › R2 buckets0.638是
5D10 · Bindings › Service bindings0.608—

第 1 名这一个 chunk 就同时讲了「怎么给 Pages 项目配置 R2 binding」「要重新部署」和「用 context.env 访问」,所以证据状态是 sufficient(够用)。 Qwen 引用了排名 1、2、3、4 的 chunk 共 4 处,每处引用的原文都逐字存在,校验通过。

即使是成功的 case 也有一条缝:写 Pages 的 Wrangler 配置语法的那个 chunk 排在第 8,在 Top-5 之外。这条缝我们记录了,但没有去补。

03 · 没有证据的时候 · 基础

Q9a:问一件语料里根本没有的事

问题Workers KV 的免费额度是多少?超出后如何计费?

先查语料:这份 snapshot 里有 34 个 chunk(13 页)提到 KV,但全部是 binding 或配置用法,没有免费额度,也没有计费。这不是检索失败——没有任何一个 chunk 是检索「漏掉」的。

检索照样返回了 5 个 chunk,分数 0.574–0.593,内容是 Workers、Pages、R2 的限额和免费额度。 Q5 的第 5 名分数是 0.608:光看分数,分不开「有证据」和「没证据」。

  • 正常路径:证据状态 insufficient,系统拒答,不调用模型。
  • 实验里故意绕过这道闸门,让 Qwen 面对同样的 5 个干扰 chunk,它的回答是:「提供的证据中未包含 Workers KV 的免费额度及超出后的计费信息。」没有数字,没有把别的产品的额度说成 KV 的,也没有引用。

「提供的证据中没有」,不能写成「Cloudflare 文档里没有」。语料里没有 ≠ 检索没找到 ≠ 官方没有。这一次只调用了一次模型,说明这次没越界,不说明模型稳定;整件事缺失,也是最容易拒答的一种。

04 · 真正麻烦的问题 · 进阶

Q7:看起来全相关,却答不全

问题环境变量、secret 和普通配置应该分别怎么设置?本地开发和线上部署有什么区别?

一个完整的回答,需要这四块材料:

块回答里必须有首次出现
C1普通环境变量(vars)不加密,用来放普通配置第 52 名
C2secret 是加密的,用来放 API key 这类敏感值第 36 名
C3线上怎么设置 secret(wrangler secret put 或 dashboard)第 36 名
C4本地开发:.dev.vars / .env,不要提交进 git第 1 名

先看 Top-10。读标题,你会觉得检索做得不错:

#文档 · 位置对应
1D25 · Compare secrets and environment variables > Local development with secretsC4
2D06 · Secrets > Local Development with SecretsC4
3D08 · Secrets > Local developmentC4
4D24 · Non-inheritable keys and environments > Secrets in local developmentC4
5D10 · Secrets > Local development with secretsC4
6D06 · Secrets > Compare secrets and environment variables—
7D25 · Environment variables > Compare secrets and environment variables—
8D06 · Secrets > Local Development with Secrets—
9D05 · Environment variables and secretsC4
10D19 · Migration > Variables, secrets and bindings—

全是 secrets、本地开发、环境变量,没有一条跑题。但它只覆盖了需要的四块里的 1/4:只有 C4。 C1、C2、C3 其实就在语料里,在前 60 名里,只是排在 52、36、36 名。

semantic similarity ≠ evidence sufficiency。「和问题相关」与「足够回答问题」是两件事。检索做的是前者。

一个标注上的未决点:「Compare secrets and environment variables」这类 chunk 在 benchmark 里被标成无关,但从人读的角度它对 C1/C2 有帮助。是否算证据,是作者尚待复核的 Gold 问题,这里没有改动,也没有为它调整任何分数。

05 · 依次怀疑什么 · 进阶

六个假设,每一个都真的跑了

每一步都是:假设 → 实验 → 结果 → 判断。没有提升的实验也写在这里,因为这篇文章最大的价值就在于:技术不是加上去就有效,每个机制对应的是不同的 failure。

A

是 Top-k 太小吗?

Q8 局部有效 · Q7 无效

假设
需要的 chunk 排在第 6–10 名,被 Top-5 切掉了。
实验
只改 k:3、5、6、10,同一个排序的前缀。
结果
Q8(Pages 和 R2 的免费额度是否共用)在 k=6 才补上缺的一块:覆盖 1/2 → 2/2。 Q7 在 k=3 到 10 一直是 1/4。k 放大到 10,四道题合计的无关 chunk 从 8 涨到 24,提示词长度约 1.6 倍。
判断
Q8 是真正的 cutoff 问题;Q7 不是,缺的内容在第 36–52 名。k 保持 5。
Top-k 覆盖(revised 问法)
kQ7Q8四题无关 chunk
31/41/24
51/41/28
61/42/210
101/42/224
B

是重复的 chunk 占了位置吗?

重复是现象,不是根因

假设
文档里同一段话出现在好几页,把 Top-5 挤满了;去掉重复,缺的证据就会上来。
实验
在前 60 名的候选池上,三种做法各一次(参数事先定死,不调):完全相同的 chunk 只留一个;近似重复也折叠;MMR(λ=0.2)。
结果
重复确实严重:前 60 名里有 6 个完全重复、13 个近似重复(含前者),Top-5 里 4 个是冗余的。但三种做法的 Top-10 覆盖都还是 1/4。去掉重复后,空出来的位置被别的 secrets 或本地开发 chunk 填上。MMR 反而把 C1 从第 52 位推到第 59 位(按它的顺序排整个候选池)。
判断
重复在浪费位置,但它不是缺证据排得深的原因。去重有用,只是解决的是另一个问题。

第一次见 MMR?在相关性之外,对和已选结果太像的 chunk 扣分,让结果更分散。

C

更强的相关性打分,能把它救回来吗?

有上升,仍进不了 Top-10

假设
embedding 只是粗排;用更强的模型逐个判断「问题—chunk」相关性,对的会排上来。
实验
对前 60 名用 BAAI/bge-reranker-v2-m3 重排,只看 reranker 的分数,不和原 cosine 混合。
结果
C2、C3 从第 36 名到第 18 名,C1 从 52 到 49,Top-10 覆盖仍是 1/4。回答「线上怎么设置 secret」的 wrangler secret put 那一块,反而从第 43 名掉到第 55 名;一个讲 remote bindings 的无关 chunk 从第 29 名升到第 8 名。Top-10 里还是有一半是重复内容。
判断
更强的 relevance scorer 不会自动解决:问题里同时有「区别」和「本地开发」,它被这两部分吸引,把直接回答怎么做的那一块打得很低。

第一次见 reranker?先召回一批候选,再用更重的模型逐个重新判断问题和 chunk 的相关性。

D

是一句话里问了太多件事吗?

有影响,不是主因

假设
两个问题混在一起,embedding 被其中一个带偏。
实验
只按「?」机械拆成两句(不改措辞),各自检索,再交错合并。第一句管变量和 secret,第二句管本地与线上。
结果
只问第一句,C1 从 52 到 39,C2、C3 从 36 到 19,有提前,但仍在 Top-10 之外。第二句一个必需的块都没找到,连 C4 也要到第 37 名。合并后的 Top-10 覆盖还是 1/4。
另一例
Q8 上也试过手写拆问题:「Pages 的免费额度」和「R2 的免费额度」两个子问题,合并后的 10 条候选只覆盖 1 类;不拆的原问题,Top-10 就覆盖 2 类(缺的那块在第 6 名)。拆开没有帮上忙,还多花了一次检索。
判断
复合问题确实稀释了信号(提前了十几名),但把它拆开并不能让证据进到 Top-10。
E

是用户的说法和文档的措辞对不上吗?

分数更高,证据没上来

假设
用文档的词来问,检索就会更准。
实验
两个事先写定、各跑一次的改写:一个保留中文、换成 Cloudflare 术语;一个直接照着文档措辞写的英文,它带有泄漏嫌疑,只用来测上限。
结果
两个版本的 cosine 都比原问题高,但 C1/C2/C3 仍然很深:中文改写 85 / 107 / 72,英文改写 53 / 48 / 44(原问题 52 / 36 / 36),Top-10 覆盖都是 1/4。
判断
更「像文档」不等于更有证据价值。cosine 更高,只说明离那一片文字更近。
看这两个改写的原文
中文术语版:Cloudflare Workers 里的 environment variables、secrets 和普通配置(vars)应该分别怎么设置?本地开发(Wrangler)和线上部署(production)有什么区别?

对齐文档的英文版:How do I configure plain text environment variables in the Wrangler configuration file, add encrypted secrets with wrangler secret put for production, and use .dev.vars or .env files for local development in Cloudflare Workers and Pages?
F

是 embedding 模型本身的问题吗?

一个 case 局部改善,不足以换基线

假设
bge-m3 这个第一阶段表示本身是原因。
实验
只换模型,每个模型各自重新生成文档向量:bge-m3(基线)、nomic-embed-text-v1.5、snowflake-arctic-embed-l-v2.0。
结果
nomic 在 Q7 上局部明显更好:C2、C3 排到第 2、3 名,Top-10 覆盖 3/4。但「普通变量」那块掉到第 134 名;arctic 和 bge-m3 一样,Top-10 只有 1/4。回到整个 benchmark(中文问法):完整可评分的 4 题上,两者 Recall@5 都是 0.56,但 MRR 是 0.88 对 0.54;只能部分评分的 4 题上,Recall@5 是 0.88 对 0.38。
判断
不要为了救一个 failure case 就替换全局 baseline。bge-m3 是在整个 benchmark 上选出来的,nomic 在 Q7 上的好处,换来的是别处的损失。nomic 偏英语,它为什么这样排中文问题,还没有验证。
三个 embedding 模型,Q7 的首次出现位置
模型C1C2C3C4Top-10
bge-m3(基线)52363611/4
nomic-embed-text-v1.51342393/4
snowflake-arctic-embed-l-v2.038222241/4

放在一起看

下面是每一次尝试之后,C1、C2、C3 第一次出现的位置。绿色是进了 Top-10 的。

Q7:C1 / C2 / C3 首次出现的位置
尝试C1C2C3
基线 · 整句检索523636
Top-k 放大到 3–10(同一个排序的前缀,位置不变)523636
近似去重402424
MMR (λ=0.2)594644
Reranker491818
按句拆开 · 第 1 句391919
改写 · 中文术语版8510772
改写 · 对齐文档的英文版534844
Embedding · arctic-embed-l-v2.0382222
Embedding · nomic-embed-text-v1.5(C1 在 134)13423

六种办法,没有一个让 C1–C3 都进入 Top-10。每一种机制对应的是不同的 failure;这个问题的 failure 不在它们对应的那一层。

06 · Recovery Loop · 综合

搜不到的时候,帮用户继续

到这里,Q7 可以停在「搜不到,所以不知道」。但还有另一条路:系统承认证据不够,并帮用户把问题改成更容易回答的样子。

  1. 原问题证据不完整
  2. Qwen 给 3 个更窄的问题
  3. 用户选一个当作新的普通 query
  4. 重新检索
  5. 有依据的回答+ 引用校验

模型只看到原问题和当前的 Top-5 证据,规则是通用的:不回答原问题、不补 Cloudflare 的事实、只给 2–3 个更窄的问题。没有告诉它类别、哪些 chunk 排名低、之前做过哪些实验,也没有告诉它「正确的拆法」。参数和之前一致:qwen3.8-flash,temperature 0,不开 thinking,只调用一次,建议一旦生成就冻结,不重试、不润色,之后才去检索。

它给出的理由是:「原问题同时询问了环境变量、secret和普通配置的设置方法,以及本地与线上部署的区别,涉及多个独立主题且当前证据主要聚焦于本地开发中的 secret 处理。」建议的问题是:

  • S1在 Cloudflare Workers 中,如何区分设置普通环境变量(vars)和敏感信息(secrets)?
  • S2线上部署时,如何通过 Wrangler CLI 或 Dashboard 为 Worker 添加 secrets?
  • S3本地开发和线上部署在加载 .dev.vars/.env 文件与生产环境配置时有何具体区别?

假设用户点了 S2。这就是一次新的普通 query,用的还是同一套 bge-m3,什么都没有改。三个建议各自的结果:

每个建议当作新问题检索(首次出现位置)
问题C1C2C3C4Top-10
原问题52363611/4
S131161631/4
S25218292/4
S34512820711/4

它的前三名是 Via the dashboard、Via Wrangler、Upload secrets alongside code:dashboard 的路径、wrangler secret put,以及随代码上传 secrets 的 --secrets-file。

S2:C3 从第 36 名到第 2 名。原问题里排在第 36 名的那一块,换成一个只问「线上怎么设置 secret」的问题之后,进了前 3。

只对 S2 做一次最终回答(用 S2 自己的 Top-5,同样的引用规则):

部署后给 Worker 添加 secret 有两条路 [1][2]:在 dashboard 的 Workers & Pages → 你的 Worker → Settings → Variables and Secrets 里添加,类型选 Secret,再 Deploy;或者用 wrangler secret put <KEY>,它会创建并立即部署新版本。用 gradual deployments 时,改用 wrangler versions secret put。还可以用 --secrets-file 随代码一起上传 [3]。

以上是对完整回答的转述;引用 3 处,校验通过:引用的 chunk 都来自 S2 自己的 Top-5,原文逐字存在。

看模型的完整回答
在线上部署时,可以通过以下两种方式添加 Worker secrets:

1. **通过 Dashboard**:
   - 在 Cloudflare dashboard 中进入 **Workers & Pages** 页面 [1]。
   - 选择你的 Worker > **Settings** [1]。
   - 在 **Variables and Secrets** 下选择 **Add** [1]。
   - 选择类型 **Secret**,输入 **Variable name** 和 **Value**(值将在 Wrangler 和 dashboard 中隐藏)[1]。
   - 可选地,选择 **Add variable** 以添加更多 secrets [1]。
   - 最后选择 **Deploy** 以实施更改 [1]。

2. **通过 Wrangler CLI**:
   - 使用 `wrangler secret put <KEY>` 命令。这会创建一个新的 Worker 版本并立即部署 [2]。
   - 如果使用 gradual deployments,则使用 `wrangler versions secret put <KEY>` 命令。这仅创建新版本,随后需使用 `wrangler versions deploy` 进行部署 [2]。
   - 注意:Wrangler 3.73.0 之前的版本需要指定 `--x-versions` 标志 [2]。
   - 此外,可以使用 `--secrets-file` 标志在部署代码的同时上传 secrets,例如 `npx wrangler deploy --secrets-file .env.production` 或 `npx wrangler versions upload --secrets-file secrets.json` [3]。

同时保留限制,recovery 不是万能的:

  • S1(普通变量和 secret 的区别)没有解决:C1 提前到第 31 名、C2 到第 16 名,仍在 Top-10 之外。Q7 里最基础的那一块,依然没有被找回来。
  • S3(本地开发与线上配置)基本只是覆盖了原来就能找到的 C4,没有新进展。
  • 只有一个 case,一组建议,一次调用;最终回答没有用「证据清单」闸门,因为这个问题没有为它写过清单。

好的 RAG 不一定一次回答所有问题,但应该知道什么时候证据不够,并帮助用户继续。这里的结果是 partial:一个子问题被完整、有依据地回答了,另一个仍然没有。

07 · 结论

碰到 RAG failure,这样想

  1. 相关 ≠ 足够Q7 的 Top-10 没有一条跑题,却只覆盖了需要的四块里的 1/4。
  2. 高 cosine ≠ 能回答语料里没有 KV 额度时,Top-5 的分数是 0.574–0.593;有答案的 Q5 的第 5 名是 0.608。用分数阈值挡不住。
  3. 语料里没有 ≠ 检索没找到先查语料,再怪检索;回答里也只能说「提供的证据中没有」,不能说「官方文档里没有」。
  4. reranker、去重、改写、拆问题,各有适用条件Q8 的问题靠 k=6 就补上了;Q7 上,这几样都没能让缺的证据进 Top-10。先判断失败发生在哪一层,再选机制。
  5. 别为了一个 query 去过拟合 baselinenomic 在 Q7 上局部更好,但整个 benchmark 上 bge-m3 仍更强;换基线要用整个 benchmark 做,而不是一个 case。
  6. 复合问题的 recovery 可以是产品能力,不必继续堆检索让系统说清证据不够,并给出更窄的问题,S2 就把 C3 从第 36 名带到第 2 名,并得到一个有引用的回答。

还没解决的

  • Q7 里 C1/C2(普通变量和 secret 的区别)仍然找不回来,哪怕用专门的问题问。
  • 「Compare secrets and environment variables」这类 chunk 算不算证据,是作者要复核的 Gold 问题。
  • 所有结论来自这份 snapshot、这套 benchmark 和几个 case;每个实验都只有一次运行,不说明模型或方法是稳定的。

读完你应该能回答的是:面对 retrieval failure,下一步先判断什么——是证据不在语料里,还是在前 k 之外,还是在那里但排得深,以及这时系统能不能先承认不够、帮人继续。

08 · 复现笔记

如果你想自己复现,大概需要什么

这不是一份完整的 setup guide。它告诉有经验的工程师:材料从哪来,用了什么,实验在哪里。代码和语料在作者的仓库里,目前没有公开,页面上没有下载入口。

数据
Cloudflare 官方文档,每页取它的 Markdown 导出(index.md),共 26 页,snapshot 批次 v1-2026-10-04,在 2026-10-04 一个很短的时间窗里取完,逐文件记录 sha256。它是一段取数据的区间,不对应官方仓库的某一个 commit;之后再更新,要新建一个批次,不覆盖这个。三层的关系:raw(原样保存的响应)→ normalized(只去掉文档记录的界面元素,对 6 个主题混杂的长页只保留相关章节)→ chunk。
切分
按结构切:标题、段落、列表项、表格、代码块、<details> 各是一个块;标题和它下面紧跟的说明绑在一起;一个 chunk 不跨标题;每个 chunk 尽量不超过约 2400 个字符,相邻 chunk 之间零重叠;表格和代码块不拆开。整个语料只有一个 chunk 超过上限(5759 字符,一张兼容性矩阵表),整张保留。chunk 的 id 里带正文的哈希。
Embedding
输入 = 文档标题 + 标题路径 + chunk 正文。基线是 bge-m3(1024 维);对比过 nomic-embed-text-v1.5、snowflake-arctic-embed-l-v2.0。更早的 bake-off 里还跑过 bge-small-en-v1.5(512 token 窗口,会截断 57 个 chunk)。当前 baseline 仍然是 bge-m3。
Reranker
BAAI/bge-reranker-v2-m3,只在 Q7 的实验里用,没有进入 baseline;通过第三方的 ONNX 导出在 CPU 上运行。
生成
qwen3.8-flash(阿里云 Model Studio 的 OpenAI 兼容接口),temperature 0,enable_thinking=false,JSON 输出;密钥和地址只从环境变量读取:DASHSCOPE_API_KEY、DASHSCOPE_BASE_URL。
环境
Python 3.11、ONNX Runtime 1.30.0、numpy 2.4.6,4 个 CPU 线程,没有 GPU;模型文件在本地缓存里,不进仓库。
实验材料的位置(作者仓库)
experiments/rag/cloudflare-docs-practice-03/
  snapshots/            语料 snapshot(raw、normalized、manifest)
  ingest/               切分规则与 chunk
  embedding/            embedding bake-off 与 benchmark
  case-01/              Q5 的完整流程、Q9a 边界测试
  q8-decomposition/     手写拆问题
  topk-sensitivity/     Top-k
  q7-diversity/         去重与 MMR
  q7-reranker/          reranker
  q7-query-split/       按句拆分
  q7-query-rewrite/     改写
  q7-embedding-comparison/  embedding 对比
  q7-recovery-loop/     建议、再检索与最终回答

每个实验目录里都有它自己的 report.md,和一个离线检查脚本;这页上的数字来自那里的 results.json。