<二>Sping-AI alibaba 入门-记忆聊天及持久化

请看文档,流程不再赘述:官网及其示例

基础概念

1. ChatClient

ChatClient 本质上是一个封装了 AI 模型交互复杂性的 Fluent API(流畅接口),它的设计目标是让开发者无需关注底层组件的协同细节,就能快速实现与大语言模型(LLM)的交互。

类比来看,它就像应用开发中的「服务层」------ 上层应用只需调用其接口,底层的模型调用、参数处理、组件协调等工作都由它自动完成。

创建ChatClient

  1. 自动注入由Spring Boot 自动配置创建的默认 ChatClient.Builder 实例
java 复制代码
@RestController
public class ChatController {
    private final ChatClient chatClient;
    
    // 注入Spring Boot自动配置的ChatClient.Builder
    public ChatController(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }
    
    @GetMapping("/chat")
    public String chat(String input) {
        // 构建用户消息并发送请求
        return this.chatClient.prompt()
                .user(input)       // 设置用户输入
                .call()            // 调用AI模型
                .content();        // 获取模型响应内容
    }
}
  1. 自行创建一个 ChatClient.Builder 实例并用它来得到 ChatClient 实例
java 复制代码
@Configuration
public class ChatConfig {
    // 注入第一个ChatModel(如OpenAI模型)
    @Autowired
    private ChatModel openAiModel;
    
    // 注入第二个ChatModel(如本地LLM模型)
    @Autowired
    private ChatModel localModel;
    
    // 创建第一个ChatClient实例
    @Bean("openAiChatClient")
    public ChatClient openAiChatClient() {
        return ChatClient.builder(openAiModel)
                .withMemory(ChatMemoryFactory.createDefaultMemory()) // 自定义记忆策略
                .withOption(ChatOption.temperature(0.7)) // 设置模型参数
                .build();
    }
    
    // 创建第二个ChatClient实例
    @Bean("localChatClient")
    public ChatClient localChatClient() {
        return ChatClient.create(localModel); // 使用默认配置快速创建
    }
}

ChatClient 常用方法

  1. 请求构建方法:定义与 AI 模型的交互内容

    方法 功能描述 返回类型 示例代码
    prompt() 开始构建提示词(Prompt),支持链式调用添加多种类型消息。 PromptBuilder chatClient.prompt().user("你好").system("回答要简洁")
    user(String content) 添加用户消息(User Message)到提示词中,通常表示用户输入。 PromptBuilder .user("推荐一部电影")
    system(String content) 添加系统消息(System Message)到提示词中,用于指导模型行为(如人设、格式)。 PromptBuilder .system("你是一个电影推荐专家")
    assistant(String content) 添加助手消息(Assistant Message)到提示词中,通常用于历史对话。 PromptBuilder .assistant("《肖申克的救赎》是一部经典电影...")
    withFunction(FunctionDefinition function) 注册工具 / 函数调用(Function Calling),允许模型调用外部工具。 PromptBuilder .withFunction(getWeatherFunction())
  2. 响应处理方法:获取并解析 AI 模型的输出

  3. 高级配置方法:定制模型行为与交互策略

示例
java 复制代码
// 示例1:标准同步调用(获取文本)
String response = chatClient.prompt()
    .system("你是一个电影推荐专家")
    .user("推荐一部科幻电影")
    .call()
    .content();

// 示例2:结构化数据解析
record Movie(String title, String genre, int year) {}

Movie movie = chatClient.prompt()
    .user("推荐一部经典科幻电影")
    .call()
    .entity(Movie.class);

// 示例3:流式响应(实时展示)
chatClient.prompt()
    .user("写一篇关于AI的文章")
    .stream()
    .content()
    .subscribe(
        chunk -> System.out.print(chunk),  // 实时打印每段内容
        error -> System.err.println("Error: " + error),
        () -> System.out.println("\n文章生成完成")
    );

// 示例4:工具调用(Function Calling)
FunctionDefinition weatherFunction = FunctionDefinition.builder()
    .name("getWeather")
    .description("获取指定城市的当前天气")
    .parameter("city", "城市名称", String.class)
    .build();

