你有大量内部文档、产品手册或知识库,想让 AI 基于这些资料回答问题?RAG(检索增强生成) 就是最佳方案。本文手把手教你用 Embeddings API + 向量检索构建一个完整的 RAG 问答系统。
RAG 架构概览
RAG 的核心思路很简单:先检索,再生成。
用户提问 → 向量检索相关文档 → 将文档 + 问题发送给 LLM → 生成回答
这比直接让 AI 回答要好得多 — AI 不再"编造",而是基于你的真实数据作答。
Step 1:文档切分(Chunking)
将原始文档切分为适当大小的文本块:
def chunk_text(text, chunk_size=500, overlap=50):
"""将文本切分为重叠的文本块"""
chunks = []
start = 0
while start < len(text):
end = start + chunk_size
chunk = text[start:end]
chunks.append(chunk)
start = end - overlap # 重叠部分避免语义断裂
return chunks
# 示例:切分一篇产品文档
with open("product_manual.txt", "r") as f:
text = f.read()
chunks = chunk_text(text)
print(f"切分为 {len(chunks)} 个文本块")
Step 2:生成 Embeddings 向量
from openai import OpenAI
client = OpenAI(
api_key="sk-你的密钥",
base_url="https://api2everything.xyz/v1"
)
def get_embeddings(texts, model="text-embedding-3-small"):
"""批量获取文本的 embedding 向量"""
response = client.embeddings.create(
model=model,
input=texts
)
return [item.embedding for item in response.data]
# 为所有文本块生成向量
embeddings = get_embeddings(chunks)
print(f"生成了 {len(embeddings)} 个向量,维度:{len(embeddings[0])}")
Step 3:向量存储与检索
这里用 numpy 实现简单的向量检索。生产环境推荐使用 ChromaDB、Pinecone 或 Milvus:
import numpy as np
class SimpleVectorStore:
def __init__(self):
self.vectors = []
self.texts = []
def add(self, texts, embeddings):
self.texts.extend(texts)
self.vectors.extend(embeddings)
def search(self, query_embedding, top_k=3):
"""余弦相似度检索"""
query = np.array(query_embedding)
scores = []
for vec in self.vectors:
v = np.array(vec)
score = np.dot(query, v) / (np.linalg.norm(query) * np.linalg.norm(v))
scores.append(score)
top_indices = np.argsort(scores)[-top_k:][::-1]
return [(self.texts[i], scores[i]) for i in top_indices]
# 构建向量库
store = SimpleVectorStore()
store.add(chunks, embeddings)
Step 4:RAG 问答
def rag_query(question, model="claude-sonnet-4-6-20250514"):
"""RAG 问答:检索 + 生成"""
# 1. 将问题转为向量
q_embedding = get_embeddings([question])[0]
# 2. 检索最相关的文本块
results = store.search(q_embedding, top_k=3)
context = "\n\n---\n\n".join([text for text, score in results])
# 3. 生成回答
response = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": f"基于以下参考资料回答用户问题。如果资料中没有相关信息,请如实说明。\n\n参考资料:\n{context}"},
{"role": "user", "content": question}
]
)
return response.choices[0].message.content
# 测试
answer = rag_query("这个产品支持哪些支付方式?")
print(answer)
Embeddings 模型对比
| 模型 | 维度 | 特点 | 价格(中转后) |
|---|---|---|---|
| text-embedding-3-small | 1536 | 性价比最高,速度快 | ≈¥0.0002/1K tokens |
| text-embedding-3-large | 3072 | 精度最高,适合复杂检索 | ≈¥0.001/1K tokens |
| text-embedding-ada-002 | 1536 | 上一代,兼容性好 | ≈¥0.001/1K tokens |
生产环境优化建议
- 使用专业向量数据库 — ChromaDB(轻量)、Milvus(分布式)、Pinecone(托管)
- 混合检索 — 结合关键词搜索 + 向量检索,提升召回率
- Reranking — 检索后用 Rerank 模型对结果二次排序
- 智能分块 — 按段落、标题、语义边界切分,而不是固定字数
- 元数据过滤 — 为每个 chunk 添加来源、日期等元数据,支持精确过滤
- 评估与迭代 — 用 RAGAS 等框架评估问答质量,持续优化
常见问题
RAG 和直接把文档放进 prompt 有什么区别?
模型的上下文窗口有限(如 128K tokens),且长文本会增加成本和延迟。RAG 只检索最相关的片段(通常 3-5 个),既节省 token 又提高准确率。
中文文档的效果好吗?
text-embedding-3 系列对中文支持很好。建议切分时按句子或段落边界切分,避免中文语义被截断。
需要多大的向量数据库?
1 万个文本块约占 100MB 存储空间(1536维向量)。中小企业知识库通常在这个量级,ChromaDB 本地部署即可。