文章目录
-
- 1_Advisor介绍
- 2_reread介绍
- [3_实现 ReReadingAdvisor](#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
方法:
java
public interface BaseAdvisor extends CallAdvisor, StreamAdvisor {
Scheduler DEFAULT_SCHEDULER = Schedulers.boundedElastic();
@Override
default ChatClientResponse adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain) {
ChatClientRequest processedChatClientRequest = before(chatClientRequest, callAdvisorChain);
ChatClientResponse chatClientResponse = callAdvisorChain.nextCall(processedChatClientRequest);
return after(chatClientResponse, callAdvisorChain);
}
@Override
default 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)));
}
@Override
default 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 能够更深入地理解问题,发现复杂的模式,从而在各种推理任务中表现得更加强大。
可以按照如下方式编写提示词模版:
properties
{Input_Query}
Read the question again: {Input_Query}
论文链接地址:https://arxiv.org/pdf/2309.06275。
3_实现 ReReadingAdvisor
基于 BaseAdvisor 来实现自定义的 Advisor,避免了重复代码的编写,只需专注自己的业务即可:
java
@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}
""";
@Override
public 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();
}
@Override
public ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain) {
return chatClientResponse;
}
@Override
public int getOrder() {
return 0;
}
}
还可以进行扩展比如增加一个设置 Order 的方法。