spinny:~/writing $ less rag-langchain-deep-dive.md
12大型语言模型(LLMs)如 GPT-4 和 Claude 功能强大,但存在一个根本性的局限:它们的知识在训练时就被冻结了。它们无法访问你的内部文档、数据库或实时信息。**检索增强生成(RAG)** 通过将 LLM 的生成能力与从外部来源检索信息的能力相结合,完美地解决了这个问题。34## 问题:LLM 的局限性56在谈论 RAG 之前,重要的是要理解为什么我们需要它。781. **静态知识**:LLM 只知道训练期间看到的内容。如果你询问其截止日期之后发生的事件,它无法回答。92. **幻觉**:当 LLM 不知道答案时,它倾向于编造一个,生成看似合理但完全虚假的信息。103. **无法访问私有数据**:通用 LLM 无法访问你公司的内部文档、工单或代码库。1112RAG 通过在查询时从外部来源检索**相关上下文**来解决这三个问题。1314## 什么是 RAG?1516检索增强生成是一种架构,它使用从外部知识库检索的信息来丰富发送给 LLM 的提示。RAG 不是仅依赖模型的参数化知识,而是先**搜索**相关信息,然后将其**注入**到提示中,使模型能够生成准确、有据可依的回答。1718```mermaid19graph LR20 User["用户"] -- "问题" --> Retriever21 Retriever -- "搜索相关\n文档" --> VectorStore["向量数据库"]22 VectorStore -- "相关\n文档" --> Retriever23 Retriever -- "上下文 + 问题" --> LLM24 LLM -- "有据可依的\n回答" --> User25```2627## RAG 的详细工作原理2829RAG 架构由两个主要阶段组成:**索引**(离线)和 **检索 + 生成**(在线)。3031### 阶段 1:索引(文档摄取)3233索引阶段为语义搜索准备文档,由四个步骤组成。3435```mermaid36graph TD37 A["文档\n(PDF, HTML, MD, DB)"] --> B["文档加载器"]38 B --> C["文本分割器"]39 C --> D["文本块"]40 D --> E["嵌入模型"]41 E --> F["数值向量"]42 F --> G["向量数据库\n(ChromaDB, Pinecone, FAISS)"]43```4445#### 1. 文档加载4647文档可以来自任何来源:PDF 文件、网页、数据库、Markdown 文件、API。**文档加载器**读取这些文档并将其转换为结构化文本。4849#### 2. 文本分割(Chunking)5051LLM 的上下文窗口有限,而文档可能很长。**文本分割器**将文档分成更小的片段,称为 *chunks*。分块的质量至关重要:太小的块会丢失上下文,太大的块会稀释相关性。5253最常见的策略包括:54- **递归字符分割**:使用 `\n\n`、`\n`、`. ` 等分隔符递归分割文本,尊重文档结构。55- **语义分割**:使用嵌入来找到文本中的自然断点。56- **块重叠**:在连续块之间包含重叠部分,以保留边界处的上下文。5758#### 3. 嵌入5960每个块通过嵌入模型(如 OpenAI 的 `text-embedding-3-small`)转换为**数值向量**(嵌入)。这些向量捕获文本的语义含义:含义相似的句子在多维空间中的向量会很接近。6162#### 4. 向量数据库6364向量存储在**向量数据库**中,如 ChromaDB、Pinecone、Weaviate 或 FAISS。该数据库针对**相似性搜索**进行了优化:给定查询,找到最相似的向量(因此也是最相关的文本块)。6566### 阶段 2:检索 + 生成6768当用户提出问题时:69701. 使用相同的嵌入模型将问题转换为嵌入。712. 向量数据库通过**相似性搜索**(通常是余弦相似度或欧几里得距离)找到最相似的块。723. 检索到的块作为上下文插入到提示中。734. LLM 根据提供的上下文生成回答。7475## 使用 LangChain 构建 RAG 管道7677**LangChain** 是最流行的用于构建基于 LLM 应用的 Python(和 JavaScript)框架。它为 RAG 管道的每个组件提供高级抽象。7879### 安装8081```bash82pip install langchain langchain-openai langchain-community chromadb83```8485### 步骤 1:加载文档8687LangChain 为不同的数据源提供了数十个文档加载器。8889```python90from langchain_community.document_loaders import (91 PyPDFLoader,92 WebBaseLoader,93 DirectoryLoader,94 TextLoader,95)9697# 加载 PDF98pdf_loader = PyPDFLoader("docs/manual.pdf")99pdf_docs = pdf_loader.load()100101# 加载网页102web_loader = WebBaseLoader("https://docs.example.com/guide")103web_docs = web_loader.load()104105# 从目录加载所有 .md 文件106dir_loader = DirectoryLoader("./knowledge_base", glob="**/*.md", loader_cls=TextLoader)107md_docs = dir_loader.load()108109all_docs = pdf_docs + web_docs + md_docs110```111112### 步骤 2:将文档分割成块113114```python115from langchain.text_splitter import RecursiveCharacterTextSplitter116117text_splitter = RecursiveCharacterTextSplitter(118 chunk_size=1000,119 chunk_overlap=200,120 separators=["\n\n", "\n", ". ", " ", ""],121)122123chunks = text_splitter.split_documents(all_docs)124print(f"原始文档: {len(all_docs)}, 块: {len(chunks)}")125```126127`chunk_overlap` 参数至关重要:它在连续块之间创建重叠,使上下文不会在边界处丢失。128129### 步骤 3:创建嵌入和向量数据库130131```python132from langchain_openai import OpenAIEmbeddings133from langchain_community.vectorstores import Chroma134135embedding_model = OpenAIEmbeddings(model="text-embedding-3-small")136137vectorstore = Chroma.from_documents(138 documents=chunks,139 embedding=embedding_model,140 persist_directory="./chroma_db",141)142```143144### 步骤 4:创建检索器145146检索器是给定查询后从向量数据库中获取最相关块的组件。147148```python149retriever = vectorstore.as_retriever(150 search_type="similarity",151 search_kwargs={"k": 4},152)153154relevant_docs = retriever.invoke("身份验证如何工作?")155for doc in relevant_docs:156 print(doc.page_content[:200])157 print("---")158```159160### 步骤 5:构建 RAG 链161162现在让我们将所有内容与 LLM 和提示模板组合在一起。163164```python165from langchain_openai import ChatOpenAI166from langchain_core.prompts import ChatPromptTemplate167from langchain_core.runnables import RunnablePassthrough168from langchain_core.output_parsers import StrOutputParser169170llm = ChatOpenAI(model="gpt-4o", temperature=0)171172prompt = ChatPromptTemplate.from_template("""173仅根据提供的上下文回答问题。174如果上下文中没有足够的信息,请说你不知道。175176上下文:177{context}178179问题:{question}180181回答:182""")183184def format_docs(docs):185 return "\n\n".join(doc.page_content for doc in docs)186187rag_chain = (188 {"context": retriever | format_docs, "question": RunnablePassthrough()}189 | prompt190 | llm191 | StrOutputParser()192)193194response = rag_chain.invoke("系统中的身份验证如何工作?")195print(response)196```197198## 高级 RAG 技术199200基本管道运行良好,但有几种技术可以显著提高回答质量。201202### 多查询检索203204有时用户的查询含糊不清,或与文档中使用的语言不一致。**多查询检索器**自动生成原始问题的变体,以捕获多个视角。205206```python207from langchain.retrievers import MultiQueryRetriever208209multi_retriever = MultiQueryRetriever.from_llm(210 retriever=vectorstore.as_retriever(),211 llm=llm,212)213214docs = multi_retriever.invoke("安全最佳实践是什么?")215```216217### 上下文压缩218219并非块中的所有内容都与查询相关。**上下文压缩检索器**使用 LLM 从每个检索到的块中仅提取相关部分。220221```python222from langchain.retrievers import ContextualCompressionRetriever223from langchain.retrievers.document_compressors import LLMChainExtractor224225compressor = LLMChainExtractor.from_llm(llm)226compression_retriever = ContextualCompressionRetriever(227 base_compressor=compressor,228 base_retriever=retriever,229)230```231232### 混合搜索233234纯语义搜索并不总是最优的。**混合搜索**将语义搜索(嵌入)与词汇搜索(BM25、关键词匹配)相结合,以获得更好的结果。235236```python237from langchain.retrievers import EnsembleRetriever238from langchain_community.retrievers import BM25Retriever239240bm25_retriever = BM25Retriever.from_documents(chunks)241bm25_retriever.k = 4242243semantic_retriever = vectorstore.as_retriever(search_kwargs={"k": 4})244245hybrid_retriever = EnsembleRetriever(246 retrievers=[bm25_retriever, semantic_retriever],247 weights=[0.4, 0.6],248)249```250251### 对话式 RAG(带记忆)252253要构建一个能记住对话上下文的 RAG 聊天机器人,需要添加一个记忆组件,根据对话历史重新表述用户的问题。254255```python256from langchain.chains import create_history_aware_retriever257from langchain_core.prompts import MessagesPlaceholder258259contextualize_prompt = ChatPromptTemplate.from_messages([260 ("system", "根据聊天历史和用户的最新问题,"261 "重新表述问题使其在没有历史记录的情况下也能被理解。"),262 MessagesPlaceholder("chat_history"),263 ("human", "{input}"),264])265266history_aware_retriever = create_history_aware_retriever(267 llm, retriever, contextualize_prompt268)269```270271## 最佳实践2722731. **选择正确的块大小**:尝试不同的大小(500-1500 个标记)。较小的块用于精确回答,较大的块用于更广泛的上下文。2742. **使用文档元数据**:将来源、日期和类别作为元数据添加到块中。这允许在检索期间过滤结果。2753. **评估质量**:使用 [RAGAS](https://docs.ragas.io/) 等框架来衡量 *faithfulness*、*relevancy* 和 *context precision* 等指标。2764. **处理文档更新**:实施重新摄取管道,使向量数据库与数据源保持同步。2775. **添加重新排序器**:在初始检索后,使用重新排序模型(如 Cohere Rerank)根据实际相关性重新排序结果。278279## 结论280281RAG 已成为构建需要访问特定且最新知识的 AI 应用的标准架构。LangChain 通过为管道的每个组件提供抽象,极大地简化了实现过程。282283**下一步:**284- **本地实验**:从 ChromaDB 和少量文档开始,熟悉管道流程。285- **探索 LangSmith**:使用 [LangSmith](https://smith.langchain.com/) 在生产中监控和调试你的链。286- **尝试不同的嵌入模型**:比较 `text-embedding-3-small`、`text-embedding-3-large` 等模型以及 Sentence Transformers 的开源模型。287- **查阅文档**:[LangChain 文档](https://python.langchain.com/docs/) 是一个优秀且持续更新的资源。288
:RAG 与 LangChain:检索增强生成完全指南lines 1-288 (END) — press q to close