JAVA程序员自救之路——SpringAI之Advisor,简化RAG开发

JAVA程序员自救之路——SpringAI之Advisor,简化RAG开发

编程文章jaq1232025-05-22 11:03:584A+A-

之前一篇文章讲过RAG的一个demo。详细内容请看,JAVA程序员自救之路——SpringAI与ES实现RAG

最近看了一下SpringAI的1.0.0-snapshot的文档,他在介绍构建RAG应用时提到了,Advisor。并且已经集成了一些现成的Advisor供我们使用。利用这个,我们可以更简化我们之前的代码。

首先我们讲一下什么是advisor。

Spring AI 中的 Advisors 是一个关键概念,用于在模型交互过程中动态调整或增强提示词(Prompt)、控制生成过程,或注入业务逻辑。它的核心思想是对 AI 模型的输入/输出进行拦截和增强,类似于 AOP(面向切面编程)中的拦截器。

Advisor 的核心作用:

  • 动态修改提示词:在发送给模型前,自动添加上下文、示例或格式化内容。
  • 结果后处理:对模型生成的文本进行过滤、校验或结构化解析。
  • 上下文管理:跨多次对话维护状态(如历史记录、用户偏好)。
  • 业务规则注入:根据业务需求限制或引导模型的输出。

2. Advisor 的工作原理

Advisor 在请求处理链中的位置:

用户输入 → [Advisor 预处理] → 模型处理 → [Advisor 后处理] → 返回用户
  • 预处理阶段:修改或增强输入的 Prompt
  • 后处理阶段:处理模型的 Response

很像AOP的环绕切面。

SpringAI提供了很多这种Advisor,比如用于记录日志的SimpleLoggerAdvisor,还有记录上下文的MessageChatMemoryAdvisor(并非所有LLM模型支持),PromptChatMemoryAdvisor(内存),
VectorStoreChatMemoryAdvisor(向量数据库)
,用于敏感词校验的SafeGuardAdvisor。当然,我们还可以自定义一个Advisor。只需实现Advisor接口就可以。CallAroundAdvisor是非流式的,StreamAroundAdvisor是流式的。

下面我们,看一下代码上如何使用Advisor来实现RAG。

首先,我们还是引入新的SpringAI模块。他主要是用来利用向量数据库检索来实现LLM增强。

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

然后我们在代码中接入Advisor:QuestionAnswerAdvisor。

                SearchRequest searchRequest = SearchRequest.builder().query(question).similarityThreshold(0.5d).topK(5).build();
                // Calling the chat model with the question
                ChatResponse chatResponse =chatClient.prompt()
                        .advisors(new QuestionAnswerAdvisor(vectorStore, searchRequest),new SimpleLoggerAdvisor())
                        .user(question)
                        .call().chatResponse();
                String response =  Optional.ofNullable(chatResponse)
                        .map(ChatResponse::getResult)
                        .map(Generation::getOutput)
                        .map(AbstractMessage::getText)
                        .orElse(null);
                StringJoiner stringJoiner = new StringJoiner(System.lineSeparator());
                Optional.ofNullable(chatResponse)
                        .map(ChatResponse::getMetadata)
                        .map(metadata -> (List<Document>)metadata.get(QuestionAnswerAdvisor.RETRIEVED_DOCUMENTS))
                        .orElse(new ArrayList<>())
                        .stream().forEach(doc -> {
                                stringJoiner.add(doc.getMetadata().get(PagePdfDocumentReader.METADATA_FILE_NAME).toString());
                        });

                return response
                        + System.lineSeparator() + System.lineSeparator() +
                        "答案仅供参考相关文件查阅: " + System.lineSeparator() +
                        stringJoiner
                        ;

关键就在于这句话

SearchRequest searchRequest = SearchRequest.builder().query(question).similarityThreshold(0.5d).topK(5).build()


ChatResponse chatResponse =chatClient.prompt().advisors(new QuestionAnswerAdvisor(vectorStore, searchRequest),new SimpleLoggerAdvisor())

通过构造QuestionAnswerAdvisor,来实现RAG。具体之前咱们写的处理逻辑,现在都由它来代替完成了。至此,我们就改造完成了。大部分逻辑SpringAI帮助封装了。我们只需要使用就可以了。原来将近一百行的代码,现在只需要一行就完成了。

我们看下效果,跟之前的差不多,好像还更精确了,不知道是不是错觉,哈哈。

#spring# #java# #ai#

点击这里复制本文地址 以上内容由jaq123整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

苍茫编程网 © All Rights Reserved.  蜀ICP备2024111239号-21