从零搭建 AI 日记本:Milvus 向量数据库 + RAG 实战

从零搭建 AI 日记本:Milvus 向量数据库 + RAG 实战

用 Milvus 给 LLM 装上"记忆",实现语义搜索和智能问答。本文带你从连接数据库到完整的 RAG 管线,代码来自 demo/src/ 下的真实实现。


一、为什么 Agent 需要向量数据库?

传统 Web 应用把数据存在 MySQL 里,靠 idLIKE 关键字做增删改查。但 AI Agent 面对的是语义级别的需求:

sql 复制代码
❌ SQL:    SELECT * FROM diary WHERE content LIKE '%开心%'
✅ 语义:   "我最近心情比较好的日记有哪些?"

LIKE 只能做字符串匹配------"愉快""高兴""happy"都搜不到,而语义搜索能理解这些词表达的是同一种情绪。

Agent 把知识、记忆放在向量数据库中,做语义检索------这就是 RAG(检索增强生成)的基础设施。

ini 复制代码
Agent = LLM + Memory + Tool + RAG + MCP + Skills
                ↑
         向量数据库(Milvus)

二、Milvus 是什么?

Milvus 是一款开源向量数据库,专为海量高维向量数据设计。它解决的核心问题是:如何在百万级向量中,毫秒级找到最相似的 Top-K?

对比传统数据库:

MySQL Milvus
存什么 结构化数据(int, varchar) 高维向量(float\[\])
怎么查 WHERE id = 1 / LIKE '%关键词%' 向量相似度(余弦距离)
索引 B+Tree IVF_FLAT / HNSW
典型场景 订单、用户、商品 语义搜索、推荐、图像检索

Zilliz Cloud 是基于 Milvus 的全托管云服务,免去自建集群的运维成本。本文使用 Zilliz Cloud 的 Serverless 实例。


三、第一步:连接数据库,创建 Collection

3.1 连接 Zilliz Cloud

js 复制代码
import { MilvusClient } from '@zilliz/milvus2-sdk-node';

const client = new MilvusClient({
    address: process.env.MILVUS_ADDRESS,  // 云服务地址
    token: process.env.MILVUS_TOKEN       // API Key
});

const health = await client.checkHealth();
// { isHealthy: true } → 连接成功

Milvus 是 C/S 架构,客户端通过 gRPC 与服务端通信。checkHealth() 是连接后的第一个操作,确认集群状态正常。

3.2 创建 Collection(相当于 MySQL 的 Table)

js 复制代码
await client.createCollection({
    collection_name: 'AI_Dairy',
    fields: [
        { name: 'id', data_type: DataType.VarChar, max_length: 50, is_primary_key: true },
        { name: 'vector', data_type: DataType.FloatVector, dim: 1024 },
        { name: 'content', data_type: DataType.VarChar, max_length: 5000 },
        { name: 'date', 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 }
    ]
});

Collection 的 Schema 比 MySQL 灵活------可以在插入数据时动态添加字段,但生产环境建议显式定义。核心字段是 vector(1024 维浮点向量),这是语义搜索的基础。

3.3 创建索引

js 复制代码
await client.createIndex({
    collection_name: 'AI_Dairy',
    field_name: 'vector',
    index_type: IndexType.IVF_FLAT,
    metric_type: MetricType.COSINE
});

没有索引时,每次查询都要把库里所有向量和查询向量逐一算相似度(O(n))。索引就像图书馆的分类系统------先缩小范围再精排:

复制代码
图书检索类比:
  无索引 → 每本书翻一遍
  IVF_FLAT → 文学馆 → 小说 → 科幻 → 找到《三体》

四、第二步:向量化并写入数据

4.1 文本转向量(Embedding)

LLM 只能处理数字。Embedding 模型把文本映射到高维向量空间,语义相近的文本向量距离也更近:

js 复制代码
import { OpenAIEmbeddings } from '@langchain/openai';

