大模型结合向量数据库使用
1、为什么要让它们结合?
大语言模型虽然强大,但有两个天生的“短板”:
-
知识是“停滞”的:模型的知识截止于其训练数据的日期,对于新发生的事件或最新信息一无所知。
-
缺乏“私域知识”:它不掌握你企业内部文档、特定产品手册等非公开信息,回答这类问题时容易“一本正经地胡说八道”(即产生“幻觉”)。
而向量数据库正好能弥补这些不足。它能把海量的文本、图片等非结构化数据转换成“向量”——也就是数据的语义数字指纹。向量之间的“距离”反映了它们在含义上的相近程度,这使得向量数据库能进行语义搜索,而不仅仅是关键词匹配。
2、核心工作流程:从建库到问答
向量数据库和大模型最经典的结合方式,就是构建 RAG(检索增强生成) 系统。可以这样理解:向量数据库充当大模型的“外接大脑”或“长期记忆体”,按需为模型提供它本身不具备的、最新的、特定领域的专业知识,从而让模型的回答更精准、更可靠。
核心工作流程
这个结合过程通常分为两个阶段,我整理了一个流程图来帮你快速理解它的核心工作流程:

它们的结合使用可以拆解为“离线建库”和“在线问答”两个核心环节。
阶段一:离线建库(准备知识库)
这个阶段的目标是将你的专属知识“搬进”向量数据库。
-
数据准备:收集你拥有的各种文档(如PDF、Word、网页等)。
-
文本分块:将长文档切分成语义完整的、长度适中的小块(Chunk)。通常会设置一定的重叠区域,防止切断关键信息。
-
向量化:调用一个嵌入模型,将每个文本块转换为一个固定维度的数值向量(例如,OpenAI的
text-embedding-ada-002模型会生成1536维的向量)。 -
存储入库:将这些文本块和它们对应的向量,以及一些元数据(如标题、来源),一并存入向量数据库。
阶段二:在线问答(实时生成回答)
当用户提问时,系统实时运作:
-
问题向量化:使用与建库阶段完全相同的嵌入模型,将用户的问题也转化为一个向量。
-
相似度检索:向量数据库在已存储的海量向量中,通过特定的索引算法(如HNSW、IVF)快速找到与问题向量最相似的Top-K个向量,并返回它们对应的原始文本块。
-
上下文增强:将检索到的这些相关文本块作为“参考资料”,与用户的原始问题组合成一个内容丰富的“增强提示词”。
-
大模型生成:将这个增强后的提示词输入给大语言模型。模型不再凭空想象,而是基于提供的参考资料来生成最终的回答,这大大提升了回答的准确性和可靠性。
3、进阶技巧与趋势
除了基础流程,为了让结合效果更好,业界也有一些进阶玩法:
-
混合检索 (Hybrid Search):将“向量检索”(语义)和“关键词检索”(精确匹配)结合,再通过重排序(Rerank)模型综合打分,可以得到更全面的结果。
-
持久化聊天记忆:利用向量数据库存储对话历史,让大模型在长对话中也能“记住”之前聊了什么,实现有上下文关联的连续对话。
-
处理多模态数据:将图片、音视频也转化为向量,构建能处理多模态内容的RAG系统,实现“以图搜图”或图文混合问答。
-
图与向量的深度融合:对于复杂的逻辑推理(如“A公司的实控人和B供应商有什么关系?”),更先进的做法是将向量数据库与图数据库结合,让AI既能理解语义,又能进行关系推理。
4、spring ai示例
服务实现
package com.ybw.service;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.document.Document;
import org.springframework.ai.reader.TextReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.vectorstore.filter.Filter;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Service
@Slf4j
public class RagService {
// 自动注入Ollama ChatClient(qwen3.5:4b)
@Resource(name = "chatClient")
private ChatClient chatClient;
// 自动注入Qdrant向量存储
@Resource
private VectorStore vectorStore;
// 文本切分器,自动分块
private final TokenTextSplitter textSplitter = TokenTextSplitter.builder()
// 最大分块数量,默认300
.withMaxNumChunks(300)
// 最小分块字符数,默认80
.withMinChunkSizeChars(80)
.build();
/**
* 加载本地文本文档,向量化存入Qdrant
*
* @param resource 文本文件
* @param metadata 元信息
*/
public void loadDocumentToVectorDB(org.springframework.core.io.Resource resource, Map<String, Object> metadata) {
//1、读取txt文件
TextReader reader = new TextReader(resource);
List<Document> rawDocs = reader.get();
//2、为文档打上标签
rawDocs.forEach(doc -> {
doc.getMetadata().putAll(metadata);
});
//2、文本分片
List<Document> chunks = textSplitter.transform(rawDocs);
//3、批量写入向量库,自动调用nomic-embed-text向量化
vectorStore.add(chunks);
log.info("文档入库完成,分片数量:{}", chunks.size());
}
/**
* RAG问答:检索向量库+大模型生成答案
*
* @param question 用户问题
* @param expression 向量数据库-过滤条件
* @methodName: ragChat
* @return: java.lang.String
* @author: ybw
* @date: 2026/7/18
**/
public String ragChat(String question, Filter.Expression expression) {
// 1. 根据问题语义检索Top4相似文档片段
List<Document> similarDocs = vectorStore.similaritySearch(
SearchRequest.builder()
.query(question)
// 设置 topK 为 3,即只返回相似度最高的 3 条文档
.topK(3)
// 设置相似度阈值为 0.3,即只返回相似度大于 0.3 的文档
.similarityThreshold(0.3)
// 设置过滤条件,只返回 category=programming 的文档
.filterExpression(expression)
.build()
);
// 2. 拼接检索上下文
StringBuilder context = new StringBuilder();
for (Document doc : similarDocs) {
context.append(doc.getText()).append("\n\n");
}
// 3. 构造RAG提示词,约束大模型仅使用检索内容作答
String promptTemplate = """
你是专业知识库助手,请严格根据下面提供的参考资料回答用户问题,
如果参考资料中没有相关内容,直接回复“暂无相关资料”,禁止编造信息。
参考资料:
{context}
用户问题:{question}
""";
// 4. 调用Qwen3.5:4b生成回答
log.info("context:{}", context);
log.info("promptTemplate:{}", promptTemplate);
return chatClient.prompt()
.user(u -> u.text(promptTemplate)
.param("context", context.toString())
.param("question", question))
.call()
.content();
}
}
测试
package com.ybw.service;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.ai.vectorstore.filter.Filter;
import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import java.io.IOException;
import java.util.Map;
@SpringBootTest
@Slf4j
public class RagServiceTest {
@Autowired
private RagService ragService;
@Test
public void loadDocumentToVectorDB() throws IOException {
Resource resource = new ClassPathResource("file/a.txt");
Map<String, Object> metadata = Map.of("category", "database", "type", "test");
log.info("file是否存在:{},路径:{}", resource.exists(), resource.getFilePath());
ragService.loadDocumentToVectorDB(resource, metadata);
}
@Test
public void ragChat() {
String question = "小松鼠做了什么?";
Filter.Expression expression = new FilterExpressionBuilder().eq("category", "database").build();
String answer = ragService.ragChat(question,expression);
log.info("question:{}, answer:{}", question, answer);
}
}
更多推荐

所有评论(0)