构建完整的 RAG 系统,让 Agent 能基于外部知识库回答专业问题。
构建完整的 RAG(Retrieval-Augmented Generation)系统,让 Agent 能基于外部知识库回答问题。
LLM 的两大缺陷:
1. 知识截止日期:训练数据有时间限制,不知道最新论文
2. 幻觉:可能编造不存在的信息
RAG 的解决思路:先检索相关文档,再让 LLM 基于文档回答。
document_loading.py)
from langchain_core.documents import Document
doc = Document(
page_content="BEVFormer uses spatiotemporal transformers...",
metadata={"source": "BEVFormer", "year": 2022, "venue": "ECCV"}
)
| 格式 | 加载器 | 用法 |
|---|---|---|
| PyPDFLoader | Loader("paper.pdf").load() |
|
| 网页 | WebBaseLoader | Loader("https://...").load() |
| CSV | CSVLoader | Loader("data.csv").load() |
| 文本 | TextLoader | Loader("file.txt").load() |
chunking_strategies.py)| chunk_size | 效果 |
|---|---|
| 太小 (100) | 检索精确,但可能丢失上下文 |
| 太大 (2000) | 上下文完整,但检索精度下降 |
| 中等 (300-500) | 推荐:平衡精度和完整性 |
相邻块的重叠字符数(通常为 chunk_size 的 10-20%)。
为什么需要 overlap?
答案可能恰好落在两个 chunk 的边界上,overlap 确保不会丢失这段信息。
# 1. 递归分割(推荐默认)
RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=100)
# 2. 固定字符分割(简单但可能截断句子)
CharacterTextSplitter(chunk_size=500, separator="")
# 3. 语义分割(最智能,需要 embedding 模型)
SemanticChunker(embeddings)
embeddings_vectorstore.py)
文本 → Embedding 模型 → 768 维向量 → 向量数据库 → 相似度搜索
| 选项 | 维度 | 成本 |
|---|---|---|
| sentence-transformers (all-MiniLM-L6-v2) | 384 | 免费,本地运行 |
| OpenAI text-embedding-3-small | 1536 | 按 token 收费 |
| 选项 | 特点 |
|---|---|
| ChromaDB | 本地持久化,内置元数据过滤,推荐开发用 |
| FAISS | 纯内存,速度最快,需手动保存 |
# 基础搜索
docs = vector_store.similarity_search("BEV perception", k=3)
# MMR 搜索(避免结果重复)
docs = vector_store.max_marginal_relevance_search(query, k=3, fetch_k=6)
# 带元数据过滤
docs = vector_store.similarity_search(query, filter={"year": {"$gte": 2022}})
basic_rag_chain.py)
# 1. 构建检索器
retriever = vector_store.as_retriever(search_kwargs={"k": 3})
# 2. RAG Prompt
prompt = ChatPromptTemplate.from_messages([
("system", "基于以下参考资料回答:\n{context}"),
("human", "{question}"),
])
# 3. 组合
def format_docs(docs):
return "\n\n".join(d.page_content for d in docs)
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt | llm | StrOutputParser()
)
# 4. 使用
answer = rag_chain.invoke("BEVFormer 的架构是什么?")
advanced_retrieval.py)
# 自动生成多个查询变体,综合检索结果
retriever = MultiQueryRetriever.from_llm(retriever=base, llm=llm)
# "BEV perception" → ["BEV perception survey", "bird's eye view detection", ...]
用户问题 → LLM 生成"假设答案" → 用假设答案的 embedding 检索
当问题和文档用词不一致时非常有效。
# 从自然语言中提取元数据过滤条件
# "2022年后的 BEV 论文" → filter={"year": {"$gte": 2022}}
rag_agent.py) ⭐这是 RAG 最强大的用法:Agent 可以自主决定何时检索、检索什么。
@tool
def query_papers(question: str) -> str:
"""查询论文知识库"""
docs = knowledge_base.query(question)
return format_docs(docs)
# 与其他工具一起注册
tools = [query_papers, calculator, search_papers]
| RAG Chain | RAG Agent | |
|---|---|---|
| 检索次数 | 1 次 | 多次,Agent 自主决定 |
| 工具组合 | 无 | 检索 + 计算 + 搜索... |
| 灵活性 | 固定流程 | 动态决策 |
| 步骤 | 关键决策 |
|---|---|
| 文档加载 | 选对 Loader(PDF/Web/CSV) |
| 分块 | chunk_size 500 + overlap 100(中文英文有所不同) |
| Embedding | 本地免费(sentence-transformers)vs 云端收费(OpenAI) |
| 向量存储 | ChromaDB(持久化)vs FAISS(纯内存) |
| 检索 | 默认 similarity + MMR,特殊场景用 Multi-Query / HyDE |
| RAG Agent | 检索作为工具,Agent 自主决策何时使用 |