检索增强生成(RAG)

什么是检索增强生成(RAG)? 检索增强.

什么是检索增强生成(RAG)?

检索增强生成(Retrieval Augmented Generation,简称 RAG)是一种强大的技术,用于克服大型语言模型(LLM)在处理长文本内容、事实准确性和上下文感知方面的局限性。在实际应用中,AI 模型虽然具备强大的生成能力,但其训练数据往往存在时效性限制,且无法直接访问企业的私有文档或特定领域的专业知识。

Spring AI 提供了完整的 RAG 实现框架,通过模块化的架构设计,允许开发者既可以使用开箱即用的 Advisor API 快速构建 RAG 流程,也可以根据业务需求自定义个性化的检索增强方案。

Spring AI 中的 Advisor 架构

Spring AI 的 Advisor API 是实现 RAG 功能的核心组件。它提供了一种声明式的方式来增强聊天客户端的能力,使得文档检索与 AI 响应生成能够无缝协作。

要使用 QuestionAnswerAdvisor 或 VectorStoreChatMemoryAdvisor,首先需要在项目中添加以下依赖:

<dependency>
   <groupId>org.springframework.ai</groupId>
   <artifactId>spring-ai-advisors-vector-store</artifactId>
</dependency>

QuestionAnswerAdvisor 的工作原理

在深入了解 QuestionAnswerAdvisor 之前,我们需要理解向量数据库在 RAG 系统中的关键作用。向量数据库专门用于存储经过 embedding 处理的文档数据,这些数据对于 AI 模型来说是”未知”的外部知识。当用户提出问题时,系统会首先从向量数据库中检索与问题相关的文档,然后将这些文档作为上下文提供给 AI 模型,从而生成更加准确和基于事实的答案。

QuestionAnswerAdvisor 正是实现了这一流程的核心组件。假设你已经将数据加载到 VectorStore 中,可以使用以下代码实现 RAG:

ChatResponse response = ChatClient.builder(chatModel)
        .build().prompt()
        .advisors(QuestionAnswerAdvisor.builder(vectorStore).build())
        .user(userText)
        .call()
        .chatResponse();

在上述示例中,QuestionAnswerAdvisor 会对向量数据库中的所有文档执行相似度搜索。如果需要限制搜索的文档范围,可以通过 SearchRequest 配置 SQL 风格的过滤表达式,这种表达式在不同的 VectorStore 实现之间具有良好的可移植性。

配置搜索阈值和返回数量

可以通过 SearchRequest 灵活配置相似度阈值和返回结果数量:

var qaAdvisor = QuestionAnswerAdvisor.builder(vectorStore)
        .searchRequest(SearchRequest.builder().similarityThreshold(0.8d).topK(6).build())
        .build();

这里我们将相似度阈值设置为 0.8,表示只返回与用户问题高度相关的文档;同时设置 topK 为 6,即最多返回 6 条检索结果。这种配置方式可以帮助你在检索质量和响应速度之间取得平衡。

动态过滤表达式

有时我们需要在运行时动态调整过滤条件。Spring AI 提供了 FILTER_EXPRESSION 参数来实现这一功能:

ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(QuestionAnswerAdvisor.builder(vectorStore)
        .searchRequest(SearchRequest.builder().build())
        .build())
    .build();

// 在运行时更新过滤表达式
String content = this.chatClient.prompt()
    .user("请回答我的问题 XYZ")
    .advisors(a -> a.param(QuestionAnswerAdvisor.FILTER_EXPRESSION, "type == 'Spring'"))
    .call()
    .content();

这种动态过滤机制特别适用于需要根据用户权限、场景或时间条件来限制搜索范围的场景。

自定义提示词模板

QuestionAnswerAdvisor 使用默认模板将检索到的文档与用户问题进行融合。如果你需要自定义这种行为,可以通过 .promptTemplate() 方法提供自己的 PromptTemplate:

PromptTemplate customPromptTemplate = PromptTemplate.builder()
    .renderer(StTemplateRenderer.builder().startDelimiterToken('<').endDelimiterToken('>').build())
    .template("""
            <query>

            以下是相关上下文信息。

			---------------------
			<question_answer_context>
			---------------------

			根据提供的上下文信息回答问题,不要使用先验知识。

			请遵循以下规则:

			1. 如果答案不在上下文中,请明确说明不知道。
			2. 避免使用"根据上下文..."或"提供的信息显示..."等表述。
            """)
    .build();

String question = "Anacletus 和 Birba 的冒险发生在哪里?";

QuestionAnswerAdvisor qaAdvisor = QuestionAnswerAdvisor.builder(vectorStore)
    .promptTemplate(customPromptTemplate)
    .build();

String response = ChatClient.builder(chatModel).build()
    .prompt(question)
    .advisors(qaAdvisor)
    .call()
    .content();

需要注意的是,自定义模板必须包含两个占位符:query 用于接收用户问题,question_answer_context 用于接收检索到的上下文内容。

RetrievalAugmentationAdvisor 进阶使用

对于更复杂的 RAG 场景,Spring AI 提供了 RetrievalAugmentationAdvisor。要使用此组件,需要添加以下依赖:

<dependency>
   <groupId>org.springframework.ai</groupId>
   <artifactId>spring-ai-rag</artifactId>
</dependency>

基础 RAG 流程(Naive RAG)

最简单的 RAG 实现方式是直接使用向量存储进行文档检索,然后将这些文档作为上下文提供给语言模型:

Advisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
        .documentRetriever(VectorStoreDocumentRetriever.builder()
                .similarityThreshold(0.50)
                .vectorStore(vectorStore)
                .build())
        .build();

String answer = chatClient.prompt()
        .advisors(retrievalAugmentationAdvisor)
        .user(question)
        .call()
        .content();

默认情况下,RetrievalAugmentationAdvisor 不允许检索结果为空。当没有找到相关文档时,它会指示模型不要回答用户问题。如果希望允许空上下文,可以通过 ContextualQueryAugmenter 配置:

Advisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
        .documentRetriever(VectorStoreDocumentRetriever.builder()
                .similarityThreshold(0.50)
                .vectorStore(vectorStore)
                .build())
        .queryAugmenter(ContextualQueryAugmenter.builder()
                .allowEmptyContext(true)
                .build())
        .build();

String answer = chatClient.prompt()
        .advisors(retrievalAugmentationAdvisor)
        .user(question)
        .call()
        .content();

VectorStoreDocumentRetriever 支持基于元数据的过滤表达式,你可以在实例化时配置或在运行时通过 FILTER_EXPRESSION 参数动态提供:

Advisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
        .documentRetriever(VectorStoreDocumentRetriever.builder()
                .similarityThreshold(0.50)
                .vectorStore(vectorStore)
                .build())
        .build();

String answer = chatClient.prompt()
        .advisors(retrievalAugmentationAdvisor)
        .advisors(a -> a.param(VectorStoreDocumentRetriever.FILTER_EXPRESSION, "type == 'Spring'"))
        .user(question)
        .call()
        .content();

高级 RAG 流程

高级 RAG 通过引入查询转换(Query Transformation)来优化检索效果。一个常见的场景是用户的原始问题可能表述不够清晰或包含歧义,这时可以先使用 LLM 对问题进行重写,使其更适合向量检索:

Advisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
        .queryTransformers(RewriteQueryTransformer.builder()
                .chatClientBuilder(chatClientBuilder.build().mutate())
                .build())
        .documentRetriever(VectorStoreDocumentRetriever.builder()
                .similarityThreshold(0.50)
                .vectorStore(vectorStore)
                .build())
        .build();

String answer = chatClient.prompt()
        .advisors(retrievalAugmentationAdvisor)
        .user(question)
        .call()
        .content();

此外,你还可以使用 DocumentPostProcessor API 对检索到的文档进行后处理,例如执行文档重排序以提高相关性、去除无关或冗余的文档、压缩文档内容以减少噪音等。

模块化 RAG 架构

Spring AI 的 RAG 实现采用了模块化设计理念,灵感来源于论文《Modular RAG: Transforming RAG Systems into LEGO-like Reconfigurable Frameworks》。这种设计将 RAG 流程拆分为多个独立的模块,使得开发者可以根据具体需求灵活组合。

查询转换模块(Pre-Retrieval)

检索前模块负责处理用户查询,以获得最佳的检索效果。查询转换是一种常见的技术,用于解决查询表述不当、歧义术语、复杂词汇或不支持的语言等问题。

使用 QueryTransformer 时,建议在 ChatClient.Builder 中将温度参数设置为较低值(如 0.0),以确保结果更加确定性和准确。大多数聊天模型的默认温度对于查询转换任务来说过高,可能会降低检索效果。

压缩查询转换器(CompressionQueryTransformer)

CompressionQueryTransformer 使用大型语言模型将对话历史和后续问题压缩为一个独立的查询,从而捕捉对话的核心内容。这对于对话历史较长且后续问题与上下文相关的情况特别有用:

Query query = Query.builder()
        .text("它的第二大城市是什么?")
        .history(new UserMessage("丹麦的首都是哪里?"),
                new AssistantMessage("哥本哈根是丹麦的首都。"))
        .build();

QueryTransformer queryTransformer = CompressionQueryTransformer.builder()
        .chatClientBuilder(chatClientBuilder)
        .build();

Query transformedQuery = queryTransformer.transform(query);

该组件使用的提示词模板可以通过 builder 的 promptTemplate() 方法进行自定义。

重写查询转换器(RewriteQueryTransformer)

RewriteQueryTransformer 使用大型语言模型对用户查询进行重写,以便在查询目标系统(如向量存储或网络搜索引擎)时获得更好的结果。

这种转换器在用户查询表述不够理想的情况下特别有价值,例如查询过于简短、包含口语化表达或缺乏必要的上下文信息时。

总结

本文详细介绍了 Spring AI 中检索增强生成(RAG)的完整实现方案。从基础的 QuestionAnswerAdvisor 到高级的 RetrievalAugmentationAdvisor,再到模块化的查询转换架构,Spring AI 提供了一套完整且灵活的工具体系。

在实际项目中选择合适的 RAG 方案时,建议根据业务需求进行评估:对于简单的场景,可以使用 QuestionAnswerAdvisor 快速实现;对于需要更多定制的场景,可以利用 RetrievalAugmentationAdvisor 的模块化架构进行组合;而对于处理复杂对话或需要查询优化的场景,则可以引入查询转换模块来提升检索质量。

RAG 技术的核心价值在于将 AI 的生成能力与私有知识库相结合,使企业能够构建既智能又可控的 AI 应用。通过合理使用 Spring AI 提供的这些工具,开发者可以快速构建生产级的 RAG 应用,同时保持代码的可维护性和可扩展性。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注