1、为什么要让它们结合?

        大语言模型虽然强大,但有两个天生的“短板”:

  1. 知识是“停滞”的:模型的知识截止于其训练数据的日期,对于新发生的事件或最新信息一无所知。

  2. 缺乏“私域知识”:它不掌握你企业内部文档、特定产品手册等非公开信息,回答这类问题时容易“一本正经地胡说八道”(即产生“幻觉”)。

        而向量数据库正好能弥补这些不足。它能把海量的文本、图片等非结构化数据转换成“向量”——也就是数据的语义数字指纹。向量之间的“距离”反映了它们在含义上的相近程度,这使得向量数据库能进行语义搜索,而不仅仅是关键词匹配。

2、核心工作流程:从建库到问答

        向量数据库和大模型最经典的结合方式,就是构建 RAG(检索增强生成) 系统。可以这样理解:向量数据库充当大模型的“外接大脑”或“长期记忆体”,按需为模型提供它本身不具备的、最新的、特定领域的专业知识,从而让模型的回答更精准、更可靠

核心工作流程

        这个结合过程通常分为两个阶段,我整理了一个流程图来帮你快速理解它的核心工作流程:

       

        它们的结合使用可以拆解为“离线建库”和“在线问答”两个核心环节。

阶段一:离线建库(准备知识库)

这个阶段的目标是将你的专属知识“搬进”向量数据库。

  1. 数据准备:收集你拥有的各种文档(如PDF、Word、网页等)。

  2. 文本分块:将长文档切分成语义完整的、长度适中的小块(Chunk)。通常会设置一定的重叠区域,防止切断关键信息。

  3. 向量化:调用一个嵌入模型,将每个文本块转换为一个固定维度的数值向量(例如,OpenAI的text-embedding-ada-002模型会生成1536维的向量)。

  4. 存储入库:将这些文本块和它们对应的向量,以及一些元数据(如标题、来源),一并存入向量数据库。

阶段二:在线问答(实时生成回答)

        当用户提问时,系统实时运作:

  1. 问题向量化:使用与建库阶段完全相同的嵌入模型,将用户的问题也转化为一个向量。

  2. 相似度检索:向量数据库在已存储的海量向量中,通过特定的索引算法(如HNSW、IVF)快速找到与问题向量最相似的Top-K个向量,并返回它们对应的原始文本块。

  3. 上下文增强:将检索到的这些相关文本块作为“参考资料”,与用户的原始问题组合成一个内容丰富的“增强提示词”。

  4. 大模型生成:将这个增强后的提示词输入给大语言模型。模型不再凭空想象,而是基于提供的参考资料来生成最终的回答,这大大提升了回答的准确性和可靠性。

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);
    }
}

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