2026年11月4日

6分钟阅读

Embed 5 与 Rerank 5 现已上线 OpenRouter

通过单一 API 访问 Cohere 最先进的检索模型,统一计费,无需额外 SDK。

Promotional graphic announcing Cohere Embed 5 and Rerank 5 availability on OpenRouter, featuring white text and logos against an abstract black, blue, and purple gradient background.

Embed 5 和 Rerank 5 现已在 OpenRouter 上线。下面介绍如何通过一个 API,用几十行 Python 代码将它们变成可用的 RAG 流水线。 单一 API.

这是 Cohere 迄今最强大的检索模型,在企业文档和复杂检索任务上均展现出领先性能。如果你已经在使用 OpenRouter,可以立即开始使用——沿用现有模型所用的同一 API 密钥和额度,无需任何额外设置。

RAG 会在 LLM 撰写答案之前先检索相关片段,从而让答案以你自己的数据为依据。嵌入搜索提供了一种快速有效的第一阶段近似。随后,重排序器会同时读取查询和每个候选片段,为已检索到的内容增加精确度。

Cohere 模型的两个特性塑造了这一设计:

  • Embed 5 Pro 与 Fast 共享同一个嵌入空间。 使用 embed-5-pro 建立索引以获得质量,然后使用 embed-5-fast 进行查询以降低延迟。
  • Rerank 5 的分数经过校准。 分数在不同查询和领域之间含义一致,因此可以用同一个相关性阈值在弱上下文到达 LLM 之前将其过滤掉。

到最后,你将拥有一个简短的 Python 脚本,能够基于你的文档回答问题并给出带分数的引用。同样的方法几乎可以应用于任何检索用例,从企业搜索和客户支持到知识管理和文档发现。

Architecture diagram of a RAG pipeline using Cohere models: Embed 5 Pro indexes documents into a vector store, Embed 5 Fast processes user queries in a shared vector space, and Rerank 5 Pro refines search results before passing them to a chat model to generate cited answers.
RAG 流水线分为两部分:一个构建可搜索索引的离线任务,以及一条针对每个问题运行的在线路径。了解哪些调用只发生一次、哪些调用按请求发生,有助于解释流水线的大部分成本和延迟。索引对每个语料库运行一次。之后每个问题需要一次 Embed 5 Fast 调用、一次 Rerank 5 调用和一次聊天调用,全部通过 OpenRouter 的 API 完成。

设置

你需要 Python 3.10 或更高版本、一个 OpenRouter API 密钥, 以及 OpenRouter Python SDK,以及用于内存向量存储的 NumPy。

pip install openrouter numpy
export OPENROUTER_API_KEY="sk-or-..."
import os
import numpy as np
from openrouter import OpenRouter

client = OpenRouter(api_key=os.environ["OPENROUTER_API_KEY"])

EMBED_INDEX_MODEL = "cohere/embed-5-pro"   # indexing: highest quality
EMBED_QUERY_MODEL = "cohere/embed-5-fast"  # queries: lowest latency
RERANK_MODEL      = "cohere/rerank-5-pro"  # or cohere/rerank-5-fast
CHAT_MODEL        = "deepseek/deepseek-v4.1-flash-1017"       # any chat model on OpenRouter
DIMS = 1024                                    # 256 | 512 | 768 | 1024 | 1536 | 2048

Embed 5 返回六种尺寸的 Matryoshka 向量,默认 2048 维。我们 建议 采用 1,024 维 int8 向量作为质量与成本的最佳平衡点;OpenRouter 的 嵌入 API 返回浮点数,因此本文使用 1,024 维浮点数。

NumPy 存储让这个演示保持简单。在生产环境中,请将其替换为你偏好的向量数据库,例如 pgvector、Qdrant、Pinecone 或 Weaviate。

索引

索引会把每个文本块转换成一个向量,也就是一串数字,含义相近的内容在向量空间中彼此靠近。每个语料库只需运行一次,因此这是最值得使用最高质量模型的步骤。

把每个文档切分成块,用 Embed 5 Pro 以 search_document进行嵌入,并对向量做归一化,使点积等于余弦相似度。

示例语料库是一个虚构 API 的文档,其中故意设置了几处近似但不匹配的内容:

