96SEO 2026-05-09 09:49 2
我们经常惊叹于LLM那似乎无所不知的口才。但是一旦你把它扔进严肃的医疗场景,让它去诊断或者给出用药建议,那种“一本正经胡说八道”的幻觉问题,简直让人后背发凉。毕竟医生kan病是人命关天的大事,容不得半点虚构。所以RAG技术应运而生,它就像是给大模型外挂了一个“随身图书馆”,让它回答问题时有据可依。

不过普通的RAG也不是完美的银弹。在实际的工程落地中,我们发现单纯依赖向量检索,有时候会抓不住关键的关键词;而单纯依赖传统的关键词匹配,又往往像个呆板的机器人,读不懂人类的言外之意。今天我们就来一场硬核的实战,不玩虚的,从零开始,手搓一个医疗知识混合检索系统。我们将把BM25全文检索和向量语义检索拧成一股绳,kankan这所谓的“混合检索”到底Neng不Neng解决大模型在垂直领域的痛点。
一、 寻找“燃料”:医疗数据的获取与准备巧妇难为无米之炊。要构建一个医疗问答系统, 得有高质量的医疗数据。Ru果你还在漫无目的地百度搜索,那效率可就太低了。在这个AI圈子里HuggingFace就像是GitHub一样神圣,上面不仅有各种魔改的大模型,geng有海量的专业数据集。当然Ru果你觉得访问国外网站有些卡顿,或者想要geng接地气的中文语料,ModelScope绝对是个宝藏之地,这可是中国版的HuggingFace,里面藏着不少中文医疗领域的珍宝。
这次实战,我们直接从ModelScope上拉取一份中文医疗对话数据。下载下来后通常是一个CSV格式的表格文件。这玩意儿kan起来平平无奇,里面却藏着成千上万条真实的医患对话记录,这就是我们RAG系统的“知识源泉”。在代码层面我们需要Zuo的第一件事,就是把这些数据从硬盘中读出来清洗一下喂给我们的系统。
1.1 数据加载与预处理在Python的世界里处理CSV简直是易如反掌。我们需要把数据加载到内存中,通常我们会把“问题”作为检索的入口,把“答案”作为我们希望召回的目标。这里有个小细节要注意,在构建索引之前,我们得确保文本的格式是干净的,不Neng有乱码或者奇怪的符号,否则会严重影响后续的分词和向量化效果。
二、 双引擎驱动:理解混合检索的核心理念在正式敲代码之前,我们先得把思路理清楚。为什么我们要搞“混合检索”?这其实是为了弥补两种技术的短板。
想象一下用户问了一个非常专业的术语,比如某种特定的化学药物名称。Ru果是向量检索,它可Neng会根据语义的相似度,找来一堆关于“药物”的通用回答,却漏掉了那个精确匹配的名词。而Ru果是BM25全文检索,它就像个严格的图书管理员,只要你输入的关键词在文档里出现了它就Neng精准地把书抽出来但它读不懂“感冒”和“流感”在语义上的关联。
所以我们的策略是:“精确匹配 + 语义理解”。我们用BM25保证关键词的命中率,用向量检索保证语义的覆盖面。Zui后把两者的评分加权融合,这就是混合检索的精髓。
三、 实战环节:构建向量数据库先让我们把向量检索这部分搞定。虽然我们Ke以自己写代码算余弦相似度,但既然是工程实战,Zui好还是用上成熟的工具。ChromaDB是一个轻量级但功Neng强大的向量数据库,非常适合这种本地化的RAG项目。
我们需要封装一个类,专门用来管理这个向量数据库。这个类不仅要负责存数据,还要负责把文本变成向量。
3.1 封装数据库连接器下面这段代码,是我们与向量世界交互的桥梁。我们初始化了一个ChromaDB的客户端,并创建了一个集合。你Ke以把这个集合想象成一个专门存放医疗知识向量的仓库。
import chromadb
from chromadb.config import Settings
# 封装向量数据库
class MyVectorDBConnector:
# collection_name:向量数据库中集合的名称。
def __init__:
# 初始化 ChromaDB 客户端 并重置数据库
chroma_client = chromadb.Client)
# 创建一个 集合 collection 在向量数据库中,集合是存储向量数据以及相关元数据的容器
# get_or_create_collection 方法用于获取或创建一个集合,Ru果集合不存在则创建一个新集合。
self.collection = chroma_client.get_or_create_collection
# 定义一个函数,用于将文本转换为向量表示,并返回一个包含向量表示的列表。
self.client = get_normal_client
3.2 获取向量与批量处理
这里有个坑得提醒大家。大模型的API通常对一次处理的文本数量有限制,Ru果你一次性把几千条文档扔过去,接口直接就报错了。所以我们必须写一个带batch_size的函数,分批次地去调用Embedding模型,把文本转化成高维空间的向量。
# 封装向量模型与API的交互操作,通过自定义函数 get_embeddings 提供向量模型的调用。
def get_embeddings:
data = self.client.embeddings.create.data
return
# get_embeddings函数的变体版,因为各个模型对一次Neng处理的文本条数有限制且每个平台不一致,新增一个batch_size参数用以控制。
def get_embeddings_batch:
all_embeddings =
for i in range, batch_size):
batch_text = texts
data = self.client.embeddings.create.data
all_embeddings.extend
return all_embeddings
3.3 添加文档与检索接口
有了向量,我们就Ke以把它们存进数据库了。同时我们也需要一个查询接口,输入一个问题,它Neng在库里找到Zui相似的几条记录。
def add_documents:
'''向 collection 中添加文档与向量'''
embeddings = self.get_embeddings_batch
# 向 collection 中添加文档与向量
self.collection.add(
embeddings=embeddings, # 每个文档的向量
documents=outputs, # 文档的原文
ids= # 每个文档的 id
)
# 定义检索函数, 在向量数据库里进行检索操作
def search:
'''检索向量数据库'''
# self.collection.query 这是 ChromaDB 集合对象的一个方法,用于在集合中执行查询操作。
results = self.collection.query(
# query_embeddings是查询文本的向量表示
# 调用在类初始化时传入的嵌入函数 self.embedding_fn,把查询文本 query 转换为向量。
# 要注意的是期望接收一个字符串列表作为输入,所以这里把 query 放在列表 里。
query_embeddings=self.get_embeddings_batch,
# 指定要返回的Zui相似文档的数量。
n_results=top_n
)
# 返回检索结果 results 是一个字典,其中包含了和查询向量Zui相似的 top_n 个文档的相关信息
return results
四、 传统利器:BM25全文检索的实现
向量部分搞定了现在我们来kankan老牌劲旅——BM25。在Python里rank_bm25库是实现这一算法的神器。BM25的核心思想其实hen朴素:一个词在文档里出现得越频繁,且在所有文档中越稀缺,那这个词就越重要。
但是中文不像英文那样单词之间有空格,所以我们得先用jieba这个库进行分词,把句子切成一个个词,才Neng算频率。
import jieba
from rank_bm25 import BM25Okapi
import numpy as np
# BM25进行全文检索
def bm25_search:
# 在运用 BM25 算法进行全文检索时需要对文档进行分词
# 文档分词 jieba.lcut 函数会把 instructions 列表里的每个文档 doc 进行分词
tokenized_corpus =
# 初始化一个BM25Okapi对象
bm25 = BM25Okapi
# 问题分词:对查询的问题也需要进行分词处理
tokenized_query = jieba.lcut
# 通过BM25算法计算查询词与文档的相似度分数
bm25_scores = bm25.get_scores
# 获取前3个结果
bm25_results = bm25.get_top_n
print
print
# 关键步骤:BM25分数归一化到区间
# 因为BM25的原始分数范围不固定,为了后面和向量分数加权,必须归一化
bm25_scores = np.array
max_score = bm25_scores.max
min_score = bm25_scores.min
bm25_scores_normalized = /
print
print
return bm25_scores_normalized
五、 核心融合:向量检索的归一化处理
在混合之前,我们还得把向量检索的分数也处理一下。向量检索通常计算的是“距离”,距离越小越相似。而BM25是分数越大越相似。为了统一标准,我们需要把距离转换成相似度,并且也压缩到0到1之间。
# 向量相似度检索
def vector_search:
# 创建一个向量数据库对象
vector_db = MyVectorDBConnector
query_embedding = np.array)
doc_embeddings = np.array)
# 计算查询向量和文档向量之间的欧氏距离
vector_scores = np.linalg.norm
# 将距离转换为相似度分数并归一化到区间
max_score = np.max
min_score = np.min
# 距离越小,相似度越高。用 1 减去归一化后的距离
vector_scores_normalized = 1 - /
print
print
return vector_scores_normalized
六、 终极合体:混合检索系统
到了Zui激动人心的时刻了!我们现在有了两份评分表:一份是BM25的,一份是向量的。接下来我们要Zuo的就是加权融合。比如我们Ke以给BM25设个权重0.3,向量检索设个权重0.7。把两个分数乘以权重加起来就是Zui终的“混合分数”。Zui后按这个分数排序,取Top K的结果。
这里有个小细节要注意,代码里的Index_CSV = Index_result + ,这种操作,其实是在处理索引的对应关系,确保我们的分数Neng和原始文档对得上号。
# 混合检索:组合BM25和词向量相似度检索的结果
def hybrid_search:
bm25_scores_normalized = bm25_search # BM25分数
vector_scores_normalized = vector_search # 向量分数
# 将两种方法的分数进行加权组合
# bm25_weight控制BM25的重要性,控制向量的重要性
combined_scores = bm25_weight * bm25_scores_normalized + * vector_scores_normalized
print
# 根据组合分数对结果排序并返回前top_k个Zui相关的文档
# argsort默认升序,用于倒序
top_index = combined_scores.argsort
print
print
# 输出混合搜索的结果
hybrid_results = for i in top_index]
return hybrid_results
七、 效果验证与反思
代码写完了是不是真的好用?我们得找个倒霉蛋...哦不找个典型的病例来测一下。比如我们问:“扁桃体发炎怎么办”。
if __name__ == '__main__':
# 查询的问题
query = "扁桃体发炎怎么办"
# 混合检索
hybrid_results = hybrid_search
print
跑一下代码,kankan打印出来的Top3结果。根据日志,我们可Neng会得到索引为196、xx、xx的结果。我们打开CSV表格,对着索引一kan,嘿,还真召回了几条关于扁桃体炎的建议。这说明我们的系统基本逻辑是跑通了。
不过在测试中我也发现了一个奇怪的现象。有时候,排在前列的某条结果,kan起来跟“扁桃体”没啥关系,甚至有点风马牛不相及。这可Neng是数据源本身的问题,或者是某些通用词汇在向量空间里靠得太近导致的。这就提醒我们,数据清洗和权重调优是永无止境的。Ru果有时间,还得深入查查为什么那条奇怪的数据会被捞出来这本身就是优化的起点。
从零开始构建一个医疗知识混合检索系统,虽然代码量不算巨大,但涉及的技术点却非常丰富。从数据的获取与清洗,到BM25与向量检索的各自实现,再到Zui后的分数归一化与加权融合,每一步dou考验着我们对细节的把控。
这个“麻雀虽小五脏俱全”的系统,展示了RAG技术在垂直领域的巨大潜力。它不再是大模型在那儿自言自语,而是基于真实的医疗知识库,给出有理有据的回答。虽然目前还只是初体验,离真正的临床辅助诊断还有hen长的路要走,但这无疑是通往智Neng医疗助手坚实的一步。
废话少说就是干!希望这篇实战记录Neng给你的AI项目带来一点灵感。赶紧去试试吧,让你的大模型从此不再“幻觉”!
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback