你有没有遇到过这种崩溃的情况:
公司要做个AI知识库,你信心满满地用LangChain搭了个RAG系统,结果:
- 用户问个稍微复杂点的问题,AI就开始"我不太确定"
- 文档明明有答案,检索却找不到相关的片段
- 多轮对话刚到第三轮,AI就把之前说的全忘了
- 老板问"我们上季度销售额多少",AI答"抱歉我无法获取实时数据"
然后你百度了一圈,GitHub翻了三页,最后发现......
RAG根本不是你想象的那种"文档检索+LLM"的简单拼接!
今天咱们就来彻底搞懂2025年RAG的所有玩法,从基础的Naive RAG到前沿的Agentic RAG,保证你看完就能在公司里当RAG专家。
先打个比方:为什么RAG比你想的复杂?
RAG就像你在海底捞等位拿的那个号码牌。
你以为的RAG:
把文档塞进向量库 → 用户提问 → 检索相关内容 → LLM回答
实际的RAG:
用户问"你们公司2024年Q3的销售政策是什么?"
简单RAG:检索到"2024"、"销售"、"政策"这些关键词,但找到的是2024年Q1的旧政策
高级RAG:理解了"Q3"这个时间概念,找到了对应的时间范围,还关联了相关政策变更的历史记录
看出差距了吗?
RAG技术演进路线图
先给你一张全景图,让你知道自己在哪个段位:
| 段位 | 技术方案 | 特点 | 适用场景 |
|---|---|---|---|
| 🥉 青铜 | Naive RAG | 简单粗暴,能跑就行 | Demo演示、简单问答 |
| 🥈 白银 | Advanced RAG | 混合检索+重排序 | 生产环境基础版 |
| 🥇 黄金 | Modular RAG | 模块化设计,可插拔 | 中大型项目 |
| 💎 铂金 | Self-RAG | 自我判断何时检索 | 复杂推理任务 |
| 👑 钻石 | Corrective RAG | 自动纠错和修复 | 高准确性要求 |
| 🏆 王者 | Agentic RAG | 智能体驱动,会主动行动 | 企业级复杂应用 |
下面一个个来拆解。
1. Naive RAG(青铜段位)- 新手的第一个坑
这是什么: 最基础的RAG实现,就是把文档切成chunks,向量化后存进向量数据库,检索时用相似度找最相关的几个chunks。
# ❌ 很多人的第一版实现(包括三个月前的我)
from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings
def naive_rag(query, documents):
# 简单粗暴地切分文档
texts = [doc.page_content for doc in documents]
# 直接向量化
embeddings = OpenAIEmbeddings()
vectorstore = FAISS.from_texts(texts, embeddings)
# 检索最相关的3个片段
docs = vectorstore.similarity_search(query, k=3)
return "\n".join([doc.page_content for doc in docs])
为什么这玩意儿会坑你:
- 语义漂移:文档切得太碎,上下文丢失严重
- 检索精度低:只看相似度,不考虑时序、重要性等因素
- 无语义理解:"Q3"和"第三季度"在它看来是完全不同的词
- 冷启动问题:新文档进来需要重新整个向量化
适用场景:
- 文档结构简单,问题都是事实性查询
- 对准确性要求不高,做个demo给老板看
- 你刚接触RAG,想先跑个"Hello World"
2. Advanced RAG(白银段位)- 职场必备技能
看到Naive RAG的问题后,聪明人开始优化了。核心思路:在检索前、检索中、检索后都加料。
2.1 检索前优化:查询改写
# ✅ 查询扩展 - 让搜索更全面
def query_expansion(query, llm):
prompt = f"""
将以下查询扩展为3个相关的搜索查询,覆盖不同角度:
原始查询:{query}
返回格式:
1. [扩展查询1]
2. [扩展查询2]
3. [扩展查询3]
"""
expanded_queries = llm.invoke(prompt)
return [query] + parse_queries(expanded_queries)
# 用户问:"Python性能优化"
# 扩展后:
# 1. Python性能优化
# 2. Python代码加速技巧
# 3. Python内存优化方法
# 4. Python并发编程提升性能
2.2 检索中优化:混合检索
# ✅ 混合检索 - 关键词 + 语义双保险
from langchain.retrievers import BM25Retriever, EnsembleRetriever
def hybrid_search(query, documents):
# BM25:基于关键词匹配(擅长精确匹配)
bm25_retriever = BM25Retriever.from_documents(documents)
bm25_retriever.k = 5
# 向量检索:基于语义相似度(擅长理解意图)
vector_retriever = FAISS.from_documents(
documents, embeddings
).as_retriever(search_kwargs={"k": 5})
# 组合两种检索器,用RRF算法融合结果
ensemble_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, vector_retriever],
weights=[0.4, 0.6] # 语义检索权重稍高
)
return ensemble_retriever.get_relevant_documents(query)
为什么要混合?
想象一下:用户搜"React Hooks"
- 纯向量检索可能返回"Vue Composition API"(语义相似但不是用户要的)
- 纯关键词检索可能漏掉"useState和useEffect的使用技巧"(没有精确匹配Hooks)
- 混合检索:两个都能找到,互补短板
2.3 检索后优化:重排序
# ✅ 重排序 - 让最相关的排在最前面
from sentence_transformers import CrossEncoder
def rerank_results(query, documents, top_k=3):
# 使用交叉编码器重排序
cross_encoder = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')
# 计算每个文档与查询的相关性分数
pairs = [[query, doc.page_content] for doc in documents]
scores = cross_encoder.predict(pairs)
# 按分数排序,取top_k
scored_docs = list(zip(documents, scores))
scored_docs.sort(key=lambda x: x[1], reverse=True)
return [doc for doc, score in scored_docs[:top_k]]
重排序的骚操作:
- 初筛用便宜的向量检索,召回100个候选
- 精排用贵但准的交叉编码器,从100个里选出最好的5个
- 成本和效果的完美平衡
3. Modular RAG(黄金段位)- 大厂的选择
当RAG系统变得复杂时,大厂们把它拆成了模块,像搭乐高一样灵活组合:
# 🏗️ Modular RAG - 像搭乐高一样灵活组合
class ModularRAG:
def __init__(self):
# 查询理解模块
self.query_analyzer = QueryAnalyzer()
# 多种检索模块,按需选择
self.retrieval_modules = {
"semantic": SemanticRetriever(), # 语义检索
"keyword": KeywordRetriever(), # 关键词检索
"time_aware": TimeAwareRetriever(), # 时间感知检索
"graph": GraphRetriever(), # 图谱检索
"hybrid": HybridRetriever() # 混合检索
}
# 后处理模块
self.reranker = CrossEncoderReranker()
self.context_compressor = ContextCompressor()
# 路由器:决定用哪些模块
self.router = QueryRouter()
def process(self, query):
# 1. 分析查询类型
query_type = self.query_analyzer.analyze(query)
# 2. 路由器决定使用哪些检索模块
selected_modules = self.router.route(query_type)
# 3. 并行执行多个检索策略
all_results = []
for module_name in selected_modules:
results = self.retrieval_modules[module_name].retrieve(query)
all_results.extend(results)
# 4. 重排序 + 去重
reranked = self.reranker.rerank(query, all_results)
# 5. 上下文压缩(去掉冗余信息)
final_context = self.context_compressor.compress(query, reranked)
return final_context
模块化的核心优势:
- 可插拔设计:需要新的检索策略?加个模块就行
- 动态路由:根据问题类型自动选择最优的检索组合
- 并行处理:多个检索策略同时跑,取最优结果
- 易于维护:每个模块独立,改一个不影响其他
查询路由的实现思路:
class QueryRouter:
def route(self, query_analysis):
"""根据查询分析结果,决定使用哪些检索模块"""
modules = []
# 如果涉及时间,加入时间感知检索
if query_analysis.has_time_constraint:
modules.append("time_aware")
# 如果是实体关系查询,加入图谱检索
if query_analysis.is_entity_relation:
modules.append("graph")
# 如果包含专业术语,加入关键词检索
if query_analysis.has_technical_terms:
modules.append("keyword")
# 默认都加语义检索
modules.append("semantic")
return modules
4. Self-RAG(铂金段位)- 会思考的RAG
核心理念:不是所有问题都需要检索!
你问ChatGPT"1+1等于几",它需要去检索文档吗?显然不需要。Self-RAG就是让AI自己判断什么时候该检索,什么时候直接回答。
# 🧠 Self-RAG - 会自我判断的RAG
class SelfRAG:
def __init__(self, llm, retriever):
self.llm = llm
self.retriever = retriever
def process(self, query):
# 步骤1:判断是否需要检索
need_retrieval = self._should_retrieve(query)
if need_retrieval:
# 步骤2:检索相关信息
docs = self.retriever.get_relevant_documents(query)
# 步骤3:评估检索结果的相关性
relevant_docs = self._filter_relevant(query, docs)
if relevant_docs:
# 步骤4:基于检索内容生成答案
answer = self._generate_with_context(query, relevant_docs)
# 步骤5:验证答案是否被检索内容支持
if self._is_supported(answer, relevant_docs):
return answer
else:
# 答案不被支持,重新生成
return self._regenerate(query, relevant_docs)
else:
# 检索结果不相关,直接用LLM回答
return self._generate_without_context(query)
else:
# 不需要检索,直接回答
return self._generate_without_context(query)
def _should_retrieve(self, query):
"""判断是否需要检索"""
prompt = f"""
判断回答以下问题是否需要检索外部信息:
问题:{query}
如果问题涉及:
- 具体的数据、时间、地点
- 最近发生的事件
- 专业领域的细节
- 公司内部信息
那么需要检索。
如果问题是:
- 常识性问题
- 简单计算
- 通用概念解释
那么不需要检索。
回答:需要检索 或 不需要检索
"""
result = self.llm.invoke(prompt)
return "需要检索" in result.content
Self-RAG的三个关键判断:
- Retrieve Token:要不要检索?
- ISREL Token:检索结果相关吗?
- ISSUP Token:生成的答案有依据吗?
用户问:"Python是什么?"
→ 判断:常识问题,不需要检索
→ 直接回答
用户问:"我们公司上季度的KPI完成情况?"
→ 判断:涉及具体数据,需要检索
→ 检索 → 验证 → 回答
5. Corrective RAG(钻石段位)- 自我纠错的RAG
核心改进:检索到错误内容也不怕,系统会自动修复!
# 🔧 Corrective RAG - 会自我修复的RAG
class CorrectiveRAG:
def __init__(self, llm, retriever, web_search=None):
self.llm = llm
self.retriever = retriever
self.web_search = web_search # 备用的网络搜索
self.quality_evaluator = RetrievalQualityEvaluator()
def process(self, query, max_attempts=3):
for attempt in range(max_attempts):
# 1. 检索
docs = self.retriever.get_relevant_documents(query)
# 2. 评估每个文档的质量
evaluated_docs = []
for doc in docs:
score = self.quality_evaluator.evaluate(query, doc)
evaluated_docs.append((doc, score))
# 3. 根据评估结果分类
correct_docs = [d for d, s in evaluated_docs if s == "correct"]
ambiguous_docs = [d for d, s in evaluated_docs if s == "ambiguous"]
incorrect_docs = [d for d, s in evaluated_docs if s == "incorrect"]
# 4. 决定下一步行动
if correct_docs:
# 有正确的文档,直接生成答案
return self._generate_answer(query, correct_docs)
elif ambiguous_docs and not incorrect_docs:
# 只有模糊的文档,尝试知识精炼
refined_docs = self._refine_knowledge(query, ambiguous_docs)
return self._generate_answer(query, refined_docs)
else:
# 检索结果不靠谱,启动纠错机制
if attempt < max_attempts - 1:
# 尝试查询改写
query = self._rewrite_query(query)
continue
else:
# 最后一次尝试:用网络搜索兜底
if self.web_search:
web_results = self.web_search.search(query)
return self._generate_answer(query, web_results)
else:
return "抱歉,没有找到相关信息"
def _rewrite_query(self, query):
"""查询改写:换个方式问"""
prompt = f"""
以下查询没有找到好的结果,请改写成更容易检索到答案的形式:
原查询:{query}
改写要求:
1. 保持原意
2. 使用更通用的词汇
3. 拆分复杂问题
改写后的查询:
"""
return self.llm.invoke(prompt).content
class RetrievalQualityEvaluator:
def evaluate(self, query, doc):
"""评估检索结果的质量"""
prompt = f"""
评估以下文档对于回答查询的有用程度:
查询:{query}
文档:{doc.page_content}
评估标准:
- correct:文档直接回答了查询,信息准确可靠
- ambiguous:文档部分相关,但信息不完整或需要补充
- incorrect:文档与查询无关或信息错误
评估结果(只回答correct/ambiguous/incorrect):
"""
result = self.llm.invoke(prompt).content.strip().lower()
return result if result in ["correct", "ambiguous", "incorrect"] else "ambiguous"
Corrective RAG的纠错流程:
用户提问 → 检索文档 → 质量评估
↓
┌─────────┼─────────┐
↓ ↓ ↓
正确 模糊 错误
↓ ↓ ↓
直接生成 知识精炼 查询改写
↓ ↓
生成答案 重新检索
↓
还是不行?
↓
网络搜索兜底
6. Adaptive RAG(钻石段位)- 因人而异的RAG
核心理念:不同用户、不同场景,用不同的策略!
# 🎯 Adaptive RAG - 自适应的RAG
class AdaptiveRAG:
def __init__(self, llm, retriever):
self.llm = llm
self.retriever = retriever
self.strategies = {
"simple": self._simple_strategy, # 简单问题
"complex": self._complex_strategy, # 复杂问题
"multi_hop": self._multi_hop_strategy # 多跳推理
}
def process(self, query, user_context=None):
# 1. 分析查询复杂度
complexity = self._analyze_complexity(query)
# 2. 选择对应策略
strategy = self.strategies.get(complexity, self._simple_strategy)
# 3. 执行策略
return strategy(query, user_context)
def _analyze_complexity(self, query):
"""分析查询复杂度"""
prompt = f"""
分析以下查询的复杂度:
查询:{query}
复杂度分类:
- simple:单一事实查询,如"Python是什么"
- complex:需要综合多个信息,如"比较React和Vue的优缺点"
- multi_hop:需要多步推理,如"A公司CEO的母校在哪个城市"
复杂度(只回答simple/complex/multi_hop):
"""
return self.llm.invoke(prompt).content.strip().lower()
def _simple_strategy(self, query, user_context):
"""简单策略:直接检索+生成"""
docs = self.retriever.get_relevant_documents(query, k=3)
return self._generate(query, docs)
def _complex_strategy(self, query, user_context):
"""复杂策略:多角度检索+综合"""
# 扩展查询,获取更多视角
expanded_queries = self._expand_query(query)
all_docs = []
for eq in expanded_queries:
docs = self.retriever.get_relevant_documents(eq, k=2)
all_docs.extend(docs)
# 去重 + 重排序
unique_docs = self._deduplicate(all_docs)
reranked_docs = self._rerank(query, unique_docs)
return self._generate(query, reranked_docs[:5])
def _multi_hop_strategy(self, query, user_context):
"""多跳策略:分解问题,逐步推理"""
# 分解问题
sub_questions = self._decompose_query(query)
# 逐个回答子问题
intermediate_answers = []
for sq in sub_questions:
docs = self.retriever.get_relevant_documents(sq, k=2)
answer = self._generate(sq, docs)
intermediate_answers.append({"question": sq, "answer": answer})
# 综合所有中间答案,生成最终答案
return self._synthesize(query, intermediate_answers)
def _decompose_query(self, query):
"""将复杂问题分解为子问题"""
prompt = f"""
将以下复杂问题分解为可以独立回答的子问题:
问题:{query}
子问题列表(每行一个):
"""
result = self.llm.invoke(prompt).content
return [q.strip() for q in result.split('\n') if q.strip()]
Adaptive RAG的适应维度:
| 维度 | 简单模式 | 复杂模式 |
|---|---|---|
| 检索数量 | 3个文档 | 10+个文档 |
| 查询扩展 | 不扩展 | 多角度扩展 |
| 重排序 | 可选 | 必须 |
| 推理步骤 | 单步 | 多步 |
| 响应时间 | 快 | 慢但准 |
7. Graph RAG(钻石段位)- 理解关系的RAG
2024-2025年的大热门! 用知识图谱增强RAG,让AI理解实体之间的关系。
# 🕸️ Graph RAG - 知识图谱增强的RAG
class GraphRAG:
def __init__(self, llm, vector_store, graph_db):
self.llm = llm
self.vector_store = vector_store
self.graph_db = graph_db # Neo4j、NetworkX等
self.entity_extractor = EntityExtractor()
def build_knowledge_graph(self, documents):
"""从文档构建知识图谱"""
for doc in documents:
# 1. 提取实体
entities = self.entity_extractor.extract(doc.page_content)
# 2. 提取实体间的关系
relations = self._extract_relations(doc.page_content, entities)
# 3. 添加到图数据库
for entity in entities:
self.graph_db.add_node(entity.name, entity.type, entity.properties)
for relation in relations:
self.graph_db.add_edge(
relation.source,
relation.target,
relation.type
)
def retrieve(self, query):
"""图谱增强的检索"""
# 1. 从查询中提取实体
query_entities = self.entity_extractor.extract(query)
# 2. 在图谱中查找相关实体和关系
graph_context = []
for entity in query_entities:
# 获取实体的邻居节点(1-2跳)
neighbors = self.graph_db.get_neighbors(entity.name, depth=2)
# 获取相关的关系路径
paths = self.graph_db.get_paths(entity.name)
graph_context.extend(neighbors + paths)
# 3. 传统向量检索
vector_results = self.vector_store.similarity_search(query, k=5)
# 4. 融合图谱上下文和向量检索结果
combined_context = self._merge_contexts(graph_context, vector_results)
return combined_context
def _extract_relations(self, text, entities):
"""提取实体间的关系"""
entity_names = [e.name for e in entities]
prompt = f"""
从以下文本中提取实体之间的关系:
文本:{text}
实体列表:{entity_names}
返回格式(每行一个关系):
实体1 | 关系类型 | 实体2
例如:
马云 | 创立 | 阿里巴巴
阿里巴巴 | 总部位于 | 杭州
"""
result = self.llm.invoke(prompt).content
return self._parse_relations(result)
Graph RAG vs 传统RAG的对比:
用户问:"马云创立的公司的总部在哪?"
传统RAG:
→ 检索"马云"、"公司"、"总部"相关文档
→ 可能找到很多不相关的内容
→ 答案可能不准确
Graph RAG:
→ 识别实体:马云
→ 图谱查询:马云 --创立--> 阿里巴巴 --总部位于--> 杭州
→ 直接得到答案:杭州
Graph RAG的独特优势:
- 关系理解:知道"马云"和"阿里巴巴"的关系
- 多跳推理:能推理出"马云创立的公司"是谁
- 可解释性:能展示推理路径
- 全局视角:理解整个知识网络的结构
适用场景:
- 金融风控:理解公司间的股权关系
- 医疗诊断:理解症状、疾病、药物的关系
- 法律咨询:理解法条、案例、当事人关系
- 企业知识库:理解组织架构、项目关系
8. RAG Fusion(钻石段位)- 多路召回融合
核心思路: 用LLM生成多个查询,从不同角度检索,然后融合结果。
# 🔀 RAG Fusion - 多路召回融合
class RAGFusion:
def __init__(self, llm, retriever):
self.llm = llm
self.retriever = retriever
def process(self, query):
# 1. 生成多个相关查询
generated_queries = self._generate_queries(query)
all_queries = [query] + generated_queries
# 2. 对每个查询进行检索
all_results = {}
for q in all_queries:
results = self.retriever.get_relevant_documents(q)
for rank, doc in enumerate(results):
doc_id = doc.metadata.get('id', hash(doc.page_content))
if doc_id not in all_results:
all_results[doc_id] = {"doc": doc, "ranks": []}
all_results[doc_id]["ranks"].append(rank + 1)
# 3. 使用RRF(Reciprocal Rank Fusion)融合排序
fused_results = self._reciprocal_rank_fusion(all_results)
return fused_results
def _generate_queries(self, query, num_queries=3):
"""生成多个相关查询"""
prompt = f"""
基于以下查询,生成{num_queries}个不同角度的相关查询:
原始查询:{query}
要求:
1. 每个查询覆盖原问题的不同方面
2. 使用不同的表达方式
3. 保持与原问题的相关性
生成的查询(每行一个):
"""
result = self.llm.invoke(prompt).content
return [q.strip() for q in result.split('\n') if q.strip()][:num_queries]
def _reciprocal_rank_fusion(self, all_results, k=60):
"""RRF融合算法"""
fused_scores = {}
for doc_id, data in all_results.items():
# RRF公式:score = sum(1 / (k + rank))
score = sum(1 / (k + rank) for rank in data["ranks"])
fused_scores[doc_id] = {"doc": data["doc"], "score": score}
# 按分数排序
sorted_results = sorted(
fused_scores.values(),
key=lambda x: x["score"],
reverse=True
)
return [item["doc"] for item in sorted_results]
RAG Fusion的工作流程:
用户问:"如何提高Python代码性能?"
↓
生成多个查询:
1. "Python性能优化技巧"
2. "Python代码加速方法"
3. "Python内存优化"
4. "Python并发编程"
↓
分别检索,每个查询返回Top 5
↓
RRF融合:出现在多个结果中的文档得分更高
↓
返回融合后的Top K结果
9. Agentic RAG(王者段位)- 会主动行动的RAG
这是2025年最前沿的方向! RAG系统变成了智能体,不只是被动回答,还会主动规划、调用工具、迭代优化。
# 🤖 Agentic RAG - 智能体驱动的RAG
class AgenticRAG:
def __init__(self, llm):
self.llm = llm
self.tools = {
"search_knowledge_base": KnowledgeBaseSearcher(),
"web_search": WebSearcher(),
"database_query": DatabaseQuerier(),
"calculator": Calculator(),
"code_executor": CodeExecutor()
}
self.planner = TaskPlanner()
self.memory = ConversationMemory()
def process(self, query):
# 1. 任务规划 - 分解复杂问题
plan = self.planner.create_plan(query, self.tools.keys())
# 2. 执行计划 - 动态选择工具
results = []
for step in plan.steps:
tool_name = step.tool
tool_params = step.parameters
# 执行工具
result = self.tools[tool_name].execute(**tool_params)
results.append({"step": step.description, "result": result})
# 根据中间结果,可能需要调整计划
if step.requires_adaptation:
plan = self.planner.adapt_plan(plan, result)
# 3. 综合所有结果生成最终答案
final_answer = self._synthesize(query, results)
# 4. 保存到记忆
self.memory.save(query, final_answer)
return final_answer
class TaskPlanner:
def create_plan(self, query, available_tools):
"""创建执行计划"""
prompt = f"""
你是一个任务规划专家。根据用户查询,制定执行计划。
用户查询:{query}
可用工具:{available_tools}
工具说明:
- search_knowledge_base:搜索内部知识库
- web_search:搜索互联网
- database_query:查询数据库
- calculator:进行数学计算
- code_executor:执行代码
请制定执行计划,格式如下:
步骤1:[工具名] - [参数] - [目的]
步骤2:[工具名] - [参数] - [目的]
...
执行计划:
"""
result = self.llm.invoke(prompt).content
return self._parse_plan(result)
Agentic RAG的实际例子:
用户问:"我们公司Q3的销售额相比Q2增长了多少?"
传统RAG:
→ 检索"Q3销售额"、"Q2销售额"
→ 可能找不到直接的对比数据
→ 回答不准确
Agentic RAG:
→ 规划:
步骤1:database_query - 查询Q2销售额
步骤2:database_query - 查询Q3销售额
步骤3:calculator - 计算增长率
→ 执行:
Q2销售额:1000万
Q3销售额:1200万
增长率:(1200-1000)/1000 = 20%
→ 回答:"Q3销售额相比Q2增长了20%,从1000万增长到1200万"
Agentic RAG的核心特性:
| 特性 | 说明 |
|---|---|
| 主动规划 | 不只是被动回答,会主动分解问题 |
| 工具调用 | 能调用各种外部工具和API |
| 动态调整 | 根据中间结果调整执行策略 |
| 多步推理 | 能处理需要多个步骤的复杂问题 |
| 记忆能力 | 记住对话历史,支持多轮交互 |
10. Multi-Modal RAG(王者段位)- 多模态RAG
不只处理文本,还能处理图片、表格、音视频!
# 🎨 Multi-Modal RAG - 多模态RAG
class MultiModalRAG:
def __init__(self):
self.text_encoder = TextEncoder()
self.image_encoder = ImageEncoder() # CLIP等
self.table_encoder = TableEncoder()
self.text_store = VectorStore("text")
self.image_store = VectorStore("image")
self.table_store = VectorStore("table")
def index_document(self, document):
"""索引多模态文档"""
# 索引文本
if document.text:
text_embedding = self.text_encoder.encode(document.text)
self.text_store.add(document.id, text_embedding, document.text)
# 索引图片
if document.images:
for img in document.images:
img_embedding = self.image_encoder.encode(img)
# 同时生成图片描述
img_caption = self._generate_caption(img)
self.image_store.add(img.id, img_embedding, img_caption)
# 索引表格
if document.tables:
for table in document.tables:
# 将表格转换为结构化描述
table_desc = self._table_to_text(table)
table_embedding = self.table_encoder.encode(table_desc)
self.table_store.add(table.id, table_embedding, table)
def retrieve(self, query, modalities=["text", "image", "table"]):
"""多模态检索"""
results = {}
# 文本检索
if "text" in modalities:
text_query_emb = self.text_encoder.encode(query)
results["text"] = self.text_store.search(text_query_emb, k=5)
# 图片检索(文本到图片)
if "image" in modalities:
# 使用CLIP等模型,支持文本查询图片
img_query_emb = self.image_encoder.encode_text(query)
results["image"] = self.image_store.search(img_query_emb, k=3)
# 表格检索
if "table" in modalities:
table_query_emb = self.table_encoder.encode(query)
results["table"] = self.table_store.search(table_query_emb, k=3)
return self._merge_results(results)
def _generate_caption(self, image):
"""生成图片描述"""
# 使用视觉语言模型生成描述
return vision_model.generate_caption(image)
def _table_to_text(self, table):
"""将表格转换为文本描述"""
prompt = f"""
将以下表格转换为自然语言描述:
{table.to_string()}
描述应包含:
1. 表格的主题
2. 关键数据点
3. 数据趋势(如有)
"""
return self.llm.invoke(prompt).content
Multi-Modal RAG的应用场景:
- 技术文档:代码 + 架构图 + 流程图
- 产品手册:文字说明 + 产品图片 + 规格表格
- 财务报告:分析文字 + 数据图表 + 财务表格
- 医疗诊断:病历文字 + 医学影像 + 检验报告
实战踩坑指南:血泪换来的经验
坑1:文档切分不合理,上下文丢失
# ❌ 脑残式切分:固定长度,不管语义
def stupid_chunking(text, chunk_size=500):
return [text[i:i+chunk_size] for i in range(0, len(text), chunk_size)]
# 问题:可能把一句话切成两半
# ✅ 智能切分:保持语义完整性
from langchain.text_splitter import RecursiveCharacterTextSplitter
def smart_chunking(text):
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200, # 重叠部分保留上下文
separators=["\n\n", "\n", "。", ",", " "], # 按优先级切分
length_function=len
)
return splitter.split_text(text)
坑2:Embedding模型选错,检索效果差
# ❌ 错误选择:用通用模型处理专业领域
embeddings = OpenAIEmbeddings() # 通用模型
# ✅ 正确选择:根据场景选模型
# 中文场景
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-large-zh-v1.5")
# 英文场景
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-large-en-v1.5")
# 多语言场景
embeddings = HuggingFaceEmbeddings(model_name="intfloat/multilingual-e5-large")
坑3:检索数量不合理
# ❌ 贪多嚼不烂:检索太多,LLM看花眼
docs = retriever.get_relevant_documents(query, k=50)
# ❌ 太少不够用:检索太少,信息不全
docs = retriever.get_relevant_documents(query, k=1)
# ✅ 合理数量:根据场景调整
# 简单问答:3-5个
# 复杂分析:5-10个
# 综合报告:10-15个(配合重排序)
坑4:忽视元数据过滤
# ❌ 只看相似度,不管时效性
docs = vectorstore.similarity_search(query, k=5)
# 问题:可能返回过时的文档
# ✅ 结合元数据过滤
docs = vectorstore.similarity_search(
query,
k=5,
filter={"date": {"$gte": "2024-01-01"}} # 只要2024年以后的
)
2025年RAG技术趋势预测
趋势1:RAG + Agent深度融合
不再是简单的检索+生成,而是智能体主导的复杂任务处理。
趋势2:多模态RAG成为标配
文本、图片、表格、音视频统一处理。
趋势3:实时更新RAG
知识库实时更新,不需要重新索引整个库。
趋势4:个性化RAG
根据用户画像、历史行为动态调整检索策略。
趋势5:端侧RAG
在手机、边缘设备上运行轻量级RAG。
如何选择适合你的RAG方案?
| 你的情况 | 推荐方案 | 理由 |
|---|---|---|
| 刚入门,做Demo | Naive RAG | 简单快速,先跑起来 |
| 生产环境,预算有限 | Advanced RAG | 性价比最高 |
| 中大型项目,需要扩展 | Modular RAG | 灵活可扩展 |
| 高准确性要求 | Corrective RAG | 自动纠错 |
| 复杂推理任务 | Self-RAG + Adaptive RAG | 智能判断 |
| 实体关系查询 | Graph RAG | 理解关系 |
| 企业级复杂应用 | Agentic RAG | 全能选手 |
写在最后
记住一句话:RAG不是银弹,选对方案比堆技术更重要。
不要为了用RAG而用RAG,要先想清楚:
- 你的用户真正需要什么?
- 你的数据有什么特点?
- 你的团队能维护多复杂的系统?
选对适合的RAG方案,比追求最新潮的技术更重要。
你在项目中用的是什么RAG方案?遇到了什么坑?
评论区聊聊,看看有没有更骚的操作。
(如果等不及想动手试试,推荐从LangChain或LlamaIndex开始,它们把这些方案都封装好了,懒人福音~)