当前位置: 首页 > backend >正文

spring-ai-alibaba使用

spring-ai-alibaba 在spring-ai上做了升级,,可以轻松集成阿里的大模型

  1. 引入依赖
  <dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter</artifactId><version>1.0.0-M6.1</version></dependency>
  1. yml配置
server:port: 9090
spring:ai:chat:client:# 禁止自动生成  ChatClient.Builder ===> 如果有多个大模型需要关闭enabled: falsedashscope:# 会根据属性 自动注入一个 dashscopeChatModelapi-key: xxxchat:options:model: qwen-plus

这个starter会根据配置文件自动注入一个 ChatModel,,,也会自动注入一个ChatClient.Builder,每一种模型对应一种ChatModel,如果项目中用到了多个不同的模型,,需要自己构建 ChatModel的bean,,,
先将自动装配的类给屏蔽掉@SpringBootApplication(exclude = {DashScopeAutoConfiguration.class}),再注入自己要用的ChatModelChatClient

@Configuration
public class ChatConfig {/**** spring-ai-alibaba-starter 会自动装配一个 ChatModel* @return*/@Beanpublic ChatClient dashscopeChatClient(ChatModel dashscopeChatModel){ChatClient chatClient = ChatClient.builder(dashscopeChatModel).build();return chatClient;}@Beanpublic ChatModel dashscopeChatModel(){DashScopeChatOptions options = DashScopeChatOptions.builder().withModel("qwen-plus").withTemperature(0.8D).build();DashScopeChatModel dashScopeChatModel = new DashScopeChatModel(new DashScopeApi(TestApiKey.API_KEY), options);return dashScopeChatModel;}
}

ChatModel 和 ChatClient的区别

ChatClient : 是高级抽象,适合快速上手和简单场景
ChatModel: 是底层接口,,提供更精细的控制,,,
如果只是想快速调用模型,用ChatClient,,如果需要调参或者高级功能,用ChatModel

Advisor

advisor: 拦截器,,就是在执行ai的前后,,进行拦截,,处理发送的请求消息,,和返回的拦截消息,,,
自定义Advisor ,,需要实现两个接口,,一个是非流式的,一个是流式的,,

/*** 自定义日志打印  拦截器*/
public class MyCustomAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {private static final Logger log = LoggerFactory.getLogger(MyCustomAdvisor.class);private AdvisedRequest before(AdvisedRequest request){log.info("ai request:{}",request.userText());return request;}private void observerAfter(AdvisedResponse response){log.info("ai response:{}",response.response().getResult().getOutput().getText());}@Overridepublic AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {// 前置方法advisedRequest = this.before(advisedRequest);// 被增强的方法AdvisedResponse response = chain.nextAroundCall(advisedRequest);// 后置方法this.observerAfter(response);return response;}@Overridepublic Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {advisedRequest = this.before(advisedRequest);Flux<AdvisedResponse> advisedResponse = chain.nextAroundStream(advisedRequest);// 执行完了,,需要将流聚合起来Flux<AdvisedResponse> advisedResponseFlux = (new MessageAggregator()).aggregateAdvisedResponse(advisedResponse, this::observerAfter);return advisedResponseFlux;}@Overridepublic String getName() {return this.getClass().getSimpleName();}@Overridepublic int getOrder() {return 0;}
}

所有的拦截器的顺序跟放置前后没有关系,,advisor里面有一个order,,越小,排在越前面

常用的advisor
跟Chat Memory 相关的Advisor
  • MessageChatMemoryAdvisor
    记忆的advisor,,会去根据conversationId查找历史会话内容,,添加到ai的上下文中,ai就能知道之前的对话是什么

  • PromptChatMemoryAdvisor : 这个拦截器读出来的会话内容,,是没有结构的文本,,用户消息可能和系统回复的消息,混淆起来,,一般用MessageChatMemoryAdvisor

  • VectorStoreChatMemoryAdvisor: 可以用向量数据库来存储检索历史对话

ChatMemory

ChatMemory : 一个接口,,定义了数据怎么存储,,从哪里获取,,可以自定义一个基于自己数据库的ChatMemory
spring ai提供了一些内置的ChatMemory :

  • InMemoryChatMemory : 基于内存存储的ChatMemory
  • JdbcChatMemory
  • CassandraChatMemory
  • Neo4jChatMemory

但是都不好用,,可以自定义自己的ChatMemory,,因为Message接口,,下面有很多种实现类,,每一种实现类的字段都不一样,,所以序列化需要带上类的类型,,,kyro就可以轻松实现,,默认情况下kyro需要将所有要序列化的类都提前注册,,通过kryo.register(SystemMessage.class)
,,否则会报错,,
也可以设置这两个属性值:

