🌞 我造了一个"AI 日记本":用向量数据库 Milvus 让 AI 能"记住"你的心情

1 阅读8分钟

写在前面:之前我们做 RAG,向量存在内存里(MemoryVectorStore),数据一重启就没了。老师说:"内存向量存储是学习用的,生产环境要用真正的向量数据库。" 今天的主角是 Milvus(Zilliz Cloud)——一个专为海量高维向量数据设计的开源数据库。我们做了一个"AI 日记本":把日记存进 Milvus,然后问 AI "我最近做了什么快乐的事",AI 自动检索相关日记、结合内容、生成温暖的回答。当看到 AI 准确地说出"你 1 月 10 日去公园散步很开心,1 月 13 日做晚饭很有成就感"时,我整个人都惊了——这不就是 AI 版的"懂你"吗?


一、从内存到真正的向量数据库

1.1 之前的问题

之前我们做 RAG,用的是 MemoryVectorStore

const vectorStore = await MemoryVectorStore.fromDocuments(documents, embeddings);

老师说:

"内存向量数据库——学习用,轻量。生产环境要用真正的向量数据库,像 MySQL 之于 Web 应用一样。"

内存向量存储的问题:

问题说明比喻
数据不持久服务重启,数据全丢写在沙子上,潮水一冲就没了
容量有限受内存限制,不能存太多书包就这么大,装不了太多书
不能共享多台服务器不能共用你这本笔记只有你自己能看

1.2 Milvus 是什么?

老师说:

"Milvus 是一款开源的向量数据库,专为处理海量高维向量数据而设计。AI Agent 产品都会使用 Milvus 这样的 Vector Store。像 Web 应用会把数据存在 MySQL 里面,基于 CRUD 实现各种业务功能。Agent 会把知识、记忆放在 Milvus 数据库中,对知识做语义检索、增删改查等各种功能。"

MySQL : Web 应用 = Milvus : AI 应用

对比MySQLMilvus
存什么结构化数据(用户、订单)高维向量(文本的语义向量)
查什么WHERE name LIKE '%关键词%'余弦相似度找语义相近的向量
索引方式B+ 树IVF_FLAT、AUTOINDEX 等向量索引
典型应用电商、CMSRAG、推荐系统、AI 记忆

我们用 Zilliz Cloud——基于 Milvus 的全托管云服务,不用自己部署服务器。


二、连接 Milvus 并创建集合

2.1 连接 Zilliz Cloud

main.mjs

import { MilvusClient } from '@zilliz/milvus2-sdk-node';
import dotenv from 'dotenv/config';

const ADDRESS = process.env.MILVUS_ADDRESS;
const TOKEN = process.env.MILVUS_TOKEN;
const COLLECTION_NAME = 'ai_dairy';
const VECTOR_DIM = 1024;

const client = new MilvusClient({
    address: ADDRESS,
    token: TOKEN,
});

async function main() {
    const checkHealth = await client.checkHealth();
    if (!checkHealth.isHealthy) {
        console.error('链接失败', checkHealth.reasons);
        return;
    }
    console.log('链接成功,集群状态正常。');
}

MilvusClient 是连接 Milvus 的客户端,checkHealth() 检查服务是否正常。

2.2 创建集合(Collection)

老师说:

"MySQL 里叫表(Table),Milvus 里叫集合(Collection)。"

// 先删除旧集合(如果有),再创建
try {
    await client.dropCollection({ collection_name: COLLECTION_NAME });
} catch (_) {}

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 },
        { name: 'content', data_type: DataType.VarChar, max_length: 5000 },
        { name: 'data', data_type: DataType.VarChar, max_length: 50 },
        { name: 'mood', data_type: DataType.VarChar, max_length: 50 },
        { name: 'tags', data_type: DataType.Array, element_type: DataType.VarChar, max_capacity: 10, max_length: 50 },
    ]
});

集合的字段定义就像 MySQL 建表时的字段声明:

