首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >万字长文详解向量数据库与RAG

万字长文详解向量数据库与RAG

原创
作者头像
闫同学
发布2026-01-29 20:14:34
发布2026-01-29 20:14:34
5480
举报
文章被收录于专栏:AI相关AI相关

在我们<u>上一篇文章</u>中,我们探讨了如何通过构建知识库,使得大模型能够从“无状态”变成“有状态”,从而增强其记忆和上下文感知的能力。知识库作为一种让大模型“拥有记忆”的方式,其形式多种多样,从传统的文件系统到数据库,再到如今被广泛使用的向量数据库。这些不同的知识载体并非都是同等适用的,尤其在语义检索的时代,向量数据库正逐渐成为行业的事实标准,成为大模型知识存储和检索的核心。

这篇文章我们会讲什么?

主要有以下这些问题:

toc

随着对知识库概念的进一步深入理解,我们也更加清晰地认识到:在整个AI系统中,Embedding向量数据库LLM(大语言模型)并非孤立存在,而是形成了一个完整的工作链路。正是这个链条,让AI能够更加智能、精确地理解问题、存储信息,并做出最为贴切的回应。而这一切的基础,正是通过向量数据库来打下的坚实基础。

接下来,我们将深入探讨向量数据库的核心概念,并结合实际案例演示,一起来更好地理解向量数据库在大模型中的实际应用。

什么是向量?

在了解向量数据库之前,我们还是要明确一下到底什么是向量

向量(Vector)在数学、物理、计算机科学等领域中有着广泛的应用。它是一个表示具有大小和方向的量,在机器学习和数据科学中,向量通常表示为一组数字(通常是浮点数),这些数字用于描述一个对象的特征或属性

比如在 Milvus 这样的向量数据库中,向量常常用来表示数据(例如文本、图像、音频等)的特征,以便进行高效的相似度检索

向量的基本概念

1、数学上的向量:

在数学中,向量是一个有大小和方向的量。通常在二维或三维空间中,向量可以表示为一个有序的数值序列:

向量的长度(或称为模)表示其大小,而方向表示其朝向。

2、向量在计算机科学中的应用:

1)特征表示: 在机器学习、深度学习、自然语言处理(NLP)等领域,向量通常用来表示数据的特征。例如:

  • 在图像处理中,一张图像可以通过卷积神经网络(CNN)提取出一组数字(向量)来表示其特征。
  • 在 NLP 中,单词、句子或文章可以通过词向量(如 Word2Vec、GloVe、BERT 等)表示为向量,这些向量捕捉了单词之间的语义关系。

2)高维空间: 向量不仅限于二维或三维空间,数据科学中的向量往往是高维的。每个维度表示某种特征。高维向量可以描述图像的颜色、形状、纹理等,或者文本的语法、语义等。

向量的表示

向量通常用一个有序的数字列表来表示。每个数字代表了某一维度的数值。在机器学习和数据科学中,我们经常处理高维向量(即向量的维度很高),这些高维向量可以表示非常复杂的数据。

例如:

  • 在二维空间中,向量可以表示为 (3, 4),其中 3 是 X 轴上的值,4 是 Y 轴上的值。
  • 在更高维度的空间中,向量可能表示为 (0.5, 1.2, -0.3, 0.7),这是一个 4 维向量。

在自然语言处理中,单词、句子或段落常常被表示为向量,通过词嵌入模型(如 Word2Vec、BERT)将其转化为向量;在计算机视觉中,图像也可以通过卷积神经网络(CNN)提取出一个向量来表示图像的特征。

向量在机器学习中的应用

图像向量:

图像通常被转换成一个高维的向量。在计算机视觉中,深度学习模型(如 CNN)将图像通过网络进行处理,最后得到一个向量表示,向量的每个元素代表图像中的某种特征。这个向量可以用于图像的检索、分类、生成等任务。

文本向量:

文本数据(如单词、句子、文章)通过自然语言处理模型(如 Word2Vec、BERT 等)转换为向量表示。这些向量捕捉了文本的语义信息,使得相似语义的文本具有相似的向量表示。比如,“猫”与“狗”的词向量应该相近,“猫”与“计算机”的词向量应该相对远离。

音频向量:

音频文件也可以通过特征提取算法(如 MFCC)转换为向量。这个向量可以表示音频信号中的频率、音调等特征,用于语音识别、音频检索等任务。

向量的常见操作

向量有很多操作,以下是一些常见的操作:

1)向量加法:

向量加法是将两个向量的对应元素相加。例如:

向量 v1 = (1, 2)v2 = (3, 4)

v1 + v2 = (1+3, 2+4) = (4, 6)

2)向量点积(内积):

向量的点积是两个向量的对应元素相乘并求和。点积结果是一个标量(数字),常用于计算两个向量的相似度:

向量 v1 = (1, 2)v2 = (3, 4)

v1 • v2 = 1*3 + 2*4 = 11

3)向量的夹角:

向量之间的夹角可以通过点积来计算。若两个向量的夹角小,则它们更相似,通常在检索系统中用于衡量相似度。

4)向量的长度(模):

向量的长度表示该向量的大小,可以通过平方和开根号来计算:

向量 v = (x, y) 的长度为 √(x^2 + y^2)

5)余弦相似度:

在高维空间中,余弦相似度是衡量两个向量相似度的常用指标。它通过计算两个向量的夹角余弦值来判断它们的相似性,值的范围从 -1 到 1,1 表示完全相同,-1 表示完全相反,0 表示没有相似度。公式:

