数据加载、分割、向量化与存储
场景:你问ChatGPT:"2024年的诺贝尔奖得主是谁?"
回答:"抱歉,我的知识截止到2023年9月..."
原因:大模型的知识来自训练数据,训练完成后就"冻结"了,无法获取最新信息。
✅ RAG解决方案:从最新的新闻数据库检索2024年的信息,让AI基于最新数据回答!
场景:你问:"我们公司的请假制度是什么?"
AI回答:"根据常见做法,年假通常是10天..."(但你们公司可能是15天!)
原因:AI没有你公司的专业知识,只能根据通用数据"猜测",容易编造不准确的信息。
✅ RAG解决方案:从公司员工手册检索真实的请假制度,AI基于真实文档回答!
场景:医生问:"这个罕见病的最新治疗方案是什么?"
AI回答:给出的是通用信息,不够专业和详细
原因:通用大模型无法覆盖所有专业领域的深度知识。
✅ RAG解决方案:从医学文献数据库检索最新研究,提供专业准确的答案!
随时添加最新信息
基于真实文档回答
无需重新训练模型
知道答案来自哪里
RAG(Retrieval-Augmented Generation,检索增强生成)是当前最热门的大模型应用技术。 它通过从外部知识库检索相关信息,然后将这些信息作为上下文提供给大模型,让AI基于真实知识回答问题。
读图方式:从左到右,先“检索增强”,再“调用大模型生成”,最后把结果返回给用户。
RAG = 检索(Retrieval) + 增强(Augmented) + 生成(Generation)
简单理解:就像考试时可以"翻书"!AI不再只靠记忆(训练数据),而是可以查阅"参考书"(知识库)来回答问题。
要让RAG系统工作,首先需要完成存储阶段的准备工作。 这个阶段是一次性的,将你的知识库数据处理成可检索的向量形式。
💡 重要提示:存储阶段是一次性准备工作,完成后就可以反复使用。 接下来我们将详细讲解每个步骤的原理和实现方法。
这是RAG的第一步,目的是将各种格式的知识源(Knowledge Base)读取到系统中,转换为纯文本格式。
PDF, Word, TXT
网页, API, 爬虫
Excel, SQL, JSON
Python, Chat Logs
💡 提示: 无论原始格式是什么,最终都需要转换为纯文本格式才能进行后续处理。
下面是几种常见数据源的完整加载代码,可以直接运行。我们提供了示例数据文件供你练习:
请下载以下示例文件,并放在你的项目的 data/ 目录下:
💡 使用方法:下载后放在项目根目录的 data/ 文件夹下,代码中使用 'data/sample_document.pdf' 即可加载
# 安装:pip install pypdf
import pypdf
def load_pdf(file_path):
"""加载PDF并返回文本列表"""
documents = []
with open(file_path, 'rb') as f:
reader = pypdf.PdfReader(f)
for i, page in enumerate(reader.pages):
text = page.extract_text()
documents.append({
'content': text,
'page': i + 1
})
return documents
# 使用示例(使用下载的示例文件)
docs = load_pdf('data/sample_document.pdf')
print(f"加载了 {len(docs)} 页")
print(f"第1页内容: {docs[0]['content'][:100]}...")# 安装:pip install requests beautifulsoup4
import requests
from bs4 import BeautifulSoup
def load_webpage(url):
"""加载网页并返回纯文本"""
response = requests.get(url)
soup = BeautifulSoup(response.content, 'html.parser')
# 移除脚本和样式
for tag in soup(['script', 'style']):
tag.decompose()
text = soup.get_text()
return text.strip()
# 使用示例(真实可访问的网页)
text = load_webpage('https://www.python.org')
print(f"内容长度: {len(text)} 字符")
print(f"内容预览: {text[:200]}...")# 安装:pip install pandas openpyxl
import pandas as pd
def load_excel(file_path):
"""加载Excel并转为文本"""
df = pd.read_excel(file_path)
# 方式1:整表转文本
text = df.to_string(index=False)
# 方式2:按行转文本(可选)
rows = []
for _, row in df.iterrows():
row_text = ' | '.join(str(v) for v in row)
rows.append(row_text)
return text
# 使用示例(使用下载的示例文件)
text = load_excel('data/主数据表.xlsx')
print(f"表格内容:\n{text[:300]}...")# 无需安装额外库
def load_text(file_path):
"""加载文本文件"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
return f.read()
except UnicodeDecodeError:
# 尝试其他编码
with open(file_path, 'r', encoding='gbk') as f:
return f.read()
# 使用示例(使用下载的示例文件)
text = load_text('data/article.txt')
print(f"文件长度: {len(text)} 字符")
print(f"内容预览: {text[:200]}...")文档分割(Chunking)是RAG系统中最关键的环节之一。分割得好,检索准确;分割不好,答案就会不准确或不完整。
按照固定的字符数或token数切分文档,并设置重叠区域。
def split_text_fixed(text, chunk_size=10, overlap=5):
"""固定长度分割,带重叠"""
chunks = []
start = 0
while start < len(text):
end = start + chunk_size
chunk = text[start:end]
chunks.append(chunk)
# 下一个chunk从(end - overlap)开始
start = end - overlap
return chunks按照段落、句子等自然语义边界切分,保持语义完整性。
def split_text_semantic(text, min_chunk_size=10):
"""语义分割:按段落分割"""
# 按双换行符分割段落
paragraphs = text.split('\n\n')
chunks = []
current_chunk = ""
for para in paragraphs:
if len(current_chunk) + len(para) <= min_chunk_size:
current_chunk += para + "\n\n"
else:
if current_chunk:
chunks.append(current_chunk.strip())
current_chunk = para + "\n\n"
if current_chunk:
chunks.append(current_chunk.strip())
return chunks向量化是将分割好的文本块转换成计算机能理解的数字向量。这是RAG系统的核心技术。
问题:计算机只认识0和1,无法理解"苹果"、"水果"这些词的含义。
解决:通过向量化,把"苹果"转换成 [0.2, 0.8, 0.3, ...],计算机就能处理了。
"我喜欢吃苹果" → 计算机不懂[0.23, 0.81, 0.45, 0.67, ...] → 计算机能处理传统的文字匹配只能找到完全相同的词,而向量化能理解语义。
向量化后的数据可以建立索引,实现毫秒级的相似度搜索。
需要逐个比对每条数据
速度慢,效率低
无法理解语义
通过向量索引快速定位
毫秒级响应
理解语义相似性
向量化是大模型技术栈的核心基础,贯穿整个AI应用流程。
用户输入:"什么是人工智能?"
转换成向量:[0.23, 0.81, 0.45, ..., 0.67] (1536维)
在知识库中找到最相关的文档(RAG技术)
基于检索到的知识,生成准确的回答
将用户查询向量化,在向量数据库中找到最相关的内容。 比传统关键词搜索更智能。
向量化企业知识库,让大模型能够检索和引用专业知识, 避免幻觉问题。
将用户行为和商品信息向量化,计算相似度实现个性化推荐。
向量化常见问题库,快速匹配用户问题并给出最佳答案。
[0.234, 0.812, 0.456, 0.678, 0.123, ..., 0.891]
# 通常是512维、768维或1536维虽然实际向量有几百上千维,但我们可以用2D坐标系来直观理解。语义相近的词在空间中距离更近!
向量还支持数学运算,可以进行语义推理:
现在你已经理解了向量化的概念和作用,让我们看看如何在实际中使用它。
Embedding模型本质上是一个深度神经网络,它通过大量文本数据的训练,学会了将文本映射到高维向量空间。 这个过程类似于"压缩"文本的语义信息到一个固定长度的数字序列中。
虽然我们无法直接解释每个维度的具体含义,但可以理解为:
💡 关键:这些维度是模型自动学习的,不是人工设计的!这就是深度学习的魔力。
检索增强生成(RAG)是当前最热门的大模型应用技术。它的核心流程是:
用户提问 → 向量化 → 检索相关文档 → 生成答案
Chroma、Pinecone、FAISS等,支持高效相似度搜索
余弦相似度、欧几里得距离等,衡量语义相关性
向量索引、缓存机制、批量处理提升响应速度
权限控制、数据脱敏、审计日志保障数据安全
没有向量化,就无法实现语义检索,RAG技术也就无从谈起。
大模型经常"一本正经地胡说八道",通过向量化+知识库检索,可以让AI基于真实数据回答:
相比于微调大模型(需要大量数据和算力),向量化+检索的方案:
只需调用Embedding API,无需训练模型
新增文档即时生效,无需重新训练
向量化不仅适用于文本,还能处理图片、音频、视频:
场景:用户咨询产品问题
流程:
✅ 效果:准确率90%+,响应时间<1秒
场景:员工查询内部文档
流程:
✅ 效果:节省80%查找时间,提高工作效率
场景:商品个性化推荐
流程:
✅ 效果:点击率提升30%,转化率提升20%
场景:新闻网站去除重复文章
流程:
✅ 效果:自动识别95%重复内容
场景:律师查找相似案例
流程:
✅ 效果:检索时间从数小时降至数秒
场景:学生学习辅导
流程:
✅ 效果:学习效率提升50%
和第4章一样,我们使用国内可用的API来实现向量化。这里介绍通义千问的Embedding服务。
💡 根据你的Python版本选择命令:
# Python 3.x (推荐)
pip3 install dashscope
# 或者使用 pip
pip install dashscopeimport dashscope
from dashscope import TextEmbedding
import os
# 设置API Key(从环境变量读取)
dashscope.api_key = os.getenv('DASHSCOPE_API_KEY')
# 生成文本向量
response = TextEmbedding.call(
model='text-embedding-v2', # 通义千问的Embedding模型
input='人工智能正在改变世界'
)
# 获取向量
embedding = response.output['embeddings'][0]['embedding']
print(f"向量维度: {len(embedding)}") # 1536维
print(f"向量前5个值: {embedding[:5]}")使用余弦相似度来衡量两个向量的相似程度,值越接近1表示越相似。
余弦相似度本质上是在比较两个向量的方向是否一致(夹角是否小),而不是比较它们的长度。
cos_sim(a, b) = (a · b) / (||a|| * ||b||)cos_sim(a,b)=a·b。把向量想象成“指向某个方向的箭头”。 余弦相似度主要关心的是两支箭头的方向是否一致,而不是长度。
为了方便手算,我们用 2 维向量举例(真实 Embedding 往往是 768/1536 维,但计算逻辑完全一样)。
# 假设两个向量(2维)
v1 = [3, 4]
v2 = [6, 8]v1 · v2 = 3*6 + 4*8 = 18 + 32 = 50|v1| = sqrt(3^2 + 4^2) = sqrt(9 + 16) = 5
|v2| = sqrt(6^2 + 8^2) = sqrt(36 + 64) = 10cos_sim(v1, v2) = (v1 · v2) / (|v1| * |v2|)
= 50 / (5 * 10)
= 1.0结果为 1.0,说明 v2 与 v1 方向完全一致(只是长度放大了)。
下面用一个简单 2D 图展示:两支向量夹角 θ 越小,cos(θ) 越接近 1。
numpy 是Python中用于科学计算的基础库,提供了强大的数组和矩阵运算功能。 我们需要用它来计算向量的相似度。
# 安装numpy(根据Python版本选择)
pip3 install numpy
# 或
pip install numpy💡 提示:如果你使用Anaconda或Jupyter,numpy通常已经预装了。
from dashscope import TextEmbedding # 从dashscope导入Embedding模型
import numpy as np # 导入numpy库,简写为np
def cosine_similarity(vec1, vec2):
"""计算两个向量的余弦相似度"""
return np.dot(vec1, vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2))
if __name__ == "__main__":
# 生成两个文本的向量
text1 = "机器学习是人工智能的一个分支"
text2 = "深度学习属于机器学习领域"
# 向量化文本1
response1 = TextEmbedding.call(
model='text-embedding-v2',
input=text1
)
vec1 = response1.output['embeddings'][0]['embedding']
# 向量化文本2
response2 = TextEmbedding.call(
model='text-embedding-v2',
input=text2
)
vec2 = response2.output['embeddings'][0]['embedding']
# 计算相似度
similarity = cosine_similarity(vec1, vec2)
print(f"'{text1}' 和 '{text2}' 的相似度: {similarity:.4f}")
# 输出:相似度: 0.8523(非常相似!)如果需要处理多个文本,可以批量调用,提高效率。
from dashscope import TextEmbedding
import numpy as np
def cosine_similarity(vec1, vec2):
"""计算两个向量的余弦相似度"""
return np.dot(vec1, vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2))
if __name__ == "__main__":
# 批量向量化多个文本
texts = [
"人工智能",
"机器学习",
"深度学习",
"神经网络"
]
response = TextEmbedding.call(
model='text-embedding-v2',
input=texts # 直接传入列表
)
# 获取所有向量
embeddings = [item['embedding'] for item in response.output['embeddings']]
print(f"成功向量化 {len(embeddings)} 个文本")
# 计算所有文本之间的相似度矩阵
for i, text1 in enumerate(texts):
for j, text2 in enumerate(texts):
if i < j: # 只计算上三角
sim = cosine_similarity(embeddings[i], embeddings[j])
print(f"'{text1}' ↔ '{text2}': {sim:.4f}")将生成的向量和原始文本存储到专门的向量数据库中,以便快速检索。
普通数据库(如MySQL)只能做精确匹配,无法计算向量相似度。 向量数据库专门为相似度检索优化,能快速找到最相似的向量。
Chroma是最简单易用的向量数据库,支持两种使用方式:
Chroma内置embedding模型,无需额外安装,开箱即用
使用通义千问、OpenAI等外部embedding,质量更高
最简单的方式,Chroma自带embedding模型,无需额外配置:
# 安装:pip install chromadb
import chromadb
# 1. 创建客户端
client = chromadb.Client()
# 2. 创建集合(使用默认embedding函数)
collection = client.create_collection("my_documents")
# 3. 添加文档(自动向量化,无需手动embedding)
collection.add(
documents=[
"Python是一种编程语言",
"机器学习是人工智能的分支",
"深度学习使用神经网络"
],
ids=["doc1", "doc2", "doc3"]
)
# 4. 查询相似文档(自动embedding查询文本)
results = collection.query(
query_texts=["什么是AI?"],
n_results=2
)
print(results)Client vs PersistentClient(一定要分清)
chromadb.Client():默认是内存模式(进程内)。适合快速演示与临时实验;程序退出后数据一般不会保留。chromadb.PersistentClient(path="..."):是本地持久化模式(写到磁盘目录)。适合开发/单机部署:重启程序后还能继续使用之前的向量库。chromadb.HttpClient(host, port) 连接远程服务,这时就需要提供 host/IP。生产环境通常建议使用高质量的外部 Embedding 服务(例如通义千问、OpenAI 等),以获得更稳定、更好的检索效果。 下面以阿里云百炼(DashScope / 通义千问 Embedding)为例,按步骤做到“照着就能跑”。
关键澄清:调用 Embedding 接口时不需要传你的本机 IP。 你需要传的是:model(模型名) + input(文本或文本列表)。 SDK 会通过 HTTPS 访问官方服务域名。 只有在公司网络需要走代理/网关时,才会涉及“把请求转发到某个 IP/域名”。
# 建议在虚拟环境中安装(venv/conda 都可以)
# 基础依赖
pip install chromadb dashscope
# 如果你后面需要读取 Excel(第1章加载Excel示例用到)
pip install pandas openpyxl到阿里云百炼控制台创建 API Key(建议按环境区分:dev/test/prod)。 不要把 Key 写死在代码里,用环境变量更安全。
# macOS / Linux
export DASHSCOPE_API_KEY="你的key"
# Windows PowerShell
$env:DASHSCOPE_API_KEY="你的key"import os
import dashscope
from dashscope import TextEmbedding
dashscope.api_key = os.getenv("DASHSCOPE_API_KEY")
if not dashscope.api_key:
raise RuntimeError("请先设置环境变量 DASHSCOPE_API_KEY")
def embed_texts(texts):
"""输入:str 或 List[str];输出:List[List[float]]"""
if isinstance(texts, str):
texts = [texts]
resp = TextEmbedding.call(
model="text-embedding-v2",
input=texts
)
# 失败时给出明确报错,便于定位(Key/配额/网络)
if not resp or not hasattr(resp, "output"):
raise RuntimeError(f"Embedding 调用失败:{resp}")
embeddings = [e["embedding"] for e in resp.output["embeddings"]]
return embeddings
vec = embed_texts("人工智能正在改变世界")[0]
print(len(vec), vec[:8])
batch = embed_texts(["机器学习", "深度学习", "自然语言处理"])
print(len(batch), len(batch[0]))关键点:向量库在写入与查询必须使用同一种 embedding(同模型/同参数/同预处理),否则相似度没有意义。
import os
import chromadb
import dashscope
from dashscope import TextEmbedding
dashscope.api_key = os.getenv("DASHSCOPE_API_KEY")
if not dashscope.api_key:
raise RuntimeError("请先设置环境变量 DASHSCOPE_API_KEY")
class DashScopeEmbeddingFunction:
def __init__(self, model: str = "text-embedding-v2"):
self.model = model
def name(self) -> str:
return f"dashscope:{self.model}"
def _embed(self, texts):
if isinstance(texts, str):
texts = [texts]
resp = TextEmbedding.call(model=self.model, input=texts)
if not resp or not hasattr(resp, "output"):
raise RuntimeError(f"Embedding 调用失败:{resp}")
return [e["embedding"] for e in resp.output["embeddings"]]
def embed_documents(self, input):
return self._embed(input)
def embed_query(self, input):
return self._embed(input)
def __call__(self, input):
return self.embed_documents(input)
# 1) 创建客户端(开发阶段内存即可;生产建议 PersistentClient)
client = chromadb.Client()
# 2) 创建集合(关键:绑定 embedding_function)
collection = client.get_or_create_collection(
name="my_documents",
embedding_function=DashScopeEmbeddingFunction()
)
# 3) 写入(会自动调用 dashscope_embedding_function 做向量化)
collection.add(
documents=[
"人工智能(AI)是计算机科学的一个分支",
"机器学习是人工智能的重要方向",
"深度学习使用神经网络进行特征学习"
],
ids=["doc1", "doc2", "doc3"],
metadatas=[
{"source": "demo", "type": "txt"},
{"source": "demo", "type": "txt"},
{"source": "demo", "type": "txt"}
]
)
# 4) 查询(同样会自动向量化 query_texts 并做相似检索)
results = collection.query(
query_texts=["AI 包括哪些方向?"],
n_results=2
)
print(results)DASHSCOPE_API_KEY 是否生效
前面示例使用的是“嵌入式/本地 Chroma”(chromadb.Client() / PersistentClient(path=...))。
在生产环境,常见做法是把 Chroma 作为独立的向量数据库服务部署(类似 MySQL/Redis 的部署方式)。
此时应用需要连接到远程服务地址,也就是你说的:要提供 host / 端口(可能是 IP)。
你需要“传 IP”的场景是:连接向量库(Chroma Server)。
你不需要“传 IP”的场景是:调用 DashScope Embedding(默认访问官方域名;除非公司要求走代理/网关)。
# 拉起一个 Chroma Server(示例端口 8000)
# 注意:正式生产通常会加反向代理、鉴权、持久化卷等
docker run --rm -p 8000:8000 \
-v $(pwd)/chroma_data:/chroma/chroma \
chromadb/chroma:latest如果你所在环境不方便装 Docker(或希望更轻量),可以直接用 Python 安装并启动 Chroma Server。 这种方式适合开发机、内网单机服务;规模化生产建议仍以 Docker/K8s 为主。
关键点:一定要指定一个持久化数据目录(例如 ./chroma_data),否则重启后向量库可能丢失。
# 1) 创建虚拟环境(推荐)
python3 -m venv .venv
source .venv/bin/activate
# 2) 安装 ChromaDB
pip install -U chromadb
# 3) 创建数据目录(持久化)
mkdir -p ./chroma_data
# 4) 启动 Server(不同版本参数可能略有差异,以 chroma run --help 为准)
chroma run --host 0.0.0.0 --port 8000 --path ./chroma_data# 1) 创建虚拟环境(PowerShell)
python -m venv .venv
.venv\Scripts\Activate.ps1
# 2) 安装 ChromaDB
pip install -U chromadb
# 3) 创建数据目录(持久化)
mkdir chroma_data
# 4) 启动 Server
chroma run --host 0.0.0.0 --port 8000 --path .\chroma_dataWindows 提示: 如果遇到依赖编译/环境问题,更推荐在 WSL2(Ubuntu)里按 Linux 方式安装运行。
# 方式1:看端口是否在监听(macOS/Linux)
lsof -i:8000
# 方式2:Windows
netstat -ano | findstr :8000
你也可以直接运行下面的“应用端连接示例”(chromadb.HttpClient),能正常创建/查询 collection 就说明服务可用。
chroma 命令,检查是否激活了虚拟环境,或执行 python -m pip show chromadb 确认安装成功。8000 被占用就换端口,例如 --port 18000,应用侧 CHROMA_PORT 要同步。./chroma_data 可写。import os
import chromadb
# 远程 Chroma Server 地址(生产环境一般是内网 IP / 域名 / K8s Service)
CHROMA_HOST = os.getenv("CHROMA_HOST", "127.0.0.1")
CHROMA_PORT = int(os.getenv("CHROMA_PORT", "8000"))
# 连接远程服务:这一步就是你说的“要传 IP/host”的地方
client = chromadb.HttpClient(host=CHROMA_HOST, port=CHROMA_PORT)
collection = client.get_or_create_collection(name="my_documents")
collection.add(
documents=["hello chroma"],
ids=["doc1"],
)
res = collection.query(query_texts=["hello"], n_results=1)
print(res)
初学者:先用内置embedding快速上手,理解RAG流程
进阶项目:切换到通义千问embedding,提升检索质量
生产环境:建议使用外部embedding,配合持久化存储
Chroma会自动找到与"什么是AI?"最相关的文档:
{
'documents': [
['机器学习是人工智能的分支', '深度学习使用神经网络']
],
'distances': [[0.45, 0.52]]
}一个完整的向量数据库需要支持以下核心功能:
使用特殊的索引结构(如HNSW、IVF)加速向量检索,将查询时间从O(n)降低到O(log n)
支持多种相似度算法:余弦相似度、欧氏距离、点积等,根据场景选择最合适的算法
除了向量,还可以存储和过滤元数据(如时间、分类、标签),实现混合检索
支持数据持久化到磁盘,重启后数据不丢失,适合生产环境使用
除了基础的存储和查询,Chroma还支持更多高级功能:
# 1. 持久化存储(数据保存到磁盘)
import chromadb
# 创建持久化客户端
client = chromadb.PersistentClient(path="./chroma_db")
collection = client.get_or_create_collection("my_docs")
# 2. 添加带元数据的文档
collection.add(
documents=["Python是一种编程语言", "机器学习很有趣"],
metadatas=[
{"category": "编程", "difficulty": "初级"},
{"category": "AI", "difficulty": "中级"}
],
ids=["doc1", "doc2"]
)
# 3. 使用元数据过滤查询
results = collection.query(
query_texts=["学习编程"],
n_results=5,
where={"category": "编程"} # 只查询编程类文档
)
# 4. 更新文档
collection.update(
ids=["doc1"],
documents=["Python是一种强大的编程语言"]
)
# 5. 删除文档
collection.delete(ids=["doc2"])轻量级向量数据库(推荐初学者)
pip3 install chromadb💡 适合场景:学习、原型开发、小型项目(10万条以内)
云端向量数据库(推荐生产环境)
pip3 install pinecone-client💡 适合场景:生产环境、大型企业、需要高可用性的项目
开源向量数据库(GraphQL支持)
pip3 install weaviate-client💡 适合场景:需要GraphQL、混合搜索的中型项目
高性能向量数据库(Rust开发)
pip3 install qdrant-client💡 适合场景:对性能要求高、需要私有化部署的项目
企业级向量数据库(支持分布式)
pip3 install pymilvus💡 适合场景:大型企业、海量数据(亿级以上)、需要分布式部署
💡 我的建议:初学者先用Chroma掌握概念,项目大了再根据需求升级到Qdrant或Pinecone。
现在我们来做一个完整的RAG存储阶段实战,将前面学的加载→分割→向量化→存储四个步骤串联起来:
# 1) 建议:使用虚拟环境(强烈推荐)
python3 -m venv .venv
source .venv/bin/activate
# 2) 清理掉可能的脏安装(在虚拟环境里执行)
python -m pip uninstall -y chromadb
python -m pip cache purge
# 3) 重新安装(如果你是 Python3.12,优先用较新的 chromadb 版本;如果仍不稳,建议换 Python3.11)
python -m pip install -U pip
python -m pip install "chromadb>=0.5"# 完整RAG存储阶段实战示例
# 安装依赖:pip install "chromadb>=0.5" dashscope pypdf beautifulsoup4 pandas openpyxl
import chromadb
import dashscope
from dashscope import TextEmbedding
import pypdf
from bs4 import BeautifulSoup
import requests
import pandas as pd
from typing import List
import re
import os
# 配置API密钥
dashscope.api_key = os.getenv("DASHSCOPE_API_KEY", "")
if not dashscope.api_key:
raise RuntimeError("请先设置环境变量 DASHSCOPE_API_KEY,然后再运行")
class RAGStoragePipeline:
"""RAG存储阶段完整流程"""
def __init__(self, db_path: str = "./rag_db"):
"""初始化RAG存储管道"""
self.db_path = db_path
self.client = chromadb.PersistentClient(path=db_path)
self.collection = None
def load_documents(self, file_paths: List[str]) -> List[str]:
"""1️⃣ 加载文档"""
print("🔄 开始加载文档...")
documents = []
for file_path in file_paths:
if file_path.endswith('.pdf'):
# 加载PDF文件
content = self._load_pdf(file_path)
documents.append(content)
print(f"✅ 已加载PDF: {file_path}")
elif file_path.endswith('.txt'):
# 加载文本文件
content = self._load_txt(file_path)
documents.append(content)
print(f"✅ 已加载TXT: {file_path}")
elif file_path.startswith('http'):
# 加载网页内容
content = self._load_webpage(file_path)
documents.append(content)
print(f"✅ 已加载网页: {file_path}")
print(f"🎉 共加载 {len(documents)} 个文档")
return documents
def _load_pdf(self, file_path: str) -> str:
"""加载PDF文件"""
with open(file_path, 'rb') as file:
reader = pypdf.PdfReader(file)
text = ""
for page in reader.pages:
text += page.extract_text()
return text
def _load_txt(self, file_path: str) -> str:
"""加载文本文件"""
with open(file_path, 'r', encoding='utf-8') as file:
return file.read()
def _load_webpage(self, url: str) -> str:
"""加载网页内容"""
response = requests.get(url)
soup = BeautifulSoup(response.content, 'html.parser')
# 移除脚本和样式
for script in soup(["script", "style"]):
script.decompose()
return soup.get_text()
def split_documents(self, documents: List[str], chunk_size: int = 500, overlap: int = 50) -> List[str]:
"""2️⃣ 分割文档"""
print("🔄 开始分割文档...")
chunks = []
for doc in documents:
# 清理文本
doc = re.sub(r'\s+', ' ', doc.strip())
# 固定长度分割
for i in range(0, len(doc), chunk_size - overlap):
chunk = doc[i:i + chunk_size]
if len(chunk.strip()) > 50: # 过滤太短的片段
chunks.append(chunk.strip())
print(f"🎉 共分割出 {len(chunks)} 个文档块")
return chunks
def create_embedding_function(self):
"""3️⃣ 创建向量化函数"""
class DashScopeEmbeddingFunction:
"""符合 Chroma v0.5+ 的 EmbeddingFunction 协议(必须有 name())"""
def name(self):
return "dashscope-text-embedding-v2"
def _embed_texts(self, texts):
if isinstance(texts, str):
texts = [texts]
embeddings = []
for text in texts:
response = TextEmbedding.call(
model='text-embedding-v2',
input=text
)
embeddings.append(response.output['embeddings'][0]['embedding'])
return embeddings
def __call__(self, input):
return self._embed_texts(input)
def embed_documents(self, documents):
return self._embed_texts(documents)
def embed_query(self, input):
return self._embed_texts(input)
return DashScopeEmbeddingFunction()
def store_documents(self, chunks: List[str], collection_name: str = "rag_documents"):
"""4️⃣ 存储文档到向量数据库"""
print("🔄 开始存储文档...")
# 创建embedding函数
embedding_function = self.create_embedding_function()
# 创建集合
# 注意:如果你之前用同名 collection 创建过(embedding_function 不同),Chroma 会做冲突校验并报错。
# 解决办法:换一个 collection_name,或者删除/更换持久化目录(db_path)。
self.collection = self.client.get_or_create_collection(
name=collection_name,
embedding_function=embedding_function
)
# 生成文档ID
doc_ids = [f"doc_{i}" for i in range(len(chunks))]
# 添加元数据
metadatas = [
{
"source": f"chunk_{i}",
"length": len(chunk),
"created_at": "2024-01-01"
}
for i, chunk in enumerate(chunks)
]
# 批量添加文档
self.collection.add(
documents=chunks,
ids=doc_ids,
metadatas=metadatas
)
print(f"🎉 成功存储 {len(chunks)} 个文档块到向量数据库")
def run_complete_pipeline(self, file_paths: List[str], collection_name: str = "rag_documents"):
"""运行完整的RAG存储管道"""
print("🚀 开始RAG存储阶段完整流程...")
print("=" * 50)
# 1️⃣ 加载文档
documents = self.load_documents(file_paths)
# 2️⃣ 分割文档
chunks = self.split_documents(documents)
# 3️⃣ 向量化 + 4️⃣ 存储
self.store_documents(chunks, collection_name)
print("=" * 50)
print("🎉 RAG存储阶段完成!")
print(f"📊 数据库路径: {self.db_path}")
print(f"📋 集合名称: {collection_name}")
print(f"📄 文档块数量: {len(chunks)}")
return self.collection
# 使用示例
if __name__ == "__main__":
# 创建RAG存储管道
rag_pipeline = RAGStoragePipeline(db_path="./my_rag_db")
# 准备文档路径(可以是PDF、TXT、网页URL)
document_sources = [
"../data/sample_document.pdf", # 本地PDF文件
"../data/article.txt", # 本地文本文件
"https://www.python.org", # Python官网
]
# 运行完整流程
collection = rag_pipeline.run_complete_pipeline(
file_paths=document_sources,
collection_name="python_docs"
)
# 测试查询
print("\n🔍 测试查询功能...")
results = collection.query(
query_texts=["什么是Python?"],
n_results=3
)
print("查询结果:")
for i, (doc, metadata, distance) in enumerate(zip(
results['documents'][0],
results['metadatas'][0],
results['distances'][0]
), 1):
print(f"\n{i}. 相似度: {1 - distance:.4f}")
print(f" 文档: {doc[:100]}...")
print(f" 元数据: {metadata}")🚀 开始RAG存储阶段完整流程...
==================================================
🔄 开始加载文档...
✅ 已加载PDF: data/sample_document.pdf
✅ 已加载TXT: data/article.txt
✅ 已加载网页: https://www.python.org
🎉 共加载 3 个文档
🔄 开始分割文档...
🎉 共分割出 15 个文档块
🔄 开始存储文档...
🎉 成功存储 15 个文档块到向量数据库
==================================================
🎉 RAG存储阶段完成!
📊 数据库路径: ./my_rag_db
📋 集合名称: python_docs
📄 文档块数量: 15
🔍 测试查询功能...
查询结果:
1. 相似度: 0.8756
文档: Python是一种解释型、高级编程语言...
元数据: {'source': 'chunk_3', 'length': 485, 'created_at': '2024-01-01'}完成了RAG存储阶段的学习后,下一章我们将学习RAG查询阶段!
第7章将带你实现完整的RAG系统,包括:
请认真完成以下练习,巩固RAG存储阶段核心知识
⚠️ 重要提示:这些题目涵盖了面试常见考点,请务必掌握!
1. 什么是RAG?请说明RAG存储阶段的四个核心步骤。
2. 文档分割为什么重要?列举至少3种分割策略并说明其适用场景。
3. 对比Chroma、Pinecone、Weaviate三种向量数据库的优缺点和适用场景。
1. 【场景题】在一个企业级知识库项目中,你会如何设计RAG存储架构?请考虑数据量、性能、成本等因素。
2. 【技术题】如何优化RAG系统的检索精度?请从文档处理、向量化、索引三个层面说明。
3. 【实战题】给定一个包含1000份PDF文档的企业知识库,请设计一个完整的RAG存储方案,包括技术选型和实现步骤。
🎯 编程挑战:实现一个支持多种文档格式的RAG存储系统
要求:
💡 提示: