Spring AI RAG 实战:从本地知识库到云服务,构建恋爱大师智能助手

0 阅读8分钟

前言

在人工智能快速发展的今天,大语言模型(LLM)展现出了强大的对话能力。然而,大模型存在两个明显的痛点:知识时效性限制幻觉问题。当用户询问模型训练数据之外的最新信息时,模型要么无法回答,要么可能“编造”不存在的答案。

RAG(Retrieval-Augmented Generation,检索增强生成)技术正是为了解决这些问题而生的。本文将带你从零开始,使用 Spring AI 框架构建一个完整的 RAG 应用——恋爱大师智能助手。

一、什么是 RAG?

RAG 是一种结合信息检索技术和 AI 内容生成的混合架构。简单来说,它的工作流程是:当用户提问时,系统先从知识库中检索相关信息,然后将这些信息与用户问题一起提交给大模型,让模型基于真实的知识进行回答

这种架构的优势显而易见:

  • 知识实时更新:只需更新知识库,无需重新训练模型
  • 答案可溯源:可以追溯到回答所依据的原始文档
  • 降低幻觉:模型有了事实依据,减少了编造信息的可能

二、RAG 核心工作流程

RAG 主要包含以下四个核心步骤:

1. 文档收集与切割

首先需要准备知识文档。原始文档可能来自网页、PDF、数据库等多种来源。我们需要对文档进行预处理:

  • 清洗:去除无关的格式、特殊字符
  • 切割:将长文档分割成适当大小的片段

常见的切割策略包括:

  • 基于固定大小(如 512 个 token)
  • 基于语义边界(按段落、句子分割)
  • 基于递归分割

2. 向量转换与存储

将文本块通过 Embedding 模型转换为高维向量。这些向量能够捕获文本的语义特征,使得语义相似的文本在向量空间中距离更近。

随后将向量和对应文本存入向量数据库,支持高效的相似性搜索。常用的向量数据库包括 SimpleVectorStore(内存型)、Pinecone、Milvus 等。

3. 文档过滤与检索

当用户提问时:

  • 将用户问题也转换为向量
  • 在向量数据库中查找与问题向量最相似的文档块
  • 常用的相似度算法有余弦相似度、欧氏距离等

4. 查询增强与生成

将检索到的相关文档与用户问题组合成增强提示,提交给大模型生成最终回答。这个环节通常还会涉及:

  • 上下文融合
  • 源引用
  • 后处理优化

三、RAG 实战:构建恋爱大师智能助手

下面我们通过一个完整的案例来实践 RAG 技术。我们将构建一个“恋爱大师”智能助手,它能根据知识库中的恋爱心理学知识,为用户提供专业的恋爱建议。

3.1 技术栈

  • Spring AI:统一的 AI 应用开发框架
  • DashScope:阿里云的大模型服务平台(本文使用其 Embedding 和 Chat 能力)
  • 向量数据库:SimpleVectorStore(内存版)和阿里云知识库服务(云版)

3.2 文档加载器实现

首先,我们需要实现文档加载器,从 classpath 下的 document/ 目录读取 Markdown 文件。

@Component
@Slf4j
public class LoveAppDocumentLoader {

    private final ResourcePatternResolver resourcePatternResolver;

    public LoveAppDocumentLoader(ResourcePatternResolver resourcePatternResolver) {
        this.resourcePatternResolver = resourcePatternResolver;
    }

    /**
     * 读取多篇Markdown文件,获取文档列表
     */
    public List<Document> loaderMarkdowns() {
        List<Document> allDocuments = new ArrayList<>();
        try {
            // 读取document目录下的所有.md文件
            Resource[] resources = resourcePatternResolver.getResources("classpath:document/*.md");
            
            for (Resource r : resources) {
                String filename = r.getFilename();
                // 配置Markdown解析器
                MarkdownDocumentReaderConfig config = MarkdownDocumentReaderConfig.builder()
                        .withHorizontalRuleCreateDocument(true)
                        .withIncludeCodeBlock(false)
                        .withIncludeBlockquote(false)
                        .withAdditionalMetadata("filename", filename)
                        .build();
                        
                MarkdownDocumentReader reader = new MarkdownDocumentReader(r, config);
                allDocuments.addAll(reader.get());
            }
        } catch (IOException e) {
            log.error("读取Markdown文件失败", e);
        }
        return allDocuments;
    }
}

3.3 向量存储配置

接下来,配置向量存储。我们使用 Spring AI 内置的 SimpleVectorStore 来保存文档向量。

@Configuration
public class LoveAppVectorStoreConfig {

    @Resource
    private LoveAppDocumentLoader loveAppDocumentLoader;

    @Bean
    VectorStore loveAppVectorStore(EmbeddingModel dashscopeEmbeddingModel) {
        // 创建基于内存的向量数据库
        SimpleVectorStore loveAppVectorStore = SimpleVectorStore
                .builder(dashscopeEmbeddingModel)
                .build();
        
        // 加载文档并添加到向量库
        List<Document> documents = loveAppDocumentLoader.loaderMarkdowns();
        loveAppVectorStore.doAdd(documents);
        
        return loveAppVectorStore;
    }
}

3.4 云知识库 RAG 配置

除了本地向量库,Spring AI 还支持对接阿里云知识库服务。这种方式更适合生产环境,因为云服务提供了更好的可扩展性和维护性。

@Configuration
public class LoveAppRagCloudAdvisorConfig {
    
    @Value("${spring.ai.dashscope.api-key}")
    private String dashScopeApiKey;

    @Bean
    Advisor loveAppRagCloudAdvisor() {
        DashScopeApi dashScopeApi = new DashScopeApi(dashScopeApiKey);
        
        // 知识库索引名称
        final String KNOWLEDGE_INDEX = "恋爱大师";
        
        // 创建文档检索器
        DashScopeDocumentRetriever retriever = new DashScopeDocumentRetriever(
            dashScopeApi,
            DashScopeDocumentRetrieverOptions.builder()
                    .withIndexName(KNOWLEDGE_INDEX)
                    .build()
        );
        
        // 创建检索增强顾问
        return RetrievalAugmentationAdvisor.builder()
                .documentRetriever(retriever)
                .build();
    }
}

3.5 恋爱大师主类实现

最后,将所有组件组装起来,实现完整的对话功能。

@Component
@Slf4j
public class LoveApp {
    
    private ChatClient chatClient;
    
    // 系统预设提示词
    private static final String SYSTEM_PROMPT = """
        扮演深耕恋爱心理领域的专家。开场向用户表明身份,告知用户可倾诉恋爱难题。\
        围绕单身、恋爱、已婚三种状态提问:单身状态询问社交圈拓展及追求心仪对象的困扰;\
        恋爱状态询问沟通、习惯差异引发的矛盾;已婚状态询问家庭责任与亲属关系处理的问题。\
        引导用户详述事情经过、对方反应及自身想法,以便给出专属解决方案。""";

    @Resource
    private VectorStore loveAppVectorStore;
    
    @Resource
    private Advisor loveAppRagCloudAdvisor;

    public LoveApp(ChatModel dashscopeChatModel) {
        // 初始化对话记忆(支持多轮对话)
        ChatMemory chatMemory = new InMemoryChatMemory();

        this.chatClient = ChatClient.builder(dashscopeChatModel)
                .defaultSystem(SYSTEM_PROMPT)
                .defaultAdvisors(
                    MessageChatMemoryAdvisor.builder(chatMemory).build(),
                    new MyLoggerAdvisor()  // 自定义日志顾问
                )
                .build();
    }