cosine_similarity(v1, v2) = (v1 • v2) / (|v1| * |v2|)

其中 |v1||v2| 是向量的模。

回顾向量数据库的概念

明白了什么是向量,我们下一步就可以继续了解向量数据库的概念。

什么是向量数据库

向量数据库是一种专门为高效存储、检索和管理高维向量数据(即向量嵌入)而设计的数据库系统。其核心价值并非简单的向量存储,而在于能够对海量向量执行高速近邻搜索(如相似度计算)等复杂运算,从而实现对非结构化数据(文本、图像、音频等)的语义级查询。

在人工智能技术栈中,向量数据库已成为关键组件,它为大型语言模型提供了持久的“记忆”与高效的检索能力,是实现检索增强生成、推荐系统、语义搜索等AI应用的基础设施。尽管常被称为“AI数据库”,但数据库技术正趋于融合,许多现代数据库已开始支持包括向量在内的多模型数据。

向量数据库和传统数据库有何不同?

想象一下,你管理一个庞大的信息库:

传统数据库:精准的图书管理员

传统数据库(如 MySQL)就像一个严谨的图书管理员,管理着一个结构清晰的档案室。

1)数据格式: 信息被整齐地记录在表格中,就像Excel一样。每一行是一条记录(如一个用户),每一列是一个属性(如姓名、年龄、ID)。数据是结构化的,格式固定。

2)工作方式: 当你提问时,你必须使用它能理解的精确语言(如SQL语句)。例如:“查找所有年龄大于30岁的用户”。

3)查询逻辑: 它根据你的指令,去表格里精确匹配符合条件的行。年龄“31”会被找到,但“三十多岁”或“中年”这种模糊概念它无法理解。它的核心是 “是与否”的精确过滤

向量数据库:理解语义的智能助手

向量数据库则像一个理解含义的智能助手,管理着一个“思想空间”。

1)数据格式: 它不直接存储原始文本、图片或声音,而是先通过AI模型将这些内容转换成高维向量(即“语义指纹”)。这个向量是一长串数字(如768个浮点数),代表了该内容的核心含义和特征。一篇关于“苹果手机”的报道和一张“iPhone照片”的向量,在数学空间里会很接近。

2)工作方式: 当你提问(例如:“帮我找一些关于气候变化影响的科学文章”),它会先将你的问题也转换成向量。

3)查询逻辑: 然后,它在自己的“思想空间”里进行相似度搜索,找出那些向量最接近你问题向量的内容。它返回的不是精确匹配的关键词,而是含义最相关的结果。它的核心是 “像不像”的相似度计算

通俗的来讲,我们可以把向量数据库想象成一个专门处理“思想指纹”的智能图书馆。普通数据库(比如Excel)存放的是规整的数字和文字,就像图书馆按书名或编号找书。而向量数据库存放的是一种“思想的指纹”——它用一串数字(向量)来代表一段文字、一张图片或一首歌的核心含义。当你提问时,它不会去机械地匹配关键词,而是去寻找和你的问题在“思想”上最相近的答案

它最厉害的本事是“快”: 即使这个图书馆里有数十亿本“书”(向量),它也能在眨眼间找到和你想法最相似的几本。这正是当前AI应用(如智能聊天机器人、个性化推荐)急需的能力——让AI不仅能即时生成回答,还能瞬间从海量知识中精准找到最相关的内容来支撑它的回答,变得更渊博、更准确。

因此,向量数据库本质上就是 “为AI大脑配备的超高速记忆检索系统”

核心差异总结

对比维度

传统数据库

向量数据库

数据存储对象

结构化数据:数字、字符串、日期等,存放在行和列中。

非结构化数据的向量嵌入:文本、图像、音频、视频的“语义指纹”(高维数字数组)。

数据关系体现

显式关系:通过外键等明确建立表与表之间的联系。

隐式关系:通过向量在空间中的距离体现语义相似性。距离越近,含义越相似。

查询范式

精确查询:使用SQL进行条件匹配(WHERE age > 30)。答案是确定的、离散的。

相似性查询:进行“近似最近邻搜索”。找到与查询目标最相似的Top K个结果。答案是按相关性排序的、连续的。

核心索引技术

B-树、哈希索引等,用于加速精确值查找。

HNSW、IVF-PQ等近似最近邻索引,用于在高维空间中快速找到相似向量。

擅长解决的问题

事务处理、精准查询:电商订单、银行交易、用户信息管理等,需要绝对准确。

语义搜索、内容推荐、AI记忆:根据描述找图片、聊天机器人上下文检索、推荐相似产品等,需要理解意图和内容。

总而言之,传统数据库是“数据的仓库”,擅长处理“是什么”的问题;向量数据库是“意义的图谱”,擅长处理“像什么”的问题。 正是这种根本性的不同,使得向量数据库成为了处理现代海量非结构化数据、构建智能应用(如RAG)不可或缺的基础设施。

向量数据库在AI Agent中扮演了何种角色?

在AI大模型的应用架构中,向量数据库正从"辅助工具"演变为"核心记忆中枢",尤其在当前主流的RAG(检索增强生成) 技术栈中,它扮演着连接静态知识与动态对话的关键桥梁角色。

RAG时代的核心基础设施

向量数据库在AI大模型中承担着三重核心角色:

1 长期记忆库:克服大模型的"知识截断"
  • 静态知识存储:大模型训练数据存在时间截断(如GPT-4训练数据截止于2023年初),向量数据库为其提供实时更新的外部知识库
  • 私有化记忆:企业专有数据(内部文档、客户资料等)通过向量化存入数据库,成为模型的"私有记忆"。
  • 知识蒸馏:将海量文档库转化为高维向量空间,实现语义级检索而非关键词匹配。
2 事实校验器:对抗"模型幻觉"
  • 提供可追溯依据:在RAG流程中,每次生成答案前,向量数据库先检索相关原文片段作为事实依据。
  • 减少凭空捏造:大模型被约束在"基于给定上下文回答",显著降低编造信息的概率。
  • 建立信任机制:每段回答可附带原文出处,用户可验证信息来源。
3 上下文增强器:提升回答质量
  • 个性化对话:存储用户历史交互向量,实现连续、个性化的对话体验。
  • 领域专业化:为不同行业(医疗、法律、金融)构建专业向量库,增强模型的专业能力。
  • 多模态扩展:统一存储文本、图像、音频的向量表示,支持跨模态检索与生成。
RAG框架中的向量数据库工作流程

简单来说,这个过程是这样的:当我们提问时,系统不会让AI直接凭空回答,而是会先将问题变成一段“数学指纹”(向量),并立即用这个指纹去专属资料库(向量数据库)里查找几份(TOP-K)最相关的原文资料;最后,AI会像一位严谨的发言人,严格基于刚刚查到的这几份资料来组织答案,从而确保回答的准确性和可靠性,有效避免“信口开河”。

实际RAG应用场景

RAG可以说在AI Agent生态中无处不在,主要有以下场景:

应用场景

传统LLM局限

RAG + 向量数据库解决方案

智能客服

无法访问最新产品文档

检索最新FAQ、产品手册后生成回答

法律咨询

可能编造法律条文

精确检索相关法律法规后解释

学术研究

知识存在时间滞后

检索最新论文摘要后综述

医疗辅助

通用建议缺乏针对性

基于患者历史病历生成建议

企业知识库

不了解内部流程

检索公司内部文档后回答问题

从向量存储到智能检索

向量数据库在AI大模型生态中的角色正在快速演进:

基础层:简单的向量存储与最近邻搜索

增强层:支持混合检索(向量+关键词+过滤)、重新排序

智能层:集成查询理解、多跳检索、自适应检索策略

4

生态层:与大模型框架(LangChain、LlamaIndex)深度集成

向量数据库已不仅仅是存储高维向量的"数据仓库",而是AI大模型的"外接大脑皮层"——它扩展了模型的记忆容量,增强了事实核查能力,并使其具备了领域专业知识。在RAG成为大模型应用标准架构的今天,向量数据库从可选组件转变为必选基础设施,成为平衡模型"创造力"与"准确性"的关键支点,支撑着更可信、更专业、更个性化的AI应用落地。

如何选择向量数据库

选择向量数据库,本质是平衡 控制力、成本、场景和复杂度

先回答三个根本问题

1)数据是否必须留在自己的服务器上?

→ 只能选 开源方案(如 Weaviate、Qdrant、Milvus)。

→ 可以考虑 托管服务(如 Pinecone),以运维成本换取开发速度。

2)应用场景是什么?

边缘/IoT设备 → 唯一选择 ObjectBox(专为资源受限环境优化)。

快速原型验证 → 首选 ChromaPinecone(上手最快)。

大规模生产系统 → 进入主流开源方案角逐。

3)数据规模与性能要求如何?

极高吞吐、超大规模(>1亿向量)MilvusVespa(分布式架构最成熟)。

高并发、低延迟Qdrant(Rust编写,性能出色)。

需要强混合搜索(向量+关键词)WeaviateVespa(原生设计支持好)。

主流方案核心定位

主流的向量数据库产品进行对比:

数据库

核心优势

最适合的场景

Milvus

功能最全、生态最成熟的云原生开源方案,适合超大规模。

企业级AI平台、需要处理海量向量的复杂生产系统。

Weaviate

“数据库”思维,强在对象-向量联合存储、混合搜索和易用性。

需要结合结构化过滤的AI应用(如推荐系统、知识库)。

Qdrant

“搜索引擎”思维,Rust带来极致性能和资源效率,API简洁。

对延迟和资源消耗敏感的高性能搜索、实时推荐。

Pinecone

完全托管、零运维,开发者体验最佳,开箱即用。

初创团队、需要快速验证业务假设、无专职运维。

Chroma

轻量、简单,与AI开发栈(如LangChain)集成极深。

AI应用原型开发、学习和轻量级项目。

四步快速决策法

1)筛除明显不匹配项

要边缘计算 → ObjectBox。

要完全免运维 → Pinecone。

要快速做Demo → Chroma。

2)在主流开源中聚焦

追求功能全面和社区规模 → 在 MilvusWeaviate 中选。

追求极致性能和简洁性 → 重点评估 Qdrant

3)用真实数据做概念验证

从2个最终候选开始,用自身业务数据的子集典型查询请求进行测试。

关键指标:查询延迟(P99)、吞吐量、插入速度、内存/CPU占用

不要只看公开报告,你的数据模式才是关键。

4)评估长期成本与锁定风险

开源方案:前期投入工程运维成本,但拥有控制权和退出自由。

托管方案:前期投入资金成本,换取速度,但需承担长期订阅费和供应商锁定。

一句话总结:初创验证用Pinecone/Chroma;重混合搜索选Weaviate;求极致性能看Qdrant;建大规模企业平台评Milvus;做边缘设备只有ObjectBox。最终务必用真实数据测试。

向量数据库对比表格

名称

是否开源

许可证

开发语言

简介

索引类型

是否支持ANN

ObjectBox

Apache-2.0

C++ (支持多语言API)

适用于移动、IoT、嵌入式设备的边缘AI向量数据库

HNSW

Marqo AI

Apache-2.0

Python

基于张量的云原生商业开源搜索分析引擎

HNSW

未知

Weaviate

BSD

Assembly, C++, Go

存储对象和向量的商业开源云原生向量数据库

支持CRUD的自定义HNSW PQ算法

是(支持CRUD的ANN算法)

Chroma

Apache-2.0

Python & TypeScript

商业开源向量数据库

未明确

Qdrant

Apache-2.0

Rust

商业开源向量相似性搜索引擎和向量数据库

HNSW (SQ & PQ)

Milvus

Apache-2.0

Go & Python

云原生商业开源向量数据库

ANNOY; HNSW; IVF_PQ; IVF_SQ等

Vespa

Apache-2.0

Java & C++

Yahoo!的商业开源向量数据库,支持向量搜索、词法搜索和结构化数据搜索

自定义HNSW(多向量混合HNSW-IF)

Vald

Apache-2.0

Go

云原生开源分布式近似最近邻密集向量搜索引擎

NGT

是(NGT)

Pinecone

专有

未明确

完全托管的向量数据库,专注于语义搜索能力

专有

是(专有),加KNN(Faiss)

使用向量数据库实现简单的RAG

在上文我们不断提到向量数据库在RAG中的重要性,那么下面我们就动手实现一个简单的RAG,来直观的感受向量数据库带来的作用。

为什么要有RAG

为什么需要RAG?我们可以从“大模型幻觉”切入

想象一下,你问一位天赋异禀但只接受过2023年初以前通用知识训练的“学者”一个问题。这位学者能结合已知模式,流畅、自信地生成一段回答。但问题在于:

1)他可能不知道你问题中涉及的、2023年后发生的具体新事件。

2)他可能不掌握你公司内部的私有数据或专业知识。

3)他有时会为了保持回答的“流畅合理”,而基于不完整的知识,编造听起来可信但实际错误的细节。

这就是当前大语言模型面临的核心困境:幻觉问题

幻觉指模型生成看似合理但事实上不正确或无法验证的信息

幻觉产生的根源

静态知识边界:大模型的训练数据是静态的、有截止日期的。它无法实时获知世界的新变化。

参数化知识的局限性:模型将海量知识压缩存储在神经网络的权重参数中。这个过程就像一场“有损压缩”,细节、精准数据(如具体数字、小众事实)容易丢失或模糊。

生成模式的固有倾向:模型的核心任务是“根据上文预测下一个最可能的词”。在缺乏明确依据时,为了延续合理的文本模式,它可能会“创作”内容,而非“引用”事实。

传统解决方案的局限

更大规模训练/微调:成本极高,无法解决知识实时性问题,且每次更新知识都需要重新训练或微调,不灵活。

提示工程:在提示词中要求模型“不要编造”,效果有限。这治标不治本,因为模型缺乏事实依据时,依然会陷入幻觉。

由于上述的原因,就催生了RAG的出现。

RAG:解决幻觉的“开卷考试”方案

RAG的核心思想是不让模型仅依赖其内部压缩的、可能过时或不完整的记忆来作答,而是先为模型提供相关的、最新的、准确的事实依据,再让它基于这些依据来组织答案。

这就像让一位学者从“闭卷作文” 转变为“开卷考试”

  • 闭卷(纯LLM) :依赖记忆,容易记错、遗漏或编造。
  • 开卷(RAG + LLM) :允许先查阅指定的、可靠的参考资料,然后基于资料作答,答案的准确性和可信度大幅提升。
RAG的工作流程(如何对抗幻觉)

1)检索:当用户提问时,系统不会直接将问题丢给大模型。而是先将问题转换成向量,在向量数据库中搜索与之最相关的知识片段。这个知识库可以是你的产品文档、最新的市场报告、公司制度、法律条文等任何可靠、最新的数据源。

2)增强:将检索到的原文片段(作为事实依据)和用户的原始问题,一起组合成一个新的、增强版的提示词,发送给大模型。

3)生成:大模型基于收到的 “问题+权威依据” 来生成最终答案。它被明确要求:“请仅根据所提供的上下文信息来回答问题。”

RAG如何直击幻觉要害?

解决知识过时问题:RAG的知识库可以随时更新。只需检索最新的文档即可,无需重新训练模型。

引入精准事实来源:模型不再依赖模糊的参数化记忆,而是基于检索到的、包含具体数字和细节的原文进行生成,极大减少了“捏造细节”的可能。

提供可验证性:系统可以保留检索到的原文出处。当答案存疑时,用户可以“溯源”,查看生成答案所依据的原始材料,建立了可信度

降低专业领域错误:对于法律、医疗、金融等专业领域,通用大模型极易出错。RAG通过引入该领域的专业资料(如病历、法典、财报),将生成答案约束在专业框架内。

