SpringBoot+openGauss DataVec构建高效RAG知识库实践

权限管理

🦁一. 写在前面

RAG(检索增强生成)技术采用“检索外部知识结合大模型生成”的混合架构,在企业问答系统、智能文档助手及研发知识库等实际应用中有效提升了回答的准确性和结果可解释性。该技术通过将权威数据集中存储在可控的数据库内,避免了传统方案依赖模型自身记忆的局限性。以openGauss的DataVec向量引擎为例,其实现了高效且可治理的向量数据管理,从技术层面解决了黑盒向量库常见的数据孤岛问题。
在这里插入图片描述

🦁二. 背景与动机

企业需要统一的数据治理、事务一致性与权限控制,同时希望利用向量检索的语义能力,但传统“关系库+独立向量库”双栈方案存在两套运维与一致性问题。openGauss DataVec通过将向量能力融入关系数据库内核,允许用户使用熟悉的SQL操作向量数据,既保留ACID事务特性,又提供高维向量检索功能,成为构建企业级RAG(检索增强生成)应用的理想底座,有效解决了多系统协同的复杂性。现在基于SpringBoot+openGauss DataVec 构建高效 RAG知识库实战。

2.1 实现逻辑

  • 嵌入:将文本转成向量(可用 OpenAI 或内部模型,文中示例默认用 mock 便于演示)。
  • 存储:在 openGauss 建表,使用 VECTOR(dim) 列保存嵌入。
  • 索引:为向量列创建 IVFFLAT 索引提升近似最近邻检索性能。
  • 检索:按距离操作符排序( <-> L2、 <=> Cosine、 <#> Inner Product)取 TopK。
  • 生成(可选):把检索到的上下文喂给大模型进行答案生成;本文只做检索演示。

2.2 项目结构

  • Application.java :应用入口。
  • RagProperties + RagConfig :RAG 配置与嵌入服务装配。
  • DatabaseInitializer :初始化 DataVec 扩展、建表与索引。
  • EmbeddingService :抽象嵌入; MockEmbeddingService 、 OpenAIEmbeddingService 两种实现。
  • DocumentRepository :插入与相似度检索。
  • RagService :业务封装。
  • RagConsoleRunner :控制台演示 Runner。
  • application.yml :数据库与 RAG 行为配置。

🦁三. 业务实现逻辑

3.1 初始化与索引

在初始化过程中,先创建 datavec 扩展,这是一个 PostgreSQL 扩展,支持向量数据类型的处理,确保数据库具备进行向量计算的能力。接着,创建一个documents的表,用于存储文档内容及其对应的向量嵌入,表结构包含一个自动递增的主键 id,存储文本文档的 content,以及存储向量表示的 embedding,后者的维度由 props.getEmbeddingDim() 确定。最后,根据配置的距离度量(如 L2、内积、余弦相似度)创建一个 IVFFLAT 索引,以支持高维向量的快速检索,使用 jdbcTemplate 执行 SQL 语句的同时,将距离度量参数传入索引创建语句中,lists = 100 则表示在索引创建时将数据分割成 100 个“列表”,这有助于提升检索性能,需根据实际数据集和需求进行调整。

@PostConstruct
public void init() {
    // 1) 创建 datavec 扩展(需权限)
    try {
        jdbcTemplate.execute("CREATE EXTENSION IF NOT EXISTS datavec");
    } catch (Exception ignored) {}

    // 2) 建表:文本 + 向量
    String createTable = "CREATE TABLE IF NOT EXISTS documents (" +
            "id BIGSERIAL PRIMARY KEY," +
            "content TEXT NOT NULL," +
            "embedding VECTOR(" + props.getEmbeddingDim() + ") NOT NULL)";
    jdbcTemplate.execute(createTable);

    // 3) 建 IVFFLAT 索引(按距离选择 ops)
    String ops = switch (props.getDistance().toLowerCase()) {
        case "l2" -> "vector_l2_ops";
        case "ip" -> "vector_ip_ops";
        default -> "vector_cosine_ops";
    };
    String createIndex = "CREATE INDEX IF NOT EXISTS idx_documents_embedding " +
            "ON documents USING ivfflat (embedding " + ops + ") WITH (lists = 100)";
    jdbcTemplate.execute(createIndex);
}
  • DataVec 语法要点:
-- 列类型:VECTOR(1536)
-- 距离操作符:
--  L2:       <-> 
--  Cosine<=>
--  InnerProd<#>

3.2 生成向量

  • 伪随机性: 利用文本内容生成随机种子,使得同样的输入文本在多次调用时返回一致的随机向量。这通过 seedFrom(text) 实现,确保了随机性与输入文本的关联性。

  • 向量生成: 使用高斯分布生成指定维度的随机数,将其作为向量的初步表示。每个维度的值是通过 nextGaussian() 方法生成的,符合正态分布。

  • 归一化: 计算向量的范数,并将向量归一化,使得向量的长度(或模)为 1。这一步确保生成的向量在后续计算中不会因规模差异而影响结果,适合做相似度计算或插值。

@Override
public float[] embed(String text) {
    Random r = new Random(seedFrom(text));
    float[] v = new float[dim];
    double norm = 0;
    for (int i = 0; i < dim; i++) { v[i] = (float) r.nextGaussian(); norm += v[i]*v[i]; }
    norm = Math.sqrt(norm);
    if (norm > 0) for (int i = 0; i < dim; i++) v[i] /= (float) norm;
    return v;
}

3.3 数据写入与检索

  • 文档存储: 通过 insert 方法,系统可以将用户上传的文本及其向量表示存储在数据库中,便于后续检索。

  • 相似性检索: 通过 searchByEmbedding 方法,用户能够根据给定的文本向量快速找到内容相似的文档,支持信息检索和推荐系统的实现。

public int insert(String content, float[] embedding) {
    String sql = "INSERT INTO documents(content, embedding) VALUES (?, ?)";
    return jdbcTemplate.update(sql, content, toVectorLiteral(embedding));
}

public List<String> searchByEmbedding(float[] query, int topK) {
    String op = switch (props.getDistance().toLowerCase()) {
        case "l2" -> "<->"; case "ip" -> "<#>"; default -> "<=>";
    };
    String sql = "SELECT content FROM documents ORDER BY embedding " + op + " ? LIMIT ?";
    return jdbcTemplate.query(sql, new Object[]{toVectorLiteral(query), topK},
            (rs, rowNum) -> rs.getString(1));
}

通过结合嵌入向量的处理与数据库的存储与检索,实现了文档的管理,使得用户能够有效地添加和查找相关信息:

public void addDocument(String content) {
    float[] v = embeddingService.embed(content);
    repository.insert(content, v);
}
public List<String> retrieve(String query) {
    float[] q = embeddingService.embed(query);
    return repository.searchByEmbedding(q, props.getSearchTopK());
}

3.4 演示效果

先往rag库中插入一段向量数据,我这里使用Qwen/Qwen3-Embedding-0.6B模型生成向量:
在这里插入图片描述
再通过openGauss的DataVec的“向量相似度”排序完成,返回前k个最相似的语句:
在这里插入图片描述
我们在提问问题:什么是openGauss DataVec?
到数据库进行向量检索,openGauss数据库用DataVec的距离操作符进行排序返回前k个相似度最高的答案,根据输出内容,我们能判断出其返回的答案是相对正确的。

🦁四. openGauss扮演的重要作用

本项目利用了openGauss强大的DataVec核心搭建了高效的RAG知识库实战。 DataVec具备强大的向量内核特性,支持 VECTOR 类型、距离操作符和向量索引,使其在性能和稳定性方面表现可靠。通过 SQL First 方法,用户可以轻松使用纯 SQL 语句操作向量和结构化数据,例如使用 ORDER BY embedding <=> ? 一条语句即可完成相似度检索。同时,DataVec 支持在同一条 SQL 查询中融合向量检索与结构化过滤,满足复杂业务场景的需求,如分类过滤、时间窗口和租户隔离。此外,IVFFLAT 等近似索引能够显著提升检索性能,并可以根据具体场景灵活调整 lists 和 probes 等参数。最后,DataVec 兼容性强,支持 JDBC 接入,降低迁移成本,并能与 Spring Boot、ORM 以及 BI 工具等生态系统良好配合。


在这里插入图片描述

🦁 其它优质专栏推荐 🦁

🌟《Java核心系列(修炼内功,无上心法)》: 主要是JDK源码的核心讲解,几乎每篇文章都过万字,让你详细掌握每一个知识点!

🌟 《springBoot 源码剥析核心系列》一些场景的Springboot源码剥析以及常用Springboot相关知识点解读

欢迎加入狮子的社区:『Lion-编程进阶之路』,日常收录优质好文

更多文章可持续关注上方🦁的博客,2025咱们顶峰相见!

<think> 我们正在讨论如何将Spring Boot与Ollama结合,使用提示词调用本地知识库。根据引用内容,特别是引用[2]和引用[3],我们可以构建一个RAG(Retrieval-Augmented Generation)应用,其中本地知识库通过向量数据库(如Elasticsearch)存储,然后使用Ollama运行的模型(如DeepSeek-R1)来生成答案。 ### 实现步骤 以下是结合Spring Boot、Ollama和本地知识库的关键步骤: 1. **环境准备**: - 安装并启动Ollama服务,下载所需的Embedding模型(如`nomic-embed-text:latest`)和Chat模型(如`deepseek-r1:8b`)[^2]。 - 搭建Elasticsearch作为向量数据库,用于存储本地知识库的向量化数据。 2. **项目配置**: - 在Spring Boot项目中集成Spring AI Alibaba和Spring AI Ollama依赖。 - 配置Ollama的API地址(通常为`http://localhost:11434`)以及Embedding模型和Chat模型的名称。 3. **本地知识库构建**: - 将本地知识库的文档(如TXT、PDF等)进行分块处理。 - 使用Embedding模型将文本块转换为向量,并存储到Elasticsearch中。 4. **检索增强生成(RAG)流程**: - 当用户输入查询时,首先使用相同的Embedding模型将查询转换为向量。 - 在Elasticsearch中执行向量相似度搜索,获取与查询最相关的文本片段(作为上下文)。 - 构建提示词(Prompt),将上下文和用户查询组合,发送给Ollama的Chat模型生成答案。 5. **Spring Boot服务层实现**: - 创建`RagService`,封装检索和生成逻辑。 - 使用`ChatClient`或`OllamaChatModel`调用模型生成答案。 ### 示例代码 以下是一个简化的RAG服务实现: ```java import org.springframework.ai.chat.ChatClient; import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; import java.util.stream.Collectors; @Service public class RagService { private final VectorStore vectorStore; // 向量存储(如ElasticsearchVectorStore) private final EmbeddingClient embeddingClient; // Embedding模型客户端 private final ChatClient chatClient; // 聊天模型客户端 @Autowired public RagService(VectorStore vectorStore, EmbeddingClient embeddingClient, ChatClient chatClient) { this.vectorStore = vectorStore; this.embeddingClient = embeddingClient; this.chatClient = chatClient; } public String query(String userQuery) { // 1. 检索相关文档 List<Document> similarDocuments = vectorStore.similaritySearch(userQuery); // 2. 构建上下文 String context = similarDocuments.stream() .map(Document::getContent) .collect(Collectors.joining("\n")); // 3. 构建提示词(使用上下文和用户查询) String promptTemplate = """ 请根据以下上下文信息回答问题: %s 问题:%s 答案: """; String prompt = String.format(promptTemplate, context, userQuery); // 4. 调用模型生成答案 return chatClient.prompt() .user(prompt) .call() .content(); } } ``` ### 测试验证 如引用[3]所示,可以通过单元测试验证RAG服务是否有效利用本地知识库: ```java @SpringBootTest class RagServiceTest { @Autowired private RagService ragService; @Test void shouldReturnContextualAnswer() { String response = ragService.query("产品退货政策"); assertThat(response) .contains("7天无理由退货") // 预期答案包含本地知识库中的内容 .doesNotContain("根据现有资料"); // 确保不是模型凭空生成 } } ``` ### 提示词优化 为了提高答案质量,可以优化提示词模板,例如: - 明确要求模型基于上下文回答,避免编造。 - 设定回答格式(如简洁、分点等)。 示例优化后的提示词: ``` 你是一个客服助手,请严格根据以下上下文信息回答问题。如果上下文没有提供足够信息,请回答“我不知道”。 上下文: %s 问题:%s ``` ### 总结 通过Spring Boot整合Ollama和向量数据库,我们可以构建一个利用本地知识库RAG应用。核心流程包括知识库向量化、相似性检索和提示词增强生成。这种方法既利用了本地模型的高效性,又能确保答案基于企业私有知识库[^2][^3]。 §§相关问题§§ 1. 如何优化Embedding模型以提高检索准确率? 2. 在Spring AI中如何实现流式响应(SSE)? 3. 如何对Ollama模型生成的答案进行内容审核? </think> ### Spring Boot + Ollama + 本地知识库集成指南 以下是基于提示词调用本地知识库的完整实现方案,结合Spring Boot、Ollama和向量数据库: --- ### 1. 核心架构 ```mermaid graph LR A[用户提问] --> B(Spring Boot应用) B --> C{向量数据库} C -->|语义检索| D[Ollama Embedding模型] B -->|提示词工程| E[Ollama Chat模型] E --> F[生成答案] ``` **流程说明**: 1. 用户提问通过Spring Boot接收 2. 使用**Embedding模型**将问题向量化 3. 在**向量数据库**中检索相关知识点 4. 将检索结果注入**提示词模板** 5. **Chat模型**基于提示词生成最终答案[^2] --- ### 2. 关键实现步骤 #### 步骤1:环境准备 ```bash # 启动Ollama服务 ollama serve # 下载所需模型 ollama pull nomic-embed-text # Embedding模型 ollama pull deepseek-r1:8b # Chat模型 ``` #### 步骤2:Spring Boot配置 **pom.xml 依赖**: ```xml <dependencies> <!-- Spring AI Ollama --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-ollama-spring-boot-starter</artifactId> <version>0.8.0</version> </dependency> <!-- 向量数据库连接器(以Elasticsearch为例) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> </dependencies> ``` **application.yml 配置**: ```yaml spring: ai: ollama: base-url: http://localhost:11434 embedding: model: nomic-embed-text:latest chat: model: deepseek-r1:8b data: elasticsearch: uris: http://localhost:9200 ``` --- ### 3. 核心代码实现 #### (1) 提示词工程服务 ```java @Service public class RagService { private final VectorStore vectorStore; // 向量数据库接口 private final ChatClient chatClient; // Ollama聊天客户端 // 提示词模板(包含知识库上下文占位符) private final String PROMPT_TEMPLATE = """ 你是一个专业客服助手,请严格根据以下上下文回答问题: ======== 上下文 ======== %s ======== 结束 ======== 问题:%s 答案: """; public String queryWithContext(String question) { // 1. 检索相关知识片段 List<Document> docs = vectorStore.similaritySearch(question); // 2. 拼接上下文 String context = docs.stream() .map(Document::getContent) .collect(Collectors.joining("\n\n")); // 3. 构建提示词 String prompt = String.format(PROMPT_TEMPLATE, context, question); // 4. 调用模型生成答案 return chatClient.prompt() .user(prompt) .call() .content(); } } ``` #### (2) 向量数据库初始化(Elasticsearch示例) ```java @Configuration public class VectorStoreConfig { @Bean public VectorStore elasticsearchVectorStore( RestHighLevelClient elasticsearchClient, EmbeddingClient embeddingClient) { return new ElasticsearchVectorStore(elasticsearchClient, embeddingClient, IndexOptions.builder() .withIndexName("knowledge_base") .withMappingJson(""" { "properties": { "embedding": { "type": "dense_vector", "dims": 768 # 与Embedding模型维度一致 } } } """) .build()); } // 知识库初始化(实际应用中需持久化存储) @PostConstruct public void initKnowledgeBase(VectorStore vectorStore) { vectorStore.add(List.of( new Document("退货政策:7天无理由退货,商品需保持完好"), new Document("配送时效:全国48小时内送达,偏远地区72小时") )); } } ``` --- ### 4. 测试验证 ```java @SpringBootTest class RagServiceTest { @Autowired private RagService ragService; @Test void testProductReturnPolicy() { String answer = ragService.queryWithContext("如何办理退货?"); // 验证答案包含知识库内容 assertThat(answer) .contains("7天无理由退货") // 来自知识库 .doesNotContain("根据我的知识"); // 避免模型幻觉 } } ``` --- ### 5. 优化技巧 1. **提示词工程优化**: ```java // 增强版提示模板 String ADVANCED_PROMPT = """ 你将被提供CONTEXT和QUESTION。 - 如果CONTEXT包含答案:直接使用CONTEXT内容回答 - 否则回答:“该问题超出我的知识范围” CONTEXT: ###%s### QUESTION: %s """; ``` 2. **检索优化**: - 使用**HyDE技术**(Hypothetical Document Embeddings):先让模型生成假设答案,再用其向量检索[^3] - 设置相似度阈值:`vectorStore.similaritySearch(question, 0.7)` 3. **流式响应**(SSE): ```java @GetMapping("/ask") public SseEmitter streamAnswer(@RequestParam String question) { SseEmitter emitter = new SseEmitter(); chatClient.prompt() .user(question) .stream() .subscribe( chunk -> emitter.send(chunk.getContent()), emitter::completeWithError, emitter::complete ); return emitter; } ``` --- ### 典型应用场景 1. 企业客服知识库问答系统 2. 法律/医疗领域专业咨询 3. 教育领域的个性化教学助手 4. 代码知识库智能查询(如API文档检索)[^3] > **关键优势**:结合本地部署的Ollama模型+私有知识库,确保数据安全性和响应速度(平均延迟<500ms)[^2]。 ---
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

狮子也疯狂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值