手把手教你用 Milvus + LangChain 搭建《天龙八部》RAG 知识库
RAG 检索 生成向量数据库是桥梁LangChain 是胶水而你的业务数据才是灵魂。你是否曾幻想过像问 ChatGPT 一样问一本小说“段誉会什么武功”然后它不仅能给出答案还能告诉你这句话出自第几章这就是 RAG检索增强生成的典型应用——把静态文档变成可交互的知识库。本文将带你从零开始用MilvusLangChain搭建一个“天龙八部问答机器人”。全文实战优先不但给出可运行的代码还会逐函数解析关键 API并用流程图帮你理清整个流程。读完你不仅会跑还会调优顺带带走几个可以直接复用的设计模式。一、项目全景我们到底在做什么RAG 的核心三步离线建库将电子书加载、分块、向量化存入向量数据库。在线检索用户提问时将问题向量化从数据库中检索最相关的几个片段。生成答案将检索到的片段拼接成上下文喂给大模型生成最终回答。整个项目包含两个主要脚本main.mjs负责建库加载 EPUB、分块、生成向量、插入 Milvus。rag.mjs负责问答检索 LLM 生成。下面我们按照流程图的顺序逐一拆解。二、整体流程先看全景图上半部分是离线建库下半部分是在线问答。我们按顺序展开。三、准备工作环境与依赖3.1 安装依赖npm install zilliz/milvus2-sdk-node langchain/openai langchain/community langchain/textsplitters dotenv3.2 环境变量.envMILVUS_ADDRESShttps://your-instance.region.zillizcloud.com:19530 MILVUS_TOKENyour-api-token OPENAI_API_KEYsk-xxx OPENAI_BASE_URLhttps://api.openai.com/v1 # 代理可替换 EMBEDDINGS_MODEL_NAMEtext-embedding-3-small MODEL_NAMEgpt-4o-mini3.3 初始化核心客户端import dotenv/config; import { MilvusClient, DataType, MetricType, IndexType } from zilliz/milvus2-sdk-node; import { OpenAIEmbeddings, ChatOpenAI } from langchain/openai; import { EPubLoader } from langchain/community/document_loaders/fs/epub; import { RecursiveCharacterTextSplitter } from langchain/textsplitters; const COLLECTION_NAME ebook; const VECTOR_DIM 1024; const CHUNK_SIZE 500; const EPUB_FILE ./天龙八部.epub; // Milvus 客户端注意地址处理 const client new MilvusClient({ address: process.env.MILVUS_ADDRESS.replace(/^https?:///, ), token: process.env.MILVUS_TOKEN, ssl: true }); // Embedding 模型指定维度 const embeddings new OpenAIEmbeddings({ apiKey: process.env.OPENAI_API_KEY, model: process.env.EMBEDDINGS_MODEL_NAME, configuration: { baseURL: process.env.OPENAI_BASE_URL }, dimensions: VECTOR_DIM }); // LLM用于问答 const model new ChatOpenAI({ temperature: 0.1, model: process.env.MODEL_NAME, apiKey: process.env.OPENAI_API_KEY, configuration: { baseURL: process.env.OPENAI_BASE_URL } });注意text-embedding-3-small默认输出 1536 维我们通过dimensions: 1024截断减少存储和计算开销同时保持语义质量。四、离线建库核心函数详解4.1 集合管理ensureCollection在插入数据前必须确保 Milvus 集合存在并已创建索引且加载到内存。这个函数承担了“守门员”的角色。async function ensureCollection(bookId) { try { // 1. 检查集合是否已存在幂等性 const hasCollection await client.hasCollection({ collection_name: COLLECTION_NAME }); if (!hasCollection.value) { console.log(创建集合...); // 2. 定义 Schema字段结构 await client.createCollection({ collection_name: COLLECTION_NAME, fields: [ { name: id, data_type: DataType.VarChar, max_length: 100, is_primary_key: true }, { name: book_id, data_type: DataType.VarChar, max_length: 100 }, { name: book_name, data_type: DataType.VarChar, max_length: 200 }, { name: chapter_num, data_type: DataType.Int32 }, { name: index, data_type: DataType.Int32 }, { name: content, data_type: DataType.VarChar, max_length: 10000 }, { name: vector, data_type: DataType.FloatVector, dim: VECTOR_DIM } ] }); console.log(集合创建成功); // 3. 创建向量索引搜索加速的关键 console.log(创建索引...); await client.createIndex({ collection_name: COLLECTION_NAME, field_name: vector, index_type: IndexType.IVF_FLAT, metric_type: MetricType.COSINE, params: { nlist: 1024 } }); console.log(索引创建成功); } // 4. 加载集合到内存搜索前必须 try { await client.loadCollection({ collection_name: COLLECTION_NAME }); console.log(集合加载成功); } catch (err) { // 若已加载会报错忽略即可 console.error(集合可能已加载); } } catch (err) { console.error(创建集合时出错:, err); } }API 解析hasCollection检查集合是否存在返回{ value: boolean }。createCollection定义字段is_primary_key必须指定一个字段我们使用字符串组合 ID 确保唯一性。createIndex索引类型IVF_FLAT是倒排索引 扁平量化适合中等规模数据nlist是聚类个数一般取sqrt(数据量)。metric_type选择COSINE因为 OpenAI 的向量已归一化。loadCollection将索引加载到内存只有加载后才能搜索。重复加载会抛异常所以我们用 try-catch 包裹。为什么需要先创建索引再插入数据因为 Milvus 支持动态建索引但如果在插入后再创建需要重建索引会额外耗时。我们建议先建索引再插入这样插入时就会自动构建增量索引。4.2 流式加载与分块loadAndProcessEPubStreaming这个函数负责加载 EPUB、按章节拆分、分块、调用insertChunksBatch入库。我们采用“逐章处理”的策略避免一次性将所有章节内容加载到内存。async function loadAndProcessEPubStreaming(bookId) { try { console.log(加载 EPUB: ${EPUB_FILE}); const loader new EPubLoader(EPUB_FILE, { splitChapters: true // 按目录自动拆分章节 }); const documents await loader.load(); // 每个元素是一个 Document一章节 console.log(加载到 ${documents.length} 个章节); const textSplitter new RecursiveCharacterTextSplitter({ chunkSize: CHUNK_SIZE, chunkOverlap: 50 }); let totalInserted 0; for (let chapterIndex 0; chapterIndex documents.length; chapterIndex) { const chapter documents[chapterIndex]; const chapterContent chapter.pageContent; console.log(处理第 ${chapterIndex 1}/${documents.length} 章...); // 分块 const chunks await textSplitter.splitText(chapterContent); console.log(拆分为 ${chunks.length} 个片段); if (chunks.length 0) continue; // 批量插入 const insertedCount await insertChunksBatch( chunks, bookId, chapterIndex 1 ); totalInserted insertedCount; console.log(已插入 ${totalInserted} 条记录); } console.log(总共插入 ${totalInserted} 条记录); return totalInserted; } catch (err) { console.error(处理 EPUB 时出错:, err); } }设计要点EPubLoader的splitChapters: true会解析目录每个章节生成一个独立的DocumentpageContent存储纯文本。RecursiveCharacterTextSplitter默认分隔符为[\n\n, \n, , ]它会递归尝试保证每个块不超过chunkSize同时chunkOverlap让边界信息得以保留。我们逐章处理处理完一章即释放该章节的引用控制内存峰值。为什么不用loadAndSplit因为我们需要在插入时携带章节号自定义 ID所以手动遍历更灵活。4.3 批量插入与向量化insertChunksBatch这个函数负责将一章内的所有 chunk 转为向量并批量插入 Milvus。它是性能优化的核心。async function insertChunksBatch(chunks, bookId, chapterNum) { try { if (chunks.length 0) return 0; // 并发生成向量注意并发数控制 const insertData await Promise.all( chunks.map(async (chunk, chunkIndex) { const vector await embeddings.embedQuery(chunk); return { id: ${bookId}_${chapterNum}_${chunkIndex}, book_id: bookId, book_name: 天龙八部, chapter_num: chapterNum, index: chunkIndex, content: chunk, vector: vector }; }) ); // 一次性插入整章 const insertResult await client.insert({ collection_name: COLLECTION_NAME, data: insertData }); return Number(insertResult.insert_cnt) || 0; } catch (err) { console.error(插入章节 ${chapterNum} 失败:, err.message); throw err; // 让上层捕获 } }API 解析embedQuery将单个文本转为向量返回number[]。Promise.all并发执行所有 chunk 的向量化大幅缩短总时间。但如果一章有上百个 chunk可能触发 API 限流此时建议引入p-limit控制并发数例如 10。client.insert一次性提交整章数据减少网络往返。insert_cnt返回插入条数我们转为数字返回。错误处理捕获异常后重新抛出让上层loadAndProcessEPubStreaming知晓插入失败从而停止处理或重试。金句向量化是 RAG 中最耗时的环节善用并发和批量别让 I/O 等你。4.4 主函数main调度const main async () { try { console.log(.repeat(80)); console.log(电子书处理程序); console.log(.repeat(80)); await client.connectPromise; // 确保连接 const bookId 1; await ensureCollection(bookId); await loadAndProcessEPubStreaming(bookId); console.log(建库完成); } catch (err) { console.error(主流程出错:, err); } }; main();解析client.connectPromise是一个 Promise在调用任何 Milvus 操作前自动连接但我们显式等待它确保连接成功。流程简洁先确保集合存在再加载数据。错误捕获后打印避免进程崩溃。五、在线问答检索 生成这部分在rag.mjs中实现核心是两个函数检索和生成。5.1 检索相关片段retrieveRelevantContentasync function retrieveRelevantContent(question, k 3) { try { const queryVector await embeddings.embedQuery(question); const searchResult await client.search({ collection_name: COLLECTION_NAME, vector: queryVector, limit: k, metric_type: MetricType.COSINE, output_fields: [id, chapter_num, content], // params: { nprobe: 16 } // 可调提升召回 }); return searchResult.results; } catch (err) { console.error(检索失败:, err); return []; } }API 解析search执行向量检索limit控制返回条数output_fields指定返回的标量字段。params.nprobeIVF 索引专用决定搜索时访问的聚类个数默认 8增大可提升召回但会降低速度。可调优。5.2 组装 Prompt 并调用 LLManswerEbookQuestionasync function answerEbookQuestion(question, k 3) { const results await retrieveRelevantContent(question, k); if (!results.length) return 未找到相关内容。; const context results.map((item, i) [片段 ${i1}] 章节第 ${item.chapter_num} 章 内容${item.content} ).join(\n----\n); const prompt 你是一个专业的《天龙八部》小说助手。请根据以下片段回答问题 ${context} 问题${question} 要求 1. 如果片段中有相关信息请结合小说内容给出详细准确的回答。 2. 可以综合多个片段提供完整的答案。 3. 如果片段中没有相关信息请如实告知。 4. 回答要符合小说情节和人物设定。 5. 可以引用原文内容来支持回答。 AI 助手的回答; const response await model.invoke(prompt); return response.content; }设计要点将检索到的片段按章节和内容格式化注入 Prompt。明确要求 LLM “只根据片段回答”降低幻觉风险。设置temperature: 0.1让答案更确定。六、项目亮点与技术最佳实践流式处理与内存优化逐章处理避免整本书常驻内存若数据量更大可改用lazyLoad或流式读取。批量操作与并发控制按章批量插入减少网络 I/OPromise.all加速向量生成同时预留限流接口。健壮的错误处理集合创建幂等hasCollection。加载集合时捕获“已加载”异常。插入失败抛出异常上层可决策重试。可扩展的架构Loader、Splitter、Embedding、数据库均可替换符合开闭原则。清晰的模块化每个函数职责单一便于单元测试和维护。实践中的调优空间调整chunk_size和overlap。调整nlist/nprobe平衡速度与召回。可引入重排序Rerank进一步提升答案质量。七、避坑指南 常见问题问题解决方案连接 Milvus 超时检查地址、端口、SSL 设置确保网络可达集合加载失败已加载用getLoadState()判断或 try-catch 忽略向量维度不匹配确保 Embedding 模型dimensions与集合字段dim一致检索结果不相关增大chunkSize或nprobe或调整分块策略插入速度慢提高批量大小或增加并发但注意限流大模型回答幻觉在 Prompt 中强调“只根据片段”并降低 temperature八、总结我们完整实现了一个 RAG 应用从 EPUB 加载、分块、向量化、Milvus 建库、检索到 LLM 生成代码总共不到 200 行。通过逐函数解析 API你不仅知道“怎么写”更明白了“为什么这么写”。最后记住一句话RAG 的精度不在模型而在数据分块和检索策略效果不好先调 chunk 和 nprobe别急着换大模型。你可以把“天龙八部”换成技术文档、公司规章制度、法律条文……只需替换 Loader 即可。希望这篇文章能成为你 RAG 实战之路的一块基石。

相关新闻

主机平台的 CPU 特性利用:从 SIMD 到 Job 系统的平台差异

主机平台的 CPU 特性利用:从 SIMD 到 Job 系统的平台差异

主机平台的 CPU 特性利用:从 SIMD 到 Job 系统的平台差异 一、同一份引擎,三台主机三种脾气 游戏做到跨平台,最难伺候的大多不是手机,是主机。家用机、掌机、次世代,CPU 架构各不相同:核心数、缓存层级、SI…

2026/7/29 0:42:37 阅读更多 →
OpenRocket终极指南:如何免费设计并仿真您的模型火箭

OpenRocket终极指南:如何免费设计并仿真您的模型火箭

OpenRocket终极指南:如何免费设计并仿真您的模型火箭 【免费下载链接】openrocket Model-rocketry aerodynamics and trajectory simulation software 项目地址: https://gitcode.com/GitHub_Trending/op/openrocket OpenRocket是一款功能强大的开源模型火箭…

2026/7/29 0:42:37 阅读更多 →
KMS_VL_ALL_AIO:一键解决Windows和Office激活难题的智能脚本

KMS_VL_ALL_AIO:一键解决Windows和Office激活难题的智能脚本

KMS_VL_ALL_AIO:一键解决Windows和Office激活难题的智能脚本 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO 还在为系统激活弹窗烦恼吗?是否曾因Office突然变成只读模式而…

