Skip to content

RAG(检索增强生成)通过在生成前检索相关文档来减少幻觉、保持知识更新,无需微调。OpenRouter 通过单一 API 提供 RAG 管道所需的三个构建块:/api/v1/embeddings(向量化)、/api/v1/rerank(重排序)、/api/v1/chat/completions(生成)。本文包含完整的四步 Python 实现:索引→检索→重排→生成,以及 chunking 策略、何时用 rerank、生产最佳实践。

RAG(Retrieval-Augmented Generation,检索增强生成)将 LLM 的回答锚定在你自己的数据上——在生成答案前先检索相关文档,从而减少幻觉、保持内容时效性,而无需微调。

OpenRouter 通过单一 API 提供 RAG 管道的三个核心组件:

  1. Embeddings — 将文档和查询转换为向量,用于语义搜索
  2. Rerank — 对检索到的候选文档重新评分,提升精准度
  3. Chat Completions — 利用检索到的上下文生成最终答案

RAG 工作原理

典型 RAG 管道分为四步:

  1. 索引(Index) — 将文档分块并为每块生成 embedding,存入向量数据库
  2. 检索(Retrieve) — 将用户查询向量化,找到最相似的文档块
  3. 重排(Rerank,可选) — 用交叉编码器对候选结果精排,提升召回精度
  4. 生成(Generate) — 将排名靠前的文档作为上下文传给 LLM 生成有依据的答案

第一步:索引文档

将文档切块,批量生成 embedding,存入向量数据库(此处用内存演示):

python
import requests

OPENROUTER_API_KEY = "YOUR_API_KEY"

chunks = [
    "OpenRouter is a unified API gateway for LLMs.",
    "RAG stands for Retrieval-Augmented Generation.",
    "Embeddings convert text into numerical vectors.",
    "Reranking uses a cross-encoder to re-score documents.",
    "Vector databases like Pinecone, Weaviate, Qdrant store embeddings.",
]

response = requests.post(
    "https://openrouter.ai/api/v1/embeddings",
    headers={"Authorization": f"Bearer {OPENROUTER_API_KEY}"},
    json={"model": "your-embedding-model", "input": chunks},
)

data = response.json()
document_embeddings = [
    {"text": chunks[item["index"]], "embedding": item["embedding"]}
    for item in data["data"]
]

生产环境中,使用向量数据库(Pinecone、Weaviate、Qdrant、pgvector 等)存储 embedding。

第二步:检索相关文档

将用户查询向量化,通过余弦相似度找到最相似的文档块:

python
import numpy as np

def cosine_similarity(a, b):
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

def retrieve(query, document_embeddings, top_n=5):
    response = requests.post(
        "https://openrouter.ai/api/v1/embeddings",
        headers={"Authorization": f"Bearer {OPENROUTER_API_KEY}"},
        json={"model": "your-embedding-model", "input": query},
    )
    query_embedding = np.array(response.json()["data"][0]["embedding"])
    
    scored = [
        {"text": doc["text"], "score": cosine_similarity(query_embedding, np.array(doc["embedding"]))}
        for doc in document_embeddings
    ]
    scored.sort(key=lambda x: x["score"], reverse=True)
    return scored[:top_n]

第三步:Rerank 精排(可选)

基于 embedding 的检索速度快但精度有限。Rerank 模型用交叉编码器将每个文档直接与查询比较,产生更准确的相关性评分——特别适合检索量大(20 个候选)但只需精选少数(3 个)的场景:

python
def rerank(query, documents, top_n=3):
    response = requests.post(
        "https://openrouter.ai/api/v1/rerank",
        headers={"Authorization": f"Bearer {OPENROUTER_API_KEY}"},
        json={"model": "your-rerank-model", "query": query, "documents": documents, "top_n": top_n},
    )
    return response.json()["results"]

retrieved_texts = [r["text"] for r in results]
reranked = rerank(query, retrieved_texts, top_n=3)

第四步:生成有依据的答案

将排名靠前的文档作为上下文传给 chat 模型:

python
def generate_answer(query, context_docs):
    context = "\n\n".join(
        f"[{i+1}] {doc['document']['text']}"
        for i, doc in enumerate(context_docs)
    )
    response = requests.post(
        "https://openrouter.ai/api/v1/chat/completions",
        headers={"Authorization": f"Bearer {OPENROUTER_API_KEY}"},
        json={
            "model": "your-chat-model",
            "messages": [
                {"role": "system", "content": "Answer based on the provided context. Cite source numbers in brackets. If context is insufficient, say so."},
                {"role": "user", "content": f"Context:\n{context}\n\nQuestion: {query}"},
            ],
        },
    )
    return response.json()["choices"][0]["message"]["content"]

何时使用 Rerank

适合使用 rerank:

  • 知识库较大(数百到数千个文档块),embedding 检索结果噪声较多
  • 精度比延迟更重要(如面向用户的 Q&A、法律医疗文档)
  • 检索大量候选(如 top 20)并需要精选最优的几个

可以跳过 rerank:

  • 知识库较小,embedding 检索精度已足够
  • 需要最低延迟(rerank 增加一次额外 API 调用)
  • 构建原型,保持管道简单

文档分块策略

分块方式对检索质量影响显著:

  • 按段落/章节分块 — 保持语义完整性,适合结构化文档
  • 固定大小 + 重叠 — 每块 200-500 token,~50 token 重叠,避免边界截断
  • 按语义边界 — 使用标题、节分隔符或句子边界创建自然分块

较小分块(200-300 token)检索更精准但可能丢失上下文;较大分块(500-1000 token)保留更多上下文但可能稀释相关性。

最佳实践

  • 索引和查询使用同一 embedding 模型 — 混用不同模型会产生不兼容的向量空间
  • 批量发送 embedding 请求 — 一次 API 调用传入多个文本,降低延迟和成本
  • 缓存 embedding — 相同文本的 embedding 是确定性的,存入数据库避免重复计算
  • 多检索少用,配合 rerank 精选 — 常见模式是先取 10-20 个候选,rerank 精选到 3-5 个
  • 在 prompt 中包含元数据 — 附上文档标题、来源 URL 等,让 LLM 能生成准确引用
  • 设置相关性阈值 — Rerank 后过滤掉低于最低相关分的文档,避免注入干扰上下文

常见问题

Q: 必须用 Rerank 吗?

A: 不是必须的。对于中小型知识库,embedding 相似度检索的精度通常已足够。Rerank 主要价值在于大规模知识库或对精度要求极高的场景。先不用 rerank 测试你的 RAG 效果,精度不满意再引入。

Q: 如何选择 chunk 大小?

A: 没有通用答案,取决于你的数据特点。一个实用起点是 400 token + 50 token 重叠。如果检索到的上下文太碎(LLM 回答缺乏连贯性),增大 chunk;如果太泛(检索结果噪声多),减小 chunk。

Q: Embeddings API 支持哪些模型?

A: 通过 https://openrouter.ai/api/v1/embeddings 可访问所有支持 embedding 的模型。详见 Embeddings API 文档,可按支持类型筛选。