Rerank 接口没有像 Chat Completions 那样统一的标准,同一段代码换个供应商就可能 404 或 422。排查时先按 HTTP 状态码分层,再处理“返回 200 但结果不对”的软错误。本文按鉴权、候选集、限流、超时和空结果五类展开,最后给一份可复用的重试代码和日志清单。模型与价格以站内模型价格为准。

说明:Cohere、Jina、Voyage 等名称均为其权利人的商标,本文仅作技术讨论;接口路径、限额与错误码可能随版本变化,请以对应厂商文档为准。

一、五类问题先分清

现象 常见状态码 典型原因 处理顺序
鉴权失败 401、403 Key 错误、请求头缺失、模型未开通 先修凭证与权限
请求被拒 400、422 候选集过大、字段不匹配、文档为空 查请求体与限额
路径错误 404 端点写错、模型名旧、Base URL 拼接错误 对照官方文档
限流或配额 429 并发突增、共享 Key、重试风暴 退避、降并发
超时或上游错误 504、5xx 候选过多、文档过长、上游过载 缩小请求、有限重试
返回为空 200 top_n 非法、字段读错、文档被过滤 加断言与告警

日志至少记录时间、端点、模型、文档条数、总字符数、top_n、状态码、耗时、重试次数和请求 ID。不要记录完整 Key,也不要把用户文档原文整段打印。

二、401 与 403:先查鉴权,再查权限

401 表示身份没有通过验证,和模型能力、余额通常无关。按下面顺序排查:

  1. 请求头是否使用 Authorization: Bearer 加 Key,有些网关则要求 x-api-key。
  2. Key 前后是否有空格、换行或多余引号。从聊天窗口复制最容易带进来。
  3. 环境变量是否被本地配置或部署平台覆盖,线上和本地是否用了不同的 Key。
  4. Key 是否对重排接口开通。有的账户只有对话权限,没有 Rerank 权限。
  5. 端点是否写错。/v1/rerank 与 /v2/rerank 的格式可能不同,把对话接口的 Base URL 直接拼上 /rerank,很容易得到 404 或 401。

403 是身份有效但权限不够,重点看模型是否开通、IP 是否在白名单、项目权限和欠费状态。401 不要重试,重试只会放大失败日志,还可能触发风控。先用最小 curl 跑通,再回到业务代码。

三、400 与 422:候选集过大、字段不匹配

候选集过大是最常见的 400 来源。托管接口通常对单次文档数、单文档 token 和请求体大小都有上限。以 Cohere 为例,官方建议单次不要超过一千条文档,单文档默认截断到 4096 token;其他厂商的数值不同,且会随模型版本调整,接入前要查对应文档。超限时的正确做法不是盲目重试,而是:

  • 把候选集分片,每片单独重排,再按分数合并;
  • 在召回阶段就把候选控制在合理范围,例如五十到两百条;
  • 对超长文档先切块或截断,不要把整篇 PDF 直接塞进 documents;
  • 去掉空字符串、null、纯空白和重复文档。

字段不匹配也会报 400 或 422:top_n 写成 top_k,或反过来;Jina、Cohere 多用 top_n,Voyage 用 top_k。documents 传了对象数组而不是字符串数组,或把 id 和 text 放错层级。top_n 大于文档数量时,有的接口报错,有的自动截断。return_documents、truncation、max_tokens_per_doc 等参数也可能不被当前供应商支持。JSON 里的 NaN、Infinity、未转义换行同样会触发解析失败。

排查方法:保留脱敏后的原始请求体,用最小示例逐项加回参数,一次只改一个变量。

四、429:限流、配额与重试风暴

429 表示请求被限流或配额用尽,常见原因有:每秒请求数或并发超过套餐、按 token 计的配额在高峰期耗尽、多个服务共用一把 Key、客户端重试没有退避。先看响应头里的 Retry-After 和配额字段,再决定等待还是降并发。

处理顺序:

  1. 确认是单 Key 限流还是账户级配额。
  2. 把重试改为指数退避加随机抖动,尊重 Retry-After。
  3. 限制并发,用队列削峰,而不是无限重试。
  4. 把批量任务放到低峰期,离线评测与线上流量分开。
  5. 为关键业务准备降级:跳过重排,保留召回顺序。

不要用固定一秒重试打满。多个实例同时重试会形成重试风暴,把小限流放大成持续故障。

五、超时、504 与 5xx

超时要分连接超时和读取超时。连接超时通常是网络、DNS、代理或 TLS 问题;读取超时更多是候选集太大、文档太长、上游排队或模型冷启动。504 一般表示网关等不到上游响应,和客户端自己的超时是两件事。

