RAG23LangChain 文档加载器 DocumentLoader & CSVLoader 笔记
一、核心概念
1. 文档加载器
- 作用:提供标准接口,读取 PDF/CSV/JSON 等不同来源数据,统一转为 LangChain 的
Document对象,做到一致化处理。 - 所有加载器(内置 / 自定义)都继承实现
BaseLoader接口。
2. Document 类(文档统一载体)
所有文档加载器最终返回
Document实例
from langchain_core.documents import Document doc = Document( page_content="Hello, world!", # ✅文档正文内容 metadata={"source": "https://example.com"} # ✅元数据,字典格式,记录来源、行号等信息 )page_content:文档文本内容metadata:元数据,记录来源文件、行号、页码等附加信息
3. BaseLoader 的两个核心方法
表格
| 方法 | 说明 | 适用场景 |
|---|---|---|
load() | 一次性全部加载,返回List[Document] | 小文件;全部读到内存 |
lazy_load() | 延迟流式加载,返回生成器,for 循环迭代获取 | 超大文件,防止内存溢出 |
所有加载器都具备这两个方法。
4. CSVLoader
专门用来读取 csv 文件,每一行 csv 生成一个Document对象。
- 每一行 csv → 一个 Document 实例
page_content:该行各个字段拼接文本metadata:记录文件 source、row 行号- 关键参数:
file_path:文件路径encoding:文件编码,一般utf‑8csv_args:字典,底层传给 python csv 库,可配置分隔符delimiter、引号quotechar、无表头时指定fieldnames字段名
⚠️
fieldnames:无表头 csv 才使用;如果 csv 自带表头,不要配置,否则会把表头当成数据行。
二、完整可运行代码
示例 1:基础 CSVLoader load () 一次性加载
from langchain_community.document_loaders.csv_loader import CSVLoader # 1. 创建加载器 loader = CSVLoader( file_path="./data/stu.csv", encoding="utf-8" ) # 2. load()一次性全部加载到内存,返回Document列表 documents = loader.load() # 遍历打印,查看每一个Document对象 for doc in documents: print(type(doc)) print("page_content:\n", doc.page_content) print("metadata:", doc.metadata) print("-" * 50)示例 2:自定义 csv 解析参数 csv_args
from langchain_community.document_loaders.csv_loader import CSVLoader loader = CSVLoader( file_path="./data/stu.csv", encoding="utf-8", csv_args={ "delimiter": ",", # 指定字段分隔符 "quotechar": '"', # 字符串包裹引号 # "fieldnames": ["name","age","gender"] # 无表头csv才打开这个配置 } ) documents = loader.load() for d in documents: print(d)示例 3:lazy_load () 流式大文件加载(生成器,节省内存)
from langchain_community.document_loaders.csv_loader import CSVLoader loader = CSVLoader( file_path="./data/stu.csv", encoding="utf-8" ) # lazy_load 返回生成器,不会一次性全部载入内存,适合超大csv for doc in loader.lazy_load(): print(doc.page_content) print(doc.metadata) print("="*40)三、运行注意事项
- 目录结构:
P3_LangChainRAG开发 ├─ data │ └─ stu.csv └─ 22CSVLoader的使用.py- 安装依赖
pip install langchain langchain-community langchain-core- stu.csv 示例内容
name,age,gender 王梓涵,25,男 刘若曦,22,女 陈俊宇,20,男 赵思瑶,28,女 黄浩然,15,男 林雨桐,20,女 周博文,20,男 吴诗琪,24,女 马子轩,22,男 孙悦然,27,女- 输出效果:每一行 csv 生成一个 Document 对象,
metadata包含source文件路径和row行号。
四、面试简答速记
- Document 有哪两个核心属性?
page_content:文档正文;metadata:元数据字典。
load()和lazy_load()区别?
load():一次性加载全部文档返回列表,小文件;大文件会内存溢出。lazy_load():生成器流式迭代读取,边读边处理,适合大文件,避免内存爆掉。
- CSVLoader 读取 csv,一行 csv 对应几个 Document? 一行 csv 生成一个 Document 对象。
官方文档地址:https://docs.langchain.com/oss/python/integrations/document_loaders
RAG24、LangChain JSONLoader 笔记
一、概述
JSONLoader:把 JSON 文件加载为 LangChain 的Document文档对象,用于 RAG 知识库构建。
⚠️ 依赖第三方库jq,需要手动安装
pip install jq底层依靠 jq 做 JSON 解析,通过jq_schema语法抽取 JSON 里面的数据。
jq_schema 抽取语法规则
表格
| 语法 | 含义 |
|---|---|
. | 代表 JSON 根对象 |
[] | 代表数组 |
.name | 抽取根下 name 字段 |
.hobby[1] | 抽取 hobby 数组的第 2 个元素(下标从 0 开始) |
.other.addr | 嵌套对象取值,取 other 里面 addr |
.[] | 遍历数组,取出数组中每一个字典对象 |
.[].name | 遍历数组,取出数组每个对象的 name 字段 |
三种 JSON 文件格式
- 普通对象 stu.json
{ "name": "周杰伦", "age": 11, "hobby": ["唱", "跳", "RAP"], "other": { "addr": "深圳", "tel": "12332112321" } }- 数组 JSON stus.json:外层
[]包裹,内部多个对象
[ {"name": "周杰伦", "age": 11, "gender": "男"}, {"name": "蔡依临", "age": 12, "gender": "女"}, {"name": "王力宏", "age": 11, "gender": "男"} ]- JsonLines 文件 stu_json_lines.json:
json_lines=True,每行一个独立 json 对象,整体没有外层数组
{"name": "周杰伦", "age": 11, "gender": "男"} {"name": "蔡依临", "age": 12, "gender": "女"} {"name": "王力宏", "age": 11, "gender": "男"}二、JSONLoader 构造参数
from langchain_community.document_loaders import JSONLoader loader = JSONLoader( file_path="xxx.json", # 必填,json文件路径 jq_schema=".", # 必填,jq抽取表达式 text_content=True, # 可选,默认True:抽取结果转为字符串作为page_content;False保留dict对象 json_lines=False # 可选,默认False;True代表文件是JsonLines格式(每行一个json) )file_path:文件路径,必填jq_schema:jq 抽取语法,必填text_content:True:把抽取内容转字符串放到page_content;False则page_content为字典对象json_lines:是否是 JsonLines 文件,每行一条 json,默认 False
调用loader.load()返回List[Document]。
三、完整可运行代码
文件目录:
./data/下放三个 json 文件
from langchain_community.document_loaders import JSONLoader print("====案例1:读取普通对象stu.json,抽取name字段====") loader1 = JSONLoader( file_path="./data/stu.json", jq_schema=".name" ) docs1 = loader1.load() for doc in docs1: print(doc.page_content) print(doc.metadata) print("\n====案例2:读取stu.json,抽取hobby数组第二个元素====") loader2 = JSONLoader( file_path="./data/stu.json", jq_schema=".hobby[1]" ) docs2 = loader2.load() print(docs2[0].page_content) print("\n====案例3:读取数组 stus.json,遍历数组取出全部对象====") loader3 = JSONLoader( file_path="./data/stus.json", jq_schema=".[]" ) docs3 = loader3.load() for idx, doc in enumerate(docs3): print(f"{idx+1}: {doc.page_content}") print("\n====案例4:读取数组 stus.json,只抽取所有name====") loader4 = JSONLoader( file_path="./data/stus.json", jq_schema=".[].name" ) docs4 = loader4.load() for doc in docs4: print(doc.page_content) print("\n====案例5:读取JsonLines格式 stu_json_lines.json====") loader5 = JSONLoader( file_path="./data/stu_json_lines.json", jq_schema=".", json_lines=True ) docs5 = loader5.load() for doc in docs5: print(doc.page_content)四、重点总结
- RAG25必须安装依赖
pip install jq,否则报错 jq_schema是核心,控制提取哪些内容;.代表根,.[]遍历数组json_lines=True专门处理每行一个独立 JSON的文件,不要用于普通数组 jsontext_content=False:page_content是字典;True会自动序列化为字符串,RAG 场景一般保持默认 Trueload()返回List[Document],每个 Document 包含page_content和metadata(会自带 source、seq_num 元数据)
五、常见坑
- jq 语法写错,返回空文档;注意数组下标从 0 开始
- 混淆普通数组 json 和 json‑lines 文件,数组 json 不要设置
json_lines=True - 文件路径错误,相对路径注意 py 文件执行位置
- 没有安装 jq 库直接运行,直接抛出异常
RAG25、课程笔记:TextLoader 和 RecursiveCharacterTextSplitter
1、TextLoader 文本加载器
作用
读取.txt文本文件,把整个文件全部内容封装成 1 个 Document 对象,返回[Document],所以len(docs)=1。
导入
from langchain_community.document_loaders import TextLoader- 参数
- 文件路径:
file_path encoding:文件编码,中文 txt 一般填utf-8,防止乱码
- 文件路径:
- 方法
.load():加载文档,返回 Document 列表
注意:TextLoader 不会做文本切分,不管 txt 多大,全部塞进一个 Document,大文件必须配合文本分割器做分片。
2、RecursiveCharacterTextSplitter 递归字符文本分割器
LangChain官方默认推荐的分割器,开箱即用效果最好。
核心特点
- 递归按照
separators分隔符列表依次切割,优先按换行、句号等自然语义切割,兼顾上下文完整性 + 控制分片大小。 chunk_size:每个分片最大字符数chunk_overlap:分片之间重叠字符,解决被切到句子中间丢失上下文的问题。separators:分割优先级列表,优先用前面符号切割,切完还超长度就用下一个符号。length_function=len:使用 len 函数统计字符长度。- 方法:
.split_documents(docs),对 loader 加载出来的 Document 列表做分片,返回多个小 Document。
依赖安装
pip install langchain-text-splitters完整可运行代码
from langchain_community.document_loaders import TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter # 1. 加载txt文本文件 loader = TextLoader( file_path="./data/Python基础语法.txt", encoding="utf-8" ) # 得到完整文档,只有1个Document docs = loader.load() print(f"加载后原始文档数量: {len(docs)}") # 2. 创建递归字符分割器 splitter = RecursiveCharacterTextSplitter( chunk_size=500, # 每一块最大字符数 chunk_overlap=50, # 块与块重叠字符 # 分割优先级:优先双换行,其次单换行,再句号、感叹号等 separators=["\n\n", "\n", "。", "!", "?", ".", "!", "?", " ", ""], length_function=len # 使用len统计字符长度 ) # 3. 执行文档切分 split_docs = splitter.split_documents(docs) print(f"切分之后分片数量:{len(split_docs)}") # 遍历打印每一个分片 for idx, doc in enumerate(split_docs): print("=" * 30 + f" 分片{idx+1} " + "=" * 30) print(doc.page_content) print("=" * 70)知识点总结
- TextLoader:只负责读取 txt,输出单个 Document 对象的列表,不做分片。
- RecursiveCharacterTextSplitter:RAG 开发最常用分片工具
- 按自然语义递归切割,不是暴力按字符截断
chunk_overlap重叠很关键:保证语义不会因为切割丢失- separators 有优先级,优先用列表靠前的符号分割
- RAG 标准流水线:
Loader加载文档 → TextSplitter切分文档 → 向量化存入向量库。
面试高频问题
Q:为什么要设置 chunk_overlap? A:防止一个完整语义句子被切分到两个 chunk,检索的时候上下文断裂;分片之间保留一部分重叠文本,保证语义连续性。
Q:RecursiveCharacterTextSplitter 和普通 CharacterTextSplitter 区别? A:普通分割器只用一个分隔符切割;递归分割器是一组分隔符递归尝试,优先用大粒度分隔符,保证尽可能在语义边界切分,效果更好。
RAG26、课程笔记:PyPDFLoader PDF 文档加载器
1、简介
PyPDFLoader:LangChain 社区提供的 PDF 文件加载器,底层依赖pypdf库,只提取 PDF 里面的文本内容,不能解析图片、扫描版 PDF。
安装依赖
pip install pypdf导入
from langchain_community.document_loaders import PyPDFLoader构造参数
表格
| 参数 | 说明 |
|---|---|
file_path | 必填,pdf 文件路径 |
mode | 读取模式:page(默认):每一页生成 1 个 Document 对象single:把整个 PDF 全部内容合并为1 个 Document |
password | 可选,加密 PDF 的访问密码 |
两种加载方法
.load():一次性全部加载,返回List[Document],小文件用;大 PDF 会占用较多内存。.lazy_load():惰性加载,迭代器,一页一页读取,不会一次性把全部内容载入内存,处理大 PDF 推荐使用。
Document 对象的
metadata元数据会自动携带页码信息{"page": 页码}。
完整可运行代码
from langchain_community.document_loaders import PyPDFLoader # 1.实例化加载器 loader = PyPDFLoader( file_path="./data/pdf1.pdf", mode="page", # 默认page模式:一页一个Document # password="" # 如果pdf加密,填写密码 ) # ----------------方式1:load() 一次性全部加载---------------- docs = loader.load() print(f"总页数/文档数量:{len(docs)}") for idx, doc in enumerate(docs): print("=" * 40 + f" 第{doc.metadata['page']+1}页 " + "="*40) print(doc.page_content) # ----------------方式2:lazy_load() 惰性加载(大文件推荐)---------------- i = 0 for doc in loader.lazy_load(): i += 1 print("=" * 30, i) print(f"页码元数据:{doc.metadata}") print(doc.page_content)结合 RecursiveCharacterTextSplitter 完整 RAG 流水线(PDF → 分片)
PDF 一页的文本依然很长,实际 RAG 项目中,PDF 加载完成后,仍然要交给分割器做语义分片
from langchain_community.document_loaders import PyPDFLoader from langchain_text_splitters import RecursiveCharacterTextSplitter # 1.加载PDF loader = PyPDFLoader(file_path="./data/pdf1.pdf") pdf_docs = loader.load() # 2.初始化分割器 splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=50, separators=["\n\n", "\n", "。", "!", "?", ".", "!", "?", " ", ""], length_function=len ) # 3.对PDF文档做切分 split_docs = splitter.split_documents(pdf_docs) print(f"原始pdf页数:{len(pdf_docs)},切分后分片总数:{len(split_docs)}") # 查看分片 for chunk in split_docs[:3]: print("-"*50) print(chunk.page_content) print(f"来源页码:{chunk.metadata['page']}")重点总结
mode="page":一页对应一个 Document,metadata 自带页码,方便溯源;single模式全部合并成 1 个 Document。.load():一次性加载;.lazy_load()惰性迭代读取,大 PDF 优先用 lazy_load,降低内存占用。- 局限:只能解析可复制文本的 PDF;扫描件、图片型 PDF 无法提取文字,此时要用 PyMuPDFLoader / UnstructuredLoader 或者 OCR 工具。
- 项目流程:
PyPDFLoader加载PDF → RecursiveCharacterTextSplitter分片 → 向量化存入向量库。
面试小问题
Q:
load()和lazy_load()的区别? A:
load():把所有文档全部读到内存,返回完整列表,小文件方便;大 PDF 内存开销大。lazy_load()返回迭代器,按需一页一页读取,不会一次性加载全部内容,内存友好,适合大体积 PDF 文件。
Q:PyPDFLoader 可以处理扫描版 PDF 吗? A:不行。扫描 PDF 本质是图片,没有文本流,PyPDF 拿不到文字,需要 OCR 识别方案。
RAG27、Vector stores 向量存储 课程笔记
这节课是 RAG 的核心,向量存储就是用来保存文本转出来的向量,做相似度检索。分为内存版(InMemoryVectorStore)和持久化磁盘版(Chroma)
一、RAG 向量存储完整流程
分为两大阶段
- 索引 / 存储阶段(把文档入库)
原始文档 → Embedding嵌入模型 → 嵌入向量 → 存入向量数据库 - 查询 / 检索阶段(用户提问)
用户查询文本 → Embedding嵌入模型 → 查询向量 → 相似度搜索 → 返回Top‑k最相似文档
通俗理解: 把一堆文档变成一堆数字数组(向量)存起来;用户提问,也转成向量,计算和库里哪个向量最接近,把对应的原文拿出来给大模型参考。
二、LangChain 向量存储统一 3 个接口(所有向量库都有这 3 套方法)
add_documents(documents, ids):新增文档,指定 iddelete(ids=[...]):根据 id 删除文档similarity_search(query, k=N):相似度检索,k代表返回几条最相似的结果
k=4:就是取匹配度最高的 4 条文档
三、第一种:内存向量存储 InMemoryVectorStore
✅特点:数据放在内存,程序关闭数据全部丢失,只适合练习、测试,不能生产环境使用。
完整可运行代码
from langchain_core.vectorstores import InMemoryVectorStore from langchain_community.embeddings import DashScopeEmbeddings from langchain_community.document_loaders import CSVLoader # 1.初始化内存向量存储,指定阿里百炼的嵌入模型 vector_store = InMemoryVectorStore(embedding=DashScopeEmbeddings()) # 2.加载csv文档 info.csv loader = CSVLoader( file_path="./data/info.csv", encoding="utf-8", source_column="source" # metadata里标记数据来源 ) documents = loader.load() # 3.把文档写入向量库,手动生成id vector_store.add_documents( documents=documents, ids=["id"+str(i) for i in range(1, len(documents)+1)] ) # 4.根据id删除文档 vector_store.delete(ids=["id1","id2"]) # 5.相似度检索 k=3 返回3条结果 result = vector_store.similarity_search( query="Python是不是简单易学", k=3 ) # 打印检索结果 for doc in result: print("page_content:", doc.page_content) print("metadata:", doc.metadata) print("-"*50)运行输出解读
返回的是list[Document]对象
page_content:原始文本内容metadata:元数据,记录来源、行号 row、文档 id 等信息- exit code 0:代表代码无报错执行成功。
内存版坑点
程序一旦停止运行,内存释放,所有向量全部消失。重启代码,需要重新加载文档、重新向量化。
四、第二种:Chroma 外部持久化向量存储
✅特点:向量数据保存到本地磁盘文件(sqlite 数据库),程序关闭数据不会丢,下次启动可以直接读取,适合做项目开发。
需要先安装依赖
pip install langchain-chroma chromadb完整可运行代码
from langchain_chroma import Chroma from langchain_community.embeddings import DashScopeEmbeddings from langchain_community.document_loaders import CSVLoader # 1.初始化Chroma向量库 vector_store = Chroma( collection_name="test", # 集合名,相当于表名 embedding_function=DashScopeEmbeddings(), # 嵌入模型 persist_directory="./chroma_db" # 磁盘文件夹,数据保存在这里 ) # 2.加载csv文档 loader = CSVLoader( file_path="./data/info.csv", encoding="utf-8", source_column="source" ) documents = loader.load() # 3.写入向量库 vector_store.add_documents( documents=documents, ids=["id"+str(i) for i in range(1, len(documents)+1)] ) # 4.删除文档 vector_store.delete(ids=["id1","id2"]) # 5.相似度检索 result = vector_store.similarity_search( query="Python是不是简单易学", k=3 ) for doc in result: print(doc.page_content) print(doc.metadata) print("-"*50)Chroma 底层原理
运行之后项目目录生成chroma_db文件夹,里面是chroma.sqlite3数据库文件。
- embeddings 表:存放二进制格式向量数组
- segments 表:存放原始文本片段
可以直接用数据库工具打开 sqlite 文件查看底层表结构。
Chroma 重点注意
- 持久化:关闭程序,向量还保存在磁盘,再次运行代码不需要重新 add_documents,直接 new Chroma 读取文件夹就可以检索。
- 如果重复执行
add_documents,会重复插入相同文档,产生重复向量。
五、内存版 vs Chroma 对比表
表格
| 对比项 | InMemoryVectorStore(内存) | Chroma(磁盘持久化) |
|---|---|---|
| 数据存储位置 | 内存 | 本地 sqlite 磁盘文件 |
| 程序重启 | 数据全部丢失 | 数据保留 |
| 适用场景 | 课程练习、demo 快速测试 | 本地 RAG 项目开发 |
| 接口方法 | add_documents / delete / similarity_search | 完全一模一样的 API |
✨LangChain 设计思想:两套向量存储对外 API 完全统一,你写好业务代码,想切换存储,只需要改初始化那一行,后面增删查代码不用改动。
六、常见看不懂的知识点通俗解释
- 什么是 Embedding 向量?一段文字,交给嵌入模型,输出一串浮点数数字数组,语义越接近的文本,向量数字越接近。相似度搜索本质在计算向量之间的距离。
- k 参数是什么?
similarity_search(xxx,k=3),k 就是我要从向量库里,拿出语义匹配度最高的 k 条文档给大模型做参考。k 不能太大,会把无关内容带进来。 - ids 参数干嘛用?每一条文档给一个唯一字符串 id,后续想删除哪条数据,直接传 id 列表,就可以精准删除对应向量和文本。
- Document 对象是什么?LangChain 封装文档对象:
page_content存正文,metadata存附加信息(来源、行号),所有 loader 加载完文件输出都是list[Document]。 - CSVLoader 参数
source_columncsv 文件有一列叫 source,这一列的值自动保存到 Document 的 metadata,用来标记这条数据来自哪里。
七、踩坑清单
- InMemoryVectorStore:跑完程序数据就没了,不要拿来做正式项目。
- Chroma 如果重复运行 add_documents,会重复导入,产生重复向量。
- DashScopeEmbeddings 需要配置环境变量
DASHSCOPE_API_KEY,否则向量化会报错。 - csv 文件必须保存 utf‑8 编码,否则 CSVLoader 读取中文乱码。
八、面试简答(巩固)
Q:InMemoryVectorStore 和 Chroma 有什么区别? A:InMemoryVectorStore 数据存内存,程序退出丢失,适合练习;Chroma 持久化到本地 sqlite 磁盘,重启数据保留。LangChain 提供统一接口,增删检索代码完全一致。
Q:similarity_search 的 k 参数含义? A:k 代表返回相似度最高的文档数量。
Q:向量存储在 RAG 中起到什么作用? A:存储文档对应的 embedding 向量;用户提问时把提问文本转为向量,做相似度检索,召回相关原文,交给大模型参考生成答案。
RAG28、课程:向量检索构建提示词
课程核心理解(通俗大白话)
这就是极简版 RAG 完整流程
- 先把一些参考文档放到内存向量库
InMemoryVectorStore(内存版,程序关掉数据就消失,不用磁盘文件,适合上课练习) - 用户输入问题
怎么减肥? - 向量库做相似度检索,拿出最相关的 2 条参考文本
- 把检索出来的参考资料塞到 Prompt 模板的
{context}占位符,用户问题塞到{input}占位符 - 交给大模型:你只能参考我给你的资料来回答,不要瞎编
- 通过 LangChain 的管道符
|组装 chain 链路,执行拿到回答
重点:
InMemoryVectorStore内存向量库,不会保存到硬盘,程序关闭所有数据清空,只适合学习测试;生产用 Chroma/Milvus。
完整可直接运行代码 28 向量检索构建提示词.py
""" 提示词:用户的提问 + 向量库中检索到的参考资料 极简RAG演示 InMemoryVectorStore内存向量库 """ from langchain_community.chat_models import ChatTongyi from langchain_core.vectorstores import InMemoryVectorStore from langchain_community.embeddings import DashScopeEmbeddings from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser # 1.初始化大模型 model = ChatTongyi(model="qwen3-max") # 2.构建提示词模板 prompt = ChatPromptTemplate.from_messages( [ ("system", "以我提供的已知参考资料为主,简洁和专业的回答用户问题。参考资料:{context}。"), ("user", "用户提问: {input}") ] ) # 3.初始化内存向量库,指定阿里embedding向量化模型 vector_store = InMemoryVectorStore(embedding=DashScopeEmbeddings(model="text-embedding-v4")) # 4.add_texts:把参考文本列表存入向量库(内部自动做embedding) vector_store.add_texts([ "减肥就是要少吃多练", "在减脂期间吃东西很重要,清淡少油控制卡路里摄入并运动起来", "跑步是很好的运动哦" ]) # 用户问题 input_text = "怎么减肥?" # 5.相似度检索 k=2,取出最匹配的2条文档 result = vector_store.similarity_search(input_text, k=2) # 6.循环拼接检索出来的文档内容,组装参考上下文 reference_text = "[" for doc in result: # doc.page_content 就是原始文本内容 reference_text += doc.page_content reference_text += "]" # 小工具函数:打印中间生成的prompt,方便调试看传给大模型的完整消息 def print_prompt(prompt_value): print(prompt_value.to_string()) print("="*20) return prompt_value # 7.使用LCEL管道 | 组装执行链 chain = prompt | print_prompt | model | StrOutputParser() # 8.invoke传入参数字典,填充模板里面两个占位符 context、input res = chain.invoke({"input": input_text, "context": reference_text}) print("大模型最终回答:") print(res)📝课程笔记
1. InMemoryVectorStore 内存向量库
- 特点:全部数据在内存,程序退出全部丢失,不会生成本地文件
- 适合:课堂快速实验,不用管理 chroma_db 文件夹
- 方法:
add_texts(字符串列表):批量存入文本,内部自动调用 Embedding 转为向量similarity_search(query, k=N):相似度检索,返回 Top‑k 最相似的 Document 对象
- 返回值 result 是 Document 对象列表:
doc.page_content:原始文本内容doc.metadata:元数据信息
2. RAG 在这里完整执行步骤
- 向向量库写入知识库文本
- 用户提问
similarity_search检索获取相关文档片段- 循环遍历检索结果,拼接成
reference_text参考上下文 - 提示词模板有两个占位符
{context}:放向量库检索出来的参考资料{input}:放用户真实问题
- LCEL 链路:
prompt | print_prompt | model | StrOutputParser()prompt:填充占位符生成完整消息print_prompt自定义中间函数:打印中间 prompt(调试神器,看你到底传给模型什么文字)model:通义千问大模型StrOutputParser():把 ChatMessage 对象提取成普通字符串
chain.invoke({"input":xxx, "context":xxx})传入字典给两个占位符赋值
3. 容易看不懂的地方拆解
①reference_text = "["循环拼接
reference_text = "[" for doc in result: reference_text += doc.page_content reference_text += "]"通俗讲:把检索出来 2 段文字拼在一起,外面包[ ],作为完整参考资料丢进 prompt 的{context}。
②print_prompt自定义函数为什么可以放进管道|
LCEL 管道
|支持放入普通函数,输入是上一步输出,return 给下一级。 作用:调试!运行时控制台打印出完整发给大模型的 prompt 字符串,方便排查为什么模型回答不对。
③ invoke 传参字典
chain.invoke({"input": input_text, "context": reference_text})模板里面有几个占位符,invoke 字典就要传几个 key。模板写了{context}和{input},字典就必须带上这两个 key。
4. 和 Chroma 向量库区别对比
表格
| 项目 | InMemoryVectorStore | Chroma |
|---|---|---|
| 存储位置 | 内存 | 本地磁盘文件夹 persist_directory |
| 持久化 | ❌关闭程序数据消失 | ✅保存到磁盘,下次运行还在 |
| 适用场景 | 上课快速 demo | 真实 RAG 项目开发 |
5. 常见踩坑点
- 忘记配置
DASHSCOPE_API_KEY环境变量,运行 Embedding 直接报错 similarity_search(k=2)k 不能大于向量库里面文档总条数- invoke 字典 key 名必须和模板占位符名字完全一致,大小写敏感
- InMemoryVectorStore 重启代码,必须重新执行
add_texts添加数据,内存清空了
6. 本节课 RAG 核心逻辑(简答题,面试常考)
- 向量存储检索匹配相关参考信息
- 将用户提问、检索出来的参考资料一起填入提示词模板
- 将组装完整提示词交给大模型得到回答
核心思想:不修改模型权重,外部给资料,让模型基于给的资料回答。
运行前确认
- 环境变量配置好
DASHSCOPE_API_KEY - 已经安装依赖包
pip install langchain langchain-community langchain-core dashscope