表格(spreadsheet)走独立的 FRTR collection。它是否参与召回由灰度门控决定;参与之后如何与主库融合由召回模式决定。下面拨动开关,看四路数据(主库/表格 × dense/sparse)如何流动、在哪里融合、为何这样设计。
同一个 chunk 在两路里的名次各贡献一个倒数;拖动它的排名:
名次 = 0 表示该路未召回此 chunk(不贡献分)。
dense 是 cosine ∈[0,1],sparse 是 BM25 无上界,两者量纲不可比 —— 直接加权会被 BM25 的大数值压垮。
RRF 只看排名不看分值,天然免疫量纲差异;融合后再做 min-max 归一化到 [0,1] 输出。
代价:RRF 分跨 query 不可比,所以 score_filter 阈值在纯 RRF 语义下会被跳过。
主库与表格 dense 共用同一 embedding 模型 + COSINE 度量,DenseScore 是跨库可比的绝对相关度 —— 这正是 RRF 不适用的场景。
RRF 丢分只留名次,会把弱表格的头名(cosine 0.4)抬到主库强结果(cosine 0.94)同列,稀释精度。故改用分数归并(FuseDenseByScore):按真实 cosine 全局降序、去重取高分。
可调 frtr_dense_score_weight 微调表格路权重(默认 1.0,只影响排序、不动阈值)。
MERGED: 表格 chunk 与文档 chunk 进同一候选池竞争排序,输出单一 chunk_list。适合"只要一个最优列表"。
SEPARATE: 表格走独立侧路,产出独立 spreadsheet_chunk_list。表格 chunk 是"文档语义代表"而非直接证据,分离后下游 Agent 可特殊处理(取整表沙箱分析)。
上面讲的都是 recall(查询) 侧。但灰度真正的意义,是它同时管着 builder(写入) 侧 —— 两个独立 binary 读同一份 zest 配置、调同一个判定函数,决定"写哪个库"与"查哪个库"。这才是灰度不可绕过的根因。
ShouldRecall()allow_allShouldRecall、读同一个 zest key,所以写进 km_spreadsheet 的文档,查询侧也一定会去 km_spreadsheet 找。若两侧判定不同源,就会出现"builder 写了新库、recall 却只查老库"→ 该文档永远召回不到。这就是为什么 recall_mode 也必须先过灰度,不能绕过(详见原则 ②)。