← 返回上一页

第6章: 私有知识库搭建:文档切分、向量化与入库

数据加载、分割、向量化与存储

🎯 为什么需要RAG?

❌ 问题1:知识截止(Knowledge Cutoff)

场景:你问ChatGPT:"2024年的诺贝尔奖得主是谁?"

回答:"抱歉,我的知识截止到2023年9月..."

原因:大模型的知识来自训练数据,训练完成后就"冻结"了,无法获取最新信息。

RAG解决方案:从最新的新闻数据库检索2024年的信息,让AI基于最新数据回答!

❌ 问题2:AI幻觉(Hallucination)

场景:你问:"我们公司的请假制度是什么?"

AI回答:"根据常见做法,年假通常是10天..."(但你们公司可能是15天!)

原因:AI没有你公司的专业知识,只能根据通用数据"猜测",容易编造不准确的信息。

RAG解决方案:从公司员工手册检索真实的请假制度,AI基于真实文档回答!

❌ 问题3:缺乏专业知识

场景:医生问:"这个罕见病的最新治疗方案是什么?"

AI回答:给出的是通用信息,不够专业和详细

原因:通用大模型无法覆盖所有专业领域的深度知识。

RAG解决方案:从医学文献数据库检索最新研究,提供专业准确的答案!

🎯 RAG的核心优势

✅ 知识实时更新

随时添加最新信息

✅ 减少幻觉

基于真实文档回答

✅ 降低成本

无需重新训练模型

✅ 可追溯来源

知道答案来自哪里

🤔 什么是RAG?

RAG(Retrieval-Augmented Generation,检索增强生成)是当前最热门的大模型应用技术。 它通过从外部知识库检索相关信息,然后将这些信息作为上下文提供给大模型,让AI基于真实知识回答问题。

🗺️ RAG 工作原理(一张图看懂端到端链路)

读图方式:从左到右,先“检索增强”,再“调用大模型生成”,最后把结果返回给用户。

离线链路:知识库准备(存储阶段) 文档 加载 分割 向量化 向量库/索引 产物:向量数据库 + 检索器(retriever) 在线链路:问答(检索 → 生成 → 加工 → 返回) 用户问题 Query Rewrite (可选) 检索 读取向量库 组装上下文 生成回答 大模型加工 返回用户 要点:检索发生在“向量库/索引”侧;后端负责把 chunks 组装成上下文;生成后可做二次加工再返回用户。
RAG 的作用
把“查到的资料(chunks)”塞进 Prompt,让大模型基于资料回答,减少幻觉、可追溯。
读者常见误区
RAG 不是“把知识训练进模型”。它更像“先检索再回答”,知识库更新后立刻生效。

💡 核心概念

RAG = 检索(Retrieval) + 增强(Augmented) + 生成(Generation)

简单理解:就像考试时可以"翻书"!AI不再只靠记忆(训练数据),而是可以查阅"参考书"(知识库)来回答问题。

🔄 RAG存储阶段:四个关键步骤

要让RAG系统工作,首先需要完成存储阶段的准备工作。 这个阶段是一次性的,将你的知识库数据处理成可检索的向量形式。

📂 1. 加载 ✂️ 2. 分割 🔢 3. 向量化 💾 4. 存储

💡 重要提示:存储阶段是一次性准备工作,完成后就可以反复使用。 接下来我们将详细讲解每个步骤的原理和实现方法。

1️⃣ 加载 (Loading)

这是RAG的第一步,目的是将各种格式的知识源(Knowledge Base)读取到系统中,转换为纯文本格式。

📄 支持的数据源

📑
非结构化数据

PDF, Word, TXT

🌐
网络数据

网页, API, 爬虫

💾
结构化数据

Excel, SQL, JSON

💬
代码/对话

Python, Chat Logs

💡 提示: 无论原始格式是什么,最终都需要转换为纯文本格式才能进行后续处理。

💻 加载方法示例

下面是几种常见数据源的完整加载代码,可以直接运行。我们提供了示例数据文件供你练习:

📦 下载示例数据文件

请下载以下示例文件,并放在你的项目的 data/ 目录下:

📑
sample_document.pdf
示例PDF文档
📊
主数据表.xlsx
示例Excel表格
📝
article.txt
示例文本文件

💡 使用方法:下载后放在项目根目录的 data/ 文件夹下,代码中使用 'data/sample_document.pdf' 即可加载

📑 PDF文档