ChatResponse response = chatClient.prompt()
    .user("北京今天天气如何?")
    .withFunction(weatherFunction)
    .call()
    .chatResponse();

// 解析并执行函数调用
if (response.hasFunctionCall()) {
    FunctionCall functionCall = response.getFunctionCall();
    // 执行对应的工具逻辑...
}

定制 ChatClient

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

  • 设置默认 System Message

    1. 固定风格设置

    通过 defaultSystem 方法可设置固定的 System Message。例如,希望模型以海盗风格回复,代码如下:

    java 复制代码
    @Configuration
    class Config {
        @Bean
        ChatClient chatClient(ChatClient.Builder builder) {
            return builder.defaultSystem("You are a friendly chat bot that answers question in the voice of a Pirate")
                   .build();
        }
    }

    2. 动态模板设置

    若需根据不同场景调整 System Message 风格,可使用模板。例如:

    java 复制代码
    @Configuration
    class Config {
        @Bean
        ChatClient chatClient(ChatClient.Builder builder) {
            return builder.defaultSystem("You are a friendly chat bot that answers question in the voice of a {voice}")
                   .build();
        }
    }

    在 Controller 中,通过 system(sp -> sp.param("voice", voice)) 动态替换模板参数:

    java 复制代码
    @RestController
    class AIController {
        private final ChatClient chatClient;
    
        AIController(ChatClient chatClient) {
            this.chatClient = chatClient;
        }
    
        @GetMapping("/ai")
        public 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. 默认模型选项(defaultOptions)

    通过 defaultOptions(ChatOptions chatOptions) 可设置通用或特定模型的交互参数,如温度系数、最大 Token 数等。例如:

    • 温度系数(Temperature) :是控制 AI 模型输出随机性和创造性的关键参数,取值范围通常在 0 到 1 之间 。
      • 数值接近 0:模型输出会更确定、保守,倾向于选择概率最高的 tokens(词汇单元),生成的内容更符合常见逻辑和模式,回答更精准、标准。例如在回答数学计算、知识类问题时,设置较低温度系数能得到准确答案。
      • 数值接近 1:模型输出的随机性增强,会更多地探索概率较低的 tokens,生成内容更具创造性、多样性和开放性,但也可能产生不那么符合逻辑或离题的内容。如用于创意写作、头脑风暴场景时,较高温度系数能激发独特想法。
    • 最大 Token 数(Max Tokens) :用于限制 AI 模型单次生成的 Token 数量上限。Token 是模型处理文本的基本单元,一个英文单词约对应 1 - 1.5 个 Token,中文中一个汉字或词语也可视为一个或多个 Token。
      • 作用:通过设置最大 Token 数,可控制模型输出长度,避免生成过长内容消耗过多资源和时间,同时也能防止模型因无限生成而陷入循环。比如在聊天机器人场景中,设置合理的最大 Token 数,能使回复简洁明了;在文章生成场景下,可根据需求设定合适长度。
      • 注意事项:设置的值需综合考虑模型的限制和实际需求。若设置过小,可能导致模型输出不完整;若设置过大,会增加计算成本和延迟,且可能超出模型的处理能力。 此外,模型对输入和输出的总 Token 数也有限制,使用时要确保输入和输出的 Token 总数不超过模型规定的最大 Token 数。

    除了温度系数和最大 Token 数,ChatOptions 还可设置其他参数,如停止词(Stop Words)用于指定模型停止生成的特定词汇;频率惩罚(Frequency Penalty)、存在惩罚(Presence Penalty)用于调整模型重复生成相同内容的概率等 ,这些参数共同协作,帮助开发者更精细地调控模型的输出行为。

    java 复制代码
    @Configuration
    class Config {
        @Bean
        ChatClient chatClient(ChatClient.Builder builder) {
            ChatOptions options = ChatOptions.builder()
                   .temperature(0.8)
                   .maxTokens(500)
                   .build();
            return builder.defaultOptions(options)
                   .build();
        }
    }

    后续交互将默认使用这些参数,如需临时调整,可通过 options(ChatOptions chatOptions) 方法覆盖。
    2. 默认工具调用(defaultFunction 与 defaultFunctions)

    • 单个函数设置:defaultFunction(String name, String description, java.util.function.Function<I, O> function) 用于注册默认工具函数。例如注册获取天气的函数:

      java 复制代码
      @Configuration
      class Config {
          @Bean
          ChatClient chatClient(ChatClient.Builder builder) {
              return builder.defaultFunction("getWeather", "获取指定城市的天气", city -> {
                  // 实现获取天气逻辑
                  return "北京今天晴天,温度25℃";
              })
                     .build();
          }
      }
    • 多个函数设置:defaultFunctions(String... functionNames) 可批量引入应用上下文中定义的函数 Bean。

    1. 默认用户消息(defaultUser)

      • defaultUser(String text):设置固定文本。
      • defaultUser(Resource text):从资源文件读取消息。
      • defaultUser(Consumer userSpecConsumer):通过 Lambda 表达式灵活定制,例如:
      java 复制代码
      @Configuration
      class Config {
          @Bean
          ChatClient chatClient(ChatClient.Builder builder) {
              return builder.defaultUser(userSpec -> userSpec.text("请推荐一部电影").param("category", "科幻"))
                     .build();
          }
      }
    2. 默认请求响应增强器(defaultAdvisors)

      defaultAdvisors 用于添加默认的请求响应增强逻辑,如实现检索增强生成(RAG)。

      • defaultAdvisors(RequestResponseAdvisor... advisor):添加单个或多个 Advisor。
      java 复制代码
      @Configuration
      class Config {
          @Bean
          ChatClient chatClient(ChatClient.Builder builder) {
              QuestionAnswerAdvisor advisor = new QuestionAnswerAdvisor();
              return builder.defaultAdvisors(advisor)
                     .build();
          }
      }
      • defaultAdvisors(Consumer advisorSpecConsumer):通过 Lambda 配置多个 Advisor,更灵活地定制增强策略。
  • 运行时覆盖默认值

    尽管设置了默认值,开发者仍可在运行时通过非 default 前缀的方法灵活覆盖。例如,已设置默认 System Message,仍可在单次调用中指定新的 System Message:

    java 复制代码
    chatClient.prompt()
       .system("你现在是一个美食评论家")
       .user("推荐一道美食")
       .call()
       .content();

    同理,options、function、user、advisors 等方法均可用于临时修改参数,实现动态灵活的交互配置。

    通过合理定制 ChatClient 默认值,开发者既能减少重复配置,又能在必要时实现个性化交互,极大提升基于 AI 模型应用的开发效率与灵活性。

2. Advisors

在使用用户输入文本构建 Prompt 调用 AI 模型时,一个常见模式是使用上下文数据附加或扩充 Prompt,最终使用扩充后的 Prompt 与模型交互。

这些用于扩充 Prompt 的上下文数据可以是不同类型的,常见类型包括:

  • 您自己的数据:这是 AI 模型尚未训练过的数据,如特定领域知识、产品文档等,即使模型已经看到过类似的数据,附加的上下文数据也会优先生成响应。
  • 对话历史记录:聊天模型的 API 是无状态的,如果您告诉 AI 模型您的姓名,它不会在后续交互中记住它,每次请求都必须发送对话历史记录,以确保在生成响应时考虑到先前的交互。

检索增强生成(RAG)的具体实现

假设已将数据加载到 VectorStore(向量数据库),可按如下方式执行 RAG:

java 复制代码
    ChatResponse response = ChatClient.builder(chatModel)
           .build()
           .prompt()
           .advisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults()))
           .user(userText)
           .call()
           .chatResponse();

