【Java SpringAI智能体开发学习 | 2】SpringAI 实用特性:自定义Advisor,结构化输出,对话记忆持久化,prompt模板,多模态

发布于:2025-09-14 ⋅ 阅读:(16) ⋅ 点赞:(0)

 自定义Advisor

Spring官方提供了MessageChatMemoryAdvisor()和QuestionAnswerAdvisor()分别用来对话记忆和增强检索,但有可能不完全符合我们具体业务的需求。因此需要自定义Advisor来帮助我们更好地完成业务。

实现步骤:

1. 选择合适的接口实现:CallAroundAdvisor:处理同步请求和响应

或StreamAroundAdvisor:处理流式请求和响应

(建议一起实现)

2. 实现

3. 设置执行顺序,实现getOrder()

4. 提供唯一标识符,实现getName()

示例:

Spring提供的日志输出Advisor是debug级别的,我们要想实现info级别的就要自定义日志输出Advisor

@Slf4j
public class MyLoggerAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {

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

    public int getOrder() {
        return 0;
    }

    private AdvisedRequest before(AdvisedRequest request) {
        log.info("request: {}", request.userText());
        return request;
    }

    private void observeAfter(AdvisedResponse advisedResponse) {
        log.info("response: {}", advisedResponse.response().getResult().getOutput().getText());
    }

    public String toString() {
        return MyLoggerAdvisor.class.getSimpleName();
    }

    public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {
        advisedRequest = this.before(advisedRequest);
        AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest);
        this.observeAfter(advisedResponse);
        return advisedResponse;
    }

    public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {
        advisedRequest = this.before(advisedRequest);
        Flux<AdvisedResponse> advisedResponses = chain.nextAroundStream(advisedRequest);
        return (new MessageAggregator()).aggregateAdvisedResponse(advisedResponses, this::observeAfter);
    }
}

结构化输出

SpringAI 通过结构化输出转换器(Structured Output Converter)来将返回的文本输出转换为结构化数据格式,如JSON,Bean等

调用前在prompt后添加提示词,明确告诉模型使用哪种格式

响应后将文本转换为实例

SpringAI 提供了多种转换器实现,如MapOutputConverter,BeanOutputConverter和ListOutputConverter

实现:

record LoveReport(String title, List<String> suggestions) {

    }
    public LoveReport doChatWithReport(String message, String chatId) {
        LoveReport loveReport = client
                .prompt()
                .system(SYSTEM_PROMPT + "要求的格式,上文定义的record的{title}和{建议List}")//在prompt加后缀,要求生成的结构
                .user(message)
                .advisors(spec -> spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
                        .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
                .call()
                .entity(LoveReport.class);//生成特定的结构
        log.info("loveReport: {}", loveReport);
        return loveReport;
    }

对话记忆持久化

一般都是基于内存来保存对话记忆上下文,当重启后记忆就会消失。所以让对话记忆持久化是我们需要去实现的。

自定义实现ChatMemory

基于内存实现的ChatMemory存储的都是Message对象,所以要想实现自定义ChatMemory就要做到写消息时Message对象转换为文本,读消息时将文本转换为对象。即序列化和反序列化。

Prompt模板

PromptTemplate支持动态生成prompt

// 定义订单信息模板
String orderTemplate = "订单编号:{orderId},客户:{customer},商品:{product},数量:{quantity},总价:{totalPrice}元。";

// 创建模板对象
PromptTemplate orderPromptTemplate = new PromptTemplate(orderTemplate);

// 准备订单相关变量
Map<String, Object> orderVariables = new HashMap<>();
orderVariables.put("orderId", "ORD20230908001");
orderVariables.put("customer", "张三");
orderVariables.put("product", "无线耳机");
orderVariables.put("quantity", 2);
orderVariables.put("totalPrice", 998);

// 生成订单信息文本
String orderInfo = orderPromptTemplate.render(orderVariables);
// 结果: "订单编号:ORD20230908001,客户:张三,商品:无线耳机,数量:2,总价:998元。"

SpringAI 提供了几种专用的模板类:

SystemPromptTemplate:设置AI的行为和背景

AssistantPromptTemplate:设置AI回复的结构

从文件中加载模板

// 从类路径资源加载系统提示模板
@Value("classpath:/prompts/system-message.st")
private Resource systemResource;

// 直接使用资源创建模板
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource);

低侵入修改代码提示词

为不同场景提供不同提示词

多模态

多模态是同时处理多种不同数据类型的能力,比如文本,音频,图像等