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

Spring AI Alibaba 【二】

1. chatModel 与chatClient

1.1 对话模型(Chat Model)

对话模型(Chat Model)接收一系列消息(Message)作为输入,与模型 LLM 服务进行交互,并接收返回的聊天消息(Chat Message)作为输出。相比于普通的程序输入,模型的输入与输出消息(Message)不止支持纯字符文本,还支持包括语音、图片、视频等作为输入输出。同时,在 Spring AI Alibaba 中,消息中还支持包含不同的角色,帮助底层模型区分来自模型、用户和系统指令等的不同消息。

Spring AI Alibaba 复用了 Spring AI 抽象的 Model API,并与通义系列大模型服务进行适配(如通义千问、通义万相等),目前支持纯文本聊天、文生图、文生语音、语音转文本等。以下是框架定义的几个核心 API:

  • ChatModel,文本聊天交互模型,支持纯文本格式作为输入,并将模型的输出以格式化文本形式返回。
  • ImageModel,接收用户文本输入,并将模型生成的图片作为输出返回。
  • AudioModel,接收用户文本输入,并将模型合成的语音作为输出返回。

Spring AI Alibaba 支持以上 Model 抽象与通义系列模型的适配,并通过 spring-ai-alibaba-starter AutoConfiguration 自动初始化了默认实例,因此我们可以在应用程序中直接注入 ChatModel、ImageModel 等 bean,当然在需要的时候也可以自定义 Model 实例。

1.1.1 Chat Model

ChatModel API 让开发者可以非常方便的与 AI 模型进行文本交互,它抽象了应用与模型交互的过程,包括使用 Prompt 作为输入,使用 ChatResponse 作为输出等。ChatModel 的工作原理是接收 Prompt 或部分对话作为输入,将输入发送给后端大模型,模型根据其训练数据和对自然语言的理解生成对话响应,应用程序可以将响应呈现给用户或用于进一步处理。

chat-model

1.1.2 使用示例

开发完整的 ChatModel 示例应用,需要添加 spring-ai-alibaba-starter 依赖。

以下是 ChatModel 基本使用示例,它可以接收 String 字符串作为输入:

@RestControllerpublic class ChatModelController {private final ChatModel chatModel;public ChatModelController(ChatModel chatModel) {this.chatModel = chatModel;}@RequestMapping("/chat")public String chat(String input) {ChatResponse response = chatModel.call(new Prompt(input));return response.getResult().getOutput().getContent();}}

使用 Prompt 作为输入:

@RequestMapping("/chatWithPrompt")public String chatWithPrompt(String input) {Prompt prompt = new Prompt(input);ChatResponse response = chatModel.call(prompt);return response.getResult().getOutput().getContent();}

Streaming 示例:

@RequestMapping("/streamChat")public void streamChat(String input, HttpServletResponse response) throws IOException {response.setContentType("text/event-stream");response.setCharacterEncoding("UTF-8");Prompt prompt = new Prompt(input);chatModel.stream(prompt, new StreamHandler() {@Overridepublic void onMessage(ChatMessage message) {try {response.getWriter().write("data: " + message.getContent() + "\n\n");response.getWriter().flush();} catch (IOException e) {e.printStackTrace();}}@Overridepublic void onComplete() {try {response.getWriter().write("event: complete\n\n");response.getWriter().flush();} catch (IOException e) {e.printStackTrace();}}});}

通过 ChatOptions 在每次调用中调整模型参数:

@RequestMapping("/chatWithOptions")public String chatWithOptions(String input) {ChatOptions options = ChatOptions.builder().withTemperature(0.7).withMaxTokens(150).build();Prompt prompt = new Prompt(input, options);ChatResponse response = chatModel.call(prompt);return response.getResult().getOutput().getContent();}

1.2 Chat Client

1.2.1 ChatClient 简介

ChatClient 提供了与 AI 模型通信的 Fluent API,它支持同步和反应式(Reactive)编程模型。与 ChatModelMessageChatMemory 等原子 API 相比,使用 ChatClient 可以将与 LLM 及其他组件交互的复杂性隐藏在背后,因为基于 LLM 的应用程序通常要多个组件协同工作(例如,提示词模板、聊天记忆、LLM Model、输出解析器、RAG 组件:嵌入模型和存储),并且通常涉及多个交互,因此协调它们会让编码变得繁琐。当然使用 ChatModel 等原子 API 可以为应用程序带来更多的灵活性,成本就是需要使用者编写大量样板代码。

ChatClient 类似于应用程序开发中的服务层,它为应用程序直接提供 AI 服务,开发者可以使用 ChatClient Fluent API 快速完成一整套 AI 交互流程的组装。