python
# 安装: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]}...")

🌐 网页内容

python
# 安装: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]}...")

📊 Excel表格

python
# 安装: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]}...")

📝 文本文件

python
# 无需安装额外库
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]}...")

2️⃣ 分割 (Splitting)

文档分割(Chunking)是RAG系统中最关键的环节之一。分割得好,检索准确;分割不好,答案就会不准确或不完整。

🤔 为什么需要分割文档?

❌ 不分割的问题
  • 整篇文档太长,超过模型上下文限制
  • 无关信息太多,干扰答案生成
  • 检索不精准,找不到关键信息
  • 成本高(token消耗大)
✅ 分割的好处
  • 每个chunk包含完整的语义单元
  • 检索更精准,找到最相关部分
  • 控制上下文长度,节省成本
  • 提高答案质量和相关性

📊 常见分割策略

1️⃣ 固定长度分割(最常用)

按照固定的字符数或token数切分文档,并设置重叠区域。

可视化示例:
Chunk 1: Python是一种高级编程语言,由Guido van Rossum于1991年创建。Python以其简洁易读的语法而闻名...
↓ 重叠50字符 ↓
Chunk 2: ...简洁易读的语法而闻名。Python支持多种编程范式,包括面向对象编程、函数式编程...
↓ 重叠50字符 ↓
Chunk 3: ...函数式编程和过程式编程。Python广泛应用于数据科学、机器学习...
python
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
📐 参数建议:
  • chunk_size:500-1000字符(中文)
  • overlap:chunk_size的10-20%

2️⃣ 语义分割(推荐)

按照段落、句子等自然语义边界切分,保持语义完整性。

python
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
✅ 优点: 保持语义完整性,不会切断句子

3️⃣ 向量化 (Embedding)

向量化是将分割好的文本块转换成计算机能理解的数字向量。这是RAG系统的核心技术。

🤔 为什么需要向量化?

1️⃣ 计算机无法直接理解文字

问题:计算机只认识0和1,无法理解"苹果"、"水果"这些词的含义。

解决:通过向量化,把"苹果"转换成 [0.2, 0.8, 0.3, ...],计算机就能处理了。

示例对比:
❌ 文字:"我喜欢吃苹果" → 计算机不懂
✅ 向量:[0.23, 0.81, 0.45, 0.67, ...] → 计算机能处理

2️⃣ 捕捉语义相似性

传统的文字匹配只能找到完全相同的词,而向量化能理解语义。

实际例子:
用户搜索:"如何学习Python?"
传统搜索:只能找到包含"学习"和"Python"的文章
向量搜索:还能找到"Python入门教程"、"Python编程指南"等相关内容

3️⃣ 支持高效检索

向量化后的数据可以建立索引,实现毫秒级的相似度搜索。

传统搜索

需要逐个比对每条数据
速度慢,效率低
无法理解语义

向量搜索

通过向量索引快速定位
毫秒级响应
理解语义相似性

🎯 向量在大模型中的作用

向量化是大模型技术栈的核心基础,贯穿整个AI应用流程。

📊 大模型技术栈中的向量流程

1
输入文本

用户输入:"什么是人工智能?"

2
向量化(Embedding)

转换成向量:[0.23, 0.81, 0.45, ..., 0.67] (1536维)

3
向量搜索

在知识库中找到最相关的文档(RAG技术)

4
大模型生成

基于检索到的知识,生成准确的回答

🔍 语义搜索

将用户查询向量化,在向量数据库中找到最相关的内容。 比传统关键词搜索更智能。

📚 RAG检索增强

向量化企业知识库,让大模型能够检索和引用专业知识, 避免幻觉问题。

🎯 推荐系统

将用户行为和商品信息向量化,计算相似度实现个性化推荐。

🤖 智能客服

向量化常见问题库,快速匹配用户问题并给出最佳答案。

📐 向量的直观理解

向量就是一串数字

文本:"人工智能"
向量:
text
[0.234, 0.812, 0.456, 0.678, 0.123, ..., 0.891]
# 通常是512维、768维或1536维

📊 向量空间可视化:语义距离

虽然实际向量有几百上千维,但我们可以用2D坐标系来直观理解。语义相近的词在空间中距离更近!

经典示例:性别与权力的向量空间

权力 性别 女性 男性 [0.2, 0.3] [0.8, 0.3] 女王 [0.2, 0.9] 皇帝 [0.8, 0.9] 距离近 距离近 距离远 最远

