找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1618

积分

0

好友

206

主题
发表于 2026-2-11 09:48:07 | 查看: 39| 回复: 0

近年来,以大型语言模型(LLM)为核心的生成式AI发展迅猛。然而,我们常常面临一个痛点:LLM的训练数据是固定的,无法获取训练后产生的新知识或企业内部的私有文档。直接向其提问内部信息,它可能会“幻觉”(即一本正经地胡说八道)。检索增强生成(RAG)技术正是为解决这一问题而生,而 Spring AI 则为Java开发者提供了便捷的实现路径。

本文将深入探讨RAG的核心原理,并手把手带你在Spring Boot项目中,基于PGVector向量数据库,构建一个能够利用自有文档进行问答的智能应用。

RAG是什么?为何需要它?

想象一下,你想向LLM咨询公司内部某份技术手册的内容。如果把整本手册(可能数百页)都作为上下文喂给它,不仅成本高昂(按Token计费),LLM也容易“迷失”在信息海洋中,抓不住重点。

RAG(Retrieval Augmented Generation)即检索增强生成,其核心思想是:在LLM生成答案前,先从一个外部知识库中检索出与问题最相关的片段,然后将这些片段与原始问题一同提交给LLM。这样,LLM的答案就有了精准、可靠的依据,极大减少了幻觉。

整个过程分为离线和在线两条主线:

  • 离线流程(知识入库):将你的文档(TXT、PDF、HTML等)进行分割、转化为向量,并存储到专门的向量数据库中。
  • 在线流程(智能问答):当用户提问时,将问题也转化为向量,从向量库中快速找到最相似的文本块,将其作为“证据”与问题一起交给LLM生成最终答案。

核心技术拆解:从文档到答案的旅程

要理解RAG,需要掌握几个关键概念。

1. 文档分块 (Chunking)
文档不能整篇存入,需要切分成有意义的片段。常见的策略有:

  • 固定大小拆分:简单快速,但可能割裂完整语义。
  • 语义/递归拆分:根据段落、句子或标记符(如\n\n)分割,能更好保持语义完整性,是更推荐的方式。

2. 嵌入与向量化 (Embedding)
这是实现语义检索的魔法。Embedding模型可以将一段文本(无论长短)转换为一个固定长度的浮点数数组(即向量)。语义相近的文本,其对应的向量在数学空间中的“距离”也更近。

3. 向量数据库 (Vector Database)
传统数据库擅长精确匹配,而向量数据库专为高效“找相似”而设计。它存储向量,并能根据一个查询向量,快速找出库中最相似的Top K个向量。常见的如Chroma、Pinecone,以及本文使用的 PGVector(PostgreSQL的扩展)。

4. 相似性搜索 (Similarity Search)
如何衡量两个向量的“相似度”?常用方法有:

  • 余弦相似度:最常用,衡量向量方向的差异,结果在[-1,1]之间,越接近1越相似。它不受向量长度影响,非常适合文本语义匹配。
    余弦相似度计算公式
  • 欧氏距离:衡量空间中的直线距离,距离越小越相似。
    欧氏距离计算公式
  • 点积:计算简单,但结果受向量长度影响。

5. 提示词模板 (Prompt Template)
这是连接检索结果与LLM的桥梁。一个典型的RAG提示词模板如下,它明确指令LLM基于给定的上下文进行回答:

基于以下上下文信息回答问题。如果你无法从中找到答案,请说“我不知道”,不要编造信息。

上下文:
{检索到的文档片段}

问题:
{用户的问题}

答案:

实战:用Spring AI快速搭建RAG应用

下面,我们以构建一个“员工信息问答”应用为例,展示具体步骤。完整环境:JDK 17, Spring Boot 3.5.0, Spring AI 1.0.0。

第1步:项目依赖与配置

首先,在 pom.xml 中引入必要的起步依赖:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>1.0.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <!-- Spring AI 核心 -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-model-openai</artifactId>
    </dependency>
    <!-- PGVector 向量存储 -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-vector-store-pgvector</artifactId>
    </dependency>
    <!-- RAG 功能支持 -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-rag</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-advisors-vector-store</artifactId>
    </dependency>
    <!-- Web支持 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

接下来是核心的 application.yml 配置。这里我们配置了:

  • LLM模型:使用 Qwen2.5-72B-Instruct
  • Embedding模型:使用 OpenAI 的 text-embedding-ada-002 (需配置API Key和Base URL)
  • 向量存储:使用 PGVector,并自动初始化表结构。
spring:
  ai:
    openai:
      base-url: ${OPENAI_BASE_URL} # 你的LLM服务地址
      api-key: ${OPENAI_API_KEY}
      chat:
        options:
          model: Qwen/Qwen2.5-72B-Instruct
      embedding:
        options:
          model: text-embedding-ada-002
    vectorstore:
      pgvector:
        initialize-schema: true # 自动建表
        index-type: HNSW       # 索引算法
        distance-type: COSINE_DISTANCE # 相似度计算方式
        dimensions: 1536       # 向量维度,需与Embedding模型匹配
  datasource:
    url: jdbc:postgresql://localhost:5432/rag_demo # 你的PG数据库
    username: ${DB_USER}
    password: ${DB_PASSWORD}

