【LangChain】本文主要是我在学习 LangChain 过程中的一些理解总结偏入门和认知梳理。一、问题模型如何获取“它不知道的信息”二、RAG 是什么三、RAG 的完整流程四、Embedding向量化五、向量数据库六、RAG 中的 Prompt七、用 LangChain 实现 RAG1. 加载文档2. 切分文本按 Token 数切分保持语义完整性3. 定义嵌入模型4. 配置 Redis 向量存储5. 将文档存入向量库离线阶段完成6. 创建检索器7. 定义提示词模板8. 定义文档格式化函数9. 构建 RAG 链10. 结果演示八、RAG 的本质九、小结上一篇 LangChain 核心组件梳理从模块到流程一、问题模型如何获取“它不知道的信息”在上一篇中我们已经了解了LangChain 可以将 AI 任务拆解为多个步骤将组件链接起来并组织成完整流程。但还有一个关键问题没有解决模型如何获取“它不知道的信息”比如本地文档企业知识库实时数据LLM的训练数据有截止日期它无法回答关于你公司内部流程的问题也无法告诉你今天发生了什么。原生 LLM 就像一个知识渊博但“与世隔绝”的专家——它的知识停留在训练完成的那一刻。举个例子假设你问 ChatGPT“我们公司今年的年假政策是什么”它会礼貌地告诉你它不知道或者更糟——编造一个听起来合理但完全错误的答案这就是“幻觉”。因为它从未见过你们公司的内部员工手册。这就引出了本文的核心内容RAG检索增强生成二、RAG 是什么RAG 检索Retrieval 生成Generation核心流程用户提问 → 检索相关内容 → 作为上下文输入模型 → 生成答案举个例子想象你在写一篇论文。纯 LLM 模式你不查任何资料全凭记忆写。写到后面可能记错了某个年份或者漏掉了重要观点。RAG 模式你先去图书馆找到几本最相关的参考书把关键段落复印下来摊在桌上。然后你一边看这些资料一边写论文。这样写出来的内容准确、有据可查。本质变化以前模型“凭记忆回答”——容易产生幻觉信息陈旧现在模型“查资料再回答”——基于实时、可信的参考资料这就像给模型配备了一个专属图书管理员每次提问时管理员先去资料库里找到最相关的几页内容递到模型面前模型再据此作答。三、RAG 的完整流程RAG 通常分为两个阶段理解这个流程是掌握 RAG 的关键。离线数据准备阶段加载文档 → 切分文本 → 向量化 → 存入向量数据库这个阶段的目标是把私有知识库“翻译”成模型能检索的形式。举个例子假设你要把一本 300 页的《公司员工手册》变成 RAG 知识库。加载文档把 PDF 文件读进来。切分文本300 页太长一次塞给模型它看不过来上下文窗口有限。所以要把它切成一段一段比如每段约 200 个 Token像把一本大书剪成一张张卡片。向量化把每张“卡片”上的文字转换成一串数字向量让计算机能“理解”它的含义。存入向量数据库把所有卡片和它们的数字指纹向量存进一个专门用来快速搜索的仓库。在线检索阶段用户提问 → 问题向量化 → 相似度检索 → 获取相关内容 → 组装 Prompt → 生成答案这个阶段的目标是根据用户问题找到最相关的资料让模型据此回答。继续上面的例子现在有员工问“年假怎么申请”系统把“年假怎么申请”也转换成一串数字向量。在向量仓库里找哪些卡片上的向量和这个问题向量最“接近”。找到最相似的那几张卡片——上面写的恰好就是年假政策的内容。把这些卡片内容和问题一起打包告诉模型“嘿参考这些资料来回答用户的问题。”模型看完资料给出准确答案“您可以在 OA 系统中提交年假申请每年享有 10 天年假……”我们可以理解为“先建索引再查询”——和搜索引擎的原理异曲同工。下面这张图清晰地展示了完整流程四、Embedding向量化1.什么是EmbeddingEmbedding 的核心思想是把人类语言的“语义”转换为计算机能计算的“数字”。举个例子“我喜欢猫” → [0.12, 0.98, -0.34, …]这个数字列表就是向量它的维度通常是固定的比如 OpenAI 的 text-embedding-3-large 模型生成 3072 维向量一个形象的理解方式想象你要向一个外星人描述各种动物的特征。外星人不认识文字只懂数字。于是你发明了一套编码系统“毛茸茸” → 0.8“会喵喵叫” → 0.9“喜欢抓老鼠” → 0.7“汪汪叫” → -0.8那么“猫”的描述可能就接近 [0.8, 0.9, 0.7, -0.8]而“狗”的描述则是 [0.7, -0.9, -0.5, 0.9]。Embedding 模型做的事情更复杂、更精确但原理是一样的——用数字来代表含义。2.为什么需要-用于计算语义相似度在向量空间中含义相近的文本其向量也彼此接近。我们可以用余弦相似度来度量两个向量的“方向一致性”——方向越一致语义越相似。实验一下“猫是一种可爱的宠物” → 向量 A“狗是人类忠实的朋友” → 向量 B“今天天气真好” → 向量 C计算相似度你会发现A 和 B 的相似度较高都是关于宠物的而它们和 C 的相似度都很低。这就是向量检索的基础。理解把“语义相似”变成“向量接近”这使得计算机可以用数学方法完成“找意思相近的内容”这种原本只有人类才能做的事。五、向量数据库1.作用存储向量并支持高效的相似度搜索。一篇文档转换成一个 1536 维的向量一百万篇文档就是约 1.5GB 的纯向量数据。如何在海量向量中快速找到与查询最相似的几条这就是向量数据库的专长。传统数据库 vs 向量数据库用一个找电影的例子来理解传统数据库如 MySQL你输入SELECT * FROM movies WHERE title LIKE %爱情%它返回所有标题里包含“爱情”两个字的电影。问题来了《泰坦尼克号》标题里没有“爱情”但它确实是一部爱情片传统数据库找不到它。向量数据库你把《泰坦尼克号》的剧情简介转换成向量存进去。你输入“推荐一些感人的爱情电影” → 转换成向量向量数据库计算发现《泰坦尼克号》的剧情向量和你的查询向量方向很接近于是把它推荐给你——哪怕标题里压根没有“爱情”二字将两者比较对比维度传统数据库向量数据库匹配方式关键词精确匹配语义相似匹配查询示例WHERE name 苹果“一种红色的水果”也能找到“苹果”相关文档语义理解能力不懂“含义”仅做字面匹配理解“语义”可基于上下文和意图检索典型工具 / 集成MySQL、PostgreSQL 等关系型数据库Chroma、RedisRediSearch、Pinecone、Milvus 等LangChain 均支持集成我们可以理解为向量数据库是一个“按语义搜索”的数据库六、RAG 中的 Prompt1.问题模型本身并不知道哪部分是用户的问题哪部分是我们提供的参考资料应该基于什么来回答所以我们需要用 Prompt 明确告诉模型错误的 Prompt 写法没有区分角色数据库表怎么设计的后面跟着一大堆检索出来的文档内容模型会困惑“我是应该直接回答数据库设计的问题还是总结这些资料”结果可能是它忽略了资料凭自己的知识回答完全丧失了 RAG 的意义。正确的 Prompt 写法根据以下检索到的上下文片段来回答问题。如果你不知道答案就说你不知道。最多只用三句话回答要简明扼要。问题{question}上下文{context}答案{anwer}这样模型就清楚了用户的需求角色(Role)我是一个基于资料回答的助手输入(Intput)问题是 X参考资料是 Y输出要求( Limit)简明扼要不超过三句不知道就说不知道本质让模型“基于资料回答”而不是自己发挥。这种 Prompt 设计是 RAG 效果的关键——既要让模型充分参考上下文又要约束它不胡编乱造。七、用 LangChain 实现 RAG我们用 LangChain 的组件来完整实现一个 RAG 流程。步骤加载文档 → 切分文本 → 向量化存储 → 创建检索器 → 构建 RAG 链 → 生成答案我们可以看段实现 RAG的完整代码fromlangchain_openaiimportOpenAIEmbeddings,ChatOpenAIfromlangchain_redisimportRedisConfig,RedisVectorStorefromlangchain_core.output_parsersimportStrOutputParserfromlangchain_core.promptsimportChatPromptTemplatefromlangchain_core.runnablesimportRunnablePassthroughfromlangchain_community.document_loadersimportUnstructuredMarkdownLoaderfromlangchain_text_splittersimportCharacterTextSplitter1. 加载文档#UnstructuredMarkdownLoader 专门用于MD文档#PDF文档可用 PyPDFLoaderloaderUnstructuredMarkdownLoader(./knowledge.md)dataloader.load()2. 切分文本按 Token 数切分保持语义完整性# ⽣成分割器text_splitterCharacterTextSplitter.from_tiktoken_encoder(encoding_namecl100k_base,#cl100k_base 是一种默认的编码格式chunk_size200,#块大小chunk_overlap50#块与块之间的重叠长度)documentstext_splitter.split_documents(data)3. 定义嵌入模型我们可以直接在LangChain官网搜索我们需要的嵌入模型以及如何安装和使用需要魔法#这里使用OpenAi的text-embedding-3-largeembeddingsOpenAIEmbeddings(modeltext-embedding-3-large)4. 配置 Redis 向量存储# Redis向量存储相关配置configRedisConfig(index_namemy_knowledge_base,redis_urlredis://localhost:6666,)vector_storeRedisVectorStore(embeddings,configconfig)5. 将文档存入向量库离线阶段完成vector_store.add_documents(documents)6. 创建检索器#通过调⽤向量数据库的as_retriever ⽅法将向量存储⽤作检索器retrievervector_store.as_retriever(search_kwargs{k:4})7. 定义提示词模板# 提示词模板promptChatPromptTemplate.from_messages([(human,根据以下上下文回答问题。如果你不知道答案就说不知道。 问题{question} 上下文{context} 答案)])8. 定义文档格式化函数#将检索到的文档转化成文本传递给提示词模板defformat_docs(docs):return\n\n.join(doc.page_contentfordocindocs)9. 构建 RAG 链#自己尝试可以使用国产的千问配置好apikey#model ChatTongyi(modelqwen-turbo)modelChatOpenAI(modelgpt-4o-mini)chain(# 检索器format_docs 分支1# question 分支2RunnablePassthrough()在链中透传数据{context:retriever|format_docs,question:RunnablePassthrough()}|prompt|model|StrOutputParser())10. 结果演示forchunkinchain.stream(数据库表怎么设计的):print(chunk,end,flushTrue)运行效果示例假设你的 knowledge.md 文档中有这样一段内容数据库表设计遵循第三范式主要分为用户表、订单表、商品表。用户表包含用户ID、姓名、注册时间等字段。订单表通过用户ID与用户表关联商品表通过商品ID与订单表关联。 当你运行代码并提问“数据库表怎么设计的”时系统会 检索出上述这段最相关的内容 将其作为上下文喂给 LLM 模型据此生成答案比如 数据库设计采用第三范式主要包含用户表存储用户基本信息、 订单表记录订单数据通过用户ID关联用户表和商品表通过商品ID关联订单表。八、RAG 的本质RAG 模型能力 外部知识我们也可以理解为RAG 并没有提升模型“智商”它只是让模型“看资料再回答”举个例子期末考试分开闭卷纯 LLM一个闭卷考试的学生只能靠记忆答题。RAG一个开卷考试的学生可以翻书找答案。显然对于需要精确、实时、私有知识的问题开卷考试的成绩会好得多九、小结从上文我们可以了解到RAG的总流程问题 → 检索 → 上下文 → 生成这是一个“最小可运行 RAG Demo”实际生产中还需要考虑缓存、召回优化、重排序等问题。还有RAG 的优势减少幻觉答案有据可查不再凭空编造支持私有数据企业文档、个人笔记都可以成为知识源提高准确性基于实时、相关的信息作答LangChain 为 RAG 提供了完整的组件支持组件 作用Document Loaders ——加载多种格式的文档Text Splitters——将长文档切分为语义完整的块Embeddings——将文本转换为向量Vector Stores——存储向量并支持相似度检索Retrievers——统一的检索接口今天的分享就到这里下章再会~