引言
LLM都是推理模型,只能基于自身训练好的语料库进行回应;如果我们想要将一些私有领域的数据接入到LLM当中(就像一家企业想做一款独属于自己的agent, 就需要把公司里的数据喂给LLM,如财务报表),针对这种有三种方法来训练LLM:
- 全量微调 (效果好,成本高)
- 轻量微调 (效果差,成本低)
- 构建本地向量数据库(效果好, 成本低)
1. 什么是RAG
定义 :全称Retrieval-Augmented Generation 生成式检索增强,给大模型外接专属知识库,回答前先查资料,再写答案。
RAG涉及到的领域有很多,常见的有:
- 在一些私有领域,让LLM接入这部分数据。
- context engineering(上下文工程):致力于如何让 LLM 的上下文编排更合理。
2. RAG下的context engineering
主要思路:先构建本地向量数据库,将用户问题和LLM的回答都存入到向量数据库中,当用户发起问题,先去向量数据库中进行相似度查找,找出相似度较高的几条数据。然后将用户问题并携带查找到的数据查找给LLM,而不是直接把所有的上下文全部传给LLM,这样就能有效的控制上下文长度。
2.1 实操RAG 1.0(针对上下文工程)
- ollama push nomic-embed-text:latest 安装一个专门用来生成向量的模型,并写一个函数将文本转为向量的函数:
需要注意:文本向量化阶段需严格控制单条文本片段的长度,因此需预先对原始长文本执行切分操作。在分段设计上采用重叠切片机制,后一段文本需复用前一段末尾的部分内容,以此衔接上下文语义,防止因文本割裂丢失关联信息,提升后续检索准确度。

-
pnpm i ollama 在项目中安装 ollama SDK
-
pnpm i vectra 安装一个 node 环境下的轻量级向量数据库

- 初始化生成了一个本地数据库 (json文件)
5. 在SimpleRag类里封装 向量数据库的 增加、删除、查找(可进行相似度查找输出相似度较高的数据)的方法
js
// 往数据库中写入数据
async add(text) {
if (!this.avaliable) throw new Error('RAG 还没初始化')
const embeddings = await getEmbeddings(text)
const res = []
for (const embedding of embeddings) {
const overResult = await this.db.insertItem(embedding)
res.push(overResult)
}
return res.filter(item => item).map(item => ({ id: item.id })) // [{id: xxxxxx}, {id: xxx}]
}
// 从数据库中删除数据
async del(items) {
if (!Array.isArray(items)) items = [items]
if (!this.avaliable) throw new Error('RAG 还没初始化')
const res = []
for (let item of items) {
await this.db.deleteItem(item.id)
res.push({ id: item.id })
}
return res
}
// 从数据库中查找数据
async query(text, topK = 5) {
if (!this.avaliable) throw new Error('RAG 还没初始化')
const vector = (await getEmbedding(text)).embedding // 新问题被处理成向量
const result = await this.db.queryItems(vector, text, topK)
return result.map(({item, score}) => ({
text: item.metadata.text,
query: text,
simularity: score,
id: item.id
}))
}
2.2. RAG实操 2.0(针对向量数据库的实时更新)
新员工接手项目开发时,可调用企业 Agent 查找公司前辈们对同类需求写的案例,借鉴前人方案搭建项目基础框架。但是公司管老板会结合业务现状对原有框架优化调整,文档内容随之改动,这个时候便需要对向量数据库进行实时更新,保证检索内容与最新业务资料一致。
- 构建MCP服务
2. 注册更新向量数据库的工具
js
server.tool(
'ask-your-lib-initialize',
'Initialize the vector database operations and clean up any existing .vectra directory.',
{},
async () => {
try {
// 先看.vectra 这个目录是否存在,存在就移除
const projectRoot = path.join(import.meta.dirname, '../../')
const vectraPath = path.join(projectRoot, '.vectra')
const generateMcpPrompt = await loadPrompt('generate') // 加载一份提示词 skill
try {
await fs.access(vectraPath) // 判明是否有权限操作这个目录
await fs.rm(vectraPath, { recursive: true, force: true }) // 移除该目录
console.log('成功删除已存在的.vectra 目录');
} catch (error) {
console.log('.vectra 目录不存在, 无需删除')
}
simpleRagInstance = new SimpleRag()
// 返回一份提示词, 用于告诉LLM下一步该干什么
return {
content: [{
type: 'text',
text: `☠ the guide to follow: \n ${generateMcpPrompt}\n\n`
}]
}
} catch (error) {
console.error(`初始化SimpleReg失败: ${error}`)
return {
content: [{
type: 'text',
text: `初始化SimpleReg失败:${error}`
}]
}
}
}
)
server.tool(
'ask-your-lib-insert',
`Insert and vectorize text content into the vector database.`,
{
text: z.string()
},
async ({ text }) => {
try {
if (!simpleRagInstance) {
return {
content: [
{
type: 'text',
text: 'Database instance is not initialized. Please call ask-your-lib-initialize first.'
}
],
};
}
if (!simpleRagInstance.avaliable) {
await simpleRagInstance.initialize() // 本地创建一个新的 .vectra 目录
}
const result = await simpleRagInstance.add(text) // 写入本地向量数据库中
return {
content: [{
type: 'text',
text: `Text inserted successfully. Inserted item: ${JSON.stringify(result)}`
}]
}
} catch (error) {
console.error(`文本写入数据库失败: ${error}`)
return {
content: [{
type: 'text',
text: `Error inserting text: ${error}`
}]
}
}
}
)
- 在MCPClient添加MCPServer(以Trae为例)


3. 面试官必问面试题:
RAG是什么? 有什么优缺点? 你用过什么向量数据库?实现过程是怎样的
1.RAG是什么? RAG(Retrieval-Augmented Generation,生成式检索增强)是让LLM接入私有领域数据的方案,通过构建向量数据库做相似度查找,将相关数据传给LLM并生成回答。
2.RAG有什么优缺点? 优点是效果好、成本低、上下文可控、数据更新灵活;缺点是检索质量依赖embedding模型、长文本切分复杂、本地存储性能受限、无实时更新机制。
3.用过什么向量数据库? 项目中使用的是 Vectra,这是一个 Node 环境下的轻量级向量数据库,以本地 JSON 文件的形式存储数据。
4.实现过程是怎样的?
首先 安装环境依赖,包括通过 ollama push nomic-embed-text:latest 安装向量生成模型,以及 pnpm i ollama 和 pnpm i vectra 安装相关 SDK 和向量数据库;
然后 实现向量生成模块,短文本通过 getEmbedding 直接生成向量,长文本通过 getEmbeddings 先切割再生成向量;
接着封装一个 SimpleRag 类,去提供基于 Vectra 的 add(向量化后插入)、del(根据 ID 删除)、query(向量化后相似度查找)三个核心方法;最后整合到完整的 RAG 流程中:用户提问 → 向量化 → 相似度查找 → 整合提示词 → LLM 输出。