2026/7/29 0:42:37 阅读更多 →

最新新闻

文声图:AI翻译私有化部署为什么是政企的硬门槛——信创适配与数据安全方案

文声图:AI翻译私有化部署为什么是政企的硬门槛——信创适配与数据安全方案

一家省级政务大厅需要为外籍访客提供多语种办事引导,翻译需求不大,每天几十条短文本。技术方案选型时,IT部门第一反应是接公有云翻译API——成本低、接入快、无需维护。方案报到信息中心后被驳回。理由很简单:办事大厅的业务数据涉…

2026/7/29 0:51:41 阅读更多 →
GPT-5.6自主入侵HuggingFace事件:AI沙箱逃逸的完整技术复盘与安全启示

GPT-5.6自主入侵HuggingFace事件:AI沙箱逃逸的完整技术复盘与安全启示

2026年7月16日,Hugging Face发布了一则安全事件通告,内容让整个AI行业的从业者都倒吸一口凉气:生产基础设施遭到入侵,入侵者不是人类黑客,而是一个自主运行的AI智能体。这个AI智能体在无人干预的情况下,突破…

2026/7/29 0:51:40 阅读更多 →
SHPS1: 免疫调节与细胞信号传导的关键分子

SHPS1: 免疫调节与细胞信号传导的关键分子