📏 距离计算结果

✅ 语义相近(距离小)
  • "女" ↔ "女王":距离 = 0.6
  • "男" ↔ "皇帝":距离 = 0.6
  • "女王" ↔ "皇帝":距离 = 0.8
❌ 语义不同(距离大)
  • "女" ↔ "男":距离 = 0.8
  • "女" ↔ "皇帝":距离 = 1.0
  • "男" ↔ "女王":距离 = 1.0

💡 关键发现

  • "女"和"女王"在性别维度相同(都是女性),所以在X轴上位置相同,距离近
  • "男"和"皇帝"在性别维度相同(都是男性),所以在X轴上位置相同,距离近
  • "女王"和"皇帝"在权力维度相同(都是统治者),所以在Y轴上位置相同,距离较近
  • "女"和"男"在性别维度完全相反,距离较远

🎯 向量运算的神奇之处

向量还支持数学运算,可以进行语义推理:

向量("女王") - 向量("女") + 向量("男") ≈ 向量("皇帝")
解释:女王去掉女性特征,加上男性特征,就得到皇帝!
向量("巴黎") - 向量("法国") + 向量("中国") ≈ 向量("北京")
解释:巴黎是法国的首都,那么中国的首都就是北京!

相似的文本,向量也相似

文本1:"机器学习"
向量1:[0.23, 0.81, 0.45, ...]
文本2:"深度学习"
向量2:[0.25, 0.79, 0.48, ...]
✅ 相似度:0.92(非常相似!)
文本3:"香蕉"
向量3:[0.89, 0.12, 0.67, ...]
❌ 与"机器学习"的相似度:0.15(完全不相关)

🎓 Embedding技术详解

现在你已经理解了向量化的概念和作用,让我们看看如何在实际中使用它。

🔬 向量化的技术原理

Embedding模型是如何工作的?

Embedding模型本质上是一个深度神经网络,它通过大量文本数据的训练,学会了将文本映射到高维向量空间。 这个过程类似于"压缩"文本的语义信息到一个固定长度的数字序列中。

📊 训练过程(简化理解)
  1. 大规模文本预训练:模型阅读数十亿条文本,学习词语之间的关系
  2. 上下文理解:通过上下文判断词语的真实含义(如"苹果"是水果还是公司)
  3. 语义编码:将学到的语义知识编码到向量的每个维度中
  4. 相似性保持:确保语义相似的文本在向量空间中距离更近
🎯 向量的每个维度代表什么?

虽然我们无法直接解释每个维度的具体含义,但可以理解为:

  • 某些维度可能编码了"情感"信息(正面/负面)
  • 某些维度可能编码了"主题"信息(科技/体育/娱乐)
  • 某些维度可能编码了"语法"信息(名词/动词)
  • 某些维度可能编码了"领域"信息(医学/法律/金融)

💡 关键:这些维度是模型自动学习的,不是人工设计的!这就是深度学习的魔力。

🌟 向量化在AI应用中的核心地位

为什么向量化如此重要?

1️⃣ RAG技术的基础

检索增强生成(RAG)是当前最热门的大模型应用技术。它的核心流程是:

用户提问 → 向量化 → 检索相关文档 → 生成答案

🔄 RAG完整处理流程图

👤 用户输入
"我们公司的年假制度是怎样的?"
🔢 向量化
第1步:查询向量化
• 使用Embedding模型将用户问题转换为向量
• 向量维度:1536维(通义千问)
• 向量值:[0.12, -0.34, 0.78, ...]
🔍 向量检索
第2步:相似度检索
• 在向量数据库中搜索最相似的文档向量
• 计算余弦相似度:cos(θ) = 0.89
• 返回Top-K个最相关文档片段(通常3-5个)
📝 重排过滤
第3步:结果重排与过滤
• 按相似度分数重新排序
• 过滤低相关性文档(相似度 < 0.7)
• 限制上下文长度(避免超出Token限制)
📋 Prompt构建
第4步:构建增强Prompt
• 系统提示:设定AI角色和回答规则
• 检索到的文档:作为上下文信息
• 用户问题:原始查询内容
🤖 LLM生成
第5步:大模型生成答案
• 调用大语言模型(如通义千问)
• 基于提供的上下文信息生成回答
• 引用来源,确保答案可追溯
💬 返回答案
"根据公司员工手册第3.2节,年假制度为:工作满1年享受10天年假..."
🔧 技术要点
📊 向量数据库