RAG通过在生成前引入一个可控制、可更新、可验证的“事实锚点”,巧妙地拆解了这个问题。它将大模型从“全知但不可靠的叙述者”,转变为“精准且富有文采的解说员”,使其答案既流畅,又可靠。这是当前在成本、效果和可行性上,平衡大模型能力与缺陷的最佳工程实践之一,也是其能在企业级场景落地的关键技术。

使用Milvus实现RAG

我们本着先了解再使用的原则先从概念入手,先带大家了解一下Milvus这个向量数据库产品,然后在使用Python代码结合Milvus实现RAG。

先简单介绍一下Milvus

Milvus 是一款领先的云原生、开源向量数据库,专门为大规模AI 应用和向量相似性搜索而设计。它就像是 AI 时代的“数据搜索引擎”,能够高效存储和检索由文本、图像、音频、视频等非结构化数据转换而成的高维向量,帮助 AI 模型快速找到最相关的信息。

关键特性

特性

说明

高性能检索

支持十亿级向量的毫秒级查询,满足实时 AI 应用需求

高可扩展性

云原生架构,支持水平扩展,轻松应对数据增长

丰富的索引

提供 10+ 种向量索引类型(HNSW、IVF、SCANN等),适配不同场景

多语言支持

提供 Python、Java、Go、Node.js 等主流语言 SDK

支持动态数据

完整 CRUD 操作,支持实时插入、更新、删除

混合查询

支持向量 + 标量(结构化数据)的联合过滤查询

基本架构

来自官网的核心架构图:

图片来自https://milvus.io/docs/zh/architecture_overview.md

简化版:

应用程序通过 Python 或 Java 等 SDK 发起一个查询时,请求首先经过访问层,在这里进行负载均衡、身份验证和初步的路由分配。随后,请求被递交给核心的协调层,这一层如同系统的大脑,其中的查询协调器负责分解和规划任务,数据管理元数据管理组件则负责定位数据的位置与状态。

规划好的任务被下发给底层的工作节点:查询节点 负责核心的计算工作,执行高效的向量相似性搜索和标量条件过滤;而数据节点 则扮演存储库的角色,不仅安全地保存着海量的向量和标量数据,还管理着构建好的索引。最终,各节点的结果被协调层汇总,并沿原路径返回给应用程序,从而完成一次高效、可靠的向量检索。

Milvus的常见术语

Milvus的核心架构围绕集合(Collection)组织数据。

其中,每个集合包含多个实体(Entity),实体由字段(Field)构成,其中最关键的是向量(Vector)字段用于存储嵌入表示。

通过分区(Partition)实现数据逻辑划分,段(Segment)作为物理存储单元,系统采用相似性搜索(Similarity Search)基于度量方式(Metric)计算向量距离。

向量索引(Vector Index)加速检索过程,支持多种索引类型。

在集群部署中,数据节点(Data Node)负责存储,查询节点(Query Node)处理搜索,索引节点(Index Node)构建索引,代理(Proxy)协调客户端请求。

整个系统通过嵌入(Embedding)技术将原始数据转化为向量,并支持标量过滤(Scalar Filter)进行混合查询,实现高效的向量数据管理。

术语解释:

术语

说明

类比

Collection (集合)

数据组织的最高层级,包含一组实体

数据库中的表

Entity (实体)

集合中的一条记录,由多个字段组成

表中的一行

Field (字段)

实体的属性,可以是向量或标量

表中的列

Vector (向量)

高维数值数组,表示数据的特征

128/256/768维数组

Scalar (标量)

传统数据类型(字符串、整数等)

普通数据列

Partition (分区)

集合的逻辑划分,便于数据管理

表的分区

Segment (段)

物理存储单元,数据持久化的最小单位

数据文件块

与其他向量数据库对比

维度

Milvus

Pinecone

Weaviate

开源协议

Apache 2.0

商业闭源

BSD 3

部署复杂度

中等

简单(托管)

简单

最大规模

十亿+ 向量

百万+ 向量

亿级向量

混合查询

✅ 优秀

✅ 支持

✅ 优秀

成本模型

自托管成本低

按使用量付费

自托管/托管

动手实现RAG

首先我们先列举一下使用Milvus实现简单RAG的基本步骤:

然后下面我们来动手实现。

第1步:环境准备

在上面的简单操作中我们就已经尝试了环境准备最重要的一步,就是先启动 Milvus 服务:

代码语言:bash
复制
docker run -d --name milvus -p 19530:19530 milvusdb/milvus:v2.6.9

然后对于我们的代码项目,最重要的两步就是安装Milvus客户端依赖和LLM API的调用,我们目前就选择DeepSeek作为我们的基础LLM能力。(具体调用DeepSeek API的代码我会统一上传到Github仓库中并分享给大家)

第2步:构建知识库

构建知识库主要有三步:

1)加载文档:读取并分割您的文本资料(PDF、TXT、网页等)

2)向量化处理:使用嵌入模型(如Sentence-BERT)将文本转为向量

3)存入Milvus:创建集合、定义字段,批量插入向量和原文

代码如下:

代码语言:go
复制
// 初始化知识库
func (r *RAGSystem) InitializeKnowledgeBase() error {
    ctx := context.Background()
    collectionName := r.config.CollectionName

    // 检查集合是否存在
    exists, err := r.milvusClient.HasCollection(ctx, collectionName)
    if err != nil {
        return err
    }

    // 如果集合已存在,先删除(为了演示)
    if exists {
        err = r.milvusClient.DropCollection(ctx, collectionName)
        if err != nil {
            return fmt.Errorf("删除集合失败: %w", err)
        }
    }

    // 创建集合
    err = r.milvusClient.CreateCollection(ctx, &entity.Schema{
        CollectionName: collectionName,
        Description:    "RAG演示知识库",
        Fields: []*entity.Field{
            {
                Name:       "id",
                DataType:   entity.FieldTypeVarChar,
                PrimaryKey: true,
                AutoID:     false,
                TypeParams: map[string]string{
                    "max_length": "100",
                },
            },
            {
                Name:     "title",
                DataType: entity.FieldTypeVarChar,
                TypeParams: map[string]string{
                    "max_length": "200",
                },
            },
            {
                Name:     "content",
                DataType: entity.FieldTypeVarChar,
                TypeParams: map[string]string{
                    "max_length": "10000",
                },
            },
            {
                Name:     "vector",
                DataType: entity.FieldTypeFloatVector,
                TypeParams: map[string]string{
                    "dim": "4", // 简化版,使用4维向量
                },
            },
        },
        EnableDynamicField: false,
    }, 2) // 分片数为2
    if err != nil {
        return fmt.Errorf("创建集合失败: %w", err)
    }

    // 插入示例文档
    err = r.insertSampleDocuments()
    if err != nil {
        return fmt.Errorf("插入文档失败: %w", err)
    }

    // 创建索引
    index, err := entity.NewIndexHNSW(entity.L2, 8, 64)
    if err != nil {
        return fmt.Errorf("创建索引失败: %w", err)
    }

    err = r.milvusClient.CreateIndex(ctx, collectionName, "vector", index, false)
    if err != nil {
        return fmt.Errorf("创建向量索引失败: %w", err)
    }

    return nil
}

// 插入示例文档
func (r *RAGSystem) insertSampleDocuments() error {
    ctx := context.Background()

    // 示例文档数据(包含最新信息)
    documents := []Document{
        {
            ID:      "doc_001",
            Title:   "闫同学人物介绍",
            Content: "闫同学,男,来自中国,26岁,天蝎座,是知名技术博主、摄影博主、技术爱好者,擅长写Go语言,喜欢打羽毛球。",
        },
        {
            ID:      "doc_002",
            Title:   "扯编程的淡公众号介绍",
            Content: "扯编程的淡,科技领域知名微信公众号,由闫同学运营,内容多为技术博客,日常生活感想,截止2026年1月,已有粉丝2000+。",
        },
    }

    // 为每个文档生成向量并插入
    var ids []string
    var titles []string
    var contents []string
    var vectors [][]float32

    for _, doc := range documents {
        // 生成简化向量(4维)
        vector := r.generateSimpleVector(doc.Content)

        ids = append(ids, doc.ID)
        titles = append(titles, doc.Title)
        contents = append(contents, doc.Content)
        vectors = append(vectors, vector)
    }

    // 插入数据
    idColumn := entity.NewColumnVarChar("id", ids)
    titleColumn := entity.NewColumnVarChar("title", titles)
    contentColumn := entity.NewColumnVarChar("content", contents)
    vectorColumn := entity.NewColumnFloatVector("vector", 4, vectors)

    _, err := r.milvusClient.Insert(ctx, r.config.CollectionName, "", idColumn, titleColumn, contentColumn, vectorColumn)

    if err != nil {
        return err
    }

    fmt.Printf("✅ 插入了 %d 个文档到知识库\n", len(documents))
    return nil
}
第3步:实现检索核心
  • 查询向量化:将用户问题同样转换为向量
  • 相似度搜索:在Milvus中执行ANN搜索,找到最相关的Top-K文档片段
  • 结果整理:按相关性排序,保留原文和元数据
代码语言:go
复制
func (r *RAGSystem) SearchDocuments(query string, topK int) ([]SearchResult, error) {
    ctx := context.Background()
    collectionName := r.config.CollectionName

    // 加载集合
    err := r.milvusClient.LoadCollection(ctx, collectionName, false)
    if err != nil {
        return nil, fmt.Errorf("加载集合失败: %w", err)
    }

    // 生成查询向量
    queryVector := r.generateSimpleVector(query)

    // 搜索参数
    sp, _ := entity.NewIndexHNSWSearchParam(32)

    // 执行搜索 - 根据最新SDK修正
    searchResults, err := r.milvusClient.Search(
        ctx,
        collectionName,
        nil,                                              // 分区列表
        "",                                               // 表达式
        []string{"title", "content"},                     // 输出字段
        []entity.Vector{entity.FloatVector(queryVector)}, // 查询向量
        "vector",                                         // 向量字段名
        entity.L2,                                        // 距离度量
        topK,                                             // topK
        sp,                                               // 搜索参数
    )

    if err != nil {
        return nil, fmt.Errorf("搜索失败: %w", err)
    }

    var results []SearchResult

    // 检查是否有结果
    if len(searchResults) == 0 {
        return results, nil
    }

    // 获取第一个查询的结果(因为我们只查询了一个向量)
    if len(searchResults) > 0 {
        searchResult := searchResults[0]

        // 获取ID列
        idCol, ok := searchResult.IDs.(*entity.ColumnVarChar)
        if !ok {
            return results, fmt.Errorf("ID列类型错误")
        }

        // 获取分数列和字段
        scores := searchResult.Scores
        fields := searchResult.Fields

        // 遍历所有结果
        for i := 0; i < searchResult.ResultCount; i++ {
            // 获取ID、分数
            id := idCol.Data()[i]
            score := float64(1.0 / (1.0 + scores[i]))

            // 获取标题和内容
            var title, content string
            for _, field := range fields {
                switch field.Name() {
                case "title":
                    if col, ok := field.(*entity.ColumnVarChar); ok {
                        title = col.Data()[i]
                    }
                case "content":
                    if col, ok := field.(*entity.ColumnVarChar); ok {
                        content = col.Data()[i]
                    }
                }
            }

            // 添加到结果列表
            results = append(results, SearchResult{
                Title:   title,
                Content: content,
                Score:   float32(score),
            })

            // 调试输出
            fmt.Printf("找到文档: ID=%s, Title=%s, Score=%.2f\n", id, title, score)
        }
    }
    return results, nil
}
第4步:集成LLM生成
  • 构建提示词:将检索到的文档作为上下文,与问题一起构造prompt
  • 调用大模型:使用GPT等生成基于上下文的回答
  • 格式化输出:展示答案并标注来源引用