字段类型说明
idVarChar主键,唯一标识
vectorFloatVector(1024)日记内容的语义向量
contentVarChar日记原文
dataVarChar日记日期
moodVarChar心情标签
tagsArray of VarChar标签数组

2.3 创建索引

await client.createIndex({
    collection_name: COLLECTION_NAME,
    field_name: 'vector',
    index_type: IndexType.IVF_FLAT,
    metric_type: MetricType.COSINE,
});

await client.loadCollection({
    collection_name: COLLECTION_NAME,
});

老师说:

"没有索引时,每次查询都要把库里的向量和查询的向量逐一算相似度(O(n)),数据量大了,慢得没法用。就像去图书馆找一本《三体》——没有索引,每本都要翻一下。有索引:文学馆 → 小说 → 科幻,迅速缩小范围。"

IVF_FLAT 是一种聚类索引,把相似的向量聚在一起,搜索时先找最近的聚类,再在聚类里细找——从 O(n) 变成 O(log n)。


三、插入数据:把日记向量化存进去

3.1 准备日记内容

const diaryContents = [
    {
        id: 'diary_001',
        content: '今天天气很好,去公园散步了,心情愉快。看到了很多花开了,春天真美好。',
        data: '2026-01-10',
        mood: 'happy',
        tags: ['生活', '散步']
    },
    {
        id: 'diary_002',
        content: '今天工作很忙,完成了一个重要的项目里程碑。团队合作很愉快,感觉很有成就感。',
        data: '2026-01-11',
        mood: 'excited',
        tags: ['工作', '成就']
    },
    {
        id: 'diary_003',
        content: '周末和朋友去爬山,天气很好,心情也很放松。享受大自然的感觉真好。',
        data: '2026-01-12',
        mood: 'relaxed',
        tags: ['户外', '朋友']
    },
    {
        id: 'diary_004',
        content: '今天学习了 Milvus 向量数据库,感觉很有意思。向量搜索技术真的很强大。',
        data: '2026-01-12',
        mood: 'curious',
        tags: ['学习', '技术']
    },
    {
        id: 'diary_005',
        content: '晚上做了一顿丰盛的晚餐,尝试了新菜谱。家人都说很好吃,很有成就感。',
        data: '2026-01-13',
        mood: 'proud',
        tags: ['美食', '家庭']
    }
];

3.2 向量化并插入

const diaryData = await Promise.all(
    diaryContents.map(async (diary) => ({
        ...diary,
        vector: await getEmbeddings(diary.content)
    }))
);

const insertResult = await client.insert({
    collection_name: COLLECTION_NAME,
    data: diaryData,
});

console.log(insertResult.insert_cnt, '条记录成功插入。');

每条日记在入库前,先通过 Embedding 模型把 content 转成 1024 维向量。 这样后面搜索时,就能用"语义"而不是"关键词"来查找了。


四、语义搜索:问一个自然语言问题

4.1 搜索函数

async function retrieveRelatedDiaries(question, k = 2) {
    const queryVector = await getEmbeddings(question);
    const searchResult = await client.search({
        collection_name: COLLECTION_NAME,
        vector: queryVector,
        limit: k,
        metric_type: MetricType.COSINE,
        output_fields: ['id', 'content', 'data', 'mood', 'tags'],
    });
    return searchResult.results;
}

搜索流程:

用户问:"我最近做了什么快乐的事情?"
  ↓ Embedding 模型
"快乐" → [1024 维向量]
  ↓ Milvus 搜索
在向量库里找余弦相似度最高的 2 条
  ↓ 返回