Chroma、Pinecone、FAISS等,支持高效相似度搜索

🎯 相似度算法

余弦相似度、欧几里得距离等,衡量语义相关性

⚡ 性能优化

向量索引、缓存机制、批量处理提升响应速度

🔒 安全考虑

权限控制、数据脱敏、审计日志保障数据安全

没有向量化,就无法实现语义检索,RAG技术也就无从谈起。

2️⃣ 解决大模型的幻觉问题

大模型经常"一本正经地胡说八道",通过向量化+知识库检索,可以让AI基于真实数据回答:

  • ✅ 企业内部文档查询
  • ✅ 产品手册智能问答
  • ✅ 法律条文精准检索
  • ✅ 医学知识库查询
3️⃣ 降低成本,提高效率

相比于微调大模型(需要大量数据和算力),向量化+检索的方案:

💰 成本更低

只需调用Embedding API,无需训练模型

⚡ 更新更快

新增文档即时生效,无需重新训练

4️⃣ 多模态AI的桥梁

向量化不仅适用于文本,还能处理图片、音频、视频:

  • 以图搜图:上传图片,找到相似图片
  • 跨模态检索:用文字搜索图片,用图片搜索文字
  • 视频理解:将视频帧向量化,实现智能剪辑

📋 实际应用场景详解

🔍 智能客服系统

场景:用户咨询产品问题
流程:

  1. 将所有FAQ向量化存入数据库
  2. 用户提问时,问题向量化
  3. 检索最相似的3-5个FAQ
  4. 大模型基于FAQ生成回答

效果:准确率90%+,响应时间<1秒

📚 企业知识库问答

场景:员工查询内部文档
流程:

  1. 将公司文档切分成段落
  2. 每个段落向量化并存储
  3. 员工提问时检索相关段落
  4. AI基于检索结果回答

效果:节省80%查找时间,提高工作效率

🛍️ 电商推荐系统

场景:商品个性化推荐
流程:

  1. 商品描述向量化
  2. 用户浏览历史向量化
  3. 计算用户兴趣向量
  4. 推荐相似商品

效果:点击率提升30%,转化率提升20%

📄 文档去重与聚类

场景:新闻网站去除重复文章
流程:

  1. 所有文章向量化
  2. 计算文章间相似度
  3. 相似度>0.9视为重复
  4. 自动聚类相似主题

效果:自动识别95%重复内容

⚖️ 法律案例检索

场景:律师查找相似案例
流程:

  1. 历史判决书向量化
  2. 输入案情描述
  3. 检索相似案例
  4. 提供判决参考

效果:检索时间从数小时降至数秒

🎓 智能学习助手

场景:学生学习辅导
流程:

  1. 教材内容向量化
  2. 学生提问向量化
  3. 检索相关知识点
  4. 生成个性化讲解

效果:学习效率提升50%

💻 使用国内Embedding API

和第4章一样,我们使用国内可用的API来实现向量化。这里介绍通义千问的Embedding服务。

📦 为什么使用通义千问Embedding?

  • 国内可用:无需翻墙,速度快
  • 中文优化:专门针对中文语义优化
  • 价格便宜:比OpenAI便宜很多
  • 免费额度:新用户有免费tokens

1. 安装SDK

💡 根据你的Python版本选择命令:

text
# Python 3.x (推荐)
pip3 install dashscope

# 或者使用 pip
pip install dashscope

2. 生成向量

python
import 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]}")

3. 计算相似度

使用余弦相似度来衡量两个向量的相似程度,值越接近1表示越相似。

📐 余弦相似度公式怎么理解?

余弦相似度本质上是在比较两个向量的方向是否一致(夹角是否小),而不是比较它们的长度。

text
cos_sim(a, b) = (a · b) / (||a|| * ||b||)
  • a · b(点积):把两个向量对应维度相乘再求和,方向越接近点积越大。
  • ||a||、||b||(向量范数):向量长度(L2范数),用来做“缩放”,让结果不受长度影响。
  • 直观理解:结果等于两个向量夹角的余弦值,夹角越小(方向越一致)相似度越接近 1。
  • 取值范围:理论上在 [-1, 1]。但在文本 embedding 场景中,很多模型向量在分布上更常见的是 [0, 1] 附近(负值较少但并非不可能)。
  • 归一化小技巧:如果你提前对向量做了 L2 归一化(让 ||a||=||b||=1),那么余弦相似度就等价于点积:cos_sim(a,b)=a·b