const embeddings = new OpenAIEmbeddings({
    apiKey: process.env.OPENAI_API_KEY,
    model: 'text-embedding-v3',           // 阿里云 DashScope
    configuration: { baseURL: process.env.OPENAI_BASE_URL },
    dimensions: 1024
});

const getEmbedding = async (text) => {
    return await embeddings.embedQuery(text);  // "今天很开心" → [0.12, -0.34, ...] (1024维)
};

这里用阿里云的 text-embedding-v3,通过 OpenAI 兼容接口调用。LangChain 的 OpenAIEmbeddings 封装了调用细节。

4.2 批量写入日记

js 复制代码
const diaryContents = [
    { id: 'diary_001', content: '今天天气很好,去公园散步了,心情愉快...', date: '2026-01-10', mood: 'happy', tags: ['生活', '散步'] },
    { id: 'diary_002', content: '今天工作很忙,完成了一个重要的项目里程碑...', date: '2026-01-11', mood: 'excited', tags: ['工作', '成就'] },
    // ... 5 条日记
];

const diaryData = await Promise.all(
    diaryContents.map(async (diary) => ({
        ...diary,
        vector: await getEmbedding(diary.content)  // 每条的 content → 向量
    }))
);

const result = await client.insert({ collection_name: 'AI_Dairy', data: diaryData });
console.log(`${result.insert_cnt} 条记录成功插入`);

对比 MySQL 的 INSERT INTO ... VALUES (...),Milvus 的插入同样简单------给 JSON 就行,不用写 SQL。Promise.all 并发调用 Embedding API,5 条的向量化总耗时 = max(单次耗时),而非累加。


五、第三步:语义搜索

向量入库后,就可以用自然语言搜日记了:

js 复制代码
const query = '我想看看关于户外活动的日记';
const queryVector = await getEmbedding(query);  // 查询也要向量化

const result = await client.search({
    collection_name: 'AI_Dairy',
    vector: queryVector,
    limit: 2,
    metric_type: MetricType.COSINE,   // 余弦相似度
    output_fields: ['id', 'content', 'date', 'mood', 'tags']
});

// 输出:
// 1. [Score]: 0.8734 --- diary_003: 周末和朋友去爬山,天气很好...
// 2. [Score]: 0.7652 --- diary_001: 今天天气很好,去公园散步了...

整个过程:

css 复制代码
用户问题 "户外活动"
  → Embedding 模型 → [0.23, -0.45, ...]
    → Milvus 索引检索(IVF_FLAT + COSINE)
      → 返回 Top-2 最相似日记

查询里没有出现"爬山""散步"这些词,但向量相似度能理解"户外活动"和它们语义相关。这就是向量数据库相比 LIKE 关键字的质变。


六、第四步:RAG ------ 让 LLM 基于日记回答问题

单独检索出日记还不够,最终目标是让 LLM 像真人一样回答。这就进入 RAG 的完整管线:

复制代码
用户问题 → Embedding → Milvus 检索 → 拼入 Prompt → LLM 生成回答

6.1 检索模块

js 复制代码
async function retrieveRelevantDiaries(question, k = 2) {
    const queryVector = await getEmbedding(question);
    const result = await client.search({
        collection_name: 'AI_Dairy',
        vector: queryVector,
        limit: k,
        metric_type: MetricType.COSINE,
        output_fields: ['id', 'content', 'date', 'mood', 'tags']
    });
    return result.results;
}

6.2 生成模块

js 复制代码
const model = new ChatOpenAI({
    temperature: 0.1,       // 低温度 = 更稳定的回答
    model: 'qwen-plus',     // 阿里云
    apiKey: process.env.OPENAI_API_KEY,
    configuration: { baseURL: process.env.OPENAI_BASE_URL }
});