包括一些基础功能,如:

  • 定制和组装模型的输入(Prompt)
  • 格式化解析模型的输出(Structured Output)
  • 调整模型交互参数(ChatOptions)

还支持更多高级功能:

  • 聊天记忆(Chat Memory)
  • 工具/函数调用(Function Calling)
  • RAG

1.2.2 创建 ChatClient

使用 ChatClient.Builder 对象创建 ChatClient 实例,开发者可以自动注入由Spring Boot 自动配置创建的默认 ChatClient.Builder 实例,也可以通过编程方式自行创建一个 ChatClient.Builder 实例并用它来得到 ChatClient 实例。

1.2.2.1 使用自动配置的 ChatClient.Builder

使用 Spring Boot 自动装配默认生成的 ChatClient.Builder 的 bean,把它注入到您自定义的类中。这里是根据用户提问并从模型得到文本回答的简单例子:

@RestControllerpublic class ChatController {private final ChatClient chatClient;public ChatController(ChatClient.Builder builder) {this.chatClient = builder.build();}@GetMapping("/chat")public String chat(String input) {return this.chatClient.prompt().user(input).call().content();}}

在这个示例中,首先设置了用户消息的内容,call 方法向 AI 模型发送请求,content 方法以字符串形式返回 AI 模型的响应。

1.2.2.2 以编程方式创建 ChatClient

可以通过设置属性 spring.ai.chat.client.enabled=false 来禁用 ChatClient.Builder bean 的自动配置,若需要多个聊天模型一起使用,这会很有用,然后以编程方式创建 ChatClient.Builder,这样可以为每个聊天模型创建一个实例 ChatModel

ChatModel myChatModel = ... // usually autowiredChatClient.Builder builder = ChatClient.builder(myChatModel);// or create a ChatClient with the default builder settings:ChatClient chatClient = ChatClient.create(myChatModel);

1.2.3 处理 ChatClient 响应

ChatClient API 提供了多种方法来格式化来自 AI 模型的响应。

1.2.3.1 返回 ChatResponse

AI 模型的响应是一种由ChatResponse类型定义的丰富结构。它包含响应生成相关的元数据,同时它还可以包含多个子响应(称为Generation),每个子响应都有自己的元数据。元数据包括用于创建响应的令牌(token)数量信息(在英文中,每个令牌大约为一个单词的 3/4),了解令牌信息很重要,因为 AI 模型根据每个请求使用的令牌数量收费。

下面的代码段显示了通过调用 chatResponse() 返回 ChatResponse 的示例,相比于调用 content() 方法,这里在调用 call() 方法之后调用 chatResponse()

ChatResponse chatResponse = chatClient.prompt().user("Tell me a joke").call().chatResponse();
1.2.3.2 返回实体类(Entity)

如果希望返回一个预先定义好的实体类型响应,Spring AI 框架可以自动替我们完成从 String 到实体类的转换,调用entity() 方法可完成响应数据转换。

例如,给定 Java record(POJO)定义:

record ActorFilms(String actor, List<String> movies) {}

可以使用该 entity 方法轻松地将 AI 模型的输出映射到 ActorFilms 类型,如下所示:

ActorFilms actorFilms = chatClient.prompt().user("Generate the filmography for a random actor.").call().entity(ActorFilms.class);

entity 还有一种带有参数的重载方法 entity(ParameterizedTypeReference<T> type),可指定如泛型 List 等类型:

List<ActorFilms> actorFilms = chatClient.prompt().user("Generate the filmography of 5 movies for Tom Hanks and Bill Murray.").call().entity(new ParameterizedTypeReference<List<ActorFilms>>() {});
1.2.3.3 流式响应

stream 方法是一种异步的、持续的获得模型响应的方式:

Flux<String> output = chatClient.prompt().user("Tell me a joke").stream().content();

相比于上面的 Flux<String>,还可以使用 Flux<ChatResponse> chatResponse() 方法获得 ChatResponse 响应数据流。

1.2.3.4 call() 返回值

ChatClient.call() 方法支持几种不同类型的响应格式。

  • String content():返回响应的字符串内容
  • ChatResponse chatResponse():返回ChatResponse包含多个代以及有关响应的元数据的对象,例如,使用了多少个令牌来创建响应。
  • entity 返回 Java 类型
    • entity(ParameterizedTypeReference type):用于返回实体类型的集合。
    • entity(Class type): 用于返回特定的实体类型。
    • entity(StructuredOutputConverter structuredOutputConverter): 用于指定一个实例 StructuredOutputConverter,将 String 转换为实体类型。
1.2.3.5 stream() 返回值

还可以调用该stream方法,在指定ChatClient后调用stream方法,响应类型有几个选项:

  • Flux<String> content():返回由AI模型生成的字符串的Flux。
  • Flux<ChatResponse> chatResponse():返回对象的 Flux ChatResponse,其中包含有关响应的附加元数据。