🧠 生活中的比喻:看“方向像不像”

把向量想象成“指向某个方向的箭头”余弦相似度主要关心的是两支箭头的方向是否一致,而不是长度。

  • 方向几乎一致:相似度接近 1(语义非常接近)
  • 夹角约 90°:相似度接近 0(几乎不相关)
  • 方向相反:相似度接近 -1(语义相反或强冲突)

🧮 手算一个最小例子:一步一步算出余弦相似度

为了方便手算,我们用 2 维向量举例(真实 Embedding 往往是 768/1536 维,但计算逻辑完全一样)。

text
# 假设两个向量(2维)
v1 = [3, 4]
v2 = [6, 8]
第1步:点积(dot product)
text
v1 · v2 = 3*6 + 4*8 = 18 + 32 = 50
第2步:模长(L2范数)
text
|v1| = sqrt(3^2 + 4^2) = sqrt(9 + 16) = 5
|v2| = sqrt(6^2 + 8^2) = sqrt(36 + 64) = 10
第3步:代入公式
text
cos_sim(v1, v2) = (v1 · v2) / (|v1| * |v2|)
               = 50 / (5 * 10)
               = 1.0

结果为 1.0,说明 v2 与 v1 方向完全一致(只是长度放大了)。

📈 图示:夹角越小,相似度越高

下面用一个简单 2D 图展示:两支向量夹角 θ 越小,cos(θ) 越接近 1。

x y v1 v2 θ cos(θ) = (v1·v2) / (|v1||v2|) θ 越小 → cos(θ) 越大 → 越相似

📦 需要安装numpy库

numpy 是Python中用于科学计算的基础库,提供了强大的数组和矩阵运算功能。 我们需要用它来计算向量的相似度。

text
# 安装numpy(根据Python版本选择)
pip3 install numpy
# 或
pip install numpy

💡 提示:如果你使用Anaconda或Jupyter,numpy通常已经预装了。

python
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(非常相似!)

4. 批量向量化(更高效)

如果需要处理多个文本,可以批量调用,提高效率。

python
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}")

4️⃣ 存储 (Storage)

将生成的向量和原始文本存储到专门的向量数据库中,以便快速检索。

🤔 为什么需要向量数据库?

普通数据库(如MySQL)只能做精确匹配,无法计算向量相似度。 向量数据库专门为相似度检索优化,能快速找到最相似的向量。

❌ 普通数据库
  • 只能精确匹配关键词
  • 无法计算向量相似度
  • 检索速度慢(百万级数据)
✅ 向量数据库
  • 语义相似度检索
  • 毫秒级查询(百万级数据)
  • 支持高维向量索引

💻 存储示例(使用Chroma)

Chroma是最简单易用的向量数据库,支持两种使用方式:

🎯 两种使用方式对比

方式一:使用内置Embedding

Chroma内置embedding模型,无需额外安装,开箱即用

方式二:使用外部Embedding

使用通义千问、OpenAI等外部embedding,质量更高

🔵 方式一:使用内置Embedding(推荐初学者)

最简单的方式,Chroma自带embedding模型,无需额外配置:

python
# 安装: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="...")本地持久化模式(写到磁盘目录)。适合开发/单机部署:重启程序后还能继续使用之前的向量库。
  • 生产环境常见做法:把 Chroma 作为独立服务部署(Server),应用用 chromadb.HttpClient(host, port) 连接远程服务,这时就需要提供 host/IP。

✅ 优点

  • 安装简单,只需pip install chromadb
  • 无需API密钥,完全离线使用
  • 代码最简洁,适合快速原型
  • 免费使用,无调用限制

🟣 方式二:使用外部Embedding(推荐生产环境)

生产环境通常建议使用高质量的外部 Embedding 服务(例如通义千问、OpenAI 等),以获得更稳定、更好的检索效果。 下面以阿里云百炼(DashScope / 通义千问 Embedding)为例,按步骤做到“照着就能跑”。

关键澄清:调用 Embedding 接口时不需要传你的本机 IP。 你需要传的是:model(模型名) + input(文本或文本列表)。 SDK 会通过 HTTPS 访问官方服务域名。 只有在公司网络需要走代理/网关时,才会涉及“把请求转发到某个 IP/域名”。

1) 安装/下载依赖(pip)

text
# 建议在虚拟环境中安装(venv/conda 都可以)