日记 001(散步,相似度 0.92)和 日记 005(做饭,相似度 0.85

4.2 搜索结果的相似度评分

searchResult.results.forEach((item, index) => {
    console.log(`${index + 1}. [Score: ${item.score.toFixed(4)}]`);
    console.log(`Content: ${item.content}`);
});

输出:

1. [Score: 0.9234]
   Content: 今天天气很好,去公园散步了,心情愉快……
2. [Score: 0.8543]
   Content: 晚上做了一顿丰盛的晚餐,尝试了新菜谱……

相似度得分(Score)越接近 1,说明和问题语义越相关。 0.92 表示"去公园散步"和"快乐"高度相关。


五、RAG 增强生成:AI 结合日记回答问题

5.1 构建 Prompt

const content = retrievedDiaries
    .map((diary, i) => `
        [日记 ${i+1}] 
        日期: ${diary.data}
        心情: ${diary.mood}
        标签: ${diary.tags?.join(', ')}
        内容: ${diary.content}
    `).join('\n\n----\n\n');

const prompt = `你是一个温暖贴心的 AI 日记助手。基于用户的日记内容回答问题。

${content}

用户问题:${question}
回答要求:
1. 如果日记中有相关信息,请结合日记内容给出详细、温暖的回答。
2. 可以总结多篇日记的内容,找出共同点或趋势。
3. 用第二人称"你"来称呼日记的作者。
AI 助手的回答:`;

const response = await model.invoke(prompt);
console.log(response.content);

5.2 AI 的回答

当问题为 "我最近做了什么快乐的事情?" 时,AI 的回答大概是这样:

"我翻看了你最近的日记,发现你做了不少快乐的事情呢!🎉

1 月 10 日,你趁着好天气去公园散了步,看到花儿盛开,心情非常愉快 🌸 1 月 13 日,你挑战了新菜谱,做了一顿丰盛的晚餐,家人都说好吃,你特别有成就感 👨‍🍳

看起来你是一个懂得从生活小事中获取快乐的人。继续保持这种热爱生活的态度吧!😊"

这就是 RAG 的完整流程:检索(Retrieval)→ 增强(Augmented)→ 生成(Generation)。


六、完整的数据流

用户问:"我最近做了什么快乐的事情?"
  ↓
1. 问题向量化(Embedding)
  ↓
2. Milvus 语义搜索(余弦相似度)
  ↓  
3. 找到最相关的 2 篇日记
  ↓
4. 把日记内容组装成 Prompt
  ↓
5. LLM 基于日记生成回答
  ↓
输出温暖的回答

七、总结:Milvus = AI 应用的"MySQL"

概念说明
Milvus开源向量数据库,存高维向量
Zilliz CloudMilvus 的全托管云服务
Collection类似 MySQL 的表
Field字段定义,含类型和约束
Vector1024 维的 Float 数组
IndexIVF_FLAT 等索引,加速搜索
Cosine Similarity余弦相似度,度量语义距离
Score相似度评分,越接近 1 越相关
RAG检索 + 增强 + 生成

MemoryVectorStoreMilvus,是从"玩具"到"产品"的跨越。真正生产级的 RAG 应用,都需要一个像 Milvus 这样的向量数据库来持久化存储和高效检索知识。


写在最后

今天最大的收获,是把向量存储从内存搬到了真正的向量数据库里。以前做的 RAG 都是"一次性"的——数据重启就没了。现在有了 Milvus,日记存进去就"永久"了,随时可以问 AI "我最近心情怎么样"、"我学到什么新东西了"。

而且 Milvus 的 SDK 用起来和 MySQL 的驱动很像——连接、建表、插数据、查数据,思路是一样的。如果你会 MySQL,学会 Milvus 只是换几个 API 名字的事。

下次面试官问你:"生产环境怎么做 RAG?"

你可以淡定地说:

"生产环境的 RAG 需要用真正的向量数据库,比如 Milvus(或 Zilliz Cloud)。流程是:① 将知识文档通过 Embedding 模型转成高维向量,存入 Milvus Collection;② 建索引(如 IVF_FLAT)加速搜索;③ 用户提问时,把问题也向量化,在 Milvus 中做余弦相似度搜索,找到最相关的 K 条记录;④ 把检索到的内容作为上下文组装成 Prompt,交给 LLM 生成回答。MemoryVectorStore 适合学习和原型验证,Milvus 适合生产部署。"

然后看着面试官满意的表情,心里默念:这波,又稳了。


本文所有代码示例均来自课堂学习资料,真实可运行。