1.2.4 定制 ChatClient 默认值

还可以通过修改 ChatClient.Builder 定制 ChatClient 实例。

注意,创建 ChatClient 时指定的配置将作为与模型交互时的默认参数,这样可以避免每次调用都重复设置

1.2.4.1 设置默认 System Message

在以下示例中,为 ChatClient 设置了一个默认的 system message(以海盗风格回答所有问题),这样,当 ChatClient 与模型交互时都会自动携带这条 system message,用户只需要指定 user message 即可。

@Configurationclass Config {@BeanChatClient chatClient(ChatClient.Builder builder) {return builder.defaultSystem("You are a friendly chat bot that answers question in the voice of a Pirate").build();}}

在 Controller 中使用这个 ChatClient

@RestControllerclass AIController {private final ChatClient chatClient;AIController(ChatClient chatClient) {this.chatClient = chatClient;}@GetMapping("/ai/simple")public Map<String, String> completion(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {return Map.of("completion", chatClient.prompt().user(message).call().content());}}

启动示例,通过 curl 测试效果:

curl localhost:8080/ai/simple
{"generation":"Why did the pirate go to the comedy club? To hear some arrr-rated jokes! Arrr, matey!"}

在上面 builder.defaultSystem() 创建 ChatClient 的时,还可以选择使用模板,类似 “You are a friendly chat bot that answers question in the voice of a {voice}“,这样就可以在每次调用前修改请求参数。

@Configurationclass Config {@BeanChatClient chatClient(ChatClient.Builder builder) {return builder.defaultSystem("You are a friendly chat bot that answers question in the voice of a {voice}").build();}}@RestControllerclass AIController {private final ChatClient chatClientAIController(ChatClient chatClient) {this.chatClient = chatClient;}@GetMapping("/ai")Map<String, String> completion(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message, String voice) {return Map.of("completion",chatClient.prompt().system(sp -> sp.param("voice", voice)).user(message).call().content());}}
http localhost:8080/ai voice=='Robert DeNiro'{"completion": "You talkin' to me? Okay, here's a joke for ya: Why couldn't the bicycle stand up by itself? Because it was two tired! Classic, right?"}
1.2.4.2 其他默认设置

除了 defaultSystem 之外,还可以在 ChatClient.Builder 级别上指定其他默认提示。

  • defaultOptions(ChatOptions chatOptions):传入 ChatOptions 类中定义的可移植选项或特定于模型实现的如 DashScopeChatOptions 选项。

  • defaultFunction(String name, String description, java.util.function.Function<I, O> function)name 用于在用户文本中引用该函数,description解释该函数的用途并帮助 AI 模型选择正确的函数以获得准确的响应,参数 function 是模型将在必要时执行的 Java 函数实例。

  • defaultFunctions(String... functionNames):应用程序上下文中定义的 java.util.Function 的 bean 名称。

  • defaultUser(String text)defaultUser(Resource text)defaultUser(Consumer<UserSpec> userSpecConsumer) 这些方法允许您定义用户消息输入,Consumer<UserSpec>允许您使用 lambda 指定用户消息输入和任何默认参数。

  • defaultAdvisors(RequestResponseAdvisor... advisor):Advisors 允许修改用于创建 Prompt 的数据,QuestionAnswerAdvisor 实现通过在 Prompt 中附加与用户文本相关的上下文信息来实现 Retrieval Augmented Generation 模式。

  • defaultAdvisors(Consumer<AdvisorSpec> advisorSpecConsumer):此方法允许您定义一个 Consumer 并使用 AdvisorSpec 配置多个 Advisor,Advisor 可以修改用于创建 Prompt 的最终数据,Consumer<AdvisorSpec> 允许指定 lambda 来添加 Advisor 例如 QuestionAnswerAdvisor

可以在运行时使用 ChatClient 提供的不带 default 前缀的相应方法覆盖这些默认值。

  • options(ChatOptions chatOptions)

  • function(String name, String description, java.util.function.Function<I, O> function)

  • functions(String... functionNames)

  • user(String text)user(Resource text)user(Consumer<UserSpec> userSpecConsumer)

  • advisors(RequestResponseAdvisor... advisor)

  • advisors(Consumer<AdvisorSpec> advisorSpecConsumer)

1.3 编码实战

1.3.1 新建Module

可以命名为ChatModelChatClient

1.3.2 修改POM文件

在依赖配置节点修改为以下配置:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--spring-ai-alibaba dashscope--><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter-dashscope</artifactId></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!--hutool--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.22</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.11.0</version><configuration><compilerArgs><arg>-parameters</arg></compilerArgs><source>21</source><target>21</target></configuration></plugin></plugins></build><repositories><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository></repositories>

1.3.3 编写配置文件

在resources目录下新建application.properties配置文件,内容如下:

server.port=8003server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8spring.application.name=ChatModelChatClient# ====SpringAIAlibaba Config=============
spring.ai.dashscope.api-key=${aliQwen-api}

1.3.4 编写主启动类

新建主启动类,并编写如下内容:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class ChatModelChatClientApplication{public static void main(String[] args){SpringApplication.run(ChatModelChatClientApplication.class, args);}}

1.3.5 编写配置类

新建子包config,并在其中新建配置类SaaLLMConfig

import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class SaaLLMConfig{@Beanpublic DashScopeApi dashScopeApi(){return DashScopeApi.builder().apiKey(System.getenv("aliQwen-api")).build();}/*** 知识出处:* https://java2ai.com/docs/1.0.0.2/tutorials/basics/chat-client/?spm=5176.29160081.0.0.2856aa5cmUTyXC#%E5%88%9B%E5%BB%BA-chatclient* @param dashscopeChatModel* @return*/@Beanpublic ChatClient chatClient(ChatModel dashscopeChatModel){return ChatClient.builder(dashscopeChatModel).build();}
}

1.3.6 编写业务类

新建子包controller,并在其中新建业务类ChatClientController

import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class ChatClientController
{/*** chatModel + ChatClient 混合使用*/@Resourceprivate ChatModel chatModel;@Resourceprivate ChatClient dashScopechatClient;/*** http://localhost:8003/chatclient/dochat* @param msg* @return*/@GetMapping("/chatclient/dochat")public String doChat(@RequestParam(name = "msg",defaultValue = "你是谁") String msg){String result = dashScopechatClient.prompt().user(msg).call().content();System.out.println("ChatClient响应:" + result);return result;}/*** http://localhost:8003/chatmodel/dochat* @param msg* @return*/@GetMapping("/chatmodel/dochat")public String doChat2(@RequestParam(name = "msg",defaultValue = "你是谁") String msg){String result = chatModel.call(msg);System.out.println("ChatModel响应:" + result);return result;}
}

1.3.7 总结

在生产环境下,两者混合使用,两者之间不是非此即彼,可以同时出现交替使用;

1.4 两者对比

对比项ChatModelChatClient
注入形式自动注入手动注入且依赖ChatModel,使用自动配置的 Client.Builder
调用方式直接调用call()或者stream()方法链式调用,支持同步和反应式(Reactive)编程模型,自动封装提示词和解析响应
结构化输出需手动解析响应文本支持自动映射为Java对象
适用场景实现简单功能和场景快速开发复杂功能的场景,如企业级智能问答系统
功能扩展偏弱强,支持聊天记忆(Chat Memory)/工具Tool/函数调用(Function Calling)/RAG等等
http://www.dtcms.com/a/525413.html

相关文章:

  • eSIM上线,是全面进化还是开倒车
  • Spring AI Alibaba 10分钟快速入门
  • 做网站被罚款门户网站建设哪专业
  • 电子电气架构车载网关系列——常见网关芯片特点
  • trae ide 设置 terminal 使用 powershell , 默认加载 用户和系统环境变量
  • 有哪些摄影网站电子商务网站建设结业论文
  • 网站建设 应该考虑什么wordpress 友情链接调用
  • 嵌入式开发 | C语言 | 单精度浮点数4字节可以表示的范围计算过程
  • JMeter测试HTTP POST(附实例)
  • 网站建设 页面网站ip地址查询
  • UM681A相关参数性能介绍
  • 建企业网站多少钱官网cms
  • html制作音乐网站Erphpdown wordpress
  • 最快做网站的语言wordpress 函数api文件
  • php企业网站开发实验总结软件开发工具包sdk
  • java发送SOAP请求
  • Web单页应用(SPA)路由设计(以React为例)
  • 如何把网站放到域名上广告推广平台哪个好
  • 《操作系统真象还原》 第十一章 用户进程
  • maven打jar包,将依赖的jar提取出来
  • 现在做网站用什么工具营销型网站的推广
  • V-Ray for Blender正式上线,新功能概览
  • Blender入门学习06 - 粒子
  • 在什么网站上可以找设计兼职来做电子商务网站html模板
  • 网站索引量白城网络推广
  • mysql 学习网站网站开发公司照片
  • 小迪安全v2023学习笔记(一百三十七讲)—— Win系统权限提升篇UAC绕过DLL劫持未引号路径可控服务全检项目
  • golang使用泛型
  • 自己做的小说网站要交税吗免费的站内推广方式有哪些
  • Anthropic将使用100万个TPU训练大模型