# 基础依赖
pip install chromadb dashscope

# 如果你后面需要读取 Excel(第1章加载Excel示例用到)
pip install pandas openpyxl

2) 开通服务并获取 API Key

到阿里云百炼控制台创建 API Key(建议按环境区分:dev/test/prod)。 不要把 Key 写死在代码里,用环境变量更安全。

text
# macOS / Linux
export DASHSCOPE_API_KEY="你的key"

# Windows PowerShell
$env:DASHSCOPE_API_KEY="你的key"

3) 最小可运行:生成 Embedding(单条/批量)

python
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]))

4) 接入 Chroma:用外部 Embedding 统一“写入”和“查询”

关键点:向量库在写入查询必须使用同一种 embedding(同模型/同参数/同预处理),否则相似度没有意义。

python
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)
💡 常见问题排查:
  • Key 无效/未设置:检查 DASHSCOPE_API_KEY 是否生效
  • 网络问题:公司网络需要代理时,优先在系统/环境配置代理或走公司网关
  • 额度/限流:批量调用比单条循环更稳定;必要时加重试与退避

🏢 生产环境:Chroma 作为独立服务部署(这时需要 host/IP)

前面示例使用的是“嵌入式/本地 Chroma”(chromadb.Client() / PersistentClient(path=...))。 在生产环境,常见做法是把 Chroma 作为独立的向量数据库服务部署(类似 MySQL/Redis 的部署方式)。 此时应用需要连接到远程服务地址,也就是你说的:要提供 host / 端口(可能是 IP)

你需要“传 IP”的场景是:连接向量库(Chroma Server)。
你不需要“传 IP”的场景是:调用 DashScope Embedding(默认访问官方域名;除非公司要求走代理/网关)。

A) 快速启动(本地用 Docker 模拟生产)

text
# 拉起一个 Chroma Server(示例端口 8000)
# 注意:正式生产通常会加反向代理、鉴权、持久化卷等

docker run --rm -p 8000:8000 \
  -v $(pwd)/chroma_data:/chroma/chroma \
  chromadb/chroma:latest

B) 非 Docker:本机/服务器用 pip 启动 Chroma Server(适合开发/单机/内网)

如果你所在环境不方便装 Docker(或希望更轻量),可以直接用 Python 安装并启动 Chroma Server。 这种方式适合开发机、内网单机服务;规模化生产建议仍以 Docker/K8s 为主。

关键点:一定要指定一个持久化数据目录(例如 ./chroma_data),否则重启后向量库可能丢失。

B1) macOS / Linux 安装与启动

text
# 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

B2) Windows 安装与启动(PowerShell / 建议 WSL2)

text
# 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_data

Windows 提示: 如果遇到依赖编译/环境问题,更推荐在 WSL2(Ubuntu)里按 Linux 方式安装运行。

B3) 如何验证 Chroma Server 已启动?

text
# 方式1:看端口是否在监听(macOS/Linux)
lsof -i:8000

# 方式2:Windows
netstat -ano | findstr :8000

你也可以直接运行下面的“应用端连接示例”(chromadb.HttpClient),能正常创建/查询 collection 就说明服务可用。

B4) 常见坑(很重要)

  • 命令找不到:如果提示找不到 chroma 命令,检查是否激活了虚拟环境,或执行 python -m pip show chromadb 确认安装成功。
  • 端口冲突:8000 被占用就换端口,例如 --port 18000,应用侧 CHROMA_PORT 要同步。
  • 目录权限:数据目录没有写权限会导致启动后写入失败;确保 ./chroma_data 可写。
  • 网络可达:如果 Chroma 跑在另一台机器,记得放通防火墙/安全组;并用内网 IP/域名访问。

C) 应用端连接远程 Chroma(HTTP Client)

python
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)

C) 网络/安全注意点(生产必看)

  • 不要暴露公网:向量库一般部署在内网;对外通过 API 服务层做鉴权与审计。
  • HTTPS/证书:如果走公网或跨机房,建议在网关/Nginx 层启用 HTTPS。
  • 鉴权:生产需要鉴权(API Key / Token / mTLS),不要裸奔;可用网关统一做认证。
  • 持久化与备份:确保 Chroma 数据目录挂载持久化存储,并定期备份。
  • embedding 一致性:无论本地还是远程,写入与查询必须用同一 embedding。

