用向量检索处理像卡纳达语(Kannada)这种黏着语

Tom 中级 8小时前 557 浏览 6 点赞 约 1 分钟

这次踩坑让我意识到,很多时候 RAG 效果差根本不是大模型的问题,而是检索环节在掉链子。尤其是面对卡纳达语这种语言,一个名字根据语法格位的不同,写法会变,导致多语言模型在语义空间里的压缩非常不稳定。再加上文学作品里有很多生僻词和具体的页码引用,单纯靠余弦相似度(Cosine Similarity)根本搜不准。

为了解决这个问题,我把检索链路重构成了“混合检索 + 确定性路由”的方案。最核心的改动是引入了 RRF(Reciprocal Rank Fusion)算法,把 BM25 的关键词检索和稠密向量检索的结果进行融合。

对于那些明确问“第 X 页发生了什么”的查询,我直接写了一个简单的正则路由(Regex Router),让它绕过语义搜索,直接走元数据(Metadata)的精确匹配。这样处理后,页码查询的准确率直接变成了 100%,彻底杜绝了幻觉。

下面是我这次实操中核心的逻辑流程,大家在做垂直领域 RAG 时可以参考:

一、构建确定性路由
先用正则判断用户意图,如果是页码查询,直接跳过向量库。

import re

def query_router(user_query):
    # 匹配类似 "page 120" 或 "第120页" 的模式
    page_pattern = r"page\s+(\d+)"
    match = re.search(page_pattern, user_query, re.IGNORECASE)
    
    if match:
        return "metadata_lookup", match.group(1)
    return "hybrid_search", None

二、实施混合检索与 RRF 融合
不再信任单一的向量得分,而是将 BM25 和 Dense 检索的结果通过 RRF 公式重新打分排序。

def rrf_fusion(dense_results, bm25_results, k=60):
    scores = {}
    # 稠密检索结果加权
    for rank, doc_id in enumerate(dense_results):
        scores[doc_id] = scores.get(doc_id, 0) + 1 / (rank + k)
    # BM25 检索结果加权
    for rank, doc_id in enumerate(bm25_results):
        scores[doc_id] = scores.get(doc_id, 0) + 1 / (rank + k)
    
    # 按得分从高到低排序
    return sorted(scores.items(), key=lambda x: x[1], reverse=True)

三、引入 Cross-Encoder 重排
在 RRF 筛选出 Top-N 候选集后,再用一个更重的 Cross-Encoder 模型做最后一遍精排,并设置一个置信度阈值,低于这个值的直接告知用户“没找到”,而不是强行生成。

通过这套组合拳,我在 50 个测试集上的 RAGAS Faithfulness 达到了 0.92,Context Recall 达到了 0.89。事实证明,在处理复杂语言或专业文档时,别迷信纯向量检索,给系统增加一些“确定性”的硬逻辑才是关键。

Gemini提示词ChromaDBRRFBM25

全部回复 (3)

副业中测试 中级 8小时前
确实,之前搞韩语也这样,纯向量搜出来的结果简直是随机。
0 回复
躺平产品经理 初级 8小时前
那混合检索怎么调权重?感觉不同语料得反复试。
0 回复
极客阿强 中级 8小时前
得加个词干提取,不然那些后缀太乱了,搜不准。
0 回复

发表回复

支持 Markdown 格式