为了回答店长的问题,我们可能找到了套餐介绍、蛋糕配方、果酱配方,以及沿关系连接的一整包资料。它们分别来自稠密、稀疏和图检索。现在的问题是:哪些资料先交给模型?同一份配方被两种方法找到,要不要算两次?
{{figure:09-rrf|图 5-12 RRF 使用名次贡献,避免直接相加语义相似度、词项分数与图路径分数。}}
稠密和稀疏分数的分布不同;图候选甚至没有可直接比较的向量相似度。本章先用 RRF(Reciprocal Rank Fusion,倒数排名融合)得到统一候选顺序。
对一条候选,在某通道排名为 rank,贡献为 1 / (k + rank)。它在多个通道出现时累加贡献。这里 k 是平滑常数,不是最终取回的候选条数;代码默认值为 60。
{{snippet:rrf}}
同一个候选在单通道里只计一次,避免列表重复造成虚假加分。以候选 id 合并跨通道结果,并记录来源通道。图证据包与单个文本块的 id 不相同,所以它们不一定在这一步合成一个候选;后面的上下文阶段还要根据证据标识避免不必要的重复。
接着用已有重排序模型对“问题+候选文本”打分:
{{snippet:rerank}}
这与第四章 4.5 讲的交叉编码器相衔接。图候选的输入是路径说明和支撑原文,不是把关系数量当作相关性。候选数太多会增加重排开销,因此每个通道仍需提前限定返回数量。
{{snippet:parallel-retrieve}}
Milvus 的稠密/稀疏查询由一个存储模块返回;图查询是另一个独立任务。编排层用并行方式发起二者,收集结果后统一融合。milvus 与 graph 单通道模式用于实验;正式主线 hybrid 不依赖一个“关系问题分类器”来决定图通道是否执行。
并行不等于零成本。响应仍受较慢通道和后续重排影响;超时任务也未必能立即终止底层数据库或模型执行,所以数据库查询本身仍需要超时和有限范围。
{{figure:09-budget|图 5-13 候选排序解决先后;上下文预算决定哪些完整证据真正进入模型。}}
{{snippet:select-context}}
top_k 限制最终采用的唯一原文块数量,char_budget 限制内容长度。图包放不下就整体跳过,不能悄悄截掉中间关系,让剩下文本看起来像完整路径。生成上下文时标题与引用号也占空间,因此还需按最终上下文长度核对。
本实现使用字符预算,便于课堂观察,并不等价于模型的 token 预算。扩展到真实生成服务时,应依据其 tokenizer 和上下文窗口计算更精确的限制。
若双人下午茶漏答,按次序查看:图候选是否出现套餐的完整组成与配方路径;三份配方原文是否齐全;重排后排在何处;预算是否将整个包排除;最终上下文中是否存在。只有证据已进入上下文而回答仍遗漏,才主要进入生成与提示词的排查。
下一节用最终上下文生成带引用的答案,并把空结果、未知引用和证据不足明确返回给调用方。