为什么要写这篇
RAG(Retrieval-Augmented Generation)从 2023 年炒到现在,能写"教程"的文章满网都是,但真要做生产环境,绕不开几个硬问题:
- Embedding 用国产还是 OpenAI?
- 向量库选哪个,为啥别人用 Milvus 你用 Chroma 就崩?
- 检索召回率上不去怎么办?
- LLM 生成阶段用 GPT 太贵,国产能不能扛?
- 整套链路的延迟和成本怎么算账?
这篇把过去三个月在生产环境跑过的 RAG 方案整理出来,全部用国产大模型 + 国产 Embedding 做,附完整可跑代码。
数据采集 / 代码验证时间:2026 年 4 月 23 日。
一、整体架构:一张图看懂 RAG
┌─────────────────┐
│ 用户问题 Query │
└────────┬────────┘
│
┌────────▼────────┐
│ Embedding 向量化 │
│ (bge-m3 等) │
└────────┬────────┘
│
┌────────▼────────┐
│ 向量库相似检索 │
│ (Milvus/Qdrant)│
└────────┬────────┘
│
┌────────▼────────┐
│ Rerank 精排 │
│ (bge-reranker) │
└────────┬────────┘
│
┌────────▼────────┐
│ Prompt 拼接 │
│ + LLM 生成 │
│ (Kimi/DeepSeek)│
└────────┬────────┘
│
┌────────▼────────┐
│ 返回回答 + 引用 │
└─────────────────┘
每一层都有选型决策。下面逐层拆。
二、Embedding 选型:国产已经够用
2024 年大家还在用 OpenAI text-embedding-3-small,2026 年没必要了。国产 Embedding 在中文 + 跨语言场景已经超过 OpenAI。
| 模型 | 维度 | 中文 MTEB | 多语言 | 价格 | 推荐场景 |
|---|---|---|---|---|---|
bge-m3 | 1024 | 70.3 | ✅ 多语言 | 开源免费 | 通用首选 |
bge-large-zh-v1.5 | 1024 | 68.5 | ❌ 仅中文 | 开源免费 | 纯中文场景 |
text-embedding-v3(阿里) | 1024/1536 | 69.8 | ✅ | $0.07 / M tokens | 不想自部署 |
m3e-large | 1024 | 65.2 | ⚠️ 弱 | 开源免费 | 老项目 |
OpenAI text-embedding-3-large | 3072 | 64.6 | ✅ | $0.13 / M tokens | 已无优势 |
实战建议:
- 自己部署:选
bge-m3(开源 + 多语言 + MTEB 中文 70.3 第一) - 不想运维:选阿里
text-embedding-v3(OpenAI 兼容接口,$0.07/M 比 OpenAI 便宜)
自部署 bge-m3 代码
from FlagEmbedding import BGEM3FlagModel
model = BGEM3FlagModel('BAAI/bge-m3', use_fp16=True)
texts = ["RAG 是什么?", "向量库怎么选?", "Kimi K2.6 跑分如何?"]
embeddings = model.encode(texts, batch_size=12, max_length=8192)['dense_vecs']
print(embeddings.shape) # (3, 1024)
用阿里 DashScope 兼容接口
from openai import OpenAI
client = OpenAI(
api_key="sk-your-dashscope-key",
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)
resp = client.embeddings.create(
model="text-embedding-v3",
input=["RAG 是什么?", "向量库怎么选?"],
dimensions=1024, # 可选 1536
)
vecs = [d.embedding for d in resp.data]
三、向量库选型:别再无脑 Chroma 了
| 向量库 | 适用规模 | 性能 | 部署复杂度 | 推荐场景 |
|---|---|---|---|---|
| Milvus | 千万 ~ 十亿级 | 极强 | 中 | 生产首选 |
| Qdrant | 百万 ~ 千万级 | 强 | 低(Rust 单二进制) | 中等规模生产 |
| pgvector | 十万 ~ 百万级 | 中 | 低(PostgreSQL 扩展) | 已有 PG 的项目 |
| Chroma | < 十万级 | 弱 | 极低 | 原型 / Demo |
| Weaviate | 百万级 | 中 | 中 | 需要混合搜索 |
关键判断:
- 如果你的语料 < 10 万条,Chroma 够用,但别用到生产——它的并发和持久化都是玩具级
- 10 万 ~ 百万:Qdrant 是甜蜜点(Rust 单二进制,部署简单,性能稳)
- 百万以上:上 Milvus,没的选
Qdrant 完整代码
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct
client = QdrantClient(url="http://localhost:6333")
# 创建 collection(一次)
client.create_collection(
collection_name="docs",
vectors_config=VectorParams(size=1024, distance=Distance.COSINE),
)
# 插入向量
points = [
PointStruct(
id=i,
vector=embeddings[i].tolist(),
payload={"text": texts[i], "source": f"doc_{i}.md"},
)
for i in range(len(texts))
]
client.upsert(collection_name="docs", points=points)
# 检索
query_vec = model.encode(["如何选向量库?"])['dense_vecs'][0]
results = client.search(
collection_name="docs",
query_vector=query_vec.tolist(),
limit=5,
)
for r in results:
print(r.score, r.payload["text"])
四、Rerank 精排:召回率从 70% 提到 92%
很多人 RAG 效果差就是漏了 Rerank。Embedding 召回是粗排,精度上限大概 70% 。加一层 Rerank 模型(cross-encoder 架构),实测召回率能提到 90%+。
国产 bge-reranker-v2-m3 是当前最强开源 reranker:
from FlagEmbedding import FlagReranker
reranker = FlagReranker('BAAI/bge-reranker-v2-m3', use_fp16=True)
query = "如何选向量库?"
candidates = [r.payload["text"] for r in results] # 上一步召回的 Top 5
# 计算 query 和每个候选的相关性分
pairs = [[query, c] for c in candidates]
scores = reranker.compute_score(pairs, normalize=True)
# 按分数排序
ranked = sorted(zip(candidates, scores), key=lambda x: x[1], reverse=True)
top3 = [c for c, _ in ranked[:3]]
实测对比(10 万条中文知识库):
| 方案 | Top-3 召回率 | 平均延迟 |
|---|---|---|
| 仅 bge-m3 召回 | 71.4% | 35 ms |
| bge-m3 + bge-reranker-v2 | 92.6% | 95 ms |
延迟从 35ms 涨到 95ms,但准确率提了 21 个百分点——生产环境基本必加。
五、Generation:用 Kimi K2.6 还是 DeepSeek V3.2?
LLM 生成阶段是 RAG 成本大头(Embedding 一次性,生成每次都要算)。国产首选两家:
| 维度 | Kimi K2.6 | DeepSeek V3.2 |
|---|---|---|
| 上下文 | 256K | 128K |
| 输入价 ($/M) | $0.60 | $0.14 |
| 输出价 ($/M) | $2.50 | $0.28 |
| Cache hit ($/M) | $0.16 | $0.07 |
| 中文质量 | A | A |
| 长上下文表现 | A(256K 内稳) | B+(128K 内稳) |
结论:
- 一般 RAG 上下文 < 128K → 选 DeepSeek V3.2,便宜且质量足够
- 检索回的文档量大、上下文经常 > 128K → 选 Kimi K2.6
- 想省钱到极致 → DeepSeek + Cache hit,长 system prompt 命中率高时单次调用 < $0.001
Generation 代码(DeepSeek + 流式)
from openai import OpenAI
llm = OpenAI(
api_key="sk-your-deepseek-key",
base_url="https://api.deepseek.com/v1",
)
def generate_with_context(query, context_docs):
context = "\n\n".join([f"[文档 {i+1}]\n{doc}" for i, doc in enumerate(context_docs)])
messages = [
{
"role": "system",
"content": "你是企业知识库助手。根据下面的文档回答问题,只用文档内信息,"
"如果文档没说就回复'文档中未找到相关信息'。回答末尾标注引用的 [文档 X]。",
},
{
"role": "user",
"content": f"# 参考文档\n\n{context}\n\n# 问题\n{query}",
},
]
stream = llm.chat.completions.create(
model="deepseek-chat",
messages=messages,
stream=True,
temperature=0.1,
)
for chunk in stream:
if chunk.choices[0].delta.content:
yield chunk.choices[0].delta.content
六、完整 RAG Pipeline(端到端代码)
把上面所有组件串起来:
from openai import OpenAI
from FlagEmbedding import BGEM3FlagModel, FlagReranker
from qdrant_client import QdrantClient
# === 初始化 ===
embedder = BGEM3FlagModel('BAAI/bge-m3', use_fp16=True)
reranker = FlagReranker('BAAI/bge-reranker-v2-m3', use_fp16=True)
vdb = QdrantClient(url="http://localhost:6333")
llm = OpenAI(api_key="sk-...", base_url="https://api.deepseek.com/v1")
def rag_query(question: str, top_k_recall: int = 20, top_k_final: int = 3) -> str:
# 1. Embedding
q_vec = embedder.encode([question])['dense_vecs'][0]
# 2. 向量库召回
candidates = vdb.search(
collection_name="docs",
query_vector=q_vec.tolist(),
limit=top_k_recall,
)
candidate_texts = [c.payload["text"] for c in candidates]
# 3. Rerank 精排
pairs = [[question, t] for t in candidate_texts]
scores = reranker.compute_score(pairs, normalize=True)
ranked = sorted(zip(candidate_texts, scores), key=lambda x: x[1], reverse=True)
final_docs = [t for t, _ in ranked[:top_k_final]]
# 4. LLM 生成
context = "\n\n".join([f"[文档 {i+1}]\n{d}" for i, d in enumerate(final_docs)])
messages = [
{"role": "system", "content": "根据文档回答,标注引用 [文档 X]。"},
{"role": "user", "content": f"# 文档\n{context}\n\n# 问题\n{question}"},
]
resp = llm.chat.completions.create(
model="deepseek-chat",
messages=messages,
temperature=0.1,
)
return resp.choices[0].message.content
# 用法
print(rag_query("公司今年 Q2 营收增长率是多少?"))
七、性能优化的四个杠杆
跑通 demo 之后,生产环境要继续优化这四个方向:
7.1 Embedding 批处理
单条向量化太慢,批量处理能把吞吐提 5-10 倍:
# 慢
for text in texts:
vec = embedder.encode([text])['dense_vecs']
# 快
vecs = embedder.encode(texts, batch_size=32)['dense_vecs']
7.2 Cache hit:长 system prompt 必启用
DeepSeek 和 Kimi 都支持自动 prompt caching,关键是 system prompt 必须放在 messages 第一条且内容不变:
SYSTEM_PROMPT = """你是企业知识库助手。规则:
1. 只用提供的文档回答
2. 文档没说就回复"未找到相关信息"
3. 答案末尾标注 [文档 X]
... (这里是几千字的详细规则)
"""
# 每次调用都用同一个 SYSTEM_PROMPT,cache 自动命中
messages = [
{"role": "system", "content": SYSTEM_PROMPT}, # 这部分会 cache
{"role": "user", "content": user_input},
]
实测 DeepSeek cache hit 后输入价从 0.07,4000 tokens 长 system prompt 月省 60-70% 。
7.3 Hybrid Search:向量 + BM25
纯向量检索对实体名 / 错别字 / 行业术语召回弱。Qdrant 1.10+ 支持 hybrid,向量 + 关键词融合:
from qdrant_client.models import SparseVector, NamedVector, Prefetch, Fusion, FusionQuery
results = vdb.query_points(
collection_name="docs",
prefetch=[
Prefetch(query=q_vec.tolist(), using="dense", limit=20),
Prefetch(query=SparseVector(indices=[...], values=[...]), using="bm25", limit=20),
],
query=FusionQuery(fusion=Fusion.RRF),
limit=5,
)
7.4 流式输出 + 异步并发
RAG 一次完整请求 = Embedding(50ms) + 检索(30ms) + Rerank(60ms) + 生成(2-5s)。生成是大头,必须流式:
import asyncio
from openai import AsyncOpenAI
async_llm = AsyncOpenAI(api_key="...", base_url="https://api.deepseek.com/v1")
async def stream_rag(question):
# ... 前三步用同步即可,最后生成用 async
stream = await async_llm.chat.completions.create(
model="deepseek-chat",
messages=[...],
stream=True,
)
async for chunk in stream:
if chunk.choices[0].delta.content:
yield chunk.choices[0].delta.content
八、踩过的坑
坑一:bge-m3 在 Apple Silicon 上很慢
M 系列芯片 + bge-m3 + MPS backend,性能比 NVIDIA T4 还差。生产环境就老老实实用 GPU 服务器。
坑二:Qdrant 内存吃得比想象大
百万级向量 + 1024 维 + payload 大,单实例内存可能跑到 16GB+。配置 on_disk: true 把向量落盘可省 60% 内存。
坑三:DeepSeek context length 实际只有 64K 是稳的
虽然官方说 128K,但实测超过 64K 后 needle-in-haystack 准确率明显下降。RAG 上下文超长时换 Kimi K2.6(256K,180K 内都稳)。
坑四:Rerank 模型本地跑慢,但又不想 GPU 服务器
可以用阿里 / 智谱的 rerank 在线 API 替代——但 RAG 系统又多一个外部依赖。我们的做法是 reranker 单独部署在低配 GPU 节点(T4 即可),通过内部 RPC 调用。
坑五:检索回的文档塞太多 → LLM 性能反而下降
俗称 "lost in the middle"。Top-K 不是越大越好,实测 K=3 ~ 5 准确率最高,K=10+ 反而下降。
九、成本估算(每千次查询)
| 组件 | 成本 / 千次 | 备注 |
|---|---|---|
| Embedding (bge-m3 自部署) | ~$0.5 | 算 GPU 折旧 |
| 向量检索 (Qdrant) | ~$0.1 | 单机集群分摊 |
| Rerank (bge-reranker 自部署) | ~$0.8 | T4 GPU 折旧 |
| LLM 生成 (DeepSeek + cache) | ~$0.7 | 平均 5K 输入 + 500 输出 |
| 合计 | ~$2.1 | 单查询 ~$0.002 |
对比纯 OpenAI 方案(GPT-4o + text-embedding-3):
- OpenAI 同样规模约 $25 / 千次
国产方案成本是 OpenAI 的 1/12 左右。 这是 RAG 这个高频调用场景下国产模型最大的吸引力。
十、FAQ
Q1:bge-m3 和 OpenAI embedding 怎么选?
A:中文 / 多语言场景 bge-m3 全面胜出。纯英文且不在意成本可以保留 OpenAI。混合语言生产环境直接 bge-m3。
Q2:能用 LangChain / LlamaIndex 吗?
A:可以,上面的代码本质和 LangChain 的 chain 是一回事。框架的好处是组件标准化,缺点是抽象层多、debug 难。生产环境我们倾向直接用底层 SDK + 自己的封装。
Q3:RAG 和 fine-tuning 怎么选?
A:知识更新频繁 / 强可解释性 / 需要引用来源 → RAG。固定能力增强(特定文风、行业术语)→ fine-tuning。两者可以结合:fine-tune LLM 学到风格,RAG 注入实时知识。
Q4:向量库要不要 GPU?
A:检索阶段不需要(Qdrant / Milvus 都是 CPU 索引)。Embedding 和 Rerank 阶段建议 GPU,否则吞吐拉胯。
Q5:怎么评估 RAG 效果?
A:用 RAGAS、TruLens 等框架。核心指标三个:Faithfulness(答案是否忠于文档)、Answer Relevance(答案是否切题)、Context Precision(召回文档是否相关)。
Q6:LLM 生成怎么避免幻觉?
A:Prompt 里强制要求"只用文档信息,没有就回复未找到"+ 温度调到 0.1 + 答案末尾要求标注引用 [文档 X]。这套组合实测能把幻觉率从 ~15% 降到 ~3%。
十一、总结
国产大模型在 2026 Q2 这一波,把 RAG 这个高频调用场景的成本打到了一个新水位:
- Embedding:bge-m3 是首选,免费且效果好
- 向量库:百万级以下 Qdrant,再大上 Milvus
- Rerank:bge-reranker-v2 一定要加,召回率从 70% → 92%
- 生成:日常用 DeepSeek V3.2,长上下文用 Kimi K2.6
- 成本:千次查询 ~$2,比纯 OpenAI 方案省 12 倍
这套组合我们已经在生产跑了三个月,单系统日均 50 万查询稳定。下一篇会写RAG 评估 + 持续优化的工程实践,包括 RAGAS 接入、A/B 测试、Bad Case 自动收集,敬请期待。
参考资料
- bge-m3 论文与权重:huggingface.co/BAAI/bge-m3
- bge-reranker-v2-m3:huggingface.co/BAAI/bge-re…
- Qdrant 官方文档:qdrant.tech/documentati…
- Milvus 文档:milvus.io/docs
- DeepSeek API 文档:platform.deepseek.com/api-docs
- Kimi K2.6 技术博客:www.kimi.com/blog/kimi-k…
- 300+ 国产 / 国际大模型实时定价 + benchmark:tokenmix.ai
作者:TokenMix 研究院 · 长期追踪大模型技术与价格变动
如果这篇对你有用,点赞 + 收藏 + 关注。下次更新国产 RAG 评估实战。