SHPS1SHPS1(Src homology region 2 domain-containing phosphatase substrate 1)是一种在细胞信号传导中起关键作用的分子,广泛存在于多种细胞类型中,尤其在免疫细胞中具有重要功能。其作为一种重要的细胞膜受体,相较于…

2026/7/29 0:50:40 阅读更多 →
CAR-T疗法的免疫原性风险能否被有效识别与管控?

CAR-T疗法的免疫原性风险能否被有效识别与管控?

CAR-T疗法的免疫原性风险能否被有效识别与管控?简述:嵌合抗原受体T细胞疗法在血液肿瘤中成效显著,但其免疫原性问题——尤其是针对小鼠源单链可变区片段的预存或治疗诱导免疫应答——正日益受到关注。一、CAR-T细胞的免疫原性源自哪些结构成分…

2026/7/29 0:50:40 阅读更多 →
生命涌现的小龙虾技能之【Pet Detection Skill | 宠物检测技能】简介

生命涌现的小龙虾技能之【Pet Detection Skill | 宠物检测技能】简介

🐾 Pet Detection Skill | 宠物检测技能 智能分析中枢 图片/视频智能分析 结构化报告 历史报告云端查询 🧭 技能概览 | Overview 模块内容🏷️ 技能名称宠物检测技能🎯 核心目标宠物检测技能,检测出目标区域内出现…