docs = [
    {"title": "Rotating API keys",
     "text": "To rotate a key without downtime, create a second key, deploy it to every service, "
             "confirm traffic on the new key in the usage dashboard, then revoke the old key."},
    {"title": "Creating API keys",
     "text": "Create keys under Settings > API Keys. Each key can be scoped to read or write and to one project."},
    {"title": "Revoking API keys",
     "text": "Revoking a key takes effect immediately. Requests with a revoked key return 401 Unauthorized."},
    {"title": "Webhook signing secrets",
     "text": "Rotate your webhook signing secret from the Webhooks page. The old secret stays valid "
             "for 24 hours so you can update your verifier."},
    {"title": "Log rotation",
     "text": "Request logs rotate daily and are kept for 30 days. Export them to S3 for longer retention."},
    {"title": "Rate limits",
     "text": "Each API key is limited to 600 requests per minute. Going over returns 429 Too Many Requests."},
]
def chunk(doc, max_chars=1500):
    """Split on blank lines, packing paragraphs up to ~max_chars."""
    out, buf = [], ""
    for para in doc["text"].split("\n\n"):
        if buf and len(buf) + len(para) > max_chars:
            out.append({"title": doc["title"], "text": buf.strip()})
            buf = ""
        buf += para + "\n\n"
    if buf.strip():
        out.append({"title": doc["title"], "text": buf.strip()})
    return out

def embed(texts, model, input_type, batch=96):
    vecs = []
    for i in range(0, len(texts), batch):
        res = client.embeddings.generate(
            model=model,
            input=texts[i:i + batch],   # one request per batch
            input_type=input_type,      # "search_document" | "search_query"
            dimensions=DIMS,
        )
        vecs += [d.embedding for d in res.data]
    v = np.array(vecs, dtype=np.float32)
    return v / np.linalg.norm(v, axis=1, keepdims=True)

chunks = [c for d in docs for c in chunk(d)]
doc_vecs = embed([c["text"] for c in chunks], EMBED_INDEX_MODEL, "search_document")

需要指出的三个关键细节:

  • input_type: 告诉 Embed 5 某段文本位于搜索的哪一侧。OpenRouter 会将其传递给 Cohere。
  • 批处理: 每次请求发送多个块,从而减少往返次数并加快索引速度。这不会改变成本,因为嵌入按 token 计价。
  • 块大小: 将整段内容打包,遵循 中的按段落策略, OpenRouter 的指南,而 1,500 个字符大约相当于 300–400 个英文 token。Embed 5 最多接受 128k token,但每个块也都会经过 Rerank 5,后者最多读取查询与块合计 32k token,并将超长内容拆分为多次处理。

检索

检索会找出向量与问题向量最接近的块。这充当两段内容之间语义相似性的表示。

使用 Embed 5 Fast 嵌入问题,作为 search_query,然后通过余弦相似度拉取一个包含 20 个块的宽泛候选集。

def retrieve(query, k=20):
    q = embed([query], EMBED_QUERY_MODEL, "search_query")[0]
    scores = doc_vecs @ q                 # cosine, since both sides are normalized
    top = np.argsort(-scores)[:k]
    return [(chunks[i], float(scores[i])) for i in top]

用 Fast 查询由 Pro 构建的索引之所以可行,是因为这两个模型 共享同一嵌入空间,只要双方使用相同的 dimensions。这正是我们推荐的拆分方式:在 Cohere 的测试 中,覆盖 40 个数据集,在 Pro 索引上使用 Fast 查询保留了全 Pro 检索质量的 98.4%,而两侧都用 Fast 则为 96.6%。若使用其他任何模型系列,则必须在两侧使用同一模型。

在这个阶段,让我们广撒网:召回是检索器的职责,精度则交给 Rerank。OpenRouter 的指南建议用 10–20 个候选送入 top 3–5 的重排。

重排序

重排序通过同时读取问题和每个文本块,对候选列表重新打分。在整个索引上运行太慢,但比向量相似度更准确,因此只对排名靠前的候选运行。重排序不需要 Embed,也不需要任何嵌入模型。它可作为简单的第二阶段,与你现有的语义、关键词或混合搜索系统配合使用。

将查询和 20 个候选发送给 Rerank 5,保留前 5 个,并丢弃任何低于相关性阈值的项。

MIN_SCORE = 0.3

def rerank(query, candidates, top_n=5):
    res = client.rerank.rerank(
        model=RERANK_MODEL,
        query=query,
        documents=[c["text"] for c, _ in candidates],
        top_n=top_n,
    )
    return [
        (candidates[r.index][0], r.relevance_score)
        for r in res.results          # sorted by relevance, highest first
        if r.relevance_score >= MIN_SCORE
    ]

每个结果都带有该候选在你列表中的位置(index)和一个 relevance_score,依据 重排序响应架构.

什么 Rerank 5 在此步骤中增加:

  • 校准后的分数。 分数在不同查询和领域之间保持一致,因此只需一个 MIN_SCORE 即可决定哪些内容进入 LLM。
  • 32k token 上下文。 长块会被整体评分。
  • 半结构化数据。 当 JSON 记录和表格格式化为 YAML 字符串时,重排序效果良好。
  • 无需重新建立索引即可升级。 重排序在查询时读取原始文本,因此切换模型只需改一行代码。