第2步:知识入库(离线流程)

我们准备一个 file.txt 放在 src/main/resources 下,内容是关于员工“小璐”的描述。然后通过一个ETL(提取、转换、加载)流程将其存入向量库。

import org.springframework.ai.document.Document;
import org.springframework.ai.reader.TextReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;

@RestController
public class KnowledgeIngestionController {

    @Autowired
    private VectorStore vectorStore;

    @Value("classpath:/file.txt")
    private Resource resource;

    @GetMapping("/ingest")
    public String ingestDocument() {
        // 1. Extract: 读取文本文件
        TextReader textReader = new TextReader(this.resource);
        List<Document> rawDocuments = textReader.read();

        // 2. Transform: 将文档按语义切分成块
        TokenTextSplitter splitter = new TokenTextSplitter(500, 100, 10, 1000, true);
        List<Document> documentChunks = splitter.apply(rawDocuments);

        // 3. Load: 将块存入向量数据库(内部会自动调用Embedding模型生成向量)
        vectorStore.add(documentChunks);

        return "文档已成功入库,共切分为 " + documentChunks.size() + " 个片段。";
    }
}

访问 /ingest 接口,你的知识库就准备好了。Spring AI 的 TokenTextSplitter 会智能地分割文档,而 vectorStore.add() 方法背后会自动调用你配置的Embedding模型为每个文本块生成向量。

第3步:智能问答(在线流程)

这是最精彩的部分,只需几行代码即可实现完整的RAG问答链。

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.api.Advisor;
import org.springframework.ai.rag.advisor.RetrievalAugmentationAdvisor;
import org.springframework.ai.rag.generation.augmentation.ContextualQueryAugmenter;
import org.springframework.ai.rag.retrieval.search.VectorStoreDocumentRetriever;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RagQaController {

    @Autowired
    private ChatClient chatClient;
    @Autowired
    private VectorStore vectorStore;

    @GetMapping("/ask")
    public String ask(@RequestParam String question) {
        // 1. 构建检索增强顾问(Retrieval Augmentation Advisor)
        Advisor ragAdvisor = RetrievalAugmentationAdvisor.builder()
                .documentRetriever(
                        VectorStoreDocumentRetriever.builder()
                                .vectorStore(vectorStore) // 指定知识库来源
                                .similarityThreshold(0.6) // 相似度阈值,过滤低质量结果
                                .topK(4) // 检索最相似的4个片段
                                .build()
                )
                .queryAugmenter(
                        ContextualQueryAugmenter.builder()
                                .allowEmptyContext(true) // 允许无检索结果
                                .build()
                )
                .build();

        // 2. 发起对话,并应用RAG顾问
        return chatClient.prompt()
                .advisors(ragAdvisor) // 挂载RAG能力
                .user(question)
                .call()
                .content();
    }
}

现在,访问 /ask?question=小璐是做什么工作的?。Spring AI 将自动执行以下操作:

  1. 将你的问题转化为向量。
  2. 在PGVector库中检索出最相关的几个关于“小璐”的文本片段。
  3. 将这些片段作为上下文,连同你的问题和内置的优化指令,一起发送给LLM。
  4. 将LLM生成的、基于上下文的专业回答返回给你。

效果对比

  • 无RAG:直接问LLM“小璐是谁?”,它可能回答不知道或胡编乱造。
  • 有RAG:LLM的回答将是:“小璐是一名专注于Java开发的计算机从业者...”,答案精准且源自你的文档。

总结与展望

通过本文,我们不仅理解了RAG如何通过“检索-增强”的机制攻克LLM的幻觉与知识滞后难题,还亲身体验了利用 Spring Boot 和Spring AI框架快速实现这一技术的便利性。从文档分块、向量化到检索与生成,Spring AI 提供了高度封装的组件,让Java开发者能专注于业务逻辑。

本文的实战示例基于文件系统和一个简单的员工档案。在实际生产环境中,你可以轻松扩展:

  • 知识源:支持PDF、Word、PPT、网页乃至数据库表。
  • 向量库:根据数据规模和要求,选用 PostgreSQL(PGVector)、Redis(RediSearch)或专业向量库。
  • 流程优化:引入更精细的重排序模型、多路检索、对话历史管理等高级特性。

RAG已成为构建企业级可信AI应用(如智能客服、知识库助手、代码分析工具)的基石技术。希望这篇从原理到实战的指南,能帮助你顺利上手,在 Spring AI 的助力下,将强大的大模型能力安全、可控地引入你的项目之中。




上一篇:SpringBoot实现轻量级Web日志查看器:告别tail -f,支持实时监控与筛选
下一篇:FireRed-OpenStoryline开源:首个具备导演思维的视频剪辑Agent,用对话驱动创作
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-23 17:07 , Processed in 0.436731 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表