LangChain 进阶:给 AI 外挂大脑 —— RAG 实战
LangChain 进阶(二):给 AI 外挂大脑 —— RAG (检索增强生成) 实战
1. 痛点:为什么 AI 总是“一本正经地胡说八道”?
在上一篇中,我们学会了如何调用 OpenAI 的 API。但是,如果你问 GPT-3.5 这样一个问题:“请总结一下昨天咱们公司发布的内部会议纪要”,或者“LangChain 0.1.0 版本具体是哪天发布的?”,它会遇到两个大问题:
- 知识截止(Knowledge Cutoff):模型的训练数据是固定的(比如截止到 2021 年或 2023 年),它不可能知道今天发生的新闻。
- 私有数据不可见:模型没有看过你电脑里的 PDF、Word 文档或公司的数据库。
当你强行问它不知道的事情时,为了满足你的要求,它往往会编造一个看起来很合理的答案。这就是幻觉(Hallucination)。
为了解决这个问题,我们可以选择**微调(Fine-tuning)**模型,把新知识“教”给它。但这太贵、太慢了。
更聪明的做法是 RAG(Retrieval-Augmented Generation,检索增强生成)。 简单来说,就是开卷考试。
- 不使用 RAG:你问老师问题,老师全凭记忆回答(容易记错或不知道)。
- 使用 RAG:你问老师问题,老师先去图书馆查阅相关书籍(检索),找到具体的段落(获取上下文),然后结合书里的内容回答你(生成)。
2. RAG 的核心架构:ETL 流程
要实现 RAG,我们需要搭建一个数据处理流水线。这个过程通常被称为 Indexing(索引),包含三个步骤:
- Load(加载):把各种来源的数据(网页、PDF、文本)加载进来。
- Split(切割):把长文章切成小块(Chunk)。为什么?因为 LLM 有输入长度限制(Context Window),而且切成小块更容易找到精准的答案。
- Embed & Store(向量化与存储):把文字转换成计算机能理解的数字列表(向量),存入专用的向量数据库。
3. 环境准备
我们需要安装一些处理数据和存储向量的库。我们将使用 BeautifulSoup 来抓取网页,使用 ChromaDB 作为本地向量数据库(因为它不需要注册账号,完全本地运行)。
请在终端执行:
1 | # 安装网页解析器 |
4. 实战代码:构建这一整套流水线
我们将构建一个应用,让 AI 能够回答关于“LLM 自主代理(LLM Powered Autonomous Agents)”这篇著名技术博客的内容。如果不使用 RAG,GPT 可能只能泛泛而谈;使用了 RAG,它能精准引用博客细节。
第一阶段:数据加载与切分 (Load & Split)
1 | import bs4 |
第二阶段:向量化与存储 (Embed & Store)
这一步是 RAG 的魔法所在。我们需要把文字变成向量(Vector)。 向量是一串很长的数字(比如 [0.12, -0.98, 0.55, ...])。 核心原理:意思相近的句子,在数学空间里的距离会很近。比如“我喜欢吃苹果”和“水果是我的最爱”在向量空间里靠得很近,而和“今天股市大跌”离得很远。
1 | from langchain_chroma import Chroma |
第三阶段:构建 RAG 链 (Generate)
现在我们有了数据库(书本),也有了检索器(查书动作),我们需要把它们和 LLM(大脑)结合起来。
逻辑是:
- 用户提问。
- 检索器去数据库找相关片段。
- 把用户提问和相关片段一起塞进提示词里。
- LLM 生成答案。
1 | from langchain_core.prompts import ChatPromptTemplate |
5. 深度解析:为什么这么设计?
为什么要 chunk_overlap(切片重叠)?
假设我们的一句话是:“LangChain 的核心作者是 Harrison Chase。”
如果我们不幸地在“是”和“Harrison”中间切开了:
片段 A: “LangChain 的核心作者是”
片段 B: “Harrison Chase。”
那么检索的时候,片段 A 缺失了关键信息,片段 B 缺失了主语。这两个片段都变成了废数据。
设置 chunk_overlap=200,意味着片段 A 的末尾会包含片段 B 的开头,片段 B 的开头会包含片段 A 的末尾,保证了语义的完整连接。
为什么 temperature=0?
在 RAG 场景下,我们希望 AI 严格忠实于检索到的事实,而不是发挥创造力。将温度设为 0 可以最大程度减少幻觉。
retriever | format_docs 是什么写法?
这是 LCEL 的嵌套用法。retriever 也是一个 Runnable(可运行对象)。当 retriever 运行完,它的输出(Document 列表)会自动作为输入流向 format_docs 函数。
6. 总结
在本篇博客中,我们完成了一个质的飞跃:从单纯的闲聊 AI,变成了基于知识库的问答 AI。
现在的流程是:
- Index: 抓取 -> 切分 -> 存向量。
- Retrieve: 用户提问 -> 找向量 -> 拿回文本。
- Generate: 文本 + 问题 -> LLM -> 答案。
但是,现在的系统还是“直来直去”的。用户问一次,它答一次。如果我想让 AI 帮我上网搜索,或者查询数据库,然后再决定怎么回答呢?这就需要引入**Agent(代理)**的概念。
在下一篇博客中,我们将探讨 LangGraph 的前身:如何利用 LangChain 的 Tool(工具)功能,让 AI 拥有“手”和“脚”。