Advisor

介绍

核心维度 核心要点
核心概念 类似于 AOP 中的拦截器,工作在由多个 Advisor 组成的责任链上,同时支持非流式(CallAroundAdvisor)和流式(StreamAroundAdvisor)处理。
工作机制 执行流程为 “请求 → Advisor链before处理 → 大模型 → Advisor链after处理 → 响应”。每个Advisor在链中有两次执行机会,并共享一个上下文(AdvisorContext)用于传递数据。
典型应用 内置了对话记忆、敏感词过滤、RAG检索增强、日志记录等常用 Advisor。
开发方式 通过实现 CallAroundAdvisor 和 StreamAroundAdvisor 接口来自定义,并通过 getOrder() 方法控制其在链中的执行顺序。

SimpleLoggerAdvisor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClientMessageAggregator;
import org.springframework.ai.chat.client.ChatClientRequest;
import org.springframework.ai.chat.client.ChatClientResponse;
import org.springframework.ai.chat.client.advisor.api.CallAdvisor;
import org.springframework.ai.chat.client.advisor.api.CallAdvisorChain;
import org.springframework.ai.chat.client.advisor.api.StreamAdvisor;
import org.springframework.ai.chat.client.advisor.api.StreamAdvisorChain;
import reactor.core.publisher.Flux;

@Slf4j
public class SimpleLoggerAdvisor implements CallAdvisor, StreamAdvisor {

@Override
public String getName() {
return this.getClass().getSimpleName();
}

@Override
public int getOrder() {
return 0;
}

@Override
public ChatClientResponse adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain) {
logRequest(chatClientRequest);

ChatClientResponse chatClientResponse = callAdvisorChain.nextCall(chatClientRequest);

logResponse(chatClientResponse);

return chatClientResponse;
}

@Override
public Flux<ChatClientResponse> adviseStream(ChatClientRequest chatClientRequest,
StreamAdvisorChain streamAdvisorChain) {
log.info("adviseStream");
logRequest(chatClientRequest);

Flux<ChatClientResponse> chatClientResponses = streamAdvisorChain.nextStream(chatClientRequest);

return new ChatClientMessageAggregator().aggregateChatClientResponse(chatClientResponses, this::logResponse);
}

private void logRequest(ChatClientRequest request) {
log.info("request: {}", request);
}

private void logResponse(ChatClientResponse chatClientResponse) {
log.info("response: {}", chatClientResponse);
}

}

AdvisorController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.SafeGuardAdvisor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

import java.util.List;

@RestController
@Slf4j
@RequestMapping("/advisor")
public class AdvisorController {

@Resource
private ChatClient ollamaChatChatClient;


@RequestMapping(value = "/chat1", produces = "text/stream;charset=utf8")
public Flux<String> chat1() {
Flux<String> flux = ollamaChatChatClient.prompt().advisors(new SimpleLoggerAdvisor()).user("你是谁").stream().content();
return flux;
}

@RequestMapping(value = "/chat2", produces = "text/stream;charset=utf8")
public Flux<String> chat2() {
Flux<String> flux = ollamaChatChatClient.prompt().advisors(new SafeGuardAdvisor(List.of("老板"), "提示词包含敏感词", 0)).user("你是谁,你是老板吗?").stream().content();
return flux;
}

@RequestMapping(value = "/chat3", produces = "text/stream;charset=utf8")
public Flux<String> chat3() {
Flux<String> flux = ollamaChatChatClient.prompt().advisors(new ReReadingAdvisor()).user("你是谁?").stream().content();
return flux;
}
}

ReReadingAdvisor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

import org.springframework.ai.chat.client.ChatClientRequest;
import org.springframework.ai.chat.client.ChatClientResponse;
import org.springframework.ai.chat.client.advisor.api.AdvisorChain;
import org.springframework.ai.chat.client.advisor.api.BaseAdvisor;
import org.springframework.ai.chat.prompt.PromptTemplate;

import java.util.Map;

public class ReReadingAdvisor implements BaseAdvisor {

private static final String DEFAULT_RE2_ADVISE_TEMPLATE = """
{re2_input_query}
Read the question again: {re2_input_query}
""";

private final String re2AdviseTemplate;

private int order = 0;

public ReReadingAdvisor() {
this(DEFAULT_RE2_ADVISE_TEMPLATE);
}

public ReReadingAdvisor(String re2AdviseTemplate) {
this.re2AdviseTemplate = re2AdviseTemplate;
}

@Override
public ChatClientRequest before(ChatClientRequest chatClientRequest, AdvisorChain advisorChain) {
String augmentedUserText = PromptTemplate.builder()
.template(this.re2AdviseTemplate)
.variables(Map.of("re2_input_query", chatClientRequest.prompt().getUserMessage().getText()))
.build()
.render();

return chatClientRequest.mutate()
.prompt(chatClientRequest.prompt().augmentUserMessage(augmentedUserText))
.build();
}

@Override
public ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain) {
return chatClientResponse;
}

@Override
public int getOrder() {
return this.order;
}

public ReReadingAdvisor withOrder(int order) {
this.order = order;
return this;
}

}