对于推理密集型查询以及长文档或特定领域文档,请使用 rerank-5-pro 。对于高并发搜索以及每个任务中需要多次搜索的智能体,请使用 rerank-5-fast 。需要注意一点: Rerank 5 仅支持文本,但可用于解析后的 PDF。

生成

生成阶段将最佳文本块作为编号来源交给 LLM,并要求它仅依据这些来源作答。这样可确保答案扎根于你的文档,并为读者提供可核查的引用。若没有任何内容达到阈值,则完全跳过 LLM 调用。

SYSTEM = (
    "Answer using only the numbered sources. "
    "Cite them inline like [1]. "
    "If the sources don't contain the answer, say you don't know."
)

def answer(query):
    hits = rerank(query, retrieve(query))
    if not hits:
        return "I couldn't find that in the docs."

    sources = "\n\n".join(
        f"[{i}] {c['title']}\n{c['text']}" for i, (c, _) in enumerate(hits, 1)
    )
    res = client.chat.send(
        model=CHAT_MODEL,
        messages=[
            {"role": "system", "content": SYSTEM},
            {"role": "user", "content": f"Sources:\n\n{sources}\n\nQuestion: {query}"},
        ],
    )
    return res.choices[0].message.content

print(answer("How do I rotate an API key without downtime?"))

将每个文本块的标题连同其文本一起发送,可以让模型引用可读的内容;如果你的文档中有 URL,也一并加上。OpenRouter 的指南在这一步改用 Responses API。要遵循该指南,请调用 client.responses.send ,并将系统提示词作为 instructions ,并将来源和问题作为“ input,然后从消息项中读取答案,在 res.output 而非来自 choices.

结果

接下来我们将验证所实施的步骤是否改善了我们的响应。

当重排序能自信地选出正确的文本块,并剔除那些在嵌入搜索中排名紧随其后的近似候选时,它多出的这次调用就物有所值。

q = "How do I rotate an API key without downtime?"

cands = retrieve(q)
hits = rerank(q, cands)

print("Embedding only (top 3):")
for c, s in cands[:3]:
    print(f"  {s:.3f}  {c['title']}")

print("After Rerank 5:")
for c, s in hits:
    print(f"  {s:.3f}  {c['title']}")
排名 仅嵌入 5 嵌入 5 + 重排序 5
1 轮换 API 密钥 · 0.522 轮换 API 密钥 · 0.808
2 Webhook 签名密钥 · 0.337 低于 0.3 的截断阈值
3 创建 API 密钥 · 0.316 低于 0.3 的截断阈值

总结

这条流水线中的所有环节都通过同一个 API 密钥和同一个客户端运行。Embed 5 Pro 负责构建索引,Embed 5 Fast 和 Rerank 5 负责处理每个问题,再由一个聊天模型撰写答案,无需分别管理多个 SDK、账户或账单。

这个单一接口也正是让流水线易于演进的原因。每个模型都只是一个字符串,因此尝试 rerank-5-fast ,或换用其他聊天模型,都只需改一行代码。先从示例语料开始,再换成你自己的文档,看看 Rerank 5 会把哪些内容排到最前。下一步可以尝试:混合搜索。在向量命中结果旁加入关键词(BM25)命中结果。Rerank 读取原始文本,因此可在一次调用中为两个列表打分。更小的索引。降到 512 或 256 维,或通过 Cohere 自己的 API 使用 int8 或二进制向量。图像和 PDF。Embed 5 可嵌入图像以及文本与图像混合的输入(如 PDF 页面),而 OpenRouter 的嵌入 rerank-5-pro,或换用其他聊天模型,都只需改一行代码。

先从示例语料开始,再换成你自己的文档,看看 Rerank 5 会把哪些内容排到最前。

接下来可以尝试的方向:

  • 混合搜索。 在向量命中结果旁加入关键词(BM25)命中结果。Rerank 读取原始文本,因此可在一次调用中为两个列表打分。
  • 更小的索引。 降到 512 或 256 维,或通过 Cohere 自己的 API 使用 int8 或二进制向量。
  • 图像和 PDF。 Embed 5 可嵌入图像以及文本与图像混合的输入(如 PDF 页面),而 OpenRouter 的嵌入 API 接受 image_url 内容部分。Rerank 5 仅支持文本,因此需基于其文本(如解析后的页面文本或说明文字)对这些结果重新排序,或对其跳过重排序。
  • 评估。 标注一小组真实查询,比较启用和不启用重排序时前5个结果的命中率,并进行调优。 MIN_SCORE 在同一集合上。
  • 探索 Compass。Cohere 的企业搜索与检索平台将 Embed 和 Rerank 整合到单一解决方案中,让企业数据上的强大搜索和 AI 应用构建变得更加容易。现已向 云客户.

实用链接

接下来阅读