    /**
     * 基础对话(支持多轮对话记忆)
     */
    public String doChat(String message, String chatId) {
        ChatResponse chatResponse = chatClient.prompt()
                .user(message)
                .advisors(spec -> spec
                    .param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
                    .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
                .call()
                .chatResponse();
        return chatResponse.getResult().getOutput().getText();
    }

    /**
     * 结构化输出 - 恋爱报告
     */
    record LoveReport(String title, List<String> suggestions) {}
    
    public LoveReport doChatWithReport(String message, String chatId) {
        LoveReport response = chatClient.prompt()
                .system(SYSTEM_PROMPT + "每次对话后都要生成恋爱结果,标题为{用户名}的恋爱报告,内容为建议列表")
                .user(message)
                .advisors(spec -> spec
                    .param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
                    .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
                .call()
                .entity(LoveReport.class);
        return response;
    }

    /**
     * 基于本地向量库的 RAG 问答
     */
    public String doChatWithLocalRag(String message, String chatId) {
        ChatResponse chatResponse = chatClient.prompt()
                .user(message)
                .advisors(spec -> spec
                    .param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
                    .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
                .advisors(new QuestionAnswerAdvisor(loveAppVectorStore))
                .call()
                .chatResponse();
        return chatResponse.getResult().getOutput().getText();
    }

    /**
     * 基于云知识库的 RAG 问答
     */
    public String doChatWithCloudRag(String message, String chatId) {
        ChatResponse chatResponse = chatClient.prompt()
                .user(message)
                .advisors(spec -> spec
                    .param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
                    .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
                .advisors(loveAppRagCloudAdvisor)
                .call()
                .chatResponse();
        return chatResponse.getResult().getOutput().getText();
    }
}

四、关键技术点解析

4.1 Spring AI 的 Advisor 机制

在 Spring AI 中,Advisor(顾问)是一个强大的拦截器概念。它可以:

  • MessageChatMemoryAdvisor:管理对话历史,实现多轮对话记忆
  • QuestionAnswerAdvisor:简单的 RAG 问答拦截器,自动将检索结果注入提示词
  • RetrievalAugmentationAdvisor:更强大的检索增强顾问,支持灵活配置

4.2 两种 RAG 实现方式对比

对比维度本地向量库云知识库服务
部署成本低,无需额外服务需开通云服务
扩展性受限于单机内存弹性扩展
维护成本需自行维护云服务托管
检索性能适合小规模数据支持海量数据
适用场景开发测试、小规模应用生产环境、大规模应用

4.3 Embedding 模型的作用

Embedding 模型是整个 RAG 系统的核心。它将文本转换为向量,使得:

  • 语义相似的文本在向量空间中距离更近
  • 支持基于语义的相似度搜索
  • 可以跨语言进行检索(取决于模型能力)

五、实践建议与最佳实践

5.1 文档切割策略

  • 固定大小切割:简单但可能切断语义完整的段落
  • 语义边界切割:按段落、句子切割,保持语义完整性
  • 递归切割:从大到小尝试不同分隔符,找到合适的分割点

建议根据实际文档类型选择合适的策略。

5.2 检索优化

  • 混合检索:结合关键词检索和语义检索
  • 重排序:检索后使用 Rank 模型对结果进行精排
  • 元数据过滤:利用文档的元数据(如时间、来源)进行过滤

5.3 提示词设计

增强提示词的模板通常如下:

基于以下参考资料回答用户问题:

【参考资料】
{检索到的文档内容}

【用户问题】
{用户输入}

请基于参考资料回答问题,如果参考资料中没有相关信息,请告知用户。

六、总结

本文通过一个完整的实战案例,介绍了 RAG 技术的核心概念和实现方式。从文档加载、向量存储到检索增强,我们使用 Spring AI 框架构建了一个恋爱大师智能助手。

RAG 技术巧妙地结合了信息检索和大模型的优势,为大模型应用开辟了新的可能性。无论是开发智能客服、知识库问答系统,还是构建专业的领域专家助手,RAG 都是一种值得考虑的技术方案。

希望这篇文章能帮助你理解并掌握 RAG 技术,在实际项目中灵活运用。如果你对 Spring AI 或 RAG 技术有任何疑问,欢迎交流讨论!