Java Spring AI 的使用之对话模型的使用

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. 调用模型的方式

大致使用调用模型的方式分为两种:ChatModelChatClient

3.1 ChatModel 模式调用模型

ChatModel 调用方式分为 cellstream 两种,cell 是老版本的返回方式,stream 是使用 Flux 的流返回方式。cell 调用入参提供了三种方式:

  • String:直接输入字符串即可。

  • Message:封装的消息类的接口,有四个具体实现:

    • UserMessage:相当于普通用户输入的消息。

    • ToolResponseMessage:相当于工具类给大模型说明如何返回信息。

    • SystemMessage:相当于管理员,用于指导 AI 的行为和响应风格,为 AI 如何解释和回复输入设置参数或规则。

    • 不同角色的作用:

      • 系统角色:指导 AI 的行为和响应风格,为 AI 如何解释和回复输入设置参数或规则。这类似于在开始对话之前向 AI 提供指令。

      • 用户角色:表示用户的输入,即用户对 AI 的问题、命令或陈述。这是 AI 响应的基础。

      • 助理角色:AI 对用户输入的响应。它不仅是一个答案或反应,对于保持对话的流畅性也至关重要。通过跟踪 AI 之前的响应(其 “助理角色” 消息),系统可确保连贯且与上下文相关的交互。助理消息也可能包含函数工具调用请求信息。

      • 工具 / 功能角色:侧重于返回其他信息以响应工具调用助手消息。
        在这里插入图片描述

  • Prompt:包含上述功能并做了增强,可以使用 List<Message> 对对话进行记忆,并可以使用 ChatOptionsChatOptions 是一项强大的功能,允许开发人员在启动应用程序时使用特定于模型的选项,然后在运行时使用 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);
        }
    }
输出:
    亲爱的,跟你分享个超有趣的狗狗故事!你知道吗,有只小狗特别喜欢跟主人玩躲猫猫。它每次都会把自己藏在窗帘后面,然后偷偷看着主人找它。最搞笑的是,它觉得自己只要把头藏起来就整个隐身了,尾巴还露在外面不停摇晃呢!结果每次都是自己暴露了位置,却还一脸得意的样子,真是太可爱啦!咱们以后也养只这么有趣的小狗好不好呀?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值