当前位置: 首页 > news >正文

BM25 算法与关键词提取在向量数据库中的实践优化

BM25 算法与关键词提取在向量数据库中的实践优化

在实际构建问答系统或语义检索场景中,向量数据库(如 Weaviate)提供了基于语义匹配的检索能力,然而我们发现 BM25 关键词检索效果不理想,甚至出现了召回率过低、查询必须精确匹配等问题。
深入排查后,发现问题根源有三:
● 使用 BM25 时,语料库未经过有效分词处理;
● 缺少显式的关键词字段;
● 用户 query 与语料库不一致,导致匹配失败。

💡 问题背景

在构建知识库检索系统过程中,使用了 Weaviate + BM25 作为关键字召回方案,但出现了以下问题:
● 查询内容若与问题文本不完全一致,无法匹配;
● BM25 检索字段未分词(tokenization);
● 用户输入 query 未标准化(如:未分词);

🚧 解决思路与每一步分析

1. 使用 dify 的 keyword 提取方法,在检索和插入时统一处理关键词(pass)

core/rag/datasource/retrieval_service.py

class RetrievalService:@classmethoddef retrieve(cls,retrieval_method: str,dataset_id: str,query: str,top_k: int,score_threshold: Optional[float] = 0.0,reranking_model: Optional[dict] = None,reranking_mode: Optional[str] = "reranking_model",weights: Optional[dict] = None,):# ....这部分代码省略了if retrieval_method == "keyword_search":keyword_thread = threading.Thread(target=RetrievalService.keyword_search,kwargs={"flask_app": current_app._get_current_object(),"dataset_id": dataset_id,"query": query,"top_k": top_k,"all_documents": all_documents,"exceptions": exceptions,},)threads.append(keyword_thread)keyword_thread.start()

● 实现方式
将retrieval_method == "keyword_search"中的代码应用到全文索引中,然后根据默认权重配置keyword_search的返回结果与BM25的搜索结果进行整合返回。
● 为什么这么做?
保证检索时对用户 query 的处理方式与语料构建时一致,避免因分词策略不同导致的召回不一致。
● 好处:
○ 一致性高:插入、检索使用同一套 keyword 提取逻辑;
○ 实现简单:封装函数、按权重检索;
○ 具备可控性:可调节关键词提取策略。
● 缺点:
○ 需修改检索逻辑;
○ 测试的时候对程序运行性能影响较高,造成卡顿现象

2. 在 Weaviate 中为 BM25 检索添加 tokenized_content 字段

核心代码

# 插入数据,附带分词字段
for q, a in qa_data:segmented_q = " ".join(jieba.lcut(q+a))client.data_object.create(data_object={"question": q,"segmentedQuestion": segmented_q,"answer": a},class_name="QAPair")result = client.query.get("QAPair", ["question", "answer"]) \.with_bm25(query=query_text, properties=["segmentedQuestion"]) \.with_limit(3) \.do()

● 为什么这么做?
将语料中的内容预先分词,并按空格分隔成字符串,存储在新字段中,供 BM25 使用。
● 好处:
○ 充分利用 BM25 的词频特征;
○ 可扩展为对任意内容的分词检索;
○ 配合 with_bm25 可轻松启用多字段召回。
● 缺点:
○ 数据迁移成本高;
○ 增加字段后需兼容现有逻辑;
○ 用户输入未标准化,召回结果任然不理想

3. 对用户输入的 Query 做分词预处理

核心代码

segmented_queries = " ".join(jieba.lcut(query))

● 为什么这么做?
与语料中的 tokenized_content 对齐,避免中文 query 无法匹配 token。
● 好处:
○ 显著提升召回率;
○ 查询语义更清晰;
○ 保证检索一致性。
● 缺点:
○ 用户体验上难以感知;
○ 仍受限于关键词提取准确性。

🔬 效果测试与对比分析

我分别测试了以下两种场景:测试代码参考 github连接

📌 未分词的 Query + 原始语料

