人工智能之RAG工程 第一章 RAG 基础与前置知识
第一章 RAG 基础与前置知识1.1 大模型与 RAG 核心背景大语言模型LLM虽然强大但在实际落地中并非无所不能。理解它的局限性是理解 RAG 价值的前提。1.1.1 LLM 的固有缺陷**知识截止Knowledge Cutoff**LLM 的知识来自于训练数据而训练数据有截止时间。例如GPT-4 的知识可能截止于 2023 年它无法回答“2026 年最新的科技趋势”或“贵公司昨天发布的财报数据”。**幻觉Hallucination**LLM 本质上是基于概率生成文本当它不知道答案时可能会“一本正经地胡说八道”编造事实、引用不存在的文献。这在严谨的商业或医疗场景中是不可接受的。**上下文窗口限制Context Window Limit**尽管模型的上下文窗口越来越大如 128k, 1M tokens但它仍然无法一次性处理海量的企业文档如数万页的技术手册或长达数年的会议记录。1.1.2 RAG 的定义与核心价值RAG检索增强生成Retrieval-Augmented Generation正是为了解决上述痛点而生。定义RAG 是一种将信息检索系统与文本生成模型相结合的技术架构。它的核心思想是**“先检索后生成”**。在用户提问时系统首先从外部知识库如公司文档、数据库中检索与问题最相关的信息然后将这些信息作为“参考依据”连同问题一起交给大模型让大模型基于这些事实来生成答案。核心价值解决知识时效性无需重新训练模型只需更新外部知识库模型就能掌握最新信息。抑制幻觉强制模型基于检索到的事实回答大大减少了胡编乱造的可能。突破上下文限制通过检索只将与问题最相关的“一小部分”知识送入模型实现了“用有限的窗口处理无限的知识”。1.1.3 RAG 的三大范式演进RAG 技术本身也在不断进化经历了三个阶段**Naive RAG朴素 RAG**最基础的形态。流程是“索引 - 检索 - 生成”。它将文档切块、向量化存储用户提问时进行向量相似度检索。缺点是检索精度不高容易受噪声干扰。**Advanced RAG高级 RAG在朴素 RAG 基础上引入了优化。例如查询重写Query Rewriting将用户模糊的问题改写得更适合检索重排序Re-ranking**对初步检索出的结果进行二次精排确保最相关的信息排在前面。**Agentic RAG智能体 RAG这是当前最前沿的方向。它引入了智能体Agent**让 AI 不仅能检索还能“思考”和“行动”。例如当一个问题需要多步推理时“对比 A 产品和 B 产品的最新价格”智能体可以自主决定先查 A 的价格再查 B 的价格最后进行对比甚至调用外部 API 来获取实时数据。1.1.4 行业应用场景分析企业知识库员工可以快速查询公司内部的政策、流程、技术文档无需在海量文件中翻找。智能客服基于最新的产品手册和 FAQ 回答用户问题保证答案的准确性和一致性。教育辅导为学生构建个性化的学习资料库AI 导师可以根据学生的提问从资料库中检索相关内容进行解答。医疗辅助医生在诊断时系统可以从海量的医学文献和病例库中检索相似案例为医生提供决策支持。用户提问RAG 范式Naive RAGAdvanced RAGAgentic RAG简单向量检索查询重写 重排序智能体规划 工具调用生成答案1.2 技术栈与环境准备工欲善其事必先利其器。搭建一个稳定、高效的开发环境是 RAG 工程的第一步。1.2.1 Python 基础与环境配置Python 是 AI 领域的首选语言。推荐使用Anaconda来管理 Python 环境和包它可以避免不同项目之间的依赖冲突。创建虚拟环境conda create -n rag_env python3.10激活环境conda activate rag_env1.2.2 核心依赖库安装RAG 系统的构建依赖于几个核心库LangChain / LlamaIndex这是两个主流的 RAG 应用开发框架。它们提供了丰富的组件可以像搭积木一样快速构建 RAG 流程。Sentence-Transformers用于将文本转换为向量Embedding的库是语义检索的核心。FAISS / ChromaDB向量数据库。FAISS 由 Meta 开发速度快适合本地部署ChromaDB 轻量级易于上手。安装命令pipinstalllangchain llemaindex sentence-transformers faiss-cpu chromadb1.2.3 开发工具链Jupyter Notebook非常适合进行 RAG 原型的快速开发和调试可以分步执行代码直观地看到每一步如索引、检索的效果。VSCode功能强大的代码编辑器适合编写大型、结构化的 RAG 项目。Postman当你将 RAG 系统封装成 API 后可以用 Postman 来测试接口的稳定性和返回结果。1.2.4 硬件与云服务选型本地部署对于开发和小型应用一台配备 NVIDIA GPU如 RTX 3090/4090的电脑即可。CPU 也可以运行但向量化和模型推理速度会慢很多。云端部署对于生产环境推荐使用云服务。GPU 加速AWS、Google Cloud、阿里云等都提供 GPU 实例可以显著加速 Embedding 和 LLM 的推理。Serverless 方案使用 Vercel、Replicate 等平台可以无需管理服务器直接部署 AI 应用按调用次数付费。Naive RAG系统下面的代码将演示如何从零开始用 LangChain 构建一个最简单的Naive RAG系统。# 1. 环境准备# 在命令行中执行以下命令# conda create -n rag_env python3.10# conda activate rag_env# pip install langchain langchain-community langchain-huggingface faiss-cpuimportosfromlangchain_community.document_loadersimportTextLoaderfromlangchain_text_splittersimportCharacterTextSplitterfromlangchain_huggingfaceimportHuggingFaceEmbeddingsfromlangchain_community.vectorstoresimportFAISSfromlangchain_core.promptsimportChatPromptTemplatefromlangchain_huggingfaceimportHuggingFacePipelinefromtransformersimportpipeline,AutoModelForSeq2SeqLM,AutoTokenizer# 2. 模拟数据准备 # 创建一个简单的文本文件作为我们的知识库sample_text RAG (Retrieval-Augmented Generation) 是一种结合检索和生成的 AI 技术。 它首先从外部知识库中检索与用户问题相关的信息。 然后将检索到的信息作为上下文交给大语言模型生成答案。 RAG 的核心优势在于可以解决大模型的知识截止和幻觉问题。 LangChain 是一个流行的 RAG 应用开发框架。 withopen(knowledge_base.txt,w,encodingutf-8)asf:f.write(sample_text)# 3. Naive RAG 核心流程 # 3.1 加载文档loaderTextLoader(knowledge_base.txt,encodingutf-8)documentsloader.load()# 3.2 文档切块 (Text Splitting)# 将长文档切分成小的片段便于向量化和检索text_splitterCharacterTextSplitter(chunk_size50,chunk_overlap10)docstext_splitter.split_documents(documents)# 3.3 向量化 (Embedding)# 使用 Sentence-Transformers 模型将文本转换为向量embeddingsHuggingFaceEmbeddings(model_namesentence-transformers/all-MiniLM-L6-v2)# 3.4 构建向量索引 (Vector Store)# 使用 FAISS 将向量存储在内存中vectorstoreFAISS.from_documents(docs,embeddings)# 3.5 检索 (Retrieval)queryRAG 技术如何解决幻觉问题# 从向量库中找出与问题最相关的 2 个片段relevant_docsvectorstore.similarity_search(query,k2)# 3.6 生成 (Generation)# 为了演示我们使用一个轻量级的本地模型 (如 google/flan-t5-small)# 实际应用中这里会替换为 GPT-4, Claude 等更强大的模型model_namegoogle/flan-t5-smalltokenizerAutoTokenizer.from_pretrained(model_name)modelAutoModelForSeq2SeqLM.from_pretrained(model_name)pipepipeline(text2text-generation,modelmodel,tokenizertokenizer,max_new_tokens100)llmHuggingFacePipeline(pipelinepipe)# 构建 Promptprompt_template 根据以下已知信息回答问题。如果已知信息中不包含答案请说“我不知道”。 已知信息 {context} 问题 {question} 回答 promptChatPromptTemplate.from_template(prompt_template)# 将检索到的文档内容拼接成字符串context_text\n\n.join([doc.page_contentfordocinrelevant_docs])# 格式化 Prompt 并调用模型input_textprompt.format(contextcontext_text,questionquery)responsellm.invoke(input_text)print(f问题:{query})print(f检索到的上下文: \n{context_text})print(f回答:{response})代码说明数据加载与切块我们创建了一个简单的文本文件并使用CharacterTextSplitter将其切分成小块。向量化与索引使用sentence-transformers模型将文本块转换为向量并用FAISS构建内存中的向量索引。检索当用户提问时系统将问题也转换为向量并在 FAISS 中查找最相似的文本块。生成将检索到的文本块和用户问题一起填入 Prompt 模板然后交给本地的小型 LLM (flan-t5-small) 生成最终答案。这个简单的例子完整展示了 Naive RAG 的核心流程是理解更复杂 RAG 系统的基础。