接前一篇使用MCP协议编写对话工具(后文简称对话工具),当我们询问大模型一些内网的内容时,大模型没有接触过相关的内容,往往会开始胡编,输出错误的内容,这时候就需要使用RAG功能,将内网的一些文档提供大模型,用以生成真实的回答。
需要搞清楚RAG与LLM之间的关系。
整个流程可以分为索引部分与查询部分。
索引过程:文档 –> 分割成块 –> 嵌入模型 –> 向量 –> 存储到向量数据库。
查询过程:用户问题 –> 嵌入模型 –> 问题向量 –> 向量数据库(检索相似块) –> 检索到的相关文本块 (–> 重排名模型)
最后将用户问题和检索到的文本块一起给LLM –> 生成回答 –> 返回给用户
所以RAG部分的流程可以和LLM使用的流程完全分开,

当前本地使用ollama运行deepseek-r1-7b,开始先拿该模型作为嵌入模型使用,但是该模型不是专门用做于嵌入,在后面的检索相关文本块上,表现较差。后搜索到六月初发布的Qwen3-Embedding,在嵌入模型的排行榜上表现优异。该模型具有多种规格(0.6B/4B/8B),因本地运行内存只有16GB,后通过ollama下载第三方上传的Qwen3-Embedding-4B:Q4_K_M。
在介绍Qwen3-Embedding的网页上,往往嵌入模型与重排名模型会一起讨论。
在查询过程最后一步,检索到相关文本块后还能再加一步,对文本块与query之间的关联性进行排行,最后可以取相关性(0-1)大于指定值的文本块发给LLM。
这里选用了 Qwen3-Reranker-4B:Q4_K_M。
在对话工具的前端页面提供一个输入,键入本地地址,通过websocket发送到mcp服务,后端根据本地地址读取本机的文件,利用嵌入模型量化数据并存入向量数据库。
当用户在前端发送信息传到MCP服务,调用LLM接口前,先调用嵌入模型,将问题量化,再在向量数据库内检索相似的文本返回,并构建新的prompt,插入到对话队列的前端,再将对话队列发送给LLM。新prompt类似下方。
// 创建系统消息,包含检索到的文档信息
const systemMessage = {
role: 'system',
content: `你是一个智能助手,拥有访问知识库的能力。以下是与你问题相关的文档信息:
${ragResult.relevantDocuments.map((doc, index) =>
`文档 ${index + 1} (来源: ${doc.metadata.filename}):
${doc.content}`
).join('\n\n')}
请基于这些文档信息回答用户问题。如果文档中没有相关信息,请基于你的知识回答,并说明信息来源。`
};
format与raw设置为true。
const response = await fetch(`${this.config.baseUrl}/api/generate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: this.config.model,
prompt: prompt,
stream: true,
raw: true,
options: {
temperature: this.config.temperature
},
format: {
type: "object",
properties: {
query: {
type: "string",
},
results: {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "number"
},
"score": {
"type": "number"
},
"reason": {
"type": "string"
}
}
}
}
}
}
})
});