✅ 优点

  • embedding质量更高,检索效果更好
  • 支持中文优化,适合中文项目
  • 可切换不同模型(OpenAI、通义千问等)
  • 适合生产环境,性能稳定

📊 两种方式详细对比

对比项
内置Embedding
外部Embedding
安装复杂度
⭐⭐⭐⭐⭐ 最简单
⭐⭐⭐ 需要API配置
检索质量
⭐⭐⭐ 一般
⭐⭐⭐⭐⭐ 优秀
使用成本
⭐⭐⭐⭐⭐ 完全免费
⭐⭐⭐ 按量付费
适合场景
学习、原型、小项目
生产环境、中文项目

💡 我的建议

初学者:先用内置embedding快速上手,理解RAG流程
进阶项目:切换到通义千问embedding,提升检索质量
生产环境:建议使用外部embedding,配合持久化存储

✅ 运行结果

Chroma会自动找到与"什么是AI?"最相关的文档:

text
{
    'documents': [
        ['机器学习是人工智能的分支', '深度学习使用神经网络']
    ],
    'distances': [[0.45, 0.52]]
}

� 向量数据库的核心功能

一个完整的向量数据库需要支持以下核心功能:

1️⃣ 向量索引

使用特殊的索引结构(如HNSW、IVF)加速向量检索,将查询时间从O(n)降低到O(log n)

2️⃣ 相似度计算

支持多种相似度算法:余弦相似度、欧氏距离、点积等,根据场景选择最合适的算法

3️⃣ 元数据过滤

除了向量,还可以存储和过滤元数据(如时间、分类、标签),实现混合检索

4️⃣ 持久化存储

支持数据持久化到磁盘,重启后数据不丢失,适合生产环境使用

💾 Chroma进阶使用

除了基础的存储和查询,Chroma还支持更多高级功能:

python
# 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"])

💡 最佳实践

  • 使用持久化:生产环境必须使用PersistentClient,避免数据丢失
  • 合理设置collection名称:不同项目使用不同collection,便于管理
  • 添加元数据:为文档添加分类、时间等元数据,方便后续过滤
  • 定期备份:重要数据要定期备份chroma_db目录
  • 批量操作:批量添加文档比单条添加效率高10倍以上

📊 常用向量数据库对比

C

Chroma

轻量级向量数据库(推荐初学者)

✅ 优点
  • 安装简单,开箱即用
  • 轻量级,适合小项目
  • Python友好,API简洁
  • 完全免费开源
❌ 缺点
  • 不适合超大规模数据
  • 性能不如专业数据库
  • 功能相对简单
📦 安装方式:
text
pip3 install chromadb

💡 适合场景:学习、原型开发、小型项目(10万条以内)

P

Pinecone

云端向量数据库(推荐生产环境)

✅ 优点
  • 完全托管,无需运维
  • 高性能,毫秒级查询
  • 自动扩展,支持亿级数据
  • API简单易用
❌ 缺点
  • 收费模式(按使用量)
  • 数据存储在云端
  • 私有化部署困难
📦 安装方式:
text
pip3 install pinecone-client

💡 适合场景:生产环境、大型企业、需要高可用性的项目

W

Weaviate

开源向量数据库(GraphQL支持)

✅ 优点
  • 支持GraphQL查询
  • 内置向量化模型
  • 支持混合搜索
  • 开源免费
❌ 缺点
  • 配置相对复杂
  • 资源占用较高
  • 学习曲线较陡
📦 安装方式:
text
pip3 install weaviate-client

💡 适合场景:需要GraphQL、混合搜索的中型项目

Q

Qdrant

高性能向量数据库(Rust开发)

✅ 优点
  • 性能极高,内存优化
  • 支持过滤查询
  • 部署简单,Docker支持
  • 开源免费
❌ 缺点
  • 生态相对较新
  • 文档不够完善
  • 社区相对较小
📦 安装方式:
text
pip3 install qdrant-client

💡 适合场景:对性能要求高、需要私有化部署的项目

M

Milvus

企业级向量数据库(支持分布式)

✅ 优点
  • 支持分布式部署
  • 功能最全面
  • 支持多种索引算法
  • 企业级稳定性
❌ 缺点
  • 部署复杂,需要集群
  • 资源消耗大
  • 学习成本高
📦 安装方式:
text
pip3 install pymilvus

💡 适合场景:大型企业、海量数据(亿级以上)、需要分布式部署

🎯 如何选择向量数据库?

