最近研学过程中发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击链接跳转到网站人工智能及编程语言学习教程。读者们可以通过里面的文章详细了解一下人工智能及其编程等教程和学习方法。下面开始对正文内容的介绍。
一、引言:RAG为何成为大模型落地的"第一性原理"
2024年,大模型技术栈正在经历从"模型中心"到"知识中心"的范式转移。当GPT-4、Claude等通用模型展现出惊人能力时,企业面临的真正挑战却是:如何让AI准确理解私有知识、避免幻觉、并溯源到真实数据?
检索增强生成(Retrieval-Augmented Generation, RAG)给出了答案。据Gartner预测,到2025年,超过70%的企业生成式AI应用将采用RAG架构。这不是简单的"向量数据库+Prompt",而是一套涉及数据工程、检索算法、生成优化、系统架构的复杂工程体系。
本文将从朴素实现出发,逐步拆解企业级RAG系统的核心组件,揭示从原型到生产的架构演进路径。
二、RAG基础架构:朴素实现的陷阱
2.1 经典三步走的局限性
最常见的RAG实现遵循"索引-检索-生成"流水线:
from langchain import OpenAIEmbeddings, Chroma, RetrievalQA # 朴素RAG的"Hello World" def naive_rag(): # 1. 加载文档并切分 loader = TextLoader("docs/") documents = loader.load() texts = RecursiveCharacterTextSplitter(chunk_size=1000).split_documents(documents) # 2. 向量化存储 vectordb = Chroma.from_documents( documents=texts, embedding=OpenAIEmbeddings() ) # 3. 检索与生成 qa = RetrievalQA.from_chain_type( llm=ChatOpenAI(), chain_type="stuff", retriever=vectordb.as_retriever() ) return qa.run("查询问题")这种实现的致命缺陷:
语义鸿沟:Embedding模型无法捕捉领域特定术语的细微差别
上下文碎片化:机械切分导致段落间逻辑断裂
检索噪声:Top-K相似度与真实相关性不完全正相关
生成失控:无法约束LLM基于检索内容回答,幻觉频发
2.2 企业级RAG的架构全景
真正的企业级RAG是分层架构:
┌─────────────────────────────────────────────────────────┐ │ 应用交互层 (API/Gateway) │ ├─────────────────────────────────────────────────────────┤ │ 查询理解层 │ 意图识别 │ 查询扩展 │ 多轮上下文管理 │ ├─────────────────────────────────────────────────────────┤ │ 检索策略层 │ 混合检索 │ 重排序 │ 知识图谱增强 │ ├─────────────────────────────────────────────────────────┤ │ 数据索引层 │ 文档解析 │ 智能分块 │ 多向量表示 │ ├─────────────────────────────────────────────────────────┤ │ 存储引擎层 │ 向量数据库 │ 图数据库 │ 传统搜索引擎 │ └─────────────────────────────────────────────────────────┘三、数据索引层:从文档到高质量知识单元
3.1 文档解析的智能进化
多模态文档的挑战:企业知识库包含PDF、Word、PPT、扫描件、表格等多种格式,传统文本提取会丢失结构化信息。
from unstructured.partition.pdf import partition_pdf from unstructured.partition.auto import partition import cv2 class IntelligentDocumentParser: """ 智能文档解析器:保留布局与结构信息 """ def __init__(self): self.ocr_engine = PaddleOCR(use_angle_cls=True, lang='ch') self.layout_model = LayoutLMv3ForSequenceClassification.from_pretrained(...) def parse(self, file_path: str) -> List[DocumentElement]: """ 解析流程: 1. 布局分析(文本块、图片、表格区域检测) 2. 结构化提取(表格转HTML、图片OCR) 3. 语义分块(基于标题层级与内容连贯性) """ elements = [] # 使用unstructured进行基础解析 raw_elements = partition( filename=file_path, strategy="hi_res", extract_images_in_pdf=True, infer_table_structure=True, chunking_strategy="by_title" ) for elem in raw_elements: if elem.category == "Table": # 表格特殊处理:保留HTML结构 table_html = elem.metadata.text_as_html elements.append(TableElement( content=table_html, headers=self._extract_table_headers(table_html), rows=self._parse_table_rows(table_html) )) elif elem.category == "Image": # 图片内容理解 image_desc = self._describe_image(elem.image_path) ocr_text = self.ocr_engine.ocr(elem.image_path) elements.append(ImageElement( description=image_desc, ocr_text=ocr_text, embedding=self._multimodal_embed(elem.image_path) )) elif elem.category == "Title": # 构建文档层次结构 elements.append(TitleElement( content=elem.text, level=self._infer_heading_level(elem), page_number=elem.metadata.page_number )) else: elements.append(TextElement( content=elem.text, type="paragraph" )) return self._build_hierarchy(elements) def _build_hierarchy(self, elements: List[DocumentElement]) -> DocumentTree: """ 基于标题层级构建文档树,用于后续智能分块 """ root = DocumentTree() current_path = [root] for elem in elements: if isinstance(elem, TitleElement): # 根据层级调整当前路径 while len(current_path) > elem.level: current_path.pop() new_section = Section(title=elem.content) current_path[-1].add_child(new_section) current_path.append(new_section) else: current_path[-1].add_content(elem) return root3.2 智能分块策略
问题:固定长度切分会破坏语义连贯性,导致"前言不搭后语"。
解决方案:多维度分块策略
class SemanticChunker: """ 基于语义连贯性的智能分块 """ def __init__(self, embedding_model): self.embedder = embedding_model self.sentence_transformer = SentenceTransformer('all-MiniLM-L6-v2') def chunk(self, document_tree: DocumentTree, max_chunk_size: int = 512) -> List[Chunk]: """ 分块策略: 1. 结构边界优先(章节、段落边界) 2. 语义相似度聚类(相邻段落相似度高则合并) 3. 滑动窗口重叠(保持上下文连贯) """ chunks = [] for section in document_tree.sections: section_chunks = self._chunk_section(section, max_chunk_size) chunks.extend(section_chunks) return chunks def _chunk_section(self, section: Section, max_size: int) -> List[Chunk]: paragraphs = section.get_paragraphs() if not paragraphs: return [] current_chunk = Chunk( content=paragraphs[0].content, metadata={ "section_title": section.title, "start_para": 0, "level": section.level } ) chunks = [] for i in range(1, len(paragraphs)): para = paragraphs[i] combined_text = current_chunk.content + "\n" + para.content # 检查长度限制 if self._token_count(combined_text) > max_size: # 保存当前块 current_chunk.metadata["end_para"] = i - 1 chunks.append(current_chunk) # 新块(带重叠) overlap_text = self._get_overlap(current_chunk.content) current_chunk = Chunk( content=overlap_text + para.content, metadata={ "section_title": section.title, "start_para": i, "level": section.level, "is_continuation": True } ) else: # 语义连贯性检查 similarity = self._semantic_similarity( current_chunk.content, para.content ) if similarity > 0.7: # 阈值可调 current_chunk.content = combined_text else: # 语义断裂,强制分块 current_chunk.metadata["end_para"] = i - 1 chunks.append(current_chunk) current_chunk = Chunk( content=para.content, metadata={ "section_title": section.title, "start_para": i, "level": section.level } ) chunks.append(current_chunk) return chunks def _semantic_similarity(self, text1: str, text2: str) -> float: emb1 = self.sentence_transformer.encode(text1) emb2 = self.sentence_transformer.encode(text2) return cosine_similarity([emb1], [emb2])[0][0]3.3 多向量表示与索引
单一向量的局限:不同查询意图需要不同的语义表示(摘要、关键词、详细内容)。
class MultiVectorIndexer: """ 为每个文档块生成多视角向量表示 """ def __init__(self): self.embedders = { 'dense': DenseEmbedder('BAAI/bge-large-zh'), # 语义理解 'sparse': BM25Embedder(), # 关键词匹配 'colbert': ColBERTEmbedder(), # 细粒度交互 'summary': SummaryEmbedder() # 高层抽象 } def index(self, chunks: List[Chunk]): for chunk in chunks: # 1. 密集向量(语义搜索) chunk.embeddings['dense'] = self.embedders['dense'].encode( chunk.content ) # 2. 稀疏向量(关键词搜索) chunk.embeddings['sparse'] = self.embedders['sparse'].encode( chunk.content ) # 3. ColBERT向量(迟交互模型) chunk.embeddings['colbert'] = self.embedders['colbert'].encode( chunk.content ) # 4. 摘要向量(用于快速过滤) summary = self._generate_summary(chunk.content) chunk.embeddings['summary'] = self.embedders['dense'].encode(summary) # 存储到混合索引 self._store_to_multimodal_index(chunk) def _store_to_multimodal_index(self, chunk: Chunk): """ 同时写入多种存储引擎 """ # Milvus存储密集向量 milvus_client.insert( collection_name="dense_collection", data=[{ "id": chunk.id, "vector": chunk.embeddings['dense'], "content": chunk.content, "metadata": chunk.metadata }] ) # Elasticsearch存储稀疏向量(BM25) es_client.index( index="sparse_index", document={ "content": chunk.content, "bm25_vector": chunk.embeddings['sparse'], "metadata": chunk.metadata } ) # Faiss存储ColBERT向量(需特殊处理) self.colbert_index.add(chunk.embeddings['colbert'])四、检索策略层:从相似度到相关性
4.1 混合检索架构
单一检索方式的不足:
向量检索:擅长语义匹配, miss 精确关键词
关键词检索:精确匹配,无法理解同义词
知识图谱:擅长关系推理,构建成本高
混合检索方案:
class HybridRetriever: """ 多路召回 + 智能融合 """ def __init__(self): self.retrievers = { 'dense': DenseRetriever(MilvusClient()), 'sparse': SparseRetriever(ElasticsearchClient()), 'graph': GraphRetriever(Neo4jClient()), 'keyword': KeywordRetriever(WhooshIndex()) } self.fusion_weights = { 'dense': 0.4, 'sparse': 0.3, 'graph': 0.2, 'keyword': 0.1 } async def retrieve(self, query: str, context: Dict, top_k: int = 10) -> List[RetrievedChunk]: """ 并行多路召回 """ # 查询理解与扩展 query_analysis = await self._analyze_query(query, context) expanded_queries = query_analysis.expanded_queries # 并行检索 retrieval_tasks = [ self._dense_retrieve(expanded_queries['semantic'], top_k*2), self._sparse_retrieve(expanded_queries['keyword'], top_k*2), self._graph_retrieve(query_analysis.entities, query_analysis.relations, top_k), self._keyword_retrieve(query, top_k*2) ] results = await asyncio.gather(*retrieval_tasks) # 结果融合(Reciprocal Rank Fusion) fused_results = self._reciprocal_rank_fusion( results, weights=self.fusion_weights ) return fused_results[:top_k] def _reciprocal_rank_fusion(self, results_list: List[List[Chunk]], weights: Dict, k=60) -> List[Chunk]: """ RRF算法:对不同来源的排序结果进行加权融合 score = Σ weight_i / (k + rank_i) """ scores = defaultdict(float) chunk_map = {} for source, chunks in zip(weights.keys(), results_list): weight = weights[source] for rank, chunk in enumerate(chunks): scores[chunk.id] += weight / (k + rank + 1) chunk_map[chunk.id] = chunk # 按融合分数排序 sorted_ids = sorted(scores.keys(), key=lambda x: scores[x], reverse=True) return [chunk_map[id] for id in sorted_ids] async def _analyze_query(self, query: str, context: Dict) -> QueryAnalysis: """ 查询意图分析与扩展 """ # 1. 意图分类 intent = await self._classify_intent(query) # 2. 实体识别与链接 entities = self._extract_entities(query) linked_entities = self._link_to_knowledge_graph(entities) # 3. 查询扩展 expansions = { 'semantic': self._semantic_expansion(query), 'keyword': self._keyword_expansion(query), 'hyde': await self._hyde_expansion(query) # Hypothetical Document Embedding } return QueryAnalysis( intent=intent, entities=linked_entities, expanded_queries=expansions ) async def _hyde_expansion(self, query: str) -> str: """ HyDE技术:生成假设文档用于检索 """ hypothetical_doc = await self.llm.generate( prompt=f"基于查询'{query}',生成一段可能包含答案的文档内容:", temperature=0.7, max_tokens=200 ) return hypothetical_doc4.2 重排序(Reranking)优化
初排 vs 精排:
当RAG系统能够准确理解企业知识、溯源到每个事实、并持续自我优化时,大模型才能真正从"玩具"转变为"生产工具"。
初排:追求召回率,使用轻量级模型(向量相似度、BM25)
精排:追求准确率,使用交叉编码器(Cross-Encoder)
class Reranker: """ 多级重排序策略 """ def __init__(self): self.cross_encoder = CrossEncoder('BAAI/bge-reranker-large') self.colbert_reranker = ColBERTReranker() self.llm_reranker = LLMReranker() # 用于复杂推理场景 def rerank(self, query: str, candidates: List[Chunk], strategy: str = "multi_stage") -> List[Chunk]: if strategy == "single": return self._cross_encoder_rerank(query, candidates) elif strategy == "multi_stage": return self._multi_stage_rerank(query, candidates) elif strategy == "adaptive": return self._adaptive_rerank(query, candidates) def _multi_stage_rerank(self, query: str, candidates: List[Chunk]) -> List[Chunk]: """ 多级精排:快速过滤 -> 精细排序 -> LLM验证 """ # Stage 1: 快速交叉编码器(轻量级) stage1_scores = self.cross_encoder.predict([ (query, c.content[:512]) for c in candidates # 截断加速 ]) # 过滤低分候选 filtered = [c for c, s in zip(candidates, stage1_scores) if s > 0.3] # Stage 2: ColBERT细粒度交互(token级匹配) stage2_scores = self.colbert_reranker.score_pairs([ (query, c.content) for c in filtered ]) # Stage 3: 对Top-5使用LLM判断相关性(最精确但最慢) top5 = sorted(zip(filtered, stage2_scores), key=lambda x: x[1], reverse=True)[:5] llm_scores = self.llm_reranker.judge_relevance(query, [c.content for c, _ in top5]) # 融合分数 final_scores = [] for (chunk, colbert_score), llm_score in zip(top5, llm_scores): final_score = 0.6 * colbert_score + 0.4 * llm_score final_scores.append((chunk, final_score)) return [c for c, _ in sorted(final_scores, key=lambda x: x[1], reverse=True)] def _adaptive_rerank(self, query: str, candidates: List[Chunk]) -> List[Chunk]: """ 自适应策略:根据查询复杂度选择重排序深度 """ complexity = self._estimate_query_complexity(query) if complexity == "simple": # 简单事实查询:只用交叉编码器 return self._cross_encoder_rerank(query, candidates) elif complexity == "complex": # 复杂推理查询:使用完整多级排序 return self._multi_stage_rerank(query, candidates) else: # 中间状态:ColBERT排序 return self._colbert_rerank(query, candidates)五、查询理解层:从字符串到结构化意图
5.1 多轮对话的上下文管理
class ConversationManager: """ 维护多轮对话的上下文状态 """ def __init__(self): self.session_store = RedisSessionStore() self.context_compressor = ContextCompressor() async def process_turn(self, session_id: str, user_query: str) -> str: # 获取历史上下文 history = await self.session_store.get_history(session_id) # 上下文压缩(防止过长) compressed_history = self.context_compressor.compress(history) # 指代消解与查询补全 resolved_query = await self._resolve_coreference( user_query, compressed_history ) # 意图识别与槽位填充 intent_analysis = await self._analyze_intent(resolved_query) # 生成检索查询(可能包含历史约束) retrieval_query = self._construct_retrieval_query( intent_analysis, compressed_history ) # 执行RAG流程 context_chunks = await self.retriever.retrieve(retrieval_query) # 生成回答 response = await self.generator.generate( query=resolved_query, context=context_chunks, history=compressed_history, intent=intent_analysis ) # 更新历史 await self.session_store.append_turn( session_id, user_query, response, retrieved_chunks=[c.id for c in context_chunks] ) return response def _construct_retrieval_query(self, intent: IntentAnalysis, history: List[Turn]) -> str: """ 结合历史上下文构建检索查询 """ # 提取历史中的实体约束 historical_entities = self._extract_entities_from_history(history) # 如果当前查询缺少关键实体,从历史补全 enriched_query = intent.query for entity in historical_entities: if entity.type in intent.missing_slots: enriched_query += f" {entity.name}" # 添加时间/范围约束 if intent.temporal_reference == "previous": enriched_query += " 基于之前的讨论" return enriched_query5.2 查询路由与意图分发
class QueryRouter: """ 根据查询类型路由到不同的处理管道 """ def __init__(self): self.pipelines = { 'factual': FactualQAPipeline(), # 事实查询:标准RAG 'analytical': AnalyticalPipeline(), # 分析查询:多文档聚合 'procedural': ProceduralPipeline(), # 流程查询:步骤分解 'creative': CreativePipeline(), # 创意查询:低检索权重 'comparative': ComparativePipeline() # 对比查询:多源对比 } async def route(self, query: str, context: Dict) -> Pipeline: # 使用轻量级分类器快速路由 intent = await self._classify_intent(query) # 动态调整策略 if intent.confidence < 0.7: # 低置信度:使用混合策略 return HybridPipeline(self.pipelines) return self.pipelines[intent.primary_type] async def _classify_intent(self, query: str) -> Intent: """ 查询意图分类 """ # 规则+模型混合分类 rules = [ (r'^(什么是|谁是|哪里|什么时候)', 'factual'), (r'(比较|对比|区别|差异)', 'comparative'), (r'(如何|怎么|步骤|流程)', 'procedural'), (r'(分析|评估|评价)', 'analytical'), (r'(写|生成|创作)', 'creative') ] for pattern, intent_type in rules: if re.search(pattern, query): return Intent(primary_type=intent_type, confidence=0.9) # 模型分类 return await self.intent_classifier.predict(query)六、生成优化层:控制与溯源
6.1 上下文压缩与选择
class ContextCompressor: """ 在有限的上下文窗口内最大化信息密度 """ def compress(self, chunks: List[Chunk], max_tokens: int = 4000) -> str: """ 策略: 1. 去重(相似度过高的块合并) 2. 重排序(按信息增益排序) 3. 摘要(对长块提取关键句) 4. 结构化(添加元数据标签) """ # 1. 语义去重 unique_chunks = self._deduplicate(chunks, threshold=0.85) # 2. 信息增益评分 scored_chunks = [(c, self._information_gain_score(c)) for c in unique_chunks] scored_chunks.sort(key=lambda x: x[1], reverse=True) # 3. 动态填充 selected = [] current_tokens = 0 for chunk, score in scored_chunks: chunk_tokens = self._estimate_tokens(chunk.content) if current_tokens + chunk_tokens > max_tokens: # 尝试摘要压缩 summary = self._summarize_chunk(chunk, max_tokens - current_tokens) if summary: selected.append(Chunk( content=summary, metadata={**chunk.metadata, "is_summary": True} )) break selected.append(chunk) current_tokens += chunk_tokens # 4. 格式化输出 return self._format_context(selected) def _information_gain_score(self, chunk: Chunk) -> float: """ 评估 chunk 对回答问题的信息增益 """ # 基于与查询的相关性、新颖性、权威性评分 relevance = chunk.relevance_score novelty = 1 - max(self._similarity(chunk, c) for c in self.selected_chunks) authority = chunk.metadata.get('source_authority', 0.5) return 0.5 * relevance + 0.3 * novelty + 0.2 * authority6.2 引用溯源与幻觉抑制
class CitationGenerator: """ 生成带引用的回答,支持溯源验证 """ def __init__(self, llm_client): self.llm = llm_client async def generate_with_citations(self, query: str, context_chunks: List[Chunk]) -> str: """ 强制模型在生成时引用来源 """ # 为每个chunk分配引用ID cited_context = "" for i, chunk in enumerate(context_chunks): cited_context += f""" [^{i+1}] 来源:{chunk.metadata['source']}, 页码:{chunk.metadata.get('page', 'N/A')} 内容:{chunk.content} """ prompt = f""" 基于以下参考资料回答问题。必须在回答中使用 [^N] 格式标注信息来源。 如果参考资料不足以回答问题,请明确说明"根据现有资料无法确定"。 问题:{query} 参考资料: {cited_context} 要求: 1. 每个事实陈述后必须紧跟引用标记 [^N] 2. 禁止添加参考资料中未提及的信息 3. 如果多个来源支持同一观点,可标注多个引用 [^1][^2] 回答: """ response = await self.llm.generate(prompt, temperature=0.3) # 后处理:验证引用有效性 validated_response = self._validate_citations(response, context_chunks) return validated_response def _validate_citations(self, response: str, chunks: List[Chunk]) -> str: """ 验证回答中的引用是否真实存在 """ import re citations = re.findall(r'\[\^(\d+)\]', response) for cit_num in set(citations): idx = int(cit_num) - 1 if idx >= len(chunks): # 虚假引用,标记或移除 response = response.replace(f'[^{cit_num}]', '[引用错误]') else: # 验证内容匹配度(防止 hallucination) chunk_content = chunks[idx].content # 使用NLI模型验证蕴含关系 entailment_score = self._check_entailment(response, chunk_content) if entailment_score < 0.5: response += f"\n[警告:引用^{cit_num}的内容可能与原文不符]" return response七、企业级部署架构
7.1 高可用架构设计
class RAGService: """ 生产级RAG服务架构 """ def __init__(self): # 多级缓存 self.cache_layers = { 'embedding': RedisCache(ttl=3600), # 查询向量缓存 'retrieval': CassandraCache(ttl=86400), # 检索结果缓存 'generation': LRUCache(maxsize=10000) # 生成结果缓存 } # 异步流水线 self.indexing_pipeline = IndexingPipeline() self.query_pipeline = QueryPipeline() # 监控与降级 self.circuit_breaker = CircuitBreaker( failure_threshold=5, recovery_timeout=30 ) self.fallback_strategy = FallbackStrategy() async def query(self, request: RAGRequest) -> RAGResponse: request_id = generate_uuid() try: # 1. 缓存检查 cached = await self._check_cache(request) if cached: return cached # 2. 查询预处理(限流、鉴权、敏感词过滤) validated_request = await self._preprocess(request) # 3. 执行RAG流程(带超时控制) result = await asyncio.wait_for( self._execute_rag(validated_request), timeout=30.0 ) # 4. 后处理与缓存 response = self._postprocess(result) await self._update_cache(request, response) # 5. 异步日志与反馈 asyncio.create_task(self._log_interaction(request_id, request, response)) return response except asyncio.TimeoutError: # 降级策略:返回缓存或简化结果 return await self.fallback_strategy.handle_timeout(request) except Exception as e: # 熔断与恢复 self.circuit_breaker.record_failure() if self.circuit_breaker.is_open: return await self.fallback_strategy.handle_circuit_open(request) raise async def _execute_rag(self, request: RAGRequest) -> RAGResult: """ 完整的RAG执行流程 """ # 并行执行:查询理解 + 缓存预热 query_analysis, _ = await asyncio.gather( self.query_understander.analyze(request.query), self._warmup_cache(request) ) # 检索(多路召回) retrieval_results = await self.hybrid_retriever.retrieve( query_analysis.enhanced_query, filters=request.filters ) # 重排序 reranked = self.reranker.rerank( request.query, retrieval_results, strategy=request.rerank_strategy ) # 上下文构建 context = self.context_builder.build( reranked, max_tokens=request.max_context_tokens ) # 生成(流式) generation_stream = self.generator.generate_stream( query=request.query, context=context, style=request.response_style ) return RAGResult( answer_stream=generation_stream, sources=reranked, context_used=context )7.2 性能优化策略
优化层级 策略 效果 索引优化 IVF-PQ量化、HNSW图索引 检索延迟降低10x 缓存策略 查询结果缓存、Embedding缓存 命中率60%+,延迟降低5x 异步流水线 文档解析异步化、批量索引 吞吐量提升3x 硬件加速 GPU Embedding推理、FP16量化 成本降低50% 动态批处理 请求聚合、动态填充 GPU利用率提升40% 八、评估与迭代:RAG系统的持续优化
8.1 多维度评估体系
class RAGEvaluator: """ RAG系统全链路评估 """ def evaluate(self, test_set: List[QAPair]) -> Dict: metrics = { 'retrieval': self._evaluate_retrieval(test_set), 'generation': self._evaluate_generation(test_set), 'end_to_end': self._evaluate_end_to_end(test_set), 'latency': self._evaluate_latency(test_set) } return metrics def _evaluate_retrieval(self, test_set): """ 检索质量评估 """ recalls = [] precisions = [] mrrs = [] for qa in test_set: results = self.retriever.retrieve(qa.question) retrieved_ids = {r.id for r in results} relevant_ids = set(qa.relevant_chunk_ids) # Recall@K recall = len(retrieved_ids & relevant_ids) / len(relevant_ids) recalls.append(recall) # Precision@K precision = len(retrieved_ids & relevant_ids) / len(retrieved_ids) precisions.append(precision) # MRR for rank, r in enumerate(results): if r.id in relevant_ids: mrrs.append(1 / (rank + 1)) break else: mrrs.append(0) return { 'recall@10': np.mean(recalls), 'precision@10': np.mean(precisions), 'mrr': np.mean(mrrs) } def _evaluate_generation(self, test_set): """ 生成质量评估(使用LLM作为评判) """ faithfulness_scores = [] answer_relevance_scores = [] for qa in test_set: response = self.system.query(qa.question) # 忠实度:回答是否基于检索内容 faithfulness = self.llm_judge.evaluate_faithfulness( answer=response.answer, contexts=[c.content for c in response.sources] ) faithfulness_scores.append(faithfulness) # 答案相关性 relevance = self.llm_judge.evaluate_relevance( question=qa.question, answer=response.answer ) answer_relevance_scores.append(relevance) return { 'faithfulness': np.mean(faithfulness_scores), 'answer_relevance': np.mean(answer_relevance_scores) }九、结语
企业级RAG系统的构建不是简单的技术堆叠,而是数据质量、检索精度、生成控制、系统架构的综合工程。从朴素的三步走实现到生产级的分层架构,每个组件都需要针对业务场景深度优化。
2025年的RAG技术将呈现三大趋势:
多模态RAG:融合文本、图像、表格、视频的统一检索
Agentic RAG:检索与推理的动态交互,支持复杂多跳查询
边缘RAG:端侧向量数据库与轻量化模型,实现隐私保护与低延迟