← 返回资讯

Rerank 重排接口接入实战

2026-06-26

Rerank(重排)接口接收一个查询和若干候选文档,返回每份文档的相关性分数,用于在向量召回(ANN 搜索)之后做精排,大幅提升 RAG 最终答案质量。召回阶段”宽进”100 条,Rerank 后”精出” top-5 送给大模型,是当前 RAG 的标准两阶段范式。

Rerank 在 RAG 中的位置

用户查询

Embedding → 向量库 ANN 召回 top-N(速度快,精度一般)

Rerank → 精确打分,取 top-K(慢但精度高)

大模型生成回答

召回 N 通常取 50-200,Rerank 后 K 取 3-10,具体视 context 窗口大小决定。

这里有个新手最容易踩的坑:Rerank 救不回漏召的文档。它只能在你送进去的候选集里排序,候选集里没有的东西它变不出来。如果你为了省钱把召回 top-N 设成 5,那真正相关的那篇文档可能排在向量召回的第 20 名之外,压根没进候选集,Rerank 再强也无能为力。所以生产上常见的做法是”宽召回、窄精排”:召回阶段舍得多要一些(50-100 条),Rerank 阶段再狠狠砍到 3-5 条喂给大模型,两段各司其职,别指望一个环节包打天下。

为什么 Rerank 比向量召回准:双塔 vs 交叉编码

搞懂这一点,你才知道 Rerank 该放在链路的哪一步、能不能替代 Embedding。

  • Embedding + ANN 召回用的是”双塔”结构:查询和文档分别独立编码成向量,两者从头到尾没有见过面,相似度是编码完成之后才用余弦相似度或点积算出来的。这意味着模型在编码文档的时候根本不知道你会问什么,语义交互是”事后补的”,天然有精度损失——但好处是文档向量可以提前算好存进向量库,检索时只做一次 ANN 近邻查找,毫秒级返回,能扛住百万级文档规模。
  • Rerank 用的是”交叉编码”结构:把 query 和一份 document 拼接成一个序列,一起塞进模型做前向计算,query 里的每个词都能在 attention 层直接看到 document 里的每个词,语义颗粒度细得多,能分辨”字面像但语义不相关”和”字面不像但语义相关”这两种情况——这正是双塔模型最容易翻车的地方。代价是它没法预先建索引,每次都要现算,计算量随文档数线性增长,绝对扛不住对全库做交叉编码。这就是为什么 Rerank 只能站在 ANN 召回之后,负责在几十上百条候选里精挑细选,而不能取代召回。

主流 Rerank 模型对比

模型接口形式优势局限
Cohere rerank-v3.5SaaS API多语言强,易接入需境外账号
bge-reranker-v2-m3本地部署 / OpenAI 兼容中文优秀,免费需 GPU 资源
jina-reranker-v2SaaS API支持多语言,有免费额度精度略逊 Cohere
cross-encoder/ms-marco本地部署英文强中文效果差

Python 示例(OpenAI 兼容 Rerank API)

部分聚合网关(如力达云)提供 OpenAI 兼容格式的 Rerank 端点:

import os, requests

RERANK_URL = os.environ.get("RERANK_BASE_URL", "https://api.lidayun.com/v1/rerank")
API_KEY    = os.environ["OPENAI_API_KEY"]

def rerank(query: str, documents: list[str], model: str = "bge-reranker-v2-m3", top_n: int = 5):
    resp = requests.post(
        RERANK_URL,
        headers={"Authorization": f"Bearer {API_KEY}"},
        json={
            "model": model,
            "query": query,
            "documents": documents,
            "top_n": top_n,
            "return_documents": True,
        },
        timeout=30,
    )
    resp.raise_for_status()
    return resp.json()["results"]   # [{index, relevance_score, document}]

# 使用示例
query = "大模型 API 如何计费?"
docs  = [
    "token 是大模型计费的基本单位……",
    "API 网关支持多种模型……",
    "提示词缓存可以降低成本……",
]
results = rerank(query, docs, top_n=2)
for r in results:
    print(f"score={r['relevance_score']:.4f}  {r['document']['text'][:40]}")

Python 示例(Cohere 原生 SDK)

import cohere, os

co = cohere.Client(os.environ["COHERE_API_KEY"])

results = co.rerank(
    model="rerank-v3.5",
    query="大模型 API 如何计费?",
    documents=[
        "token 是大模型计费的基本单位……",
        "API 网关支持多种模型……",
        "提示词缓存可以降低成本……",
    ],
    top_n=2,
)
for r in results.results:
    print(f"index={r.index} score={r.relevance_score:.4f}")

Node.js 示例(兼容接口)

const resp = await fetch(process.env.RERANK_BASE_URL ?? "https://api.lidayun.com/v1/rerank", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
  },
  body: JSON.stringify({
    model: "bge-reranker-v2-m3",
    query: "大模型 API 如何计费?",
    documents: ["token 是计费基本单位……", "提示词缓存降成本……"],
    top_n: 2,
  }),
});
const { results } = await resp.json();
console.log(results);

上面三段代码用意各不相同:第一段走的是 OpenAI 兼容格式,好处是你可以把 base_url 换成任意兼容网关,不用改一行调用代码,适合自建/多供应商切换的场景;第二段是 Cohere 原生 SDK,字段名和返回结构跟兼容格式不完全一致(比如原生 SDK 用 r.index/r.relevance_score,是对象属性,兼容接口返回的是字典 r['relevance_score']),照抄别人的代码时经常在这里踩坑,报 AttributeErrorKeyError,先确认自己用的是哪一种客户端;第三段是纯 fetch 写法,没有任何 SDK 依赖,适合 Node 的 Serverless 函数里图轻量。