async function answerDiaryQuestion(question, k = 2) {
    const diaries = await retrieveRelevantDiaries(question, k);

    // 把检索结果拼成上下文
    const context = diaries.map((d, i) => `
        [日记 ${i + 1}]
        日期: ${d.date}  心情: ${d.mood}  标签: ${d.tags?.join(',')}
        内容: ${d.content}
    `).join('\n\n----\n\n');

    const prompt = `你是一个温暖、贴心的 AI 日记助手。请根据以下日记内容回答:
        ${context}
        用户问题:${question}
        回答要求:
        1. 结合日记内容给出详细、温暖的回答
        2. 可以总结多篇日记,找出共同点或趋势
        3. 用第二人称"你"来称呼作者
        4. 有同理心,让用户感到被理解
        AI 助手的回答:`;

    const response = await model.invoke(prompt);
    return response.content;
}

6.3 运行效果

js 复制代码
await answerDiaryQuestion('我最近做了什么让我开心的事情?', 2);

// ================================================================================
// 问题:我最近做了什么让我开心的事情?
// ================================================================================
// 检索相关日记
// 日记1 相似度: 0.8521 --- 今天天气很好,去公园散步了,心情愉快。
// 日记2 相似度: 0.7834 --- 晚上做了一顿丰盛的晚餐,尝试了新菜谱。家人都说很好吃。
//
// [AI 回答]
// 最近你做了几件让人开心的事情呢!1 月 10 号那天天气很好,你去公园散步,
// 看到了很多花开了,享受了春天的美好。还有一天晚上,你尝试了新菜谱,
// 做了一顿丰盛的晚餐,家人都很喜欢------这种被认可的感觉一定很温暖吧!
// 看起来你是个懂得在生活中寻找小确幸的人~

RAG 管网线完整跑通了: 用户的自然语言问题 → 向量检索 → 拼入 Prompt → LLM 生成带上下文的温暖回复。


七、总结

一条主线串起全部知识点:

markdown 复制代码
传统数据库(MySQL)
  → 无法做语义搜索
    → 引入向量数据库(Milvus)
      → Embedding 模型把文本转为向量
        → 向量入库 + 建索引(IVF_FLAT)
          → 用户问题向量化 → 相似度检索
            → 检索结果拼入 Prompt
              → LLM 生成基于真实日记的回答(RAG)

Milvus 和 MySQL 不是替代关系,而是互补:

  • MySQL 管结构化数据:用户表、订单表、日记的 CRUD
  • Milvus 管语义数据:知识检索、记忆搜索、推荐匹配

这就是 Agent 记忆系统的核心基础设施。Cursor、Claude Code 等产品背后,都是同一套"向量数据库 + RAG"的逻辑------只是工程规模更大,知识库更复杂。


本文涉及的源文件:demo/src/main.mjsdemo/src/index.mjsdemo/src/query.mjsdemo/src/rag.mjs

技术栈:Milvus / Zilliz Cloud · LangChain · OpenAI Embeddings · 阿里云 DashScope

相关推荐
夜郎king2 小时前
解决AI图文解析偏差:CodeBuddy多模型交叉校验+腾讯地图Skill经纬度定位实战
大数据·人工智能
智慧物业老杨2 小时前
物业费公共收益维修资金三类资金分账的合规管控
大数据·人工智能·物联网
kingcjh973 小时前
具身智能数据处理的工程化实践
人工智能·数据挖掘
dadaobusi3 小时前
Sniper
人工智能
AI人工智能集结号3 小时前
AI回答采集:基于PostgreSQL JSONB的原始数据存储与可追溯性方案
数据库·人工智能·postgresql
WeilinerL3 小时前
CodeGraph:让AI理解代码仓库的神器
前端·人工智能
DFT计算杂谈3 小时前
交错磁研究进展材料物性与交叉应用
数据库·人工智能·python·opencv·算法
CCYe、4 小时前
企业 AI 网关采购需求清单:一份集团级 RFP 的能力映射
人工智能
xfan_me4 小时前
汽车维保记录精准版 API 快速接入指南
人工智能·汽车