JAVA程序员自救之路——SpringAI之Advisor,简化RAG开发
之前一篇文章讲过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#