简单学习 --> Rag

一、 RAG 的本质:为大模型外接"动态内存"

在计算机架构中,CPU 的算力再强,也受限于 L1/L2 缓存和内存的大小。LLM(大语言模型)同理:它的权重矩阵(模型本身)相当于只读存储器(ROM) ,而它的上下文窗口(Context Window)相当于极其昂贵的运行内存(RAM)

RAG(检索增强生成)的核心思想是:绝不修改 ROM(不微调大模型),而是通过外部系统实时将最相关的数据加载到 RAM 中,供 LLM 进行即时计算与推理。

例子:

大模型(LLM)是一个智商高达 150 但"没背过你们公司业务手册"的学霸。 用户提问时,如果直接让他裸考(直接问 LLM),他可能会胡编乱造(幻觉)。

RAG 就是学霸的场外书童

书童飞速去图书馆(向量数据库)翻出相关的 3 页资料,复印下来连同考卷一起递给学霸。学霸看着这 3 页资料总结出完美答案。

RAG 工作流:

  1. 意图捕获: 拦截用户的查询请求。

  2. 知识寻址: 借助向量引擎,在庞大知识库中定位到最相关的"知识块"。

  3. 上下文组装: 将找到的"知识块"拼接到系统提示词(Prompt)中。

  4. 推理输出: LLM 基于刚刚被注入的背景知识生成回复。

二、 主流技术架构与选型

在实际工程落地中,架构选型主要考虑运维成本掌控力的平衡。

方案类型 技术栈示例 适用场景与优缺点
全云服务 (主流) OpenAI API + Pinecone/Zilliz + 纯 Python / LangChain 推荐。开发快,无需关心底层扩容。缺点是数据出境或依赖第三方厂商。
半云服务 (企业常用) LLM API + LlamaIndex + 私有部署向量数据库 (如 Milvus/Qdrant) 平衡之选。模型用云端保证智商,数据留在本地库保证隐私。
全本地化 (研究向) 本地部署 Llama 3 + 本地向量数据库 + LangChain 门槛高。适合对数据安全极度敏感或做底层研究的团队,对硬件算力要求极高。

为什么向量数据库推荐用云服务? 和传统 MySQL 一样,一旦数据量变大,向量数据库也需要面对分布式架构、主从同步、读写分离、高可用等问题。向量检索极耗内存和 CPU,云服务能有效转嫁高昂的运维和机器成本。

三、 向量数据库的三部曲

向量数据库的本质可以概括为:将万物映射为坐标(Embedding),用数学计算距离(Similarity),并修筑高速公路(Indexing)以实现海量秒查。

1. 核心灵魂:万物皆坐标 (Embeddings)

计算机无法理解人类语言中的"相似",但能理解空间中坐标点的距离。Embedding 就是把文本压缩成一个固定维度的数组。

代码解析:文本转向量

python 复制代码
from langchain_community.embeddings import HuggingFaceBgeEmbeddings

# 初始化 BGE 模型 (目前开源界语义理解极强的轻量级模型)
model_name = "BAAI/bge-small-zh-v1.5"
embeddings = HuggingFaceBgeEmbeddings(model_name=model_name)

# 定义要处理的文本
text_a = "Java Spring Boot的AOP拦截器怎么写?"
text_b = "Python中如何使用装饰器实现切面编程?"
text_c = "今天晚上的夜宵打算吃小龙虾。"

# 将文本转化为高维向量 (执行 Embedding 过程)
vec_a = embeddings.embed_query(text_a)

# 打印结果:你会看到一个拥有多个浮点数的列表,这就是它的GPS坐标
print(f"文本A的向量维度: {len(vec_a)} 维")
print(f"截取前三维: {vec_a[:3]}")
2. 检索原理:计算相似度 (Similarity Search)

找到相关内容的本质,是在高维空间中找寻距离最近的两个点。最常用的尺子是余弦相似度(Cosine Similarity) ,它关注向量夹角的大小;另一把常用的尺子是点乘(Dot Product)

补充概念:什么是点乘? 点乘是两个向量对应维度相乘后再相加的数学过程。公式为:A⋅B=∑i=1naibi。

  • 物理意义: 它衡量了两个向量在相同方向上的"协同程度"。

  • 在向量库中的应用: 如果向量在存入前已经做过归一化(长度都变为 1),那么点乘的值就完全等于余弦相似度。计算点乘的硬件资源消耗通常低于计算夹角,因此被广泛使用。

代码解析:手写点乘计算相似度

python 复制代码
from scipy.spatial import distance
import numpy as np

# 假设我们将刚刚生成的三个文本向量化为了 numpy 数组
# vec_a ("Java AOP") 和 vec_b ("Python 装饰器") 都在讲编程范式
# vec_c ("吃小龙虾") 完全无关

# 计算余弦相似度 (1 - 余弦距离)
# 值越接近 1,表示极其相似;越接近 0,表示毫无关联
sim_ab = 1 - distance.cosine(vec_a, vec_b) 
sim_ac = 1 - distance.cosine(vec_a, vec_c)

print(f"Java编程 vs Python编程 的语义相似度: {sim_ab:.4f}") # 得分很高
print(f"Java编程 vs 吃小龙虾 的语义相似度: {sim_ac:.4f}")   # 得分极低
3. 性能关键:建立索引 (Indexing / ANN)

全量对比计算(暴力搜索)太慢。现代向量库使用 ANN(近似最近邻) 算法,其中最主流的是 HNSW 算法。它构建了一个多层的图结构,类似于"先坐高铁到大城市,再坐公交到具体街道",从而实现毫秒级检索。