       this.kryo= new Kryo();// kryo 要求所有序列化的类,都需要提前注册,,通过kryo.register(SystemMessage.class),,否则会抛出一场// 不要求强制注册this.kryo.setRegistrationRequired(false);// 指定kryo创建对象实例的策略,,// 如果不设置,Kryo默认使用DefaultInstantiatorStrategy,它对于没有无参构造器的类可能会无法实例化,从而导致反序列化失败。this.kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());

自定义基于文件存储的ChatMemory :

package com.cj.testaidemo.memory;import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import org.objenesis.strategy.StdInstantiatorStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.Message;import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;/**** 基于文件系统的  会话记忆** Message是个接口,, 下面每一种消息类型的类的字段都不同,,,常规的序列化不能满足要求,,序列化的时候,需要带上类型*/
public class FileBasedChatMemory implements ChatMemory {private final Logger log = LoggerFactory.getLogger(FileBasedChatMemory.class);private final String basePath ;private final Kryo kryo ;public FileBasedChatMemory(String basePath) {this.basePath = System.getProperty("user.dir")+"/tmp";this.kryo= new Kryo();// kryo 要求所有序列化的类,都需要提前注册,,通过kryo.register(SystemMessage.class),,否则会抛出一场// 不要求强制注册this.kryo.setRegistrationRequired(false);// 指定kryo创建对象实例的策略,,// 如果不设置,Kryo默认使用DefaultInstantiatorStrategy,它对于没有无参构造器的类可能会无法实例化,从而导致反序列化失败。this.kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());File file = new File(this.basePath);if (!file.exists()){file.mkdirs();}}@Overridepublic void add(String conversationId, List<Message> messages) {List<Message> messageList = getMessagesFromFile(conversationId);messageList.addAll(messages);saveMessages(conversationId,messageList);}@Overridepublic List<Message> get(String conversationId, int lastN) {// 获取指定文件名 的数据if (conversationId == null || conversationId.trim().isEmpty()){log.warn("conversationId is null or empty");return List.of();}//List<Message> messageList = getMessagesFromFile(conversationId);return messageList;}@Overridepublic void clear(String conversationId) {String filePath = this.basePath+"/"+conversationId+".kryo";File file = new File(filePath);if (file.exists()){file.delete();}}private List<Message> getMessagesFromFile(String conversationId){String filePath = this.basePath+"/"+conversationId+".kryo";File file = new File(filePath);List<Message> messages = new ArrayList<>();if (file.exists()){try (Input input = new Input(new FileInputStream(file))) {messages = kryo.readObject(input, ArrayList.class);} catch (FileNotFoundException e) {throw new RuntimeException(e);}}return messages;}private void saveMessages(String conversationId, List<Message> messages){String filePath = this.basePath+"/"+conversationId+".kryo";try (Output output = new Output(new FileOutputStream(filePath))) {kryo.writeObject(output,messages);} catch (FileNotFoundException e) {throw new RuntimeException(e);}}
}

使用自己定义的ChatMemory:

 @Beanpublic ChatClient dashscopeChatClient(ChatModel dashscopeChatModel){ChatClient chatClient = ChatClient.builder(dashscopeChatModel).defaultSystem("你是一个专业的{type}客服,从来不给自己加戏").defaultAdvisors(new MyCustomAdvisor(),// 基于内存的 存储记忆
//                        MessageChatMemoryAdvisor.builder(new InMemoryChatMemory())MessageChatMemoryAdvisor.builder(new FileBasedChatMemory(System.getProperty("user.dir"))).chatMemoryRetrieveSize(10).conversationId("1").build()).build();return chatClient;}
   public void chat(String message,String chatId){ChatResponse chatResponse = this.dashscopeChatClient.prompt().system(sp->sp.param("type","游戏账号")).user(message).advisors(advisorSpec ->// 设置会话idadvisorSpec.param(MessageChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY,chatId)// 从上下文取多少条.param(MessageChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY,10)).call().chatResponse();String text = chatResponse.getResult().getOutput().getText();System.out.println("text = " + text);}
http://www.xdnf.cn/news/18845.html

相关文章:

  • 第18章|变量:把数据装进“盒子”的正确方式
  • 机器学习 TF-IDF方法
  • 【docker apoc 部署的neo4j安装apoc插件】
  • MySQL 面试题系列(五)
  • 【Kafka】重点概念和架构总结
  • Python 入门操作指南
  • 如何在 Docker 和AKS上使用 IIS
  • iOS技术之通过Charles抓包http、https数据
  • 【Linux】基本指令学习3
  • opencv+yolov8n图像模型训练和推断完整代码
  • Clerk 用户认证系统集成文档
  • ollama离线部署+大语言模型
  • AI-调查研究-62-机器人 机械臂五大应用场景详解:从焊接到手术,从农田到太空
  • 4步用代码拆解数学建模中的TOPSIS评价决策! ! !
  • Apache Commons Lang 3
  • 野火STM32Modbus主机读取寄存器/线圈失败(二)-解决CRC校验错误
  • uC/OS-III 队列相关接口
  • 数据分析与数据挖掘
  • 企业如何构建全面的高防IP防护体系?
  • Teams Workflows 业务流程搭建与Linux自动化运维拓展应用全解析
  • 状态设计模式
  • 构建面向人工智能决策的世界模型引擎所需的基本知识体系
  • 如何在GitHub找到10k+个stars的仓库
  • podman启动mongdb的container因为权限问题导致changing ownership和读取storage.bson失败的解决方法
  • CMake构建学习笔记20-iconv库的构建
  • 算法概述篇
  • 游戏空间划分技术
  • 日语学习-日语知识点小记-构建基础-JLPT-N3阶段(20):文法+单词第7回2
  • 广告推荐模型1:逻辑回归(Logistic Regression,LR)
  • 如何拯救一家濒临破产的科技公司?