2.3 检索增强生成(RAG) — AI知识库搭建全攻略 本节导读:深入理解检索增强生成(RAG)的核心原理、架构设计和实现方法,掌握如何将外部知识库与大语言模型结合,显著提升AI回答的准确性和可靠性。 学习目标 理解RAG的核心概念和工作原理 掌握RAG系统的架构设计和组件选型 学习文档预处理和向量化关键技术 实现高效的检索和生成流程 掌握RAG系统的性能优化策略 核心概念 检索增强生成(Retrieval-Augmented Generation, RAG)是一种结合了外部知识检索和语言生成的AI技术。它通过从外部知识库中检索相关信息,然后基于这些信息生成更准确、更可靠的回答。
本节导读:深入理解检索增强生成(RAG)的核心原理、架构设计和实现方法,掌握如何将外部知识库与大语言模型结合,显著提升AI回答的准确性和可靠性。
检索增强生成(Retrieval-Augmented Generation, RAG)是一种结合了外部知识检索和语言生成的AI技术。它通过从外部知识库中检索相关信息,然后基于这些信息生成更准确、更可靠的回答。
| 特性 | 传统LLM | RAG系统 |
|---|---|---|
| 知识来源 | 内部训练数据 | 外部知识库 + 内部知识 |
| 时效性 | 受训练数据限制 | 实时更新,保持最新 |
| 事实准确性 | 可能出现幻觉 | 基于检索证据,准确性高 |
| 可解释性 | 黑盒模型 | 可追溯信息来源 |
| 适用场景 | 通用对话 | 专业领域问答 |
# 核心框架 pip install langchain llama-index # 向量数据库 pip install faiss-cpu pymilvus chromadb pinecone-client # 嵌入模型 pip install sentence-transformers openai # 文档处理 pip install pypdf python-docx beautifulsoup4 # 其他工具 pip install numpy pandas scikit-learn matplotlib
RAG系统包含检索阶段和生成阶段。
subgraph "知识库层" H[文档集合] I[向量索引] J[元数据存储] end subgraph "处理层" K[文档预处理] L[向量化] M[检索算法] end subgraph "生成层" N[提示构建] O[LLM推理] P[答案优化] end H --> K K --> I I --> M M --> C N --> F O --> P
</div> #### 查询理解与预处理 ```python import re import numpy as np from typing import List, Dict, Optional from dataclasses import dataclass @dataclass class ProcessedQuery: original_query: str cleaned_query: str query_vector: np.ndarray query_type: str entities: List[str] intent: str class QueryProcessor: def __init__(self, embedding_model): self.embedding_model = embedding_model def clean_query(self, query: str) -> str: query = re.sub(r'[^\w\s\u4e00-\u9fff]', '', query) query = re.sub(r'\s+', ' ', query).strip() return query def extract_entities(self, query: str) -> List[str]: entities = [] words = query.split() for word in words: if len(word) > 2 and word[0].isupper(): entities.append(word) return entities def classify_query_type(self, query: str) -> str: query_lower = query.lower() if any(word in query_lower for word in ['什么是', '定义', '概念']): return "factual" elif any(word in query_lower for word in ['如何', '怎么', '步骤', '方法']): return "procedural" elif any(word in query_lower for word in ['为什么', '原因', '解释', '分析']): return "explanatory" else: return "general" def process_query(self, query: str) -> ProcessedQuery: cleaned_query = self.clean_query(query) entities = self.extract_entities(query) query_type = self.classify_query_type(query) query_vector = self.embedding_model.encode([cleaned_query])[0] return ProcessedQuery( original_query=query, cleaned_query=cleaned_query, query_vector=query_vector, query_type=query_type, entities=entities, intent=f"{query_type}_query" ) # 使用示例 class MockEmbeddingModel: def encode(self, texts: List[str]) -> np.ndarray: return np.random.rand(len(texts), 128) embedding_model = MockEmbeddingModel() processor = QueryProcessor(embedding_model) query = "什么是AI知识库,它在企业中有什么应用价值?" processed = processor.process_query(query) print(f"原始查询: {processed.original_query}") print(f"清理后查询: {processed.cleaned_query}") print(f"查询类型: {processed.query_type}") print(f"提取的实体: {processed.entities}")
import os import PyPDF2 import docx from bs4 import BeautifulSoup from typing import List, Dict, Any, Optional from pathlib import Path class DocumentLoader: def __init__(self): self.supported_extensions = {'.pdf', '.docx', '.txt', '.md', '.html', '.htm'} def load_document(self, file_path: str) -> Dict[str, Any]: file_path = Path(file_path) if not file_path.exists(): raise FileNotFoundError(f"文件不存在: {file_path}") if file_path.suffix.lower() == '.pdf': return self._load_pdf(file_path) elif file_path.suffix.lower() == '.docx': return self._load_docx(file_path) elif file_path.suffix.lower() in ['.txt', '.md']: return self._load_text(file_path) else: raise ValueError(f"不支持的文件格式: {file_path.suffix}") def _load_pdf(self, file_path: Path) -> Dict[str, Any]: try: with open(file_path, 'rb') as file: pdf_reader = PyPDF2.PdfReader(file) text = "" for page in pdf_reader.pages: text += page.extract_text() + "\n" return { 'content': text, 'metadata': { 'source': str(file_path), 'file_type': 'pdf', 'page_count': len(pdf_reader.pages), 'title': file_path.stem } } except Exception as e: raise ValueError(f"PDF加载失败: {e}") def _load_text(self, file_path: Path) -> Dict[str, Any]: try: with open(file_path, 'r', encoding='utf-8') as file: text = file.read() return { 'content': text, 'metadata': { 'source': str(file_path), 'file_type': 'text', 'word_count': len(text.split()), 'title': file_path.stem } } except Exception as e: raise ValueError(f"文本文件加载失败: {e}")
import re from typing import List, Dict, Any from dataclasses import dataclass @dataclass class TextChunk: text: str chunk_id: str metadata: Dict[str, Any] source_document: str chunk_index: int class TextSplitter: def __init__(self, chunk_size: int = 500, chunk_overlap: int = 50): self.chunk_size = chunk_size self.chunk_overlap = chunk_overlap def recursive_character_split(self, text: str) -> List[str]: def split_text(text: str, separators: List[str]) -> List[str]: for separator in separators: if separator in text: parts = text.split(separator) result = [] for part in parts: if len(part) > self.chunk_size and len(separators) > 1: remaining_separators = [s for s in separators if s != separator] result.extend(split_text(part, remaining_separators)) else: result.append(part.strip()) return result return [text[i:i+self.chunk_size] for i in range(0, len(text), self.chunk_size)] separators = ['\n\n', '\n', '。', '!', '?', '.', '!', '?', ' ', ''] return split_text(text, separators) def create_chunks(self, document: Dict[str, Any]) -> List[TextChunk]: text = document['content'] metadata = document['metadata'].copy() source = metadata.get('source', 'unknown') parts = self.recursive_character_split(text) chunks = [] for i, part in enumerate(parts): if len(part.strip()) > 0: chunk = TextChunk( text=part.strip(), chunk_id=f"{source}_chunk_{i}", metadata=metadata, source_document=source, chunk_index=i ) chunks.append(chunk) return chunks # 使用示例 splitter = TextSplitter(chunk_size=300, chunk_overlap=30) sample_doc = { 'content': """AI知识库是基于大语言模型和向量数据库的技术解决方案。 它能够存储、管理和检索大量结构化和非结构化数据, 为智能问答系统提供强大的知识支撑。 在企业应用中,AI知识库可以用于: 1. 员工培训和支持 2. 客户服务自动化 3. 知识管理和文档检索 4. 智能助手和聊天机器人""", 'metadata': { 'source': 'example.txt', 'file_type': 'text', 'title': 'AI知识库概述' } } chunks = splitter.create_chunks(sample_doc) print(f"分割后的文本块数量: {len(chunks)}") for i, chunk in enumerate(chunks[:3]): print(f"块 {i+1}: {chunk.text[:100]}...")
import numpy as np from typing import List, Dict, Tuple, Optional from dataclasses import dataclass import faiss @dataclass class RetrievedDocument: doc_id: str content: str score: float metadata: Dict[str, Any] source: str @dataclass class SearchResult: query: str results: List[RetrievedDocument] total_count: int query_time: float class VectorRetriever: def __init__(self, dimension: int = 128, index_type: str = "HNSW"): self.dimension = dimension self.index_type = index_type self.index = None self.documents = [] self.doc_ids = [] self.metadata = [] self.embedding_model = MockEmbeddingModel() self._build_index() def _build_index(self): if self.index_type == "HNSW": self.index = faiss.IndexHNSWFlat(self.dimension, 32) else: self.index = faiss.IndexFlatL2(self.dimension) def add_documents(self, documents: List[Dict[str, Any]]): vectors = [] for doc in documents: text = doc['content'] vector = self.embedding_model.encode([text])[0] vectors.append(vector) self.documents.append(text) self.doc_ids.append(f"doc_{len(self.documents)}") self.metadata.append(doc.get('metadata', {})) if len(vectors) > 0: vectors_array = np.array(vectors).astype('float32') self.index.add(vectors_array) def search(self, query: str, k: int = 5, similarity_threshold: float = 0.1) -> SearchResult: import time start_time = time.time() query_vector = self.embedding_model.encode([query])[0].astype('float32') distances, indices = self.index.search(query_vector.reshape(1, -1), k) results = [] valid_indices = indices[0] valid_distances = distances[0] for idx, dist in zip(valid_indices, valid_distances): if idx == -1: continue similarity = 1.0 / (1.0 + float(dist)) if similarity < similarity_threshold: continue retrieved_doc = RetrievedDocument( doc_id=self.doc_ids[idx], content=self.documents[idx], score=similarity, metadata=self.metadata[idx], source=self.metadata[idx].get('source', 'unknown') ) results.append(retrieved_doc) query_time = time.time() - start_time return SearchResult( query=query, results=results, total_count=len(results), query_time=query_time ) # 使用示例 vector_retriever = VectorRetriever(dimension=128, index_type="HNSW") sample_documents = [ { 'content': """AI知识库是基于大语言模型和向量数据库的技术解决方案。 它能够存储、管理和检索大量结构化和非结构化数据, 为智能问答系统提供强大的知识支撑。""", 'metadata': {'category': 'AI', 'source': 'doc1.pdf'} }, { 'content': """向量数据库是专门用于存储和检索高维向量数据的数据库系统。 它支持高效的相似度搜索,广泛应用于推荐系统、 图像检索和自然语言处理等领域。""", 'metadata': {'category': 'Database', 'source': 'doc2.pdf'} } ] vector_retriever.add_documents(sample_documents) # 搜索测试 query = "什么是AI知识库" result = vector_retriever.search(query, k=2) print(f"查询: {query}") print(f"检索时间: {result.query_time:.4f}秒") print(f"结果数量: {result.total_count}") for i, doc in enumerate(result.results): print(f" {i+1}. [相似度: {doc.score:.4f}] {doc.content[:50]}...")
import json from typing import List, Dict, Any, Optional from dataclasses import dataclass import time from datetime import datetime @dataclass class RAGResponse: query: str answer: str retrieved_docs: List[str] sources: List[str] confidence_score: float response_time: float timestamp: datetime class PromptTemplate: def __init__(self): self.system_prompt = """你是一个专业的AI知识库助手,基于提供的上下文信息回答用户的问题。 请遵循以下原则: 1. 只基于提供的上下文信息回答问题 2. 如果上下文信息不足,明确告知用户 3. 回答要准确、简洁、有逻辑性 4. 重要概念要有适当解释 5. 如果涉及多个方面,分点清晰说明""" def build_rag_prompt(self, query: str, context: str, doc_count: int) -> str: return f"""{self.system_prompt} 【上下文信息】 {context} 【用户问题】 {query} 【相关信息来源数量】 {doc_count}个 请根据以上上下文信息回答用户的问题。如果上下文信息不足,请明确告知。 """ class RAGGenerator: def __init__(self, llm_model, prompt_template: PromptTemplate = None): self.llm_model = llm_model self.prompt_template = prompt_template or PromptTemplate() def generate_response(self, query: str, retrieved_docs: List[Any]) -> RAGResponse: start_time = time.time() context = self._build_context(retrieved_docs) doc_count = len(retrieved_docs) prompt = self.prompt_template.build_rag_prompt(query, context, doc_count) answer = self.llm_model.generate(prompt) response_time = time.time() - start_time sources = [doc.source for doc in retrieved_docs] confidence_score = self._calculate_confidence(retrieved_docs) return RAGResponse( query=query, answer=answer, retrieved_docs=[doc.content[:100] + "..." for doc in retrieved_docs], sources=sources, confidence_score=confidence_score, response_time=response_time, timestamp=datetime.now() ) def _build_context(self, retrieved_docs: List[Any]) -> str: if not retrieved_docs: return "未找到相关上下文信息。" context_parts = [] for i, doc in enumerate(retrieved_docs): context_parts.append(f"来源{i+1}:\n{doc.content}\n") return "\n".join(context_parts) def _calculate_confidence(self, retrieved_docs: List[Any]) -> float: if not retrieved_docs: return 0.0 avg_similarity = sum(doc.score for doc in retrieved_docs) / len(retrieved_docs) doc_count_factor = min(len(retrieved_docs) / 5.0, 1.0) confidence = (avg_similarity * 0.7 + doc_count_factor * 0.3) return round(confidence, 3) class MockLLMModel: def generate(self, prompt: str) -> str: if "AI知识库" in prompt: return "AI知识库是基于大语言模型和向量数据库的技术解决方案,能够存储、管理和检索大量结构化和非结构化数据,为智能问答系统提供强大的知识支撑。" elif "向量数据库" in prompt: return "向量数据库是专门用于存储和检索高维向量数据的数据库系统,支持高效的相似度搜索,广泛应用于推荐系统、图像检索和自然语言处理等领域。" else: return "基于提供的上下文信息,我无法回答这个问题。" # 使用示例 llm_model = MockLLMModel() rag_generator = RAGGenerator(llm_model) retrieved_docs = [ RetrievedDocument( doc_id="doc1", content="AI知识库是基于大语言模型和向量数据库的技术解决方案。", score=0.85, metadata={'category': 'AI', 'source': 'doc1.pdf'}, source='doc1.pdf' ) ] response = rag_generator.generate_response("什么是AI知识库", retrieved_docs) print(f"查询: {response.query}") print(f"回答: {response.answer}") print(f"置信度: {response.confidence_score}") print(f"响应时间: {response.response_time:.4f}秒")
import numpy as np from typing import List, Dict, Any import time class CompleteRAGSystem: def __init__(self, dimension: int = 128): self.vector_retriever = VectorRetriever(dimension=dimension) self.rag_generator = RAGGenerator(MockLLMModel()) self.document_loader = DocumentLoader() self.text_splitter = TextSplitter() def build_knowledge_base(self, document_paths: List[str]): all_documents = [] for path in document_paths: try: doc = self.document_loader.load_document(path) chunks = self.text_splitter.create_chunks(doc) all_documents.extend(chunks) print(f"成功加载文档: {path}") except Exception as e: print(f"加载文档失败 {path}: {e}") doc_dicts = [] for chunk in all_documents: doc_dict = { 'content': chunk.text, 'metadata': chunk.metadata } doc_dicts.append(doc_dict) self.vector_retriever.add_documents(doc_dicts) print(f"知识库构建完成,共 {len(doc_dicts)} 个文档块") def query(self, query: str, k: int = 3) -> RAGResponse: search_result = self.vector_retriever.search(query, k=k) response = self.rag_generator.generate_response(query, search_result.results) return response # 使用示例 if __name__ == "__main__": rag_system = CompleteRAGSystem(dimension=128) # 构建知识库 document_paths = ["example1.txt", "example2.txt"] rag_system.build_knowledge_base(document_paths) # 查询测试 query = "什么是AI知识库?" result = rag_system.query(query) print(f"问题: {result.query}") print(f"回答: {result.answer}") print(f"置信度: {result.confidence_score:.3f}") print(f"响应时间: {result.response_time:.3f}秒")
A:RAG与传统LLM的根本区别在于知识来源和更新机制:
A:选择嵌入模型需要考虑以下因素:
A:优化RAG系统检索效果的关键策略:
本节深入讲解了检索增强生成(RAG)的核心概念、架构设计和实现方法。我们学习了RAG系统的关键组件,包括查询理解、文档预处理、向量检索和生成回答,并通过实际代码示例掌握了如何构建高效的RAG系统。
关键收获:
下一节衔接:在理解RAG技术的基础上,下一节将深入讲解语义搜索与匹配技术,进一步提升知识库的检索能力和用户体验。
关键词:AI知识库搭建全攻略, 检索增强生成, RAG, 向量检索, 大语言模型, 知识库, 智能问答
难度:进阶
预计阅读:25分钟