四、 维度、模型与兼容性

  • 模型兼容性铁律: 写入和查询必须使用同一个 Embedding 模型。 不同模型提取特征的逻辑完全不同,跨模型对比就像拿美元面值直接对比日元面值,毫无意义。

  • 维度选择: 常见的维度有 384、768、1536 等。维度越高,能表达的语义细节越丰富,但存储和计算成本呈线性增加。目前通用 RAG 任务中,768 到 1536 维是平衡性能与成本的最优解。

五、 文本切片 (Chunking):操作系统的"内存分页"逻辑

这是 RAG 系统工程落地中最容易翻车的环节。大模型的上下文窗口有限,我们必须把知识库切分为标准的"块"(Chunk)再喂给它,这类似于操作系统的内存分页(Paging)

场景 A:长文档的智能切片 (递归切分)

如果是整本书或长篇论文,我们不能粗暴地按固定字数一刀切,这会切断上下文的逻辑。业界主流做法是递归切分 ,并保留重叠区(Overlap)

例子:

怎么切战斧牛排? 你不能把一整块战斧牛排一口吞下(Token 超限),你得切块。 如果你闭着眼睛按重量乱砍,可能会直接把骨头和肉的连接处剁碎(把一句话从中间截断)。 递归切分就是有技巧地沿着筋络切(优先按段落切,段落太大再按句子切)。而**重叠区(Overlap)**就是保证每一块肉上都连着一点上一块的肉筋,这样吃的时候(LLM理解时),才知道这两块肉原本是连在一起的。

代码示例:LangChain 递归字符切分器

复制代码
from langchain.text_splitter import RecursiveCharacterTextSplitter

long_document = "这里是长达十万字的技术文档原文..."

# 实例化高级文本切分器
splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,       # 每个块的最大容量限制 (牛排块大小)
    chunk_overlap=50,     # 设置 50 个字符的冗余重叠区 (连带的肉筋)
    separators=["\n\n", "\n", "。", "!", "?"] # 切分优先级,优先保证段落完整
)

chunks = splitter.create_documents([long_document])
场景 B:离散短文本 (FAQ) 的精准映射

针对类似 ["华为手机好不好?", "苹果手机好不好?"] 这种已经具有高度独立语义的短句。

工程铁律:绝不合并! 一定要确保"一个语义单元 = 一个 Chunk"。

例子:

不要把 M&M 豆融化成巧克力砖 FAQ 问答对本身就是一颗颗完整的 M&M 豆,有着独立的颜色和味道。如果你因为觉得单颗太小,非要把 100 颗不同的 M&M 豆加热融化成一个 500g 的巧克力大砖块(大 Chunk),等你想吃红色的豆子时,拿出来的全都是一堆大杂烩,这就是严重的注意力污染(Noise)

代码示例:针对 FAQ 数据的精准切分

复制代码
from langchain.text_splitter import CharacterTextSplitter

faq_text = """
华为手机好不好?答:信号好,拍照强。
苹果手机好不好?答:生态好,录像强。
今天下雨吗?答:不下。
"""

# 针对离散数据的强硬切分策略
faq_splitter = CharacterTextSplitter(
    separator="\n",     # 仅以换行符作为唯一物理边界
    chunk_size=10,      # 设积极小值,逼迫切分器在每一个换行符处都必须切断
    chunk_overlap=0     # 独立数据,绝对不允许重叠污染
)

faq_chunks = faq_splitter.create_documents([faq_text.strip()])
# 输出结果将是绝对干净的三条独立记录,彼此互不干扰。

六、 LlamaIndex 的底层逻辑:List Index 🆚 Vector Index

在框架层面,针对不同的数据量,索引方式也有区别:

索引类型 原理 是否需要 Embedding 模型 适用场景
Vector Index 计算所有文本的向量坐标,通过空间距离进行检索,只提取 Top-K 给 LLM。 海量文档、长文本、常规 RAG 场景。
List Index 直接将所有文本按顺序拼接成一个列表。查询时,把整个列表内容直接塞进 Prompt 交给 LLM 处理。 数据量极小(总 Token 小于大模型上下文窗口上限)、或者要求严格按顺序总结的场景。

总结来说,RAG 系统的上限由检索的精准度(Embedding 质量 + Chunking 策略)决定,而下限由 LLM 自身的逻辑推理能力决定。您的这套笔记已经非常清晰地覆盖了这一闭环。

相关推荐
火山引擎开发者社区14 分钟前
龙虾突然“罢工”?别慌,我们派出了“AI 医生”
人工智能
NQBJT18 分钟前
青鸾云步:基于 Cordova 的 AI 导盲机器人 APP 全栈开发实战
人工智能·app·导盲·轮足机器人·青鸾云步
深兰科技1 小时前
韩国KAIST AI半导体高管项目代表团到访深兰科技,聚焦AI算力与智能产业合作机会
人工智能·机器人·symfony·ai算力·深兰科技·韩国科学技术院·kaist
快乐on9仔1 小时前
NLP学习(一)transformers之pipeline体验
人工智能·深度学习
冬奇Lab1 小时前
Agent系列(六):记忆管理——让 Agent 记住重要的事
人工智能·agent
冬奇Lab1 小时前
一天一个开源项目(第113篇):notebooklm-py - 把 Google NotebookLM 变成可编程 API,还能接入 Claude Code
人工智能·google·开源
愚者Pro2 小时前
Flutter Widget组件学习(专为 Uniapp 转 Flutter 定制)
vue.js·学习·flutter·uni-app
字节跳动开源2 小时前
Viking AI 搜索 CLI 正式发布:会说话,就能做搜索推荐
数据库·人工智能·开源
阿杰技术2 小时前
AI 编程助手落地实战:从提效到重构的全场景指南
人工智能·重构