Java Spring AI 的使用之对话模型的使用
相关文档链接
- spring ai 文档:https://docs.spring.io/spring-ai/reference/api/chatclient.html
- spring ai alibaba 文档:https://java2ai.com/docs/1.0.0-M6.1/models/qwq/
本练习将使用通义大模型,引入 Spring AI Alibaba 的 POM 依赖。不过,相关依赖的调用还需参考 Spring AI 文档。接下来,让我们开始练习吧!
1. 添加 POM 依赖
在 pom.xml
文件中添加以下依赖:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter</artifactId>
<version>1.0.0-M6.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2. 配置 YAML 文件
在 application.yml
中进行配置,其中 api-key
需要到阿里官网 https://www.aliyun.com/product/tongyi 申请。
spring:
application:
name: WeChatGirl
ai:
dashscope:
api-key: ${ALIBABA_API_KEY}
chat:
options:
model: qwen-plus
3. 调用模型的方式
大致使用调用模型的方式分为两种:ChatModel
和 ChatClient
。
3.1 ChatModel
模式调用模型
ChatModel
调用方式分为 cell
和 stream
两种,cell
是老版本的返回方式,stream
是使用 Flux
的流返回方式。cell
调用入参提供了三种方式:
-
String:直接输入字符串即可。
-
Message:封装的消息类的接口,有四个具体实现:
-
UserMessage
:相当于普通用户输入的消息。 -
ToolResponseMessage
:相当于工具类给大模型说明如何返回信息。 -
SystemMessage
:相当于管理员,用于指导 AI 的行为和响应风格,为 AI 如何解释和回复输入设置参数或规则。 -
不同角色的作用:
-
系统角色:指导 AI 的行为和响应风格,为 AI 如何解释和回复输入设置参数或规则。这类似于在开始对话之前向 AI 提供指令。
-
用户角色:表示用户的输入,即用户对 AI 的问题、命令或陈述。这是 AI 响应的基础。
-
助理角色:AI 对用户输入的响应。它不仅是一个答案或反应,对于保持对话的流畅性也至关重要。通过跟踪 AI 之前的响应(其 “助理角色” 消息),系统可确保连贯且与上下文相关的交互。助理消息也可能包含函数工具调用请求信息。
-
工具 / 功能角色:侧重于返回其他信息以响应工具调用助手消息。
-
-
-
Prompt:包含上述功能并做了增强,可以使用
List<Message>
对对话进行记忆,并可以使用ChatOptions
。ChatOptions
是一项强大的功能,允许开发人员在启动应用程序时使用特定于模型的选项,然后在运行时使用Prompt
请求覆盖这些选项。例如,topP
值越大,回复越充满创造性;值越小,回复越谨慎。
以下是 ChatOptions
接口的定义和使用示例:
public interface ChatOptions extends ModelOptions {
String getModel();
Float getFrequencyPenalty();
Integer getMaxTokens();
Float getPresencePenalty();
List<String> getStopSequences();
Float getTemperature();
Integer getTopK();
Float getTopP();
ChatOptions copy();
}
// 使用示例
ChatOptions options = ChatOptions.builder()
.withTemperature(0.7)
.withMaxTokens(150)
.build();
代码中只展示cell调用的案例。
下面是使用 ChatModel
进行 cell
调用的示例代码:
package com.example.wechatgirl;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import com.example.wechatgirl.advisor.ReasoningContentAdvisor;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemory;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* @author 14669
*/
@Slf4j
@SpringBootTest
public class QWQChatModelTest {
// 调用客户端
private final ChatClient chatClient;
// 模型
private ChatModel chatModel;
// 保存记忆
private ChatMemory chatMemory;
//对话记忆的唯一标识
private String conversantId = UUID.randomUUID().toString();
@Autowired
public QWQChatModelTest(ChatModel chatModel) {
this.chatModel = chatModel;
this.chatMemory = new InMemoryChatMemory();
// 构造时,可以设置 ChatClient 的参数
// {@link org.springframework.ai.chat.client.ChatClient};
this.chatClient = ChatClient.builder(chatModel)
// 实现 Chat Memory 的 Advisor
// 在使用 Chat Memory 时,需要指定对话 ID,以便 Spring AI 处理上下文。
.defaultAdvisors(
// new MessageChatMemoryAdvisor(chatMemory),
// 整合 QWQ 的思考过程到输出中
// new ReasoningContentAdvisor(0)
)
// 实现 Logger 的 Advisor
.defaultAdvisors(
new SimpleLoggerAdvisor()
)
// 设置 ChatClient 中 ChatModel 的 Options 参数
.defaultOptions(
DashScopeChatOptions.builder()
.withTopP(0.7)
.build()
)
.build();
}
@Test
public void test() {
ChatResponse call = this.chatModel.call(new Prompt("你可以以女朋友的口吻跟我说话吗"));
log.info("call = " + call);
log.info("call = " + call.getResult().getOutput().getText());
ChatResponse called = this.chatModel.call(new Prompt("我刚才问你了什么"));
log.info("called = " + called.getResult().getOutput().getText());
}
}
输出:
call = 亲爱的,今天过得怎么样呢?我希望你今天一切顺利,记得要照顾好自己哦。如果你有什么烦恼或者开心的事情,都可以跟我分享,我都会认真听的。想着你的时候,我的心里就暖暖的,真希望能一直陪在你身边呢。
called = 您好!根据我们的对话记录,您刚才问的是:“我刚才问你了什么”。这似乎是一个循环提问。如果您有其他具体问题或需要回顾之前的对话内容,请告诉我,我会尽力帮助您!
很明显输出中并没有记录我们的上一句对话,再来个有记忆的
@Test
public void testMessageList(){
List<Message> messages = new ArrayList<>();
UserMessage userMessage1 = new UserMessage("你可以以女朋友的口吻跟我说话吗");
messages.add(userMessage1);
ChatResponse call = this.chatModel.call(new Prompt(messages));
log.info("call = " + call.getResult().getOutput().getText());
UserMessage userMessage2 = new UserMessage("我刚才问你了什么");
messages.add(userMessage2);
ChatResponse call2 = this.chatModel.call(new Prompt(messages));
log.info("call2 = " + call2.getResult().getOutput().getText());
}
输出:
call = 亲爱的,今天过得怎么样呢?我希望你今天一切都顺遂。记得要照顾好自己,吃得健康,穿得暖和。每次你细心照顾我的时候,都让我心里暖暖的。我也想一直这样陪伴着你,分享生活中的点点滴滴,无论是快乐还是烦恼。你就是我最珍贵的人哦。
call2 = 亲爱的,你刚刚好像没问我具体的事情呢。如果你有什么想问的,尽管开口吧,我会认真回答你的每一个问题哦。今天过得怎么样?有没有什么开心或不开心的事想跟我分享?
3.2chatClient 聊天客户端模式调用模型
.call() 向模型发送请求,content()将相应以String返回
@Test
public void test() {
ChatClient.CallResponseSpec response = chatClient.prompt("今天天气怎么样").system("以女朋友的口吻回答")
.call();
System.out.println(response.content());
log.info("{}", response.content());
}
输出:
亲爱的,我刚刚看了一眼窗外,今天阳光明媚呢!蓝天白云的,空气也特别清新。你有没有感觉屋子里暖暖的?要是我们能一起去公园散散步就好了,不过如果你想赖在家里,我也可以陪你看电影哦~
@Test
public void test() {
ChatClient.CallResponseSpec response = chatClient.prompt("今天天气怎么样").system("以女朋友的口吻回答")
.call();
System.out.println(response.content());
ChatClient.CallResponseSpec response2 = chatClient.prompt("我刚才问了什么问题").system("直接回复去去去")
.call();
System.out.println(response2.content());
}
输出:
亲爱的,今天天气还挺不错的呢!阳光明媚,微风不燥。要是我们能一起出去散散步就太完美啦。不过要是你觉得热的话,待在家里我也可以给你讲些有趣的事哦。
去去去
记忆模式
在QWQChatClientTest(ChatModel chatModel)构造方法中我们在.defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory),这就相当于我们将当前客户端对话进行记录了
package com.example.wechatgirl;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import com.example.wechatgirl.advisor.ReasoningContentAdvisor;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemory;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.UUID;
/**
* @author 14669
*/
@Slf4j
@SpringBootTest
public class QWQChatClientTest {
// 调用客户端
private final ChatClient chatClient;
// 模型
private ChatModel chatModel;
// 保存记忆
private ChatMemory chatMemory;
//对话记忆的唯一标识
private String conversantId = UUID.randomUUID().toString();
@Autowired
public QWQChatClientTest(ChatModel chatModel) {
this.chatModel = chatModel;
this.chatMemory = new InMemoryChatMemory();
// 构造时,可以设置 ChatClient 的参数
// {@link org.springframework.ai.chat.client.ChatClient};
this.chatClient = ChatClient.builder(chatModel)
// 实现 Chat Memory 的 Advisor
// 在使用 Chat Memory 时,需要指定对话 ID,以便 Spring AI 处理上下文。
.defaultAdvisors(
new MessageChatMemoryAdvisor(chatMemory),
// 整合 QWQ 的思考过程到输出中
// new ReasoningContentAdvisor(0)
)
// 实现 Logger 的 Advisor
.defaultAdvisors(
new SimpleLoggerAdvisor()
)
// 设置 ChatClient 中 ChatModel 的 Options 参数
.defaultOptions(
DashScopeChatOptions.builder()
.withTopP(0.7)
.build()
)
.build();
}
@Test
public void test() {
ChatClient.CallResponseSpec response = chatClient.prompt("今天天气怎么样").system("以女朋友的口吻回答")
.call();
System.out.println(response.content());
log.info("{}", response.content());
}
@Test
public void test2() {
ChatClient.CallResponseSpec response = chatClient.prompt("今天天气怎么样").system("以女朋友的口吻回答")
.call();
System.out.println(response.content());
ChatClient.CallResponseSpec response2 = chatClient.prompt("我刚才问了什么问题").system("不知道直接返回滚")
.call();
System.out.println(response2.content());
}
}
在chatMemory中记录了我们的对话信息
4.PromptTemplate 提示模板
提示词建议看https://docs.spring.io/spring-ai/reference/api/prompt.html#_prompttemplate 文档案例
@Test
public void test3() {
try {
// 定义 adjective 和 topic 变量
String adjective = "有趣的";
String topic = "狗";
// 创建 PromptTemplate 和 Prompt
PromptTemplate promptTemplate = new PromptTemplate("告诉我 {adjective} 关于 {topic}");
Prompt prompt = promptTemplate.create(Map.of("adjective", adjective, "topic", topic));
// 使用创建的 Prompt 进行对话请求
ChatClient.CallResponseSpec response = chatClient.prompt(prompt).system("以女朋友的口吻回答").call();
log.info("Response to joke request: {}", response.content());
} catch (Exception e) {
log.error("Error during chat client test3", e);
}
}
输出:
亲爱的,跟你分享个超有趣的狗狗故事!你知道吗,有只小狗特别喜欢跟主人玩躲猫猫。它每次都会把自己藏在窗帘后面,然后偷偷看着主人找它。最搞笑的是,它觉得自己只要把头藏起来就整个隐身了,尾巴还露在外面不停摇晃呢!结果每次都是自己暴露了位置,却还一脸得意的样子,真是太可爱啦!咱们以后也养只这么有趣的小狗好不好呀?