生产环境踩坑记录

这几个坑我在真实项目里都遇到过,比文档里的”参数说明”更值得记一遍:

报错/现象根因修法
401 Unauthorized环境变量名对不上,比如把 COHERE_API_KEY 误写成 CO_API_KEY,或者把原厂 key 塞进了聚合网关的 header 里确认走的是 Authorization: Bearer <key> 还是 SDK 内部单独的 api_key 参数,两套认证方式不能混用
429 Too Many RequestsCohere 免费层限流通常是每分钟个位数请求,批量跑 RAG 评估时很容易撞上加指数退避重试:首次等 1s,失败后翻倍,封顶别超过 30s,最多重试 3-5 次再放弃
413500(文档过大)把整篇 PDF/长文档不做切分直接塞进 documents,超出 bge-reranker 的 8192 token 或 Cohere 更保守的限制预处理阶段按 512-1024 token 切块,对每个块单独打分,取文档内最高分作为该文档的最终分数
请求超时候选文档数量多(50+)且单篇较长时,单次请求耗时超过默认 30s要么分批调用再合并排序,要么把 timeout 调到 60s,同时做好降级:超时就直接 fallback 用 ANN 原始顺序,别让整条 RAG 链路因为 Rerank 挂掉
分数区分度很差、排序看着很乱中文文档如果是 PDF 转文本,常夹带乱码、全角/半角混用、多余控制字符清洗阶段统一 normalize:去控制字符、统一全半角、合并多余空白,再喂给 Rerank

并发、批量与成本控制

Rerank 接口本身不支持流式返回——它一次性对整批文档打分,不是像 Chat 接口那样逐 token 吐字符,没有”边生成边看”的意义。但在完整 RAG 链路里,你可以让 Rerank 和”准备大模型 prompt 模板”这两件事并行做:召回结果一出来就同时发起 Rerank 请求和准备下游 prompt 拼装,Rerank 通常比大模型的首 token 延迟更短,一般不会成为整条链路的瓶颈,除非候选文档特别多或特别长。

如果你要离线批量跑 Rerank(比如做召回效果评估),用 asyncio.gather 并发发起多个请求能省不少时间,但一定要配上限流器,比如 asyncio.Semaphore(5) 控制同时在飞的请求数,不然很容易一波并发直接把 429 打过来。

计费上,Rerank 通常按”文档数”或”请求数”计价,不是像大模型那样按 token 数计价(具体费率以各家官网当前定价为准,截至 2026-06 Cohere 和力达云网关都是按文档数计费)。这意味着省钱的关键在候选集大小,不在 query 数量:如果你能把 ANN 召回阶段的 top-N 从 200 压缩到 80,Rerank 成本大致跟着打 4 折——因为交叉编码的计算量随文档数线性增长,延迟也会跟着明显下降。别在 query 端抠字省钱,候选集才是大头。

关键参数

参数说明
query用户原始查询
documents字符串数组,也可传 {text: "..."} 对象
top_n返回 top N 结果;不传则返回全部
return_documents是否在响应中携带原文,默认 false
max_chunks_per_doc长文档自动分块数,超长文本时使用

常见问题

Rerank 会显著增加延迟吗? 会增加约 100-500ms(取决于文档数量和模型)。可与大模型生成并行:先把召回结果交给 Rerank,同时异步启动大模型准备,Rerank 完成后再喂入 context。

文档太长超过 Rerank 模型限制怎么办? 使用 max_chunks_per_doc 自动切块,或在传入前先截断到模型支持的最大 token 数(bge-reranker-v2-m3 通常 8192 token)。

Rerank 分数有绝对含义吗? 不同模型的分数范围不同(有的 0-1,有的负无穷到 0),只应用于同一批文档内的相对排序,不应跨请求比较绝对值。

只有 3 条召回结果,值得 Rerank 吗? 文档数量少时 Rerank 收益有限,可设阈值:召回 < 10 条时跳过 Rerank,节省延迟和成本。

效果自检:一个简单的对照实验

光看接口文档很难感受到 Rerank 到底值不值这几百毫秒的延迟,建议自己跑一次对照实验:拿同一批召回结果,分别按”ANN 余弦相似度原始顺序”和”Rerank 打分顺序”各打印前 3 条,人工对比哪个更贴题。

# 复用前面 rerank() 函数
raw_order = docs  # 假设已经是 ANN 召回的相似度排序
reranked = rerank(query, docs, top_n=3)

print("ANN 原始顺序 top3:")
for d in raw_order[:3]:
    print(" -", d[:40])

print("Rerank 后 top3:")
for r in reranked:
    print(f" - score={r['relevance_score']:.4f}", r['document']['text'][:40])

你应该能看到:ANN 原始顺序里,有些文档只是字面上出现了查询里的关键词(比如同时含有”计费”两个字,但讲的是完全不相关的功能点),却因为词面相似排到了前面;Rerank 之后,真正回答”大模型 API 如何计费”这个问题的那条大概率会升到第一名,而字面相似但答非所问的那条会被压下去。如果你跑完发现两种顺序几乎一样,大概率是候选文档本身区分度就不大,或者召回阶段的 top-N 设得太小、候选集里压根没有明显的”陷阱文档”,可以先把召回 N 调大一点再重新观察。

另外提醒一句:不要只看 top-1 是否正确,多看 top-3 的整体质量——RAG 最终是把 top-K 一起塞给大模型做综合回答,只要前几名里没有明显跑题的干扰项,即便顺序略有出入,最终生成质量也不会差太多。


更多接入基础见大模型 API 接入完全指南接入教程专题。Rerank 的上游是 Embedding 召回,参考 Embedding 接口接入与选型;完整 RAG 链路见RAG 应用搭建指南