上述代码中,SearchRequest.defaults() 会对向量数据库中的所有文档执行相似性搜索。若需限制搜索文档类型,可使用 SearchRequest 的 SQL 筛选表达式。例如,通过 FILTER_EXPRESSION 参数在运行时动态更新过滤条件:

动态过滤表达式

SearchRequest 使用 FILTER_EXPRESSION Advisor 上下文参数在运行时更新过滤表达式:

java 复制代码
        ChatClient chatClient = ChatClient.builder(chatModel)
            .defaultAdvisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults()))
            .build();
    
        // Update filter expression at runtime
        String content = chatClient.prompt()
            .user("Please answer my question XYZ")
            .advisors(a -> a.param(QuestionAnswerAdvisor.FILTER_EXPRESSION, "type == 'Spring'"))
            .call()
            .content();

FILTER_EXPRESSION 参数允许您根据提供的表达式动态过滤搜索结果。

基于聊天记忆的 Advisor 实现

ChatMemory 接口用于存储聊天对话历史,提供消息添加、检索和清除等功能。目前有 InMemoryChatMemory(内存存储)和 CassandraChatMemory(带 time - to - live 的持久存储)两种实现方式。

创建一个包含 time-to-live 配置的 CassandraChatMemory

java 复制代码
    CassandraChatMemory.create(
    CassandraChatMemoryConfig.builder()
    .withTimeToLive(Duration.ofDays(1))
    .build());

