恐怖解谜密室逃脱
109.73M · 2026-03-09
嗨,前端的小伙伴们!作为 React 开发者,你一定对 State、Props、Component 如数家珍。但在这个 AI 席卷一切的时代,你是否想过:如何给你的应用装上一个“无限容量的大脑”?
你可能已经接入了 OpenAI 的 API,做过 Chatbot。但你有没有发现,当你问 AI 一些它训练数据之外的知识(比如你们公司的内部文档、你的私人日记)时,它就开始一本正经地胡说八道了? 这就是 “幻觉” (Hallucination)。
为了解决这个问题,RAG (Retrieval-Augmented Generation,检索增强生成) 技术横空出世。而在 RAG 的架构中,有一个核心组件,它像海马体一样负责长时记忆,它就是——向量数据库 (Vector Database)。
今天,我们将以 Milvus 为例,带你从零开始构建一个“AI 日记助手”的后端核心。别担心,我们用的是大家最熟悉的 JavaScript (Node.js)!Let's Rock!
Milvus 是世界上最流行的开源向量数据库之一。在 AI Agent 产品(如 Agentci AI)中,Milvus 几乎是标配。
想象一下你走进图书馆:
| 特性 | 普通数据库 (Relational DB) | 向量数据库 (Vector DB) |
|---|---|---|
| 核心数据 | 结构化数据 (String, Int, Boolean) | 向量 (Vectors/Embeddings) |
| 查询方式 | 精确匹配 (WHERE id = 1) | 相似度搜索 (KNN, ANN) |
| 擅长领域 | 事务处理、精确记录 | 非结构化数据检索 (文本、图像、视频搜索) |
| 底层逻辑 | B+ 树等索引 | 倒排索引、量化索引 (IVF, HNSW) |
对于前端开发者来说,你可以这样理解:
我们将基于一段真实的 Node.js 代码 demo/1.mjs 来讲解。这段代码演示了如何将几篇日记存入 Milvus,并根据用户的自然语言描述(比如“我想看户外的日记”)找到对应的记录。
首先,看代码的前几行。这就像我们在 React 里引入组件一样,我们要引入 Milvus 的 SDK。
// c:UsersMRDesktopworkspacelesson_jpaiagentmilvus-testdemo1.mjs
import {
MilvusClient,
DataType,
IndexType,
MetricType
} from '@zilliz/milvus2-sdk-node';
深度解析:
MilvusClient: 我们的主角,用于连接 Milvus 服务器。DataType: 划重点! 这是 Milvus 的数据类型定义。
FloatVector: 核心中的核心,浮点向量。用来存储 Embedding 后的数组。VarChar, Int64: 标量字段,用于存储元数据(比如日记的标题、日期)。Array: 数组类型,比如标签 ['开心', '旅游']。IndexType: 索引类型。没有索引,查询就像在图书馆一本本翻书;有了索引,就像查目录。MetricType: 度量类型。决定了怎么计算两个向量像不像(是算距离还是算角度)。// c:UsersMRDesktopworkspacelesson_jpaiagentmilvus-testdemo1.mjs
const VECTOR_DIM = 1536;
硬核知识点:为什么是 1536? 这个数字不是随便写的!它必须与你使用的 Embedding 模型输出的维度严格一致。
text-embedding-3-small 或 text-embedding-ada-002,它们生成的向量就是 1536 维的数组。接下来,我们要连接到 Milvus 实例。
// c:UsersMRDesktopworkspacelesson_jpaiagentmilvus-testdemo1.mjs
const client = new MilvusClient({
address: ADDRESS,
token: TOKEN,
});
这里的 ADDRESS 和 TOKEN 可以在 Zilliz Cloud (Milvus 的托管云服务) 上获取。
Zilliz Cloud: 这是一个全托管的 Milvus 服务,省去了自己维护 Docker/K8s 的痛苦。
in03-ebb1...zilliz.com.cn:19530。连接之后,一定要做健康检查!这就好比 React 组件 componentDidMount 后先看看数据回没回来。
// c:UsersMRDesktopworkspacelesson_jpaiagentmilvus-testdemo1.mjs
console.log("正在连接Milvus...");
const checkHealth = await client.checkHealth();
if(!checkHealth) {
console.error('连接Milvus失败');
return;
}
console.log('连接Milvus成功');
在 Milvus 里,Table 被称为 Collection(集合)。
// c:UsersMRDesktopworkspacelesson_jpaiagentmilvus-testdemo1.mjs
await client.createCollection({
collection_name: COLLECTION_NAME,
fields: [
{
name: 'id',
data_type: DataType.VarChar, // 主键
max_length: 50,
is_primary_key: true,
},
{
name: 'vector',
data_type: DataType.FloatVector, // 核心:存储向量
dim: VECTOR_DIM, // 必须匹配!1536
},
{
name: 'content',
data_type: DataType.VarChar, // 存储原始文本
max_length: 5000,
},
// ... 其他字段 (date, mood, tags)
]
});
设计哲学:
我们不仅存了 vector,还存了 content、mood 等。为什么?
这叫 “标量与向量混合查询” (Hybrid Search)。
比如用户搜:“开心的户外运动”。
mood == 'happy' 的记录。
Milvus 支持在一次查询中同时搞定这两件事,效率极高。数据存进去了,如果不建索引,查询就是暴力扫描(Brute-force),速度慢且消耗资源。
// c:UsersMRDesktopworkspacelesson_jpaiagentmilvus-testdemo1.mjs
await client.createIndex({
collection_name: COLLECTION_NAME,
field_name: 'vector', // 对向量字段建索引
index_type: IndexType.IVF_FLAT,
metric_type: MetricType.COSINE,
params: {
nlist: VECTOR_DIM, // 聚类参数
}
})
硬核参数解析:
IndexType.IVF_FLAT: 倒排文件索引。
MetricType.COSINE (余弦相似度):
// c:UsersMRDesktopworkspacelesson_jpaiagentmilvus-testdemo1.mjs
await client.loadCollection({
collection_name: COLLECTION_NAME,
})
️ 注意:在 Milvus 中,数据存储(磁盘)和计算(内存)是分离的。创建了 Collection 和 Index 后,数据只是静静地躺在磁盘里。要进行搜索,必须显式地调用 loadCollection 把索引加载到内存中。不 Load 直接搜,会报错!
这里我们准备了一些日记数据。
// c:UsersMRDesktopworkspacelesson_jpaiagentmilvus-testdemo1.mjs
const diaryContents = [
{
id: 'diary_001',
content: '今天天气很好,去公园散步了...',
// ...
tags: ['生活', '散步']
},
// ... 更多日记
];
接下来是最关键的一步:Embedding。我们需要调用 OpenAI 的接口,把 content 里的中文变成 1536 维的向量。
// c:UsersMRDesktopworkspacelesson_jpaiagentmilvus-testdemo1.mjs
const diaryData = await Promise.all(
diaryContents.map(async (entry) => ({
...entry,
vector: await getEmbeddings(entry.content), // 转换核心
}))
);
这一步是 RAG 的“写入”流程:Raw Text -> Embedding Model -> Vector -> DB。
// c:UsersMRDesktopworkspacelesson_jpaiagentmilvus-testdemo1.mjs
const inserRes = await client.insert({
collection_name: COLLECTION_NAME,
data: diaryData
})
insert 操作是原子的。Milvus 是列式存储,但在 SDK 层面,我们通常以行(Row-based)的形式传入 JSON 数组,SDK 会自动帮我们转换。
用户输入了:“我想看看关于户外活动的日记”。 这句自然语言,数据库是看不懂的。我们必须先把它也变成向量!
// c:UsersMRDesktopworkspacelesson_jpaiagentmilvus-testdemo1.mjs
const query = "我想看看关于户外活动的日记";
const queryVector = await getEmbeddings(query); // 1. 问题向量化
然后,拿着这个 queryVector 去数据库里找“长得像”的向量。
// c:UsersMRDesktopworkspacelesson_jpaiagentmilvus-testdemo1.mjs
const searchResult = await client.search({
collection_name: COLLECTION_NAME,
vectors: queryVector, // 注意这里可以一次传多个向量进行批量搜
limit: 3, // Top K:只返回最相似的 3 条
metric_type: MetricType.COSINE, // 必须和建表时一致
output_fields: ['id', 'content', 'date', 'mood', 'tags'], // 类似于 SQL 的 SELECT *
})
最后,打印结果:
// c:UsersMRDesktopworkspacelesson_jpaiagentmilvus-testdemo1.mjs
searchResult.results.forEach(result => {
console.log(`n 日记ID: ${result.id}`);
console.log(`内容: ${result.content}`);
console.log(`日期: ${result.date}`);
console.log(`心情: ${result.mood}`);
console.log(`标签: ${result.tags}`);
})
恭喜你! 你已经掌握了构建 RAG 应用最核心的后端技能。
回顾一下流程:
对于 React 开发者来说,理解这个流程至关重要。未来,你的 React 应用前端发出的请求,不再只是简单的 CRUD,而是携带语义的对话。而后端,将通过 Milvus 这样的向量数据库,赋予 AI 真正的“记忆”和“知识”。
现在,去申请一个 Zilliz Cloud 的免费实例,把这段代码跑起来吧!未来的 AI 全栈大神,就是你!