● 查询 “人工智能是什么”
● 结果为空或错误,必须输入 “什么是人工智能?” 才能命中

✅ 分词后的 Query + 分词语料字段 (segmentedQuestion)

● 查询:“人工智能是什么”
● 命中效果大幅提升,多个语料均被正确匹配

对比项 原始方式(未分词) 优化方式(分词+keyword)
召回率 较低 明显提升
用户容错性 差 好
系统复杂度 简单 增加字段、代码稍复杂
开发适应性 高 需同步维护关键词提取逻辑
查询效率 快 PostgreSQL 存储场景略卡顿

⚠️ PostgreSQL keyword 权重查询方案存在的问题

虽然我们尝试结合 BM25 + keyword 搜索 + 权重配置的方式,在 PostgreSQL 中实现 fulltext 搜索方案,但最终:
● 查询效果非常好;
● 查询速度严重卡顿;
● 不适合高并发线上环境。
因此该方案被标记为高风险。

✅ 最终推荐方案:结合 BM25 + 分词字段

综合考虑效果与性能:

  1. 为语料增加 tokenized_content 字段;
  2. 插入语料时进行分词填入该字段;
  3. 查询时对 query 分词,与该字段对齐;
  4. 使用 Weaviate 的 with_bm25 指定使用该字段检索。
    该方式在准确性、性能、维护成本之间取得了良好平衡。

🧠 总结

本次针对向量数据库中 BM25 匹配效果差的问题,从关键词缺失、分词不一致、语料结构单一等多个角度进行优化,主要提升措施包括:
● 增加分词字段 segmentedQuestion / tokenized_content;
● 插入和查询统一使用 jieba 分词;
● 尝试结合 keyword 库 + PostgreSQL,但因性能问题暂时搁置;
● 最终实现了一种高效、稳定的 hybrid 检索方案。
📌 关键词召回 + 向量语义检索,将是构建强大问答系统的未来主流方案。

相关文章:

  • 架构篇、第五章_05Jenkins的部署与构建
  • 为什么Flexray在渐渐被TSN以太网替代-AI的回答
  • WebGIS开发智慧机场项目实战(2)
  • postgres的docker版本安装
  • SG7050VAN差分晶振,X1G0042810033,EPSON爱普生以太网6G晶振
  • 如何通过URL链接让亚马逊网站返回指定像素大小的产品主图片
  • conda init执行了还是不好用
  • 升级kafka4.0.0,无ZK版本
  • windows编程:LIB和OBJ格式文件解析
  • 如何实现金蝶云星空到MySQL的数据高效集成
  • GTS-400 系列运动控制器板卡介绍(三十三)---运动程序单线程累加求和
  • 小学数学题批量生成及检查工具
  • 如何导出一个python项目中的所有依赖包及其版本信息requirements.txt
  • JS手写代码篇---手写 instanceof 方法
  • 卡尔曼滤波及变种 KF EKF ESKF的区别跟用法
  • 医学图像分析中的大规模基准测试与增强迁移学习|文献速递-深度学习医疗AI最新文献
  • Java【13_1】final、初始化块、继承(测试题)
  • 基于“岗课赛证”融通的中职“综合布线技术”课程解决方案
  • 物联网技术在银行安全用电系统中的应用与实践研究
  • 网络安全-等级保护(等保) 2-6 GB/T 36958—2018 《信息安全技术 网络安全等级保护安全管理中心技术要求》-2018-12-28 发布【现行】
  • 首映|《星际宝贝史迪奇》真人电影,不变的“欧哈纳”
  • A股三大股指低收:汽车股领涨,大金融走弱,两市成交近1.1万亿元
  • 中国人民银行等四部门联合召开科技金融工作交流推进会
  • 92岁上海交大退休教师捐赠百万元给学校,其父也曾设奖学金
  • 党建评:对违规宴饮等问题要坚决露头就打
  • 中科飞测将投资超10亿元,在上海张江成立第二总部