📊 按项目规模选择

  • 学习/原型:Chroma(最简单)
  • 小型项目:Chroma / Qdrant
  • 中型项目:Weaviate / Qdrant
  • 大型企业:Pinecone / Milvus

💰 按预算选择

  • 免费开源:Chroma / Weaviate / Qdrant / Milvus
  • 预算充足:Pinecone(省运维成本)
  • 需要托管:Pinecone / Weaviate Cloud

💡 我的建议:初学者先用Chroma掌握概念,项目大了再根据需求升级到Qdrant或Pinecone。

🚀 RAG存储阶段完整实战

现在我们来做一个完整的RAG存储阶段实战,将前面学的加载→分割→向量化→存储四个步骤串联起来:

🧯 常见报错:ImportError(cannot import name 'Embeddings' from chromadb.api.types)
这个报错通常不是代码写错,而是 chromadb 安装版本不一致(升级/降级残留导致包内部文件“混用”) 或者 Python 版本与依赖组合不兼容
text
# 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"
如果你不想切换 Python 版本,最稳的做法是:在一个全新的 venv 里重装依赖,并避免在全局 site-packages 里反复升级/降级。
python
# 完整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}")

✅ 运行结果示例

text
🚀 开始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'}

💡 代码关键点解析

  1. 🔄 完整流程:一个类包含RAG存储阶段的所有步骤,便于复用
  2. 📁 多源加载:支持PDF、TXT、网页等多种数据源
  3. ✂️ 智能分割:固定长度分割,支持重叠,过滤太短片段
  4. 🔢 批量向量化:使用通义千问embedding,支持中文优化
  5. 💾 持久化存储:使用PersistentClient,数据保存到磁盘
  6. 📊 元数据管理:为每个文档块添加源信息和统计信息
  7. 🔍 即时测试:存储完成后立即测试查询功能

🎯 使用建议

  • 初学者:先运行示例代码,理解完整流程
  • 实际项目:根据需要修改chunk_size和overlap参数
  • 生产环境:添加错误处理、日志记录、进度显示
  • 性能优化:大批量文档时,考虑分批处理和并行化

📝 本章小结

🎓 你学到了什么?

  • RAG概念:检索增强生成,让AI基于真实知识回答问题
  • 为什么需要RAG:解决知识截止、AI幻觉、专业知识缺乏等问题
  • 1️⃣ 加载:从各种数据源读取内容并转为纯文本
  • 2️⃣ 分割:将长文档切分成小块,保持语义完整性
  • 3️⃣ 向量化:将文本转换为数字向量,让计算机理解语义
  • 4️⃣ 存储:使用向量数据库存储向量,支持快速检索

🚀 下一步学习

完成了RAG存储阶段的学习后,下一章我们将学习RAG查询阶段

第7章将带你实现完整的RAG系统,包括:

  • ✅ 查询向量化和相似度检索
  • ✅ 基于检索结果生成答案
  • ✅ 完整的智能问答系统
  • ✅ 性能优化和最佳实践

📚 课后习题与面试题

请认真完成以下练习,巩固RAG存储阶段核心知识

⚠️ 重要提示:这些题目涵盖了面试常见考点,请务必掌握!

🎯 基础练习题

1. 什么是RAG?请说明RAG存储阶段的四个核心步骤。

2. 文档分割为什么重要?列举至少3种分割策略并说明其适用场景。

3. 对比Chroma、Pinecone、Weaviate三种向量数据库的优缺点和适用场景。

💼 面试题

1. 【场景题】在一个企业级知识库项目中,你会如何设计RAG存储架构?请考虑数据量、性能、成本等因素。

2. 【技术题】如何优化RAG系统的检索精度?请从文档处理、向量化、索引三个层面说明。

3. 【实战题】给定一个包含1000份PDF文档的企业知识库,请设计一个完整的RAG存储方案,包括技术选型和实现步骤。

💻 实战练习题

🎯 编程挑战:实现一个支持多种文档格式的RAG存储系统

要求:

  • 支持PDF、Word、TXT、HTML四种格式
  • 实现智能文档分割(递归+语义分割)
  • 使用Chroma存储,支持持久化
  • 添加文档元数据(文件名、类型、大小、创建时间)
  • 实现批量处理和进度显示
  • 添加错误处理和日志记录

💡 提示:

  • 使用python-docx处理Word文档
  • 使用tqdm库显示进度条
  • 使用logging模块记录日志
  • 考虑添加文档去重功能
← 上一章 下一章 →