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

自定义Advisor

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

实现步骤:

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

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

(建议一起实现)

  1. 实现

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

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

示例:

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

java 复制代码
@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

实现:

java 复制代码
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

java 复制代码
// 定义订单信息模板
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回复的结构

从文件中加载模板

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

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

低侵入修改代码提示词

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

多模态

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

相关推荐
影子240112 小时前
oralce创建种子表,使用存储过程生成最大值sql,考虑并发,不考虑并发的脚本,plsql调试存储过程,java调用存储过程示例代码
java·数据库·sql
武子康12 小时前
Java-172 Neo4j 访问方式实战:嵌入式 vs 服务器(含 Java 示例与踩坑)
java·服务器·数据库·sql·spring·nosql·neo4j
程序猿DD12 小时前
深入探索剖析 JVM 的启动过程
java
Arva .13 小时前
ConcurrentHashMap 的线程安全实现
java·开发语言
听风吟丶13 小时前
Java 9+ 模块化系统(Jigsaw)实战:从 Jar 地狱到模块解耦的架构升级
java·架构·jar
昂子的博客13 小时前
Redis缓存 更新策略 双写一致 缓存穿透 击穿 雪崩 解决方案... 一篇文章带你学透
java·数据库·redis·后端·spring·缓存
百***688213 小时前
SpringBoot中Get请求和POST请求接收参数详解
java·spring boot·spring
百***416613 小时前
Java MySQL 连接
java·mysql·adb
Jayden13 小时前
synchronized全解析:从锁升级到性能优化,彻底掌握Java内置锁
java·synchronized·synchronized面试·synchronized扫盲