内容有些长,耐心看完,对你应该有所帮助
一、向量(Embedding):AI 的"意念翻译官"
1、什么是 Embedding(嵌入、向量化)
在自然语言处理中,"Embedding" 通常翻译为嵌入,指将离散数据(如单词、图像)映射到连续向量空间的技术。
简单来说,Embedding 就是一个**从离散数据到高维连续向量空间的映射函数。**想象一下,我们将每一个词、每一段话映射到 <math xmlns="http://www.w3.org/1998/Math/MathML"> R n \mathbb{R}^n </math>Rn空间(比如 <math xmlns="http://www.w3.org/1998/Math/MathML"> n = 768 n=768 </math>n=768 或 <math xmlns="http://www.w3.org/1998/Math/MathML"> 1536 1536 </math>1536)。在这个空间里,语义相近的文本,其向量位置也会非常接近。
2、核心数学度量:相似度计算
当我们把文本变成向量后,如何判断"Pinia"和"Vuex"在语义上是相关的呢?主要靠这两个数学工具:
-
余弦相似度 (Cosine Similarity) :
它衡量的是两个向量之间夹角的余弦值。公式如下:
<math xmlns="http://www.w3.org/1998/Math/MathML"> S c ( A , B ) = cos ( θ ) = A ⋅ B ∥ A ∥ ∥ B ∥ S_c(A, B) = \cos(\theta) = \frac{\mathbf{A} \cdot \mathbf{B}}{\|\mathbf{A}\| \|\mathbf{B}\|} </math>Sc(A,B)=cos(θ)=∥A∥∥B∥A⋅B
特点:它只看方向,不看长度。在 NLP 中最常用,因为文档的长短(向量模的大小)不应影响它表达的主题语义。
-
点积 (Dot Product) :
<math xmlns="http://www.w3.org/1998/Math/MathML"> A ⋅ B = ∑ i = 1 n A i B i \mathbf{A} \cdot \mathbf{B} = \sum_{i=1}^{n} A_i B_i </math>A⋅B=∑i=1nAiBi
特点:如果向量已经经过了归一化(模为1),点积就等于余弦相似度。很多高性能向量数据库(如 Milvus)会通过归一化来把复杂的余弦计算简化为点积运算,以提升搜索速度。
总结:
-
通俗定义:把一段话变成一串数字坐标。
-
核心逻辑:在 AI 的世界里,相似的词会靠在一起。比如"西瓜"和"哈密瓜"在坐标系里的距离,肯定比"西瓜"和"波音747"要近。
-
数学必记 :余弦相似度。它不看你话有多长,只看你们聊的是不是一个方向。
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> S c = A ⋅ B ∥ A ∥ ∥ B ∥ S_c = \frac{\mathbf{A} \cdot \mathbf{B}}{\|\mathbf{A}\| \|\mathbf{B}\|} </math>Sc=∥A∥∥B∥A⋅B
二、 HNSW:AI 使用的"高速公路地图"
既然你已经掌握了相似度度量,我们要解决下一个现实问题:速度。
如果你有 1 亿条文档向量,用户问一个问题,你用余弦相似度跟这 1 亿条数据逐一计算(暴力搜索),那用户可能要等半天。
为了实现毫秒级响应,我们需要 HNSW (Hierarchical Navigable Small World) 。
1、 核心结构: 分层图(Hierarchical Structure)
为什么使用分层存储的模式,是遇到什么困难了?
HNSW 的实现基础是多层图结构(Layered Graph):
- 第 0 层(Layer 0): 包含数据集中所有的点,图最稠密,边最短。
- 上层(Upper Layers): 随着层数增高,节点数量指数级减少,边变得更长。
- 节点分布: 每个新节点在插入时,会根据概率函数随机分配到一个"最高层"。如果一个点在第 2 层,它必然也存在于第 1 层和第 0 层。
2、搜索逻辑:从"粗"到"精"
- 从顶层开始,先在一个很稀疏的图中找到离目标最近的点。
- 以这个点为入口,进入下一层,继续寻找更近的点。
- 不断下沉,直到最底层,精确定位邻居。
💡 类比理解: > 在宇宙的你要去太阳系地球取一个礼物。
- 你先坐飞船到星系(顶层,跨度大)。
- 找到了太阳系下有很多星球。
- 再到地球→某一个国家 → 地区。
- 最后步行到胡同,找到门牌号,可能还有邻居送的礼物(底层,跨度小,精度高)。

