RAG 召回差?先改切片和重排

RAG 召回差?先改切片和重排

我第一次给内部文档搭 RAG,召回回来的块总差一口气。问怎么降大模型调用成本,它回了一段讲向量库存储贵的。排查下来,症结在切片和重排这两道工序,模型本身没问题。这篇把 RAG 检索的原理拆开讲,代码能直接跑。

一、切片定召回上限

1、按照字符长度硬切(按照模型限制且)

2、按照语句段落切(保留完整句子)

切片直接决定召回的上限。固定长度切块图省事,却常把一句完整的话从中间劈开,模型拿到的块结尾是断的,语义被切断,召回自然掉。

我把同一段 226 字文档喂给两种切法。固定长度切出 2 块,其中 1 块在句子中间断开。递归字符切块同样 2 块,没有一处断句。差距就在句子完不完整。

递归切块先按段落、再按句子、最后按长度,能保住大多数句子完整,是工程上的默认选择。块大小我一般控制在两百到五百字,再留百分之十到二十的重叠给边界句,让相邻块共享上下文。

python 复制代码
def recursive_chunk(text, size=200, overlap=40):
    """先按段落、再按句子、最后按长度切。
    为什么这么写:保证句子不被切断,召回的块语义才完整。"""
    chunks = []
    for para in re.split(r"\n+", text):
        para = para.strip()
        if not para:
            continue
        # 按句切,单句超过 size 才硬切,正常句子整段保留
        sents = re.split(r"(?<=[。!?])", para)
        cur = ""
        for s in sents:
            if len(cur) + len(s) <= size:
                cur += s
            else:
                if cur:
                    chunks.append(cur)
                while len(s) > size:
                    chunks.append(s[:size])
                    s = s[size - overlap:]
                cur = s
        if cur:
            chunks.append(cur)
    return chunks

二、混合检索补语义

词法检索(BM25)擅长精确词匹配,可查询词和文档用词不一致时就抓不到。我问每月开销怎么缩减,语料里没有开销、缩减这两个词,纯词法前 3 名里根本没有讲成本的块。

语义通道补上这一块。生产环境用 bge-m3、gte 这类嵌入模型把文本编码成向量再比余弦,查询写缩减开销也能命中讲降低成本的块。我这个 demo 用同义词扩展离线近似,机制一致,两路打分后按排名融合。

融合用 Reciprocal Rank Fusion,不依赖两路分数量纲一致,实现成本低。上面那个口语查询,语义通道把讲成本的块从第 3 名外拉进前 3,重排再抬到第 2。

python 复制代码
import math, re

def expand_query(query):
    """把口语词换回语料里的标准词,模拟语义检索跨词形匹配的能力。"""
    q = re.sub(r"\s+", "", query)
    for std, cols in {"成本": ["开销", "花费", "账单"], "切块": ["切分", "分块"]}.items():
        for c in cols:
            if c in q:
                q = q.replace(c, std)
    return q

def rrf(ranks, k=60):
    """Reciprocal Rank Fusion:按排名融合,不挑两路分数量纲。"""
    fused = {}
    for rank in ranks:
        for pos, (idx, _) in enumerate(rank):
            fused[idx] = fused.get(idx, 0) + 1 / (k + pos + 1)
    return [i for i, _ in sorted(fused.items(), key=lambda x: -x[1])]

真实跑出来的前几名,口语查询每月开销怎么缩减的结果如下。

text 复制代码
纯词法前 3:c_embed、c_trap、c_distract(无讲成本的块)
语义通道前 3:c_embed、c_trap、c_cost(c_cost 被拉进前 3)
重排后前 5:c_embed、c_cost、c_trap、c_distract、c_hybrid

三、重排定答案准度

召回只要不漏,重排才决定答案准不准。融合阶段只看排名,谁词频高谁靠前,偏题块容易混进来。

我放了个陷阱块,满篇都是成本和大模型调用成本,词法分很高,排在融合第 1。但它只讲涨价和担心账单,没讲怎么降。重排阶段我加了融合没有的两个信号,块里有没有查询的意图动作词(省钱、压),以及有没有精确短语。带省钱、压的块被抬上去,陷阱块被压到第 2,讲怎么降成本的块回到第 1。

重排只挑召回前 N 条做,因为交叉编码器逐对打分很慢,全量做不现实。

python 复制代码
def rerank(query, idxs, bm25, topk=5):
    """对融合后的候选做特征重排。
    加的两个信号融合阶段没有:精确短语、意图动作词。"""
    q = re.sub(r"\s+", "", query)
    scored = []
    for idx in idxs:
        doc = CORPUS[list(CORPUS)[idx]]
        bm = bm25.score(query, idx)
        phrase = 1.0 if any(q[i:i+4] in doc for i in range(len(q)-3)) else 0.0
        intent = 1.0 if any(t in doc for t in ["省钱", "压", "降低"]) else 0.0
        length_penalty = 1.0 if len(doc) <= 200 else 0.6
        scored.append((idx, 0.3*bm + 0.8*phrase + 1.2*intent + length_penalty))
    scored.sort(key=lambda x: -x[1])
    return [i for i, _ in scored[:topk]]

四、常见坑

坑 1 用字符数估算成本。 表现是用 len(text) 估 token,中文场景账单比预期高两三倍。改用分词器的 encode 实测字符对应的 token 数。

坑 2 切块太大或太小。 块超过五百字噪声多,小于两百字上下文不够。按二百到五百字切,留百分之十到二十的重叠。

坑 3 只跑词法不跑语义。 查询和文档用词不一致时召回漏掉正确答案。加一路嵌入检索再融合。

坑 4 把召回当排序。 召回阶段只管不漏,排序交给重排。别让词频高的偏题块直接进答案。

五、小结

  1. 切片定召回上限。递归字符切块保句子完整,块控制二百到五百字。
  2. 词法加语义才稳。BM25 保精确,嵌入保语义,RRF 融合不挑量纲。
  3. 重排定答案准度。加意图词和精确短语两个信号,把偏题块压下去。
  4. 召回和重排分工。召回管不漏,重排管准,别混为一谈。
相关推荐
4SAPI1 小时前
多国产模型混合调度与企业级治理生产选型指南:4SAPI、硅基流动与 OpenRouter 的架构权衡
大数据·人工智能·架构·php
武子康1 小时前
Cosmos Curator 总览:从一条样本的组成,选对数据处理入口
人工智能·后端·agent
XMAIPC_Robot1 小时前
-40~85℃宽温实测:RK3588 机器人控制器硬件底座,长时间 SLAM 定位稳定性验证
人工智能·fpga开发·机器人·边缘计算·arm+fpga·rk3588+fpga
锋行天下2 小时前
Langgraph 运行时上下文使用示例
人工智能
zandy10112 小时前
中电金信金融 AI 工程化实践:异构模型纳管、智能体治理与 AI 原生软件工厂
人工智能·金融
吴佳浩2 小时前
中小企业跑125B 大模型:显存装不下的时候,Strata 做了什么
人工智能·llm
学IT的周星星2 小时前
LangGraph:用「流程图」的方式编排 Agent
数据库·人工智能·大模型·llm·流程图·agent
NC 王炸2 小时前
不用 API Key,也能让网页调用 AI?Opera 把本地模型接进了浏览器
人工智能·浏览器扩展·ai 工具·本地 ai·opera·webai
锋行天下2 小时前
Langgraph 实现类似Langchain记忆上下文过长裁剪
人工智能·后端
不老刘2 小时前
Antigravity 的 New Worktree 模式:给 AI 代理一个安全的“平行工位”
人工智能·git·ai编程·antigravity