SpringAI实现Reread(Advisor)
文章目录
- 1_Advisor介绍
- 2_reread介绍
- 3_实现 ReReadingAdvisor
1_Advisor介绍
Advisor 是 SpringAI 基于 AOP 机制实现与大模型对话过程的增强、拦截、修改等功能的关键组件。
所有的增强通知都需要实现 Advisor 接口。
常见的组件有如下几种:
- Advisor:顶级接口,继承了 Ordered,可以定义拦截器(Advisor)的顺序。
- ChatClientRequest:表示由 ChatClient 处理的请求,最终用于构建要发送到 LLM 的Prompt。
- ChatClientResponse:表示由 ChatClient 返回的响应。
- CallAdvisor:非流式场景的接口(增强 ChatClientRequest 和 ChatClientResponse)。
- StreamAdvisor:流式场景的接口(增强 ChatClientRequest 和 ChatClientResponse)。
- CallAdvisorChain:非流式场景的实例链,用于编排链中下一个 CallAdvisor 实例的执行。
- StreamAdvisorChain:流式场景的实例链,用于编排链中下一个 StreamAdvisor 实例的执行。
常见的实现有如下几种:
- BaseAdvisor:基础 Advisor,实现了 CallAdvisor, StreamAdvisor 两个接口。
- SimpleLoggerAdvisor:日志记录的 Advisor。
- MessageChatMemoryAdvisor:会话记忆的 Advisor。
- SafeGuardAdvisor:拦截敏感词的 Advisor。
- VectorStoreChatMemoryAdvisor:从 VectorStore 检索内存并将其添加到提示的系统文本中的Advisor。
最基本的两个接口是 CallAdvisor, StreamAdvisor,但有时并不知晓到底应该选择流式还是非流式,所以一般情况下都会一起实现。
而 BaseAdvisor 这个接口就可以方便我们快速创建自定义的 Advisor,只用实现其中的 before
和 after
方法。
根据以下源码可知,无论是流式还是非流式都会在请求传递给下一个 Advisor 前后分别调用 before
和 after
方法:
public interface BaseAdvisor extends CallAdvisor, StreamAdvisor {Scheduler DEFAULT_SCHEDULER = Schedulers.boundedElastic();@Overridedefault ChatClientResponse adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain) {ChatClientRequest processedChatClientRequest = before(chatClientRequest, callAdvisorChain);ChatClientResponse chatClientResponse = callAdvisorChain.nextCall(processedChatClientRequest);return after(chatClientResponse, callAdvisorChain);}@Overridedefault Flux<ChatClientResponse> adviseStream(ChatClientRequest chatClientRequest,StreamAdvisorChain streamAdvisorChain) {Flux<ChatClientResponse> chatClientResponseFlux = Mono.just(chatClientRequest).publishOn(getScheduler()).map(request -> this.before(request, streamAdvisorChain)).flatMapMany(streamAdvisorChain::nextStream);return chatClientResponseFlux.map(response -> {if (AdvisorUtils.onFinishReason().test(response)) {response = after(response, streamAdvisorChain);}return response;}).onErrorResume(error -> Flux.error(new IllegalStateException("Stream processing failed", error)));}@Overridedefault String getName() {return this.getClass().getSimpleName();}/*** Logic to be executed before the rest of the advisor chain is called.*/ChatClientRequest before(ChatClientRequest chatClientRequest, AdvisorChain advisorChain);/*** Logic to be executed after the rest of the advisor chain is called.*/ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain);/*** Scheduler used for processing the advisor logic when streaming.*/default Scheduler getScheduler() {return DEFAULT_SCHEDULER;}}
总结:简单来说就是责任链模式。
2_reread介绍
重读策略的核心在于让 LLMs 重新审视输入的问题,这借鉴了人类解决问题的思维方式。
通过这种方式,LLMs 能够更深入地理解问题,发现复杂的模式,从而在各种推理任务中表现得更加强大。
可以按照如下方式编写提示词模版:
{Input_Query}
Read the question again: {Input_Query}
论文链接地址:https://arxiv.org/pdf/2309.06275。
3_实现 ReReadingAdvisor
基于 BaseAdvisor 来实现自定义的 Advisor,避免了重复代码的编写,只需专注自己的业务即可:
@SuppressWarnings("all")
public class ReReadingAdvisor implements BaseAdvisor {private static final String DEFAULT_RE2_ADVISE_TEMPLATE = """{re2_input_query}Read the question again: {re2_input_query}""";@Overridepublic ChatClientRequest before(ChatClientRequest chatClientRequest, AdvisorChain advisorChain) {// 拿到之前的提示词String userPrompt = chatClientRequest.prompt().getUserMessage().getText();String re2InputQuery = PromptTemplate.builder().template(DEFAULT_RE2_ADVISE_TEMPLATE).variables(Map.of("re2_input_query", userPrompt)).build().render();return chatClientRequest.mutate() // 复制一个新的chatClientRequest.prompt(chatClientRequest.prompt().augmentUserMessage(re2InputQuery)).build();}@Overridepublic ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain) {return chatClientResponse;}@Overridepublic int getOrder() {return 0;}}
还可以进行扩展比如增加一个设置 Order 的方法。