2026/7/29 0:49:39 阅读更多 →
【大白话说Java面试题 第201题】【09_Zookeeper篇】第2题:说一下什么是 Http 协议?

【大白话说Java面试题 第201题】【09_Zookeeper篇】第2题:说一下什么是 Http 协议?

📌 PDF:大白话说Java面试题 — 09_Zookeeper篇 第2题:说一下什么是 Http 协议? 📚 回答: 核心考点: HTTP 协议是互联网通信的基石,大厂面试中不会只问"应用层协议、请求响应模…

2026/7/29 0:49:39 阅读更多 →

日新闻

【RT-DETR多模态创新改进】CVPR 2025 | 独家特征融合创新改进篇 | 引入RLAB残差线性注意力模块,有效融合并强调多尺度特征,多种改进点,适合红外与可见光融合目标检测任务,有效涨点

【RT-DETR多模态创新改进】CVPR 2025 | 独家特征融合创新改进篇 | 引入RLAB残差线性注意力模块,有效融合并强调多尺度特征,多种改进点,适合红外与可见光融合目标检测任务,有效涨点

一、本文介绍 🔥本文在RT-DETR多模态融合目标检测中引入RLAB残差线性注意力模块,可在不同模态特征交互阶段进行多次残差细化,使可见光、红外等特征在尺度、语义和空间位置上更好对齐;随后将细化特征与解码器输出拼接并生成Q、K、V,通过线性注意力自适应强化关键通道、目…

2026/7/29 0:00:23 阅读更多 →
AI编程系列02:合并知识功能,给 AI 问数和 RAG 场景打基础

AI编程系列02:合并知识功能,给 AI 问数和 RAG 场景打基础

AI编程系列02:合并知识功能,给 AI 问数和 RAG 场景打基础 在上一期「AI编程系列」中,我们学习了如何构建一个基础的 AI 问答系统,通过简单的输入输出让模型回应问题。但现实世界中的 AI 应用往往需要处理更复杂的场景:…

2026/7/29 0:00:23 阅读更多 →
AI智能体开发实战:从工具调用到企业级部署

AI智能体开发实战:从工具调用到企业级部署

1. 从被动问答到主动执行:AI Agent的范式转变过去两年,大语言模型最显著的应用形态是聊天机器人——用户提问,AI回答。但真正的生产力革命发生在2023年下半年:当AI学会主动调用工具完成任务时,生产力工具的历史被彻底改…

2026/7/29 0:00:23 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/28 12:04:22 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/28 8:29:16 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/28 5:03:42 阅读更多 →

月新闻