LLMNotes

第 12 章:Rerank

zjc 于 2026-01-12 发布

这是《LLM 应用开发 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 召回阶段追求“找得到”,重排阶段追求“排得准”。Rerank 模型将查询和候选文档一起编码,能更准确判断细粒度相关性,是提升 RAG 精度的重要环节。

12.1 召回与重排

Query
  -> recall top 100
     -> filter / dedupe
        -> rerank top 100
           -> select top 5-10
              -> build context
阶段 目标 方法
Recall 高覆盖率 ANN、BM25、规则
Filter 安全和硬条件 权限、租户、时间
Rerank 精确相关性 cross-encoder、模型评分
Select 控制上下文 分数阈值、多样性、token 预算

12.2 为什么 Rerank 有效

Embedding 通常分别编码 query 和文档,再用向量距离比较;Rerank 会同时阅读 query 与候选文档,能捕捉词序、条件和排除关系。

示例:

查询:2026 年之后有效的退款政策

候选 A:2025 年退款政策,已失效
候选 B:2026 年生效的退款政策
候选 C:2026 年运费政策

向量可能认为 A 与 B 都相似,Rerank 更容易识别“之后有效”和“退款”的联合条件。

12.3 调用示例

import os
from openai import OpenAI

client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])

def rerank(query: str, documents: list[str]) -> list[int]:
    response = client.chat.completions.create(
        model="gpt-4.1-mini",
        messages=[{
            "role": "user",
            "content": (
                "根据查询给文档排序,只输出 JSON 数组,"
                "元素为文档下标,按相关性从高到低排列。\n"
                f"查询:{query}\n文档:{documents}"
            ),
        }],
        temperature=0,
    )
    return [int(i) for i in json.loads(response.choices[0].message.content)]

专用 rerank 服务或本地 cross-encoder 通常更适合高并发场景。

12.4 Cross-Encoder

input  = [query, document]
model  = cross-encoder
output = relevance score

与向量检索对比:

维度 Embedding 召回 Cross-Encoder 重排
编码方式 query/doc 分开 query/doc 一起
精度 中等 较高
延迟
成本 可离线建索引 每对实时计算
适合 大候选集 小候选集

因此典型流程是“向量/关键词先缩圈,rerank 精排”。

12.5 候选数量

常见参数:

召回 top K:50-200
重排候选:20-100
最终上下文:3-10 个 chunk

选择依据:

  1. rerank 延迟;
  2. 模型成本;
  3. 候选噪声率;
  4. 查询意图复杂度;
  5. 上下文 token 预算;
  6. 是否还需要人工解释引用。

12.6 选择策略

只按分数取 top N 可能内容重复。

def select_chunks(scored, top_n=8, max_per_doc=2, min_score=0.0):
    selected = []
    doc_count = {}
    for item in sorted(scored, key=lambda x: x["score"], reverse=True):
        if item["score"] < min_score:
            continue
        if doc_count.get(item["doc_id"], 0) >= max_per_doc:
            continue
        selected.append(item)
        doc_count[item["doc_id"]] = doc_count.get(item["doc_id"], 0) + 1
        if len(selected) >= top_n:
            break
    return selected

其他策略:

  1. 保留父子块;
  2. 相邻块合并;
  3. 不同子查询配额;
  4. 来源多样性;
  5. 时间最新优先;
  6. 总 token 预算。

12.7 与业务规则结合

retrieval score
  + rerank score
  + authority score
  + freshness score
  - duplicate penalty
  - stale penalty

规则示例:

  1. 官方文档优先于论坛;
  2. 当前版本优先;
  3. 生效政策优先;
  4. 明确答案优先;
  5. 同一文档最多两段;
  6. 已被数据库结果否定的候选降级。

12.8 缓存

Rerank 结果可按以下键缓存:

query_normalized
retrieval_set_version
index_version
rerank_model
tenant_id

注意:

  1. 权限不同不能共享结果;
  2. 文档更新要失效缓存;
  3. 只缓存候选 ID 和分数,不缓存敏感正文;
  4. 设置 TTL;
  5. 监控命中率。

12.9 评估

指标 说明
Recall@K before 重排前召回
Recall@K after 重排后保留率
Precision@N 最终上下文相关率
NDCG@N 排序质量
Answer Correctness 最终答案正确率
Added Latency 重排延迟
Added Cost 重排成本

重排可能提高检索指标但降低答案质量,例如把长上下文排除掉,因此最终要看端到端评测。

12.10 常见问题

现象 排查
重排慢 候选过多、模型太大
结果不如召回 标注不匹配、模型不适合领域
分数集中 归一化或阈值问题
重复片段多 未按 doc 去重
上下文过长 top N 和 token 预算
新文档总靠后 新鲜度未参与
用户看到错误引用 引用映射未校验

本章小结

Rerank 用更高的计算成本换取更准确的候选排序,适合放在召回和过滤之后。要根据延迟与精度平衡候选数量,结合业务规则、多样性和 token 预算做最终选择,并用端到端答案质量验证收益。

思考题

  1. 召回和重排的目标有什么不同?
  2. Cross-Encoder 为什么比双塔向量更精确但更慢?
  3. 最终上下文选择只看 top 分数有什么问题?
  4. Rerank 缓存键应包含哪些版本信息?
  5. 如何证明引入 Rerank 有业务收益?