排查顺序:

  1. 用最小请求测试,一条 query、五条短文档,确认不是网络完全不通。
  2. 逐步增加文档数量,找到耗时拐点。
  3. 检查单文档长度和总 token,必要时截断。
  4. 检查客户端超时是否短于服务端处理时间。
  5. 对 5xx 和超时做有限次重试,超过阈值就降级。
  6. 记录请求 ID 和上游返回的 trace id,方便和供应商对账。

重排通常不是流式接口,不能靠流式输出掩盖长耗时。对延迟敏感的业务,应把重排放在异步链路,或只对前几十条候选重排。

六、返回 200 但结果为空或不对

这类软错误更隐蔽:documents 为空数组,接口返回空结果;top_n 设为 0 或负数;所有文档都被业务过滤条件排除;读错响应字段,有的返回 results,有的返回 data;index 是原文档数组的下标,但分片后忘了加偏移量;分数语义不同,有的归一化到零到一,有的输出 logits,排序方向也可能不同;返回的是文档正文而不是 index,去重时把相同文本合并了。

建议在客户端加断言:结果数量不超过 top_n,每个 index 在合法范围内,分数按供应商语义单调。发现异常时记录原始响应摘要,不要静默返回空列表。

七、一份可复用的重试代码

下面示例把错误分成三类:鉴权和参数错误不重试,429 和 5xx 退避重试,超时有限重试;重试耗尽后降级为原召回顺序。

import random
import time
import requests

def rerank(query, documents, top_n=10, model="<rerank-model>",
           url="<base-url>/v1/rerank", api_key="<key>"):
    payload = {"model": model, "query": query,
               "documents": documents, "top_n": top_n}
    headers = {"Authorization": f"Bearer {api_key}"}
    last_error = None
    for attempt in range(5):
        try:
            resp = requests.post(url, json=payload, headers=headers,
                                 timeout=(3.05, 30))
        except requests.Timeout as exc:
            last_error = exc
            time.sleep(min(2 ** attempt + random.random(), 20))
            continue

        if resp.status_code in (400, 401, 403, 404, 422):
            raise RuntimeError(f"不可重试 {resp.status_code}: {resp.text[:300]}")
        if resp.status_code == 429 or resp.status_code >= 500:
            retry_after = resp.headers.get("Retry-After")
            wait = float(retry_after) if retry_after else min(
                2 ** attempt + random.random(), 20)
            time.sleep(wait)
            last_error = RuntimeError(f"可重试 {resp.status_code}")
            continue

        data = resp.json()
        results = data.get("results") or data.get("data") or []
        if not results:
            return list(range(min(top_n, len(documents))))
        return [item["index"] for item in results[:top_n]]

    raise RuntimeError(f"重试耗尽: {last_error}")

# 调用方仍需按供应商字段做适配,例如 Voyage 使用 data 与 top_k。

如果文档分片重排,合并时要把分片内 index 加上偏移量,再统一排序。

八、日志与监控清单

每次调用至少记录:时间、端点、模型、文档条数、总字符数或估算 token、top_n、状态码、错误类型、耗时、重试次数、请求 ID。不要记录完整 Key,也不要把用户文档原文整段写入日志;需要排查时记录哈希、长度和脱敏后的前若干字符。

监控看四个指标:429 比例、超时与 5xx 比例、空结果比例、p95 延迟。再按业务线拆分,避免一个高频低价值任务把配额吃光,掩盖了核心业务的异常。

常见问题

401 和 403 有什么区别?

401 是身份没通过验证,通常是 Key 缺失、格式错误或失效;403 是身份有效但权限不够,可能是模型未开通、IP 不在白名单、项目或余额受限。先修鉴权,再查权限。

候选集超过接口上限怎么办?

在客户端分片,每片单独重排后按分数合并,并给 index 加偏移量。更根本的做法是在召回阶段控制数量,通常几十到几百条足够,不要把上千条全部送进重排。

429 重试几次合适?

没有统一答案。建议有限次重试,例如三到五次,指数退避加随机抖动,并尊重 Retry-After;同时限制并发。重试耗尽后降级,不要让请求无限堆积。

Rerank 接口超时,能像对话一样用流式吗?

一般不能。重排需要看完整个候选集才能给出稳定排序,多数接口是同步返回。延迟敏感时,应减少候选数、截断文档长度,或把重排放到异步任务。

返回 200 但没有结果,是什么原因?

先查 documents 是否为空、top_n 是否合法、业务过滤是否把文档全部排除;再查响应字段是否读错,以及分片合并时 index 是否越界。加断言和空结果告警,比事后排查更有效。

下一步

排查完成后,建议固定一组最小复现和一组回归用例,再在中商起航 AI 网关创建测试 Key 验证调用链路;计费与限额以模型价格页为准。

相关阅读: