RAG 实战:让 LLM 准确引用你的文档
检索增强生成(RAG)是解决 LLM 幻觉问题的关键技术。本文深入实践向量检索与上下文注入。
为什么需要 RAG?
大语言模型虽然强大,但存在明显的局限性:
- 知识截止:训练数据有时间边界
- 幻觉问题:会编造看似合理但错误的内容
- 私有数据:无法访问企业内部文档
RAG(Retrieval-Augmented Generation)通过在生成前检索相关文档来解决这些问题。
RAG 基本流程
用户提问 → 向量检索 → 获取相关文档 → 注入上下文 → LLM 生成回答
1. 文档处理
将文档切分为适当的 chunks:
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50
)
chunks = splitter.split_documents(documents)
2. 向量化与存储
使用 Embedding 模型将文本转为向量:
from openai import OpenAI
client = OpenAI()
def embed(text):
response = client.embeddings.create(
model="text-embedding-3-small",
input=text
)
return response.data[0].embedding
3. 检索
根据用户问题检索最相关的文档片段:
def retrieve(query, top_k=5):
query_vec = embed(query)
# 使用向量数据库(如 Pinecone、ChromaDB)进行相似度搜索
results = vector_db.similarity_search(query_vec, k=top_k)
return results
4. 生成
将检索到的文档注入 prompt:
def rag_answer(question):
docs = retrieve(question)
context = "\n\n".join([doc.text for doc in docs])
prompt = f"""根据以下参考资料回答问题。如果资料中没有相关信息,请说明。
参考资料:
{context}
问题:{question}"""
response = client.chat.completions.create(
model="claude-sonnet-4-20250514",
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.content
优化技巧
- Chunk 大小:太小丢失上下文,太大引入噪音。通常 300-800 tokens 效果较好
- 重排序:先粗检索 top-20,再用 reranker 精排取 top-5
- 混合检索:结合向量检索和关键词检索(BM25)
- 查询改写:用 LLM 改写用户问题以提高检索质量
总结
RAG 是将 LLM 与私有数据结合的最实用方案。通过合理的 chunk 策略和检索优化,可以显著提升回答的准确性和可靠性。