基于 ChatMemory 的 Advisor 实现主要有以下几种:

  • MessageChatMemoryAdvisor:将对话历史作为消息集合添加到提示中。
  • PromptChatMemoryAdvisor:把对话历史添加到提示的系统文本部分。
  • VectorStoreChatMemoryAdvisor:通过构造函数 VectorStoreChatMemoryAdvisor(VectorStore vectorStore, String defaultConversationId, int chatHistoryWindowSize) 指定向量数据库、对话 ID 和检索的聊天记录大小。

以下是一个结合多种 Advisor 的服务示例:

java 复制代码
import static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY;
import static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY;

@Service
public class CustomerSupportAssistant {
    private final ChatClient chatClient;

    public CustomerSupportAssistant(ChatClient.Builder builder, VectorStore vectorStore, ChatMemory chatMemory) {
        this.chatClient = builder
               .defaultSystem("""
                        You are a customer chat support agent of an airline named "Funnair".", Respond in a friendly,
                        helpful, and joyful manner.

                        Before providing information about a booking or cancelling a booking, you MUST always
                        get the following information from the user: booking number, customer first name and last name.

                        Before changing a booking you MUST ensure it is permitted by the terms.

                        If there is a charge for the change, you MUST ask the user to consent before proceeding.
                        """)
               .defaultAdvisors(
                        new PromptChatMemoryAdvisor(chatMemory),
                        // new MessageChatMemoryAdvisor(chatMemory), // CHAT MEMORY
                        new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults()),
                        new LoggingAdvisor()) // RAG
               .defaultFunctions("getBookingDetails", "changeBooking", "cancelBooking") // FUNCTION CALLING
               .build();
    }

    public Flux<String> chat(String chatId, String userMessageContent) {
        return this.chatClient.prompt()
               .user(userMessageContent)
               .advisors(a -> a
                        .param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
                        .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100))
               .stream().content();
    }
}

日志记录 Advisor:SimpleLoggerAdvisor

SimpleLoggerAdvisor 是一个用于记录 ChatClient 的 的请求和响应数据,这对于调试和监控您的 AI 交互非常有用。

  1. 要启用日志记录,在创建 ChatClient 时将其添加到 Advisor 链,建议置于链尾:
java 复制代码
    ChatResponse response = ChatClient.create(chatModel).prompt()
            .advisors(new SimpleLoggerAdvisor())
            .user("Tell me a joke?")
            .call()
            .chatResponse();
  1. 在配置文件(application.properties 或 application.yaml)中设置日志记录级别为 DEBUG:
java 复制代码
org.springframework.ai.chat.client.advisor=DEBUG

此外,还可通过自定义构造函数调整记录内容:

您可以使用以下构造函数自定义如何使用 SimpleLoggerAdvisor 记录来自 AdvisedRequest 和 ChatResponse 的数据:

java 复制代码
    SimpleLoggerAdvisor(
        Function<AdvisedRequest, String> requestToString,
        Function<ChatResponse, String> responseToString
    )

使用示例:

java 复制代码
    javaCopySimpleLoggerAdvisor customLogger = new SimpleLoggerAdvisor(
        request -> "Custom request: " + request.userText,
        response -> "Custom response: " + response.getResult()
    );

这使得您可以根据您的特定需求定制需要记录的信息。

简易聊天

环境变量

引入Spring AI Alibaba

记忆对话还需要我们有数据库进行存储,mysql:mysql-connector-java

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.5.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.dd.ai</groupId>
    <artifactId>spring-ai-alibaba-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-ai-alibaba-demo</name>
    <description>spring-ai-alibaba-demo</description>

    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--        引入Spring AI Alibaba相关依赖-->

        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
            <version>1.0.0.2</version>
        </dependency>
<!--Spring AI Alibaba 提供了 jdbc、redis、elasticsearch 插件可以让聊天机器人拥有"记忆"。下面以 MySQL 为例,演示如何快速编写一个带有记忆的聊天机器人。-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.32</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-memory-jdbc</artifactId>
            <version>1.0.0.2</version>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud.ai</groupId>
                <artifactId>spring-ai-alibaba-bom</artifactId>
                <version>1.0.0.2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

配置文件

java 复制代码
AI_DASHSCOPE_API_KEY: zz
#APP_ID: zzz

server:
  port: 8080

spring:
  application:
    name: spring-ai-demo
  ai:
    dashscope:
      agent:

      api-key: ${AI_DASHSCOPE_API_KEY} # 百炼API Key,如未配置环境变量,请在这里替换为实际的值
      #app-id: ${APP_ID} # 大模型应用ID,,非必须
      #workspace-id: ${WORKSPACE_ID} # 业务空间ID,可选,未配置时使用主账号空间

  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/ai?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
  jpa:
    hibernate:
      ddl-auto: update  # 自动更新数据库表结构
    show-sql: true      # 显示 SQL 语句

controller

java 复制代码
package com.dd.ai.springaialibabademo.controller;

import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

@RestController
@RequestMapping("/helloworld")
public class HelloworldController {
    private static final String DEFAULT_PROMPT = "你是一个博学的智能聊天助手,请根据用户提问回答!";

    private final ChatClient dashScopeChatClient;

    public HelloworldController(ChatClient.Builder chatClientBuilder) {
        this.dashScopeChatClient = chatClientBuilder
                .defaultSystem(DEFAULT_PROMPT)
                // 实现 Logger 的 Advisor
                .defaultAdvisors(
                        new SimpleLoggerAdvisor()
                )
                // 设置 ChatClient 中 ChatModel 的 Options 参数
                .defaultOptions(
                        DashScopeChatOptions.builder()
                                .withTopP(0.7)
                                .build()
                )
                .build();
    }

    /**
     * ChatClient 简单调用
     */
    @GetMapping("/simple/chat")
    public String simpleChat(@RequestParam(value = "query", defaultValue = "你好,很高兴认识你,能简单介绍一下自己吗?")String query) {

        return dashScopeChatClient.prompt(query).call().content();
    }
    /**
     * ChatClient 流式调用
     */
    @GetMapping("/stream/chat")
    public Flux<String> streamChat(@RequestParam(value = "query", defaultValue = "你好,很高兴认识你,能简单介绍一下自己吗?")String query, HttpServletResponse response) {

        response.setCharacterEncoding("UTF-8");
        return dashScopeChatClient.prompt(query).stream().content();
    }


}

记忆聊天

Spring AI Alibaba 提供了 jdbc、redis、elasticsearch 插件可以让聊天机器人拥有"记忆"。环境变量和配置文件都在上一步加入了,也就是以 MySQL 为例

需要修改的地方

  1. HelloworldController的构造函数
