拆开 dsh-knowledge 的检索链路,看 RRF 融合与锚点续读

多数知识库插件的文档只说「支持混合检索」,不讲混合在哪一步发生、失败时退到哪一层。dsh-knowledge 的 README 把链路拆得较细:Query Planner → 双路召回 → RRF 融合 → MMR 去冗余 → 可选 cross-encoder 重排 → Context Composer。这篇顺链走下去,重点看两个最容易被误解的地方------RRF 为什么只融合名次,以及「锚点续读」从哪里读。想看别的插件的实现形态,可先参考 完整插件清单与汉化避坑指南。

Query Planner:主查询始终来自当前消息

Query Planner 同时产出主查询和可选的查询变体 。一条容易被忽略的规则:主查询始终来自当前消息 ,而不是拼接历史对话------既避免多轮历史把当前问题带偏,也靠变体提高「换一种说法」时的召回覆盖率。多查询不是把变体拼成一个大查询,而是先分别召回,再统一融合。

多查询的代价被压在最末端

变体只在召回阶段展开,到重排阶段最终只重排一次,成本与延迟跟候选池大小相关,而不随变体数量线性增长。自动检索的预算更紧:首 Token 路径不启动本地 reranker,远程 rerank 最多调用一次,共享 4 秒总预算。

双路召回:词法走 FTS5,向量走余弦

词法路:FTS5 trigram 与 CJK 二元组

词法召回基于 SQLite FTS5 trigram 索引 ,排序用 BM25 ;查询侧识别 CJK 二元组与拉丁词 。它的价值是兜底:无 embedding、模型未下载或远程服务不可用时仍可搜索。覆盖边界也清楚:trigram 和 CJK 二元组本质是字面匹配,中文同义不同词(如「报销标准」与「差旅费用上限」)大概率对不上。

向量路:先校验维度

向量召回对查询做 embedding 后执行余弦检索,执行前校验向量维度;维度对不上说明模型和索引不配套,报错比返回一堆乱序结果好。

分块保存在独立 SQLite 文件 knowledge-chunks.sqlite(可用 chunkStorePath 调整),词法检索用 FTS5 trigram,向量用 Float32Array 常驻缓存并精确失效。目录来源以「知识库 + 规范化真实路径」确定身份,遗留的重复树会报 ambiguous_source------不猜测、不合并、不自动删除。

RRF 融合:只融合名次,不混合量纲

复制代码

rankᵢ(d) 是文档 d 在第 i 路召回里的名次 ,wᵢ 是这一路的权重,向量路权重由 rrfVectorWeight 控制。

关键在「只融合名次」。BM25 分数和 cosine 相似度是两套量纲:前者理论上无上界,后者在特定区间取值,直接相加等于让其中一路隐性主导。换成倒数名次后,第 1 名与第 2 名的差距固定,与原始分数尺度无关。

常数 60 与同分处理

分母的 60 是平滑常数,压低头部名次差距,避免第 1 名吃掉全部候选。同分结果保留原召回顺序------同样的输入得到同样的输出顺序,方便复现。

MMR 与 rerank:先减冗余,再重排

MMR 在「相关度」与「和已选结果的向量相似度」之间取舍,减少 Top K 里语义重复的片段。再往后是可选的 cross-encoder 重排,可走远程 API 或本地模型------rerankModel: local:Xenova/bge-reranker-base 就是本地写法;它在另一个独立 child process 中运行,与 embedding process 的生命周期完全隔离。

重排的降级语义
  • 失败、超时或分数无效时保留原始召回顺序,是静默降级,不是报错中断。
  • rerank 必须返回与候选一一对应、有限且落在 0, 1 的分数;缺失、越界、数量不一致或协议不匹配都被视为降级,而不是成功。
  • 搜索不会隐式下载 rerank 模型,模型必须先在本地模型页下载并通过健康检查,否则重排就是空转。

所以「配置了 rerank」和「rerank 真的生效了」是两件事,判断依据是召回测试里显示的重排状态。

Context Composer 与锚点续读

Context Composer 为每个命中动态生成有序的 ContextWindow ,按 before → anchor → after 组织证据。桥接文本不会写入索引或 embedding ,只在组装时临时拼进来;contextWindow 默认不跨标题路径,不会把相邻章节误当成本章节的延续。

anchorChunkId 与 anchorIndex

续读入口是 knowledge_get_document:支持 chunkOffset / chunkLimit 分页,也支持用 anchorChunkId 或 anchorIndex 进入锚点模式 ,可控 before、after、maxTokens、focus、crossHeading。另外,SearchHit.text 始终保留完整的 canonical anchor;超长锚点围绕命中位置按句子边界裁剪 ;相邻 chunk 的重复前后缀会被移除。旧字段 siblingContext 在 0.3.x 继续兼容,新调用方应优先用 contextWindow。

阈值只作用在可比分数上

阈值只对可比较的 vector 或 rerank relevance 分数应用,BM25 分数与 RRF 融合分不在其列。拿阈值去过滤 RRF 排名分数,过滤掉的往往是你最想要的结果。

总结

这条链路的取向可概括成三句:融合只比名次不比量纲,所以 RRF 用倒数名次加权重;重排宁可静默降级也不打断,失败、超时、分数不合规都保留原始顺序;证据只在组装时拼装,桥接文本不入库。理解这三点,召回测试里的「分数」「重排状态」才读得懂。想对照同类插件的中文清单与安装形态见 完整插件清单与汉化避坑指南。

适合与不适合

适合 :正在用 dsh-knowledge、想搞清召回顺序成因的人;要调 rrfVectorWeight、阈值或 MMR、需要知道各自作用范围的开发者;基于 anchorChunkId 做二次开发的人;需要解释「重排没生效但不是报错」的技术负责人。

不适合:只想照抄安装步骤、不关心内部机制的读者;把 RRF 当成「分数加权平均」、以为调权重就能显著改变语义排序的人;不愿先在本地模型页下载并做健康检查、却指望 rerank 自动生效的人;资料高度依赖跨章节长距离语义关联、又只开词法路的场景。

标签:dsh-knowledge、DeepSeek Harness、混合检索、RRF 融合、检索机制

本文由 DeepSeek Harness Hub 自动整理,数据来源于插件详情页。

相关推荐
I'm a winner1 小时前
《AI 赋能嵌入式开发:从 0 到全栈工程师》模块1|第4课时
人工智能
johnsong1 小时前
当决策成为免费商品:思维成本崩溃背后的治理真空
大数据·人工智能
ZhangJun951 小时前
在 32GB 内存电脑上本地搭建 Qwen3.6-35B-A3B 大模型踩坑实录
运维·人工智能·阿里云·ai·软件构建
南京码讯光电技术有限公司1 小时前
How to Design an Antenna System for an Industrial WiFi Module
数据库·人工智能
秋名山码民1 小时前
AI 用过一次,下一次怎样做得更好?——拙见 AI OS 的自适应、自迭代与受控演化
人工智能
回眸&啤酒鸭1 小时前
DeepSeek 大模型落地应用与价值实现指南
人工智能
怕浪猫2 小时前
LLM 面试必问的 8 个问题,答不上来直接淘汰
人工智能·python·面试
deepseek232 小时前
Lathoa 拆解:让 AI 故意出错比答对更难,反向出题 harness 的三重校验与共模失效困局
人工智能·大模型·可靠性工程
红海云2 小时前
AI抬高了职场的第一道门槛
人工智能