代码语言:go
复制
func (r *RAGSystem) GetRAGAnswer(question string) (string, float64, []SearchResult, error) {
    start := time.Now()

    // 1. 检索相关文档
    results, err := r.SearchDocuments(question, 3)
    if err != nil {
        return "", 0, nil, err
    }

    // 2. 构建上下文
    var contextBuilder strings.Builder
    contextBuilder.WriteString("以下是相关文档信息:\n\n")

    for i, result := range results {
        contextBuilder.WriteString(fmt.Sprintf("文档%d: %s\n", i+1, result.Title))
        contextBuilder.WriteString(fmt.Sprintf("内容: %s\n\n", result.Content))
    }

    contextStr := contextBuilder.String()

    // 3. 调用DeepSeek生成答案
    ctx := context.Background()
    resp, err := r.openAIClient.CreateChatCompletion(ctx, openai.ChatCompletionRequest{
        Model: r.config.DeepSeekModel,
        Messages: []openai.ChatCompletionMessage{
            {
                Role:    openai.ChatMessageRoleSystem,
                Content: "你是一个严谨的AI助手,必须严格基于提供的上下文信息回答问题。如果上下文信息不足,请如实告知。不要编造上下文之外的信息。",
            },
            {
                Role:    openai.ChatMessageRoleUser,
                Content: fmt.Sprintf("上下文信息:\n%s\n\n问题:%s\n\n请基于上述上下文信息回答问题:", contextStr, question),
            },
        },
        Temperature: 0.1,
        MaxTokens:   500,
    })

    elapsed := time.Since(start).Seconds()

    if err != nil {
        return "", elapsed, results, err
    }

    if len(resp.Choices) == 0 {
        return "", elapsed, results, fmt.Errorf("未收到回答")
    }

    return resp.Choices[0].Message.Content, elapsed, results, nil
}
第5步:创建交互界面

在这个案例中由于时间关系我们并没有涉及到交互界面,但是在正常情况下一个完整的项目需要有知识库的导入和导出等等。

对比效果

我们之所以说RAG可以增强LLM回答的准确率,就是由于向量数据库可以将我们的外部数据进行存储,等到查询时在进行检索后返回,所以我们来对比下询问LLM一些特殊问题时候RAG带来的效果,我们先看数据流转图:

当我们输入问题后,系统并行执行两种处理方式。

1)纯DeepSeek路径直接调用API基于训练数据生成答案;

2)RAG路径则先将问题向量化,通过Milvus向量数据库检索相关文档,构建上下文后调用DeepSeek API生成增强答案并附带参考来源。

在用上面的知识库输入后我们可以提问两个问题:

代码语言:markdown
复制
"闫同学是谁?"
"介绍一下扯编程的淡公众号"

然后我们执行RAG流程(执行之前要调通Milvus和LLM API):

问题1:"闫同学是谁?"

代码语言:markdown
复制
🔍 获取纯DeepSeek回答:
⏱️  响应时间: 6.71秒
💬 回答: “闫同学”这个称呼比较常见,通常用于泛指一位姓“闫”的学生,没有特指某一位具体人物。如果您指的是某个特定领域的知名人物、网络热点事件中的人物,或者您身边的具体个人,请提供更多背景信息,以便我为您提供更准确的解答。

例如:
- 如果您指的是**学术或公众人物**(如科学家、作家等),可以补充其全名或领域。
- 如果您指的是**网络上的热点人物**(如社交媒体上的博主、新闻事件当事人等),可以描述相关事件。
- 如果您指的是**身边的同学或朋友**,建议直接联系对方或相关人士了解。

期待您的补充信息,我将尽力帮助您!

🔍 获取RAG增强回答:
找到文档: ID=doc_002, Title=扯编程的淡公众号介绍, Score=1.00
找到文档: ID=doc_001, Title=闫同学人物介绍, Score=1.00
⏱️  响应时间: 6.04秒
💬 回答: 闫同学是来自中国的26岁男性,天蝎座,是一名知名技术博主和摄影博主,也是技术爱好者,擅长Go语言,喜欢打羽毛球。他运营着科技领域知名微信公众号“扯编程的淡”,该公众号内容以技术博客和日常生活感想为主,截至2026年1月已有2000多名粉丝。

📄 检索到的相关文档:
  1. [相似度: 1.00] 扯编程的淡公众号介绍
     内容: 扯编程的淡,科技领域知名微信公众号,由闫同学运营,内容多为技术博客�...
  2. [相似度: 1.00] 闫同学人物介绍

