Python后端AI专题22产品编号搜不到把关键词与向量用 RRF 融合用户搜索“KF-2048 端口”向量模型可能认为“产品安装说明”很相关却把精确包含KF-2048的短表格排在后面。Embedding 擅长同义表达产品编号、错误码和合同号则更适合词法检索。混合检索不是把两个分数直接相加而是先承认它们的量纲不同。Top K 后过滤攻击答案租户 B 有 10 条相似度 1.0A 只有一条 0.8。全局 Top 5 会全部选 BPython 过滤后 A 得到空列表SQL 先限制tenantA再 Top 5A 能得到自己的 0.8 记录。内存测试构造 10 条 B 记录查询仍只返回alice-1awaitstore.upsert([VectorRecord(alice-1,tenant-a,kb-1,退款期限七天,[0.8,0.6][0.0]*6),*[VectorRecord(fbob-secret-{i},tenant-b,kb-1,B 的机密退款规则,query_vector)foriinrange(10)],])resultawaitstore.search(tenant_idtenant-a,knowledge_base_idkb-1,vectorquery_vector,limit5,)assert[item.idforiteminresult][alice-1]检索单测真实结果3 passed in 0.13s。仍需 PostgreSQL 集成测试因为内存实现无法证明 SQL 的 WHERE、JOIN、pgvector 操作符和 HNSW 行为正确。为什么不能把分数直接相加向量相似度可能在 -1 到 1词法分数可能是命中词权重之和范围随查询变化Rerank 又可能输出 0—1。这样写finalvector_scorekeyword_score意味着哪个分值范围更大哪个就支配结果并非真正融合。归一化也需要稳定分布面对不同查询往往脆弱。RRF 只相信名次Reciprocal Rank Fusion 对每个结果在每条列表中的排名累计score(d) Σ 1 / (k rank_i(d))k60缓和第一名与后续名次差距。某块在向量第 2、关键词第 1它会得到1/62 1/61只在向量第 1 的块只有1/61双路都认可的结果通常上升。完整 RRF 模块from__future__importannotationsfromcollections.abcimportSequencefromapp.services.retrieval.typesimportRetrievedChunkdefreciprocal_rank_fusion(vector_results:Sequence[RetrievedChunk],keyword_results:Sequence[RetrievedChunk],*,k:int60,)-list[RetrievedChunk]:Fuse incomparable scores using only within-list ranks.by_id:dict[str,RetrievedChunk]{}scores:dict[str,float]{}forsource,namein((vector_results,vector),(keyword_results,keyword),):forrank,iteminenumerate(source,start1):ifitem.idnotinby_id:by_id[item.id]RetrievedChunk(item.id,item.text,0.0,item.document,item.page,item.section,dict(item.diagnostics),)scores[item.id]scores.get(item.id,0.0)1.0/(krank)by_id[item.id].diagnostics[f{name}_rank]rank by_id[item.id].diagnostics.update(item.diagnostics)forid_,iteminby_id.items():item.scorescores[id_]item.diagnostics[rrf_score]scores[id_]returnsorted(by_id.values(),keylambdaitem:(-item.score,item.id))这里复制一个新的RetrievedChunk避免直接修改召回列表中的对象造成测试和日志相互污染diagnostics 保存各路名次线上才能解释某个结果为何上升。词法基线怎样照顾产品编号本项目的透明基线识别英文数字及连字符编号re.findall(r[A-Za-z0-9](?:-[A-Za-z0-9])*|[\u4e00-\u9fff]{2,},text)完整 token 命中加 3 分中文字符交集提供较小补充。它不是生产级中文搜索引擎但行为可解释足以展示混合链。大规模数据应把关键词搜索下推到 PostgreSQL FTS/OpenSearch而不是records_for()把全部 chunk 读入 Python。双路候选必须应用同一权限向量查询已过滤租户、知识库和 ready 状态关键词读取的records_for()也使用完全相同条件。若其中一路权限较松RRF 会把越权结果堂而皇之地融合回来。安全约束不能只检查“主召回”。产品编号端到端用例语料含退款、KF-2048 的安装端口是 8080、差旅。查询“KF-2048 使用什么端口”断言第一名为产品记录且 diagnostics 有keyword_rank1。测试不强迫 Fake Embedding 故意失败它证明无论向量排序如何精确编号路径参与融合并可解释。本篇最终完整模块fusion.py前面的代码片段用于解释本次改动下面是本篇结束时可直接核对和替换的磁盘完整版本。from__future__importannotationsfromcollections.abcimportSequencefromapp.services.retrieval.typesimportRetrievedChunkdefreciprocal_rank_fusion(vector_results:Sequence[RetrievedChunk],keyword_results:Sequence[RetrievedChunk],*,k:int60,)-list[RetrievedChunk]:Fuse incomparable scores using only their within-list ranks.by_id:dict[str,RetrievedChunk]{}scores:dict[str,float]{}forsource,namein((vector_results,vector),(keyword_results,keyword)):forrank,iteminenumerate(source,start1):ifitem.idnotinby_id:by_id[item.id]RetrievedChunk(item.id,item.text,0.0,item.document,item.page,item.section,dict(item.diagnostics),)scores[item.id]scores.get(item.id,0.0)1.0/(krank)by_id[item.id].diagnostics[f{name}_rank]rank by_id[item.id].diagnostics.update(item.diagnostics)forid_,iteminby_id.items():item.scorescores[id_]item.diagnostics[rrf_score]scores[id_]returnsorted(by_id.values(),keylambdaitem:(-item.score,item.id))本篇练习手算并验证 RRF向量列表[A,B,C]关键词列表[B,D,A]取k60手算 A/B/C/D 得分与最终顺序说明 B 为什么第一。然后写 pytest 构造这两条RetrievedChunk列表断言B 第一A 同时包含vector_rank1和keyword_rank3C 没有keyword_rank输入对象的原始 score 不被修改。下一篇会给出完整答案并加入 Rerank为什么 RRF 第一名仍不一定最能回答当前问题。
企业数字化 ERP 产品动态
相关推荐
10个程序员真实高频使用的生产力网站推荐 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 3:13:54
智慧高速架构与实操:感知、通信、决策三层模型及雷视融合调优 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 3:13:54
STM32开发必知:四件套工具链分工与完整使用流程 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 3:13:48
小麦品种识别预处理实战:从65%到92%准确率的关键技术路径 简介:本资源是天津大学《机器学习算法与应用》课程大作业的完整实现,面向高校机器学习初学者与实践者,聚焦小麦品种(seeds数据集)的分类与聚类任务,系统验证预处理方法对模型性能的影响。项目涵盖PCA、KPCA… · 2026/9/26 3:57:32
热轧带钢表面缺陷检测:深度学习实战与产线落地指南 简介:面向计算机专业毕业设计、课程设计和期末大作业场景,这份资料提供了一套基于深度学习的热轧带钢表面缺陷自动检测完整方案,涵盖数据预处理、模型设计、训练评估与界面可视化等关键环节,包含卷积神经网络优化与迁移学习思路&a… · 2026/9/26 3:57:32
NixOS 上部署 GNS3 Server:模块化配置、安全加固与源码级原理解析 包管理器操作系统 【免费下载链接】nixpkgs Nix Packages collection & NixOS 项目地址: https://gitcode.com/GitHub_Trending/ni/nixpkgs 点击查看 免费下载 GNS3(Graphical Network Simulator 3)是业界广泛使用的网络软件模拟器&… · 2026/9/26 3:57:26
OpenClaw大模型API怎么选?DeepSeek与Kimi配置实战:TaoToken统一Key接入 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 3:57:26
win11部署OpenClaw实测:Node.js、git、npm 环境一次跑通 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 3:57:26
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46