注意: 当你锁定了底层最相似的向量后,每个向量其实都对应着数据库里的一个指针,指向一段具体的文字(文本块)。
HNSW 输出: "离你最近的知识点是编号为 #1024, #2048, #512,#1314 ......的文本块。"
系统操作: 从数据库取出这几段话。
3、构建与插入实现 (Construction)
提问:结构与搜索都有了,那么如何存储?
插入一个新向量 <math xmlns="http://www.w3.org/1998/Math/MathML"> v v </math>v 的步骤如下:
A. 确定层数 使用指数概率分布确定新节点的最高层
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> L = ⌊ − ln ( uniform ( 0 , 1 ) ) × m L ⌋ L = \lfloor -\ln(\text{uniform}(0,1)) \times m_L \rfloor </math>L=⌊−ln(uniform(0,1))×mL⌋
大部分点都只在第 0 层,极少数点能到达高层。
B. 寻找邻居 从顶层下降到第 <math xmlns="http://www.w3.org/1998/Math/MathML"> L + 1 L+1 </math>L+1层时,只进行简单的贪婪搜索,找到最接近 <math xmlns="http://www.w3.org/1998/Math/MathML"> v v </math>v 的进入点。 从第 <math xmlns="http://www.w3.org/1998/Math/MathML"> L L </math>L 层下降到第 0 层时,在每一层都会寻找与 <math xmlns="http://www.w3.org/1998/Math/MathML"> v v </math>v 最近的 <math xmlns="http://www.w3.org/1998/Math/MathML"> M M </math>M 个邻居并建立连接。
C. 启发式连接 (Heuristic Selection) 这是 HNSW 的精髓。在选择邻居时,它不只是简单选择最近的 <math xmlns="http://www.w3.org/1998/Math/MathML"> M M </math>M个点,而是采用多样化策略:
如果候选邻居 <math xmlns="http://www.w3.org/1998/Math/MathML"> A A </math>A 虽然离 <math xmlns="http://www.w3.org/1998/Math/MathML"> v v </math>v很近,但 <math xmlns="http://www.w3.org/1998/Math/MathML"> A A </math>A 已经和 <math xmlns="http://www.w3.org/1998/Math/MathML"> v v </math>v 的另一个邻居 <math xmlns="http://www.w3.org/1998/Math/MathML"> B B </math>B 非常接近了,那么 HNSW 可能会放弃连接 <math xmlns="http://www.w3.org/1998/Math/MathML"> A A </math>A,转而连接稍微远一点但在不同方向上的 <math xmlns="http://www.w3.org/1998/Math/MathML"> C C </math>C(类似:发散思维)。 这样可以保证图的连通性更广,防止搜索陷入孤立的簇。
4、原生AI(LLM)在其中的作用
即使你不提供 HNSW 数据库(或任何外部知识库),AI 依然可以和你对答如流。
1. 核心差异:参数化内存 vs. 非参数化内存
-
不带数据库(原生 AI): 依靠 参数化内存 (Parametric Memory) 。
- AI 在"出厂"前,阅读了互联网上几万亿字的内容(预训练阶段)。这些知识并没有以"文件"形式存着,而是转化为了模型内部数千亿个权重(Weights/Parameters) 。
- 这就好比一个博览群书的教授,你问他问题,他凭记忆直接回答。
-
带数据库(HNSW/RAG): 依靠 非参数化内存 (Non-parametric Memory) 。
- AI 除了脑子里的记忆,手边还放了一本《百科全书》(你的 HNSW 数据库)。
- 这就好比教授考试时允许"开卷",他先去书里翻出相关的段落,看一眼,再回答你。
两种模式的优缺点对比
| 特性 | 原生 AI(不带数据库) | AI + HNSW 数据库(RAG 模式) |
|---|---|---|
| 知识来源 | 训练时的陈旧数据(有截止日期) | 实时、私有、最新的外部数据 |
| 准确性 | 容易幻觉(一本正经胡说八道) | 极高(有据可查,不乱编) |
| 隐私性 | 无法访问你的本地私有文档 | 可以检索并处理你的私有文档 |
| 响应速度 | 极快(直接输出) | 稍慢(需要多出一步检索时间) |
| 可追溯性 | 无法告诉你答案在哪看到的 | 可以告诉你"根据文档 A 第 3 页显示..." |
AI为什么还需要数据库?
既然 AI 自己懂这么多,为什么我们还要费劲搞 HNSW 数据库?
- 打破时间限制: 原生 AI 不知道 2026 年今天发生的新闻,但你可以把今天的新闻塞进数据库。
- 私域知识: 原生 AI 不认识你的公司代码库或财务报表,但你可以通过 HNSW 让它变成你的"公司小助手"。
- 消灭幻觉: 问原生 AI 一个偏僻的法律条文,它可能记混;但让它去数据库里"搜出原条文再解释",它就绝不会出错。
所以,当你直接和 AI 交互时,你是在用它的"脑子";当你给它配上 HNSW 数据库时,你是给了它一套"无穷无尽的书架"。
总结:
- 通俗定义:你有 100 万条数据,挨个去比对相似度会慢死。HNSW 就是给这些数据建了一个"六度分隔"的导航图。
- 工作原理:用户提问一个问题(比如:"植物为什么需要阳光"),先被embedding模型进行向量化,再向量索引HNSW的内容:在大范围找(比如"与植物相关的内容"),再进小范围找("阳光对植物的作用"),最后精准定位(比如:"线粒体、叶绿素、光合作用是植物将光能转化为化学能的过程......")。这让你能在几毫秒内从百万数据中抓出答案。
💡笔记要点:
HNSW 不负责生产答案,它只负责在几毫秒内从亿级数据中,把那几本"对的参考书"找出来给 AI 读。
三、切块(Chunking):把书拆成"便利贴"
1、数据切分(Chunking):寻找语义的"原子"
简单来说:递归字符切分是"看长相"来切(物理结构),语义切分是"看意思"来切(逻辑内容)。
递归字符切分 (Recursive Character Splitting)
这是最常用的方式,它会先按段落分,段落太长再按句子分,句子太长再按空格分。
这是目前最常用、性价比最高的方案(比如 LangChain 里的 RecursiveCharacterTextSplitter)。
工作原理:
它并不是暴力地每 500 字一刀,而是拿着一组分隔符清单去递归尝试。典型的顺序是:
- 一级分隔符:
\n\n(双换行,通常是章节/段落)。 - 二级分隔符:
\n(单换行)。 - 三级分隔符:
.(句号)。 - 四级分隔符: ``(空格,词组)。
- 五级分隔符:
""(实在分不开了,就按单个字符切)。
它的逻辑:
- 它先试着用
\n\n切。如果切出来的块还是超过了你设定的chunk_size,它就在这个块内部改用\n切,以此类推。 - 目的: 尽可能保证一段话 或一句话是完整的,而不是被生硬地劈成两半。
语义切分 (Semantic Chunking)
这种方法不看回车或句号,它看的是内容的意思。
工作原理:
- 打散: 先把整篇文章拆成一根根"独苗"句子。
- 测量距离: 把每个句子都变成向量(Embedding),计算句子 A 和句子 B 之间的语义相似度。
- 寻找断层: 如果句子 A 和句子 B 的意思很接近,就把它们合并;如果到句子 C 时,发现它的意思跟前面突然"断层"了(相似度大幅下降),就在这里切一刀。
它的逻辑:
- 它认为:只要意思还没变,我就不切开。
- 例子: 如果你写了 1000 字都在讨论"番茄的种植",哪怕中间有 10 个段落,它也可能把它们切成一个巨大的块;而如果你下一句突然聊到了"拖拉机维修",它会立刻在这里起一个新块。
深度对比:你应该怎么选?
| 特性 | 递归字符切分 (推荐入门) | 语义切分 (进阶首选) |
|---|---|---|
| 判断标准 | 物理符号(换行、标点) | 语义相似度(Embedding) |
| 计算开销 | 极低。只是简单的字符串处理。 | 高。每一句都要调一次模型算向量。 |
| 准确度 | 较好,能保留段落完整性。 | 极佳。每个块都是一个独立的"知识主题"。 |
| 缺点 | 依然可能把一个跨段落的长逻辑切断。 | 慢,且依赖 Embedding 模型的质量。 |
| 适用场景 | 通用文档、格式规整的代码、说明书。 | 逻辑复杂、跳跃性大的专业论文或对话记录。 |
开发建议
在实际做项目时,建议是:
-
先从"递归字符切分"开始:
设定
chunk_size: 500,chunk_overlap: 50(后面会说)。这能解决 80% 的问题,而且运行飞快,不需要花钱调模型。 -
遇到疑难杂症再用"语义切分":
如果你发现 AI 总是"断章取义",或者你的文档逻辑特别碎片化,再考虑引入语义切分。
💡笔记要点:
- 物理切分 (递归字符)是按照作者的写作习惯在猜重点。
- 语义切分 是按照读者的理解逻辑在找重点。
2、块的大小有什么区别
问题:如果你设置 Chunk Size(切块大小)太小(比如只有 20 个字符),或者太大(比如 20000 个字符),分别会导致什么后果?
1. 如果切得太小(例如:每句一词)
- 后果 :语义碎片化(Context Loss) 。
- 检索质量 :变差。向量模型很难理解一个孤立的词。比如"它"这个词,切得太小就失去了指代对象。
- 生成效果 :变差。模型拿到的是一堆零散的成语或短句,拼凑不出完整的逻辑,容易产生"幻觉"。
- 速度:由于索引条数激增,检索压力确实会增大(虽然 HNSW 能抗,但 K-V 查询变多了)。
2. 如果切得太大(例如:整章切分)
- 后果 :语义稀释(Noise Injection) 。
- 检索质量 :中等偏下。一个 5000 字的文档,其向量是所有内容的"平均值",重点不突出。
- 生成效果 :受限。LLM 的上下文窗口会被大量无关信息塞满,不仅费钱(Token 贵),还会导致模型"注意力涣散",找不到真正的答案。
解决方案:滑动窗口(Overlap)
为了解决"切太小丢上下文"的问题,工业界标准做法是设置 Overlap(重叠度) 。
例子:Chunk Size 500, Overlap 50。 这样每个块都会包含上一个块的末尾内容,确保语义的连续性。
关于"Overlap 10%"的精妙之处
- 没有 Overlap: 假设你的文档是"张三喜欢吃苹果。李四喜欢吃香蕉。"如果你正好从中间切开,向量 1 只知道张三,向量 2 只知道李四。
- 有了 10% Overlap: 两个切块都会包含中间的衔接部分。这样当用户问"谁喜欢吃水果"时,两个向量都能被搜到,AI 拿到的信息更完整。
💡笔记要点:
- 通俗定义:书太厚,AI 读不完,得把它拆成一段一段的。
- 金律良言 :不能切太碎(会断章取义),不能切太长(会废话太多)。通常还要让前后两块有 10% 的内容重叠(Overlap) ,确保逻辑不断线。
四. RAG 的全过程:这就是场"开卷考试"
1、知识连网
运行这个AI前,我们将历史数据切块向量化,本体存储在 向量库(文件切片部分+文件切片overlap10%),向量坐标在向量库(HNSW部分),
用户提问→ 转化为向量 → 去HNSW找到最近的向量获取地址 →根据地址去获取向量库的存储数据内容 → 再根据内容去一起写成prompt交给AI → AI处理发送答案
| 描述 | 技术术语 | 核心要点 |
|---|---|---|
| 切块并留 10% Overlap | Text Chunking | 10% 的重叠是为了防止"语义断裂"(比如一句话被切成两半,通过重叠可以保留上下文)。 |
| 本体存储在向量库 | Document Store | 这部分通常是磁盘上的 KV 数据库,负责存"沉甸甸"的文字。 |
| 向量坐标在 HNSW 部分 | Vector Index | 这部分是内存里的"路标",专门负责算数学距离。 |
| 根据地址获取内容 | Metadata Fetching | 通过向量 ID(你说的地址)秒杀回传对应的原始文本。 |
| 写成 prompt 交给 AI | Context Injection | 把搜到的"知识"塞进 AI 的输入框,这叫"上下文注入"。 |
2、一个完整的"查询"路径(开发者视角)
假设你是个前端开发者,你在写一个 AI 助手,整个链路是这样的:
- 用户输入 :
"植物为什么需要阳光" - 前端/后端 :调用 Embedding API,把这句话转成向量 <math xmlns="http://www.w3.org/1998/Math/MathML"> V V </math>V。
- 向量库(HNSW 部分) :计算 <math xmlns="http://www.w3.org/1998/Math/MathML"> V V </math>V 在内存图中的位置,返回
Top 1 ID: 9527。 - 向量库(存储部分) :根据
9527迅速从磁盘/缓存中抓取文本:"光合作用是植物..."。 - LLM 拼接:把文本和问题发给 LLM。
- 用户:看到答案。
疑问?:Prompt 是怎么"拼"的?
提到的"一起写成 Prompt",在代码底层通常长这样:
System Prompt: 你是一个知识库助手。请根据以下参考资料回答用户问题。如果资料里没有,请说不知道。
参考资料(由 HNSW 检索得来):
切块内容 A...
切块内容 B...
用户问题: 植物为什么需要阳光?
AI 回答: (开始处理...)
五、 总结:你的架构图
-
离线阶段(预处理) :
<math xmlns="http://www.w3.org/1998/Math/MathML"> 文档 → 切块 ( O v e r l a p ) → 向量化 → H N S W ( 存坐标 ) + 数据库 ( 存文本 ) 。 文档 \rightarrow 切块(Overlap) \rightarrow 向量化 \rightarrow HNSW(存坐标) + 数据库(存文本)。 </math>文档→切块(Overlap)→向量化→HNSW(存坐标)+数据库(存文本)。
-
在线阶段(查询) :
<math xmlns="http://www.w3.org/1998/Math/MathML"> 问题 → 向量化 → H N S W 搜索 I D → 数据库取文本 → 拼凑 P r o m p t → A I 生成。 问题 \rightarrow 向量化 \rightarrow HNSW 搜索 ID \rightarrow 数据库取文本 \rightarrow 拼凑 Prompt \rightarrow AI 生成。 </math>问题→向量化→HNSW搜索ID→数据库取文本→拼凑Prompt→AI生成。
😁希望能帮到你🐱