📊 对比分析:
  - 时间开销: RAG比纯DeepSeek慢 -0.67秒
  - 信息质量: RAG基于 2 个相关文档生成

问题2:"介绍一下扯编程的淡公众号"

代码语言:markdown
复制
🔍 获取纯DeepSeek回答:
⏱️  响应时间: 14.38秒
💬 回答: “扯编程的淡”是一个以**幽默、轻松、生活化**的方式分享编程知识、技术观点和行业见闻的微信公众号。其内容风格独特,通常不局限于枯燥的技术教程,而是将编程与生活、职场、文化等结合,用通俗易懂的语言“扯淡”,让读者在轻松的氛围中获取信息或思考技术问题。

### 主要特点:
1. **幽默调侃的风格**  
   文章常以段子、吐槽或网络流行语开头,用轻松的语言讨论技术话题,比如用“程序员脱发”“996”等梗引发共鸣。

2. **贴近实际场景**  
   内容多围绕程序员日常(如面试、加班、技术选型)、行业热点(如AI发展、大厂动态)或学习心得,接地气且实用。

3. **非典型技术分享**  
   可能涉及技术冷知识、工具推荐、职业规划,甚至用编程思维解读生活问题(如“用算法找对象”“Debug人生”)。

4. **互动性强**  
   标题和内容常带有互动性,鼓励读者留言讨论,形成社区氛围。

### 适合人群:
- 编程初学者或学生,想以轻松方式入门技术。
- 在职程序员,希望缓解压力、寻找共鸣。
- 对科技文化感兴趣的非技术读者。

### 注意事项:
- 内容可能不够系统,适合碎片化阅读而非深度学习。
- 幽默风格可能掩盖技术深度,需结合其他资源补充学习。

若你对该公众号感兴趣,可以在微信搜索名称关注。类似风格的公众号还有“程序员小灰”“码农翻身”等,均以故事化、趣味化的方式讲解技术。

🔍 获取RAG增强回答:
找到文档: ID=doc_002, Title=扯编程的淡公众号介绍, Score=1.00
找到文档: ID=doc_001, Title=闫同学人物介绍, Score=1.00
⏱️  响应时间: 5.03秒
💬 回答: 根据提供的上下文信息,“扯编程的淡”是一个科技领域的知名微信公众号,由闫同学运营。该公众号主要发布技术博客和日常生活感想。截至2026年1月,其粉丝数量已超过2000人。

📄 检索到的相关文档:
  1. [相似度: 1.00] 扯编程的淡公众号介绍
     内容: 扯编程的淡,科技领域知名微信公众号,由闫同学运营,内容多为技术博客�...
  2. [相似度: 1.00] 闫同学人物介绍

📊 对比分析:
  - 时间开销: RAG比纯DeepSeek慢 -9.35秒
  - 信息质量: RAG基于 2 个相关文档生成

从上面的测试结果我们不难看出,使用RAG能够很大程度上的提升我们LLM回答的准确性。但前提是需要使用向量数据库为基础构建一个合理的知识库。

代码地址:

https://github.com/ibarryyan/rag-demo

小总结

从向量这一基础的数学概念,到它成为机器理解世界的“语义指纹”;从传统数据库对结构化数据的精确管理,到向量数据库对非结构化信息的语义化存储与检索——我们正见证着数据处理范式的一次根本性转移。

向量数据库并非仅仅是另一种存储工具。它通过将文本、图像等一切数据映射为高维空间中的点,并基于“距离”来衡量其语义相似性,从而解决了传统数据库无法应对的核心挑战:理解内容的意义。它因此成为AI大模型不可或缺的“长期记忆体”和“事实校验器”,通过RAG等技术,将大模型的强大生成能力与精准的外部知识结合,有效缓解了“幻觉”问题。

总而言之,向量数据库正在底层重塑我们组织、检索和利用知识的方式。它不仅是存储向量的容器,更是连接数据世界与智能世界的桥梁,是将海量非结构化数据转化为可用智能的核心基础设施。随着AI的持续演进,向量数据库作为其坚实的数据根基,必将扮演越来越重要的角色,驱动更准确、更可信、更智能的应用未来。

参考

https://www.youtube.com/watch?v=uQcBwN1PEyI

https://projector.tensorflow.org/

https://zhuanlan.zhihu.com/p/265849139

https://lakefs.io/blog/best-vector-databases/

https://www.datacamp.com/blog/the-top-5-vector-databases

https://objectbox.io/vector-database/

https://aws.amazon.com/what-is/retrieval-augmented-generation/

https://www.mindstudio.ai/blog/what-is-vector-database

https://milvus.io/docs/zh/quickstart.md

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是向量?
    • 向量的基本概念
    • 向量的表示
    • 向量在机器学习中的应用
    • 向量的常见操作
  • 回顾向量数据库的概念
    • 什么是向量数据库
    • 向量数据库和传统数据库有何不同?
  • 向量数据库在AI Agent中扮演了何种角色?
    • RAG时代的核心基础设施
    • 实际RAG应用场景
    • 从向量存储到智能检索
  • 如何选择向量数据库
    • 向量数据库对比表格
  • 使用向量数据库实现简单的RAG
    • 为什么要有RAG
      • RAG:解决幻觉的“开卷考试”方案
    • 使用Milvus实现RAG
      • 先简单介绍一下Milvus
      • 动手实现RAG
    • 对比效果
  • 小总结
  • 参考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档