java 复制代码
  public HelloworldController(JdbcTemplate jdbcTemplate, ChatClient.Builder chatClientBuilder) {
        // 构造 ChatMemoryRepository 和 ChatMemory
        
        // 创建基于 MySQL 的聊天记忆仓库,用于持久化存储对话历史记录
        ChatMemoryRepository chatMemoryRepository = MysqlChatMemoryRepository.mysqlBuilder()
                .jdbcTemplate(jdbcTemplate)
                .build();
        
        // 创建消息窗口形式的聊天记忆模块,使用上述仓库进行持久化存储
        ChatMemory chatMemory = MessageWindowChatMemory.builder()
                .chatMemoryRepository(chatMemoryRepository)
                .build();
        
        // 构建 DashScopeChatClient 实例
        this.dashScopeChatClient = chatClientBuilder
                // 设置默认系统提示语
                .defaultSystem(DEFAULT_PROMPT)
                // 添加日志记录顾问,用于调试时查看输入输出内容
                .defaultAdvisors(new SimpleLoggerAdvisor())
                // 注册记忆管理顾问,用于自动加载和保存会话历史
                .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
                // 配置模型参数:设置 TopP 采样策略为 0.7
                .defaultOptions(
                        DashScopeChatOptions.builder()
                                .withTopP(0.7)
                                .build()
                )
                // 完成客户端构建
                .build();
    }
  1. 方法参数
    @RequestParam(value = "chat-id", defaultValue = "1") String chatId
java 复制代码
/**
     * ChatClient 简单调用
     */
    @GetMapping("/simple/chat")
    public String simpleChat(@RequestParam(value = "query", defaultValue = "你好,很高兴认识你,能简单介绍一下自己吗?")String query,
                             @RequestParam(value = "chat-id", defaultValue = "1") String chatId) {

        return dashScopeChatClient.prompt(query).advisors(a -> a.param(ChatMemory.CONVERSATION_ID, chatId)).call().content();
    }
    /**
     * ChatClient 流式调用
     */
    @GetMapping("/stream/chat")
    public Flux<String> streamChat(@RequestParam(value = "query", defaultValue = "你好,很高兴认识你,能简单介绍一下自己吗?")String query, HttpServletResponse response,
                                   @RequestParam(value = "chat-id", defaultValue = "1") String chatId) {

        response.setCharacterEncoding("UTF-8");
        return dashScopeChatClient.prompt(query).advisors(a -> a.param(ChatMemory.CONVERSATION_ID, chatId)).stream().content();
    }

运行报错

原因:由于 MySQL 表的字符集不支持存储 Unicode 表情符号导致的。表情符号😊的 UTF-8 编码为F0 9F 98 8A(4 字节),超出了utf8字符集的 3 字节限制

不知道改了啥,又可以了,可能是这个,把数据库的字符集改了一下

java 复制代码
-- 将chat_db数据库的字符集改为utf8mb4
ALTER DATABASE chat_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
相关推荐
界面开发小八哥1 小时前
「Java EE开发指南」如何用MyEclipse创建一个WEB项目?(三)
java·ide·java-ee·myeclipse
idolyXyz1 小时前
[java: Cleaner]-一文述之
java
一碗谦谦粉2 小时前
Maven 依赖调解的两大原则
java·maven
九年义务漏网鲨鱼2 小时前
【大模型学习 | MINIGPT-4原理】
人工智能·深度学习·学习·语言模型·多模态
元宇宙时间2 小时前
Playfun即将开启大型Web3线上活动,打造沉浸式GameFi体验生态
人工智能·去中心化·区块链
开发者工具分享2 小时前
文本音频违规识别工具排行榜(12选)
人工智能·音视频
netyeaxi2 小时前
Java:使用spring-boot + mybatis如何打印SQL日志?
java·spring·mybatis
收破烂的小熊猫~2 小时前
《Java修仙传:从凡胎到码帝》第四章:设计模式破万法
java·开发语言·设计模式
产品经理独孤虾2 小时前
人工智能大模型如何助力电商产品经理打造高效的商品工业属性画像
人工智能·机器学习·ai·大模型·产品经理·商品画像·商品工业属性
猴哥源码2 小时前
基于Java+SpringBoot的动物领养平台
java·spring boot