# 知识库建好了但够不够用?向量检索量化覆盖率,找到你的知识盲区

知识库建好了,但够不够用?向量检索量化覆盖率,找到你的知识盲区

背景

政务热线RAG系统上线前,最怕的不是"回答错了",而是"答不上来"。

知识库是从各种政策文档、办事指南里整理出来的,几千条问答。热线那边积累了十几万条真实来电记录。问题来了:知识库能覆盖多少真实问题?哪些问题是知识库的盲区?

不能拍脑袋说"差不多够了吧"。得量化。

思路

把热线问题逐条向量化,去Milvus知识库里搜,看最高相似度是多少。

  • 相似度 ≥ 0.85:知识库里有高度匹配的答案,覆盖了
  • 相似度 0.6 ~ 0.85:知识库里有相关的,但不精确,可能需要补充
  • 相似度 < 0.6:知识库里几乎没有相关内容,纯盲区

逐条跑完,统计三个区间的比例,覆盖率一目了然。

实现

第一步:热线问题向量化

def vectorize_text(text):
    url = "http://localhost:11434/api/embeddings"
    data = {"model": "bge-m3:latest", "prompt": text}
    response = requests.post(url, json=data)
    if response.status_code == 200:
        return response.json().get('embedding', [])
    return None

本地Ollama + bge-m3,零API费用。

第二步:逐条搜索知识库

from pymilvus import MilvusClient

client = MilvusClient(
    uri="http://your_milvus_host:19530",
    token="your_token",
    db_name="default"
)

search_params = {"metric_type": "COSINE", "params": {"radius": 0.0}}

注意radius=0.0——我们要看到每条问题和知识库的最高相似度,不管多低都返回,不能设过滤阈值。

pathname = "d:\\q\\覆盖率分析.txt"
file = open(pathname, "w", encoding="utf-8")

for item in hotline_questions:
    question_text = item["Question"]
    question_vector = vectorize_text(question_text)

    # 在知识库中搜索最相似的10条
    res = client.search(
        collection_name="knowledge_base",
        data=[question_vector],
        limit=10,
        output_fields=["uid", "Question"],
        search_params=search_params
    )

    if res[0].__len__() > 0:
        best_match = res[0][0]
        file.write(
            question_text + "\t" +
            str(best_match["entity"]["uid"]) + "\t" +
            str(best_match["distance"]) + "\t" +
            best_match["entity"]["Question"] + "\n"
        )
    else:
        file.write(question_text + "\t无匹配\t0.0\t\n")

file.close()

第三步:统计覆盖率

输出文件每行四列:热线问题\t知识库uid\t最高相似度\t知识库问题

按相似度分三档统计:

区间含义建议动作
≥ 0.85高度覆盖直接可用
0.6 ~ 0.85部分覆盖需补充精确答案
< 0.6知识盲区需新建知识条目

实际跑出来,覆盖率大致分布(脱敏示意):

高度覆盖(≥0.85):约 60%
部分覆盖(0.6~0.85):约 25%
知识盲区(<0.6):约 15%

这15%就是知识库的盲区。拿着这份清单去找业务部门补知识,比拍脑袋"差不多够了吧"靠谱得多。

进阶:按业务分类统计覆盖率

盲区不是均匀分布的。"社保缴费"这种高频话题,知识库覆盖率高;"特殊工种提前退休"这种低频但专业的问题,覆盖率可能很低。

把热线问题先分类(参保、缴费、社保卡、养老、医疗……),再按类别统计覆盖率,就知道哪个业务线的知识库最缺:

参保类:覆盖率 82%  ← 不错
缴费类:覆盖率 78%  ← 还行
社保卡:覆盖率 65%  ← 得补了
特殊工种:覆盖率 31%  ← 严重不足,优先补

分类可以用LLM做,一句话搞定:

def classify_question(question_text):
    prompt = (
        question_text +
        "  分类为参保、缴费、社保卡、养老保险、失业保险、医疗保险、" +
        "工伤保险、生育保险、人事、就业培训、就业、社保档案、其他档案、" +
        "其他之间那一类问题,只需返回分类的那几个字"
    )
    response = client.chat.completions.create(
        model="deepseek-chat",
        messages=[{"role": "user", "content": prompt}],
        stream=False
    )
    return response.choices[0].message.content

分类成本很低——一条问题一次LLM调用,几厘钱。

关键参数

参数为什么
radius0.0不设过滤,全部返回,需要看到每条的真实相似度
limit10只看Top-10,取最高的那个distance作为覆盖率指标
metric_typeCOSINE余弦相似度,和去重时的标准一致
Embeddingbge-m3和知识库入库时的模型必须一致,否则向量空间不同

踩坑

1. Embedding模型必须和知识库一致

知识库入库时用的bge-m3,检索时也必须用bge-m3。换成别的模型(比如bge-large-zh-v1.5),向量空间不同,相似度没有可比性。我在换模型时就踩过——换了之后覆盖率从60%跳到85%,不是知识库变好了,是两套模型的相似度标尺不一样。

2. radius=0.0不是所有Milvus版本都支持

早期版本radius不支持0.0,会报错。升到2.4+就行了。

3. limit别设太小

有些问题的最佳匹配可能不在Top-3里。设10比较稳妥,既能看到最好的匹配,又不会因为排序噪声误导判断。

总结

  • 知识库覆盖率可以量化:把真实问题逐条向量化,搜知识库,看最高相似度
  • 分三档统计(≥0.85 / 0.6~0.85 / <0.6),盲区一目了然
  • 按业务分类统计覆盖率,知道该补哪个方向的知识
  • 全程Embedding用本地Ollama,唯一花钱的是分类那一步LLM调用,成本忽略不计
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值