1. ChatModel和Chat Client对象对比
在与各个模型进行对话时,使用对应模型的 ChatModel 对象,例如:DeepSeekChatModel、OllamaChatModel。然后通过 chatModel.call(…)、chatModel.stream(…) 直接调用模型。这期间我们需要手动构造 Prompt、处理流式响应(例如将返回结果封装成对象、从模型响应中获取回复内容)、拼装调用链(RAG 需要自己构建流程)等操作。
Spring AI 中提供了 ChatClient 对象,该对象可以看做一个更高级的“客户端 API”,建立在 ChatModel 之上,可以用链式的方式快速搭配 Prompt、系统设定、变量替换、上下文记忆等,并支持文本、JSON、实体对象等多种形式的输出。
1.1 多模态ChatClient创建
resources/application.properties
//手动管理 ChatClient 的创建需要设置为false,即不再使用ChatClient.Builder Bean 注入 spring.ai.chat.client.enabled=false@ConfigurationpublicclassMultiChatClientConfig{//创建DeepSeekChatClient@BeanpublicChatClientdeepSeekChatClient(DeepSeekChatModeldeepSeekChatModel){returnChatClient.builder(deepSeekChatModel).defaultSystem("你是一个人工智能助手,你的名字叫做小智").build();}//创建zhipuAIChatClient@BeanpublicChatClientzhipuAIChatClient(ZhiPuAiChatModelzhiPuAiChatModel){returnChatClient.builder(zhiPuAiChatModel).build();}}1.2 多模态的使用
privateChatClientdeepSeekChatClient;privateChatClientzhipuAIChatClient;publicMultiModelController(@Qualifier("deepSeekChatClient")ChatClientdeepSeekChatClient,@Qualifier("zhipuAIChatClient")ChatClientzhipuAIChatClient){this.deepSeekChatClient=deepSeekChatClient;this.zhipuAIChatClient=zhipuAIChatClient;}@GetMapping("/deepseek")publicStringchatWithDeepseek(@RequestParam("message")Stringmessage){Stringresult=deepSeekChatClient.prompt().user(message).call().content();returnresult;}@GetMapping("/idCardAnaly")publicIdCardInfoidCardAnaly(){ClassPathResourceresource=newClassPathResource("身份证.jpg");IdCardInfoidCardInfo=zhipuAIChatClient.prompt("你是一个图像识别的高手,可以对用户关于图片识别问题进行精准回复,以json格式输出识别到的身份证中姓名(name)、性别(sex)、民族(nation)、出生日期(birth)、住址(address)、身份证号(idNo)").user(c->c.text("请对图片进行描述").media(MediaType.IMAGE_JPEG,resource)).call().entity(IdCardInfo.class);returnidCardInfo;}2. Chat Memory
大语言模型(LLMs)是无状态的,即它们不会自动保留先前对话信息。
Spring AI引入Chat Memory,可以将最近N次对话上下文信息保存在内存或者外部数据库(例如PostgreSQL、MySQL、SQL Server、HSQLDB、Cassandra、Neo4j) 。以便LLM在同一会话的多轮互动中保持语义连贯。
2.1 ChatMemory接口:
Ø ChatMemory接口:提供在指定对话中添加消息、检索记忆内容、以及清理旧消息的能力。
Ø ChatMemoryRepository接口:负责消息的底层储存与提取。
ChatMemory默认实现为MessageWindowChatMemory类,如下方式创建ChatMemory:
MessageWindowChatMemorymemory=MessageWindowChatMemory.builder().maxMessages(20).build();ChatMemory对象默认将保存最近N条对话信息(默认20条),超出后按时间顺序清理(系统消息不会被清除)。最近N条对话消息存储到哪里由ChatMemoryRepository的实现决定(存储后端)。
2.2 ChatMemoryRepository
ChatMemoryRepository有如下四种实现:
- InMemoryChatMemoryRepository:使用 ConcurrentHashMap 存储消息,这种也是默认方式。
@ConfigurationpublicclassConfig{//准备 ChatMemory 对象,将聊天记忆放在内存中@BeanpublicChatMemorychatMemory(){returnMessageWindowChatMemory.builder().maxMessages(20).build();}@BeanpublicChatClientchatClient(ChatModelchatModel,ChatMemorymemory){ChatClientclient=ChatClient.builder(chatModel)//设置 chatMemory.defaultAdvisors(MessageChatMemoryAdvisor.builder(memory).build()).build();returnclient;}}- JdbcChatMemoryRepository:基于 JDBC 的关系型数据库(PostgreSQL、MySQL、SQL Server、HSQLDB)存储数据,支持通过 Spring 属性配置 schema 初始化时机与脚本位置。
#MySQL 配置 ,提前在MySQL中创建数据库和表spring.datasource.url=jdbc:mysql://localhost:3306/chat_memory?useUnicode=true&characterEncoding=UTF-8&connectionCollation=utf8mb4_unicode_ci spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver#自动创建ChatMemory存储的表结构#Spring AI 的 JdbcChatMemoryRepository 会尝试自动创建所需表结构spring.ai.chat.memory.repository.jdbc.initialize-schema=always#告诉框架当前数据库是 MariaDB/MySQL,会加载 schema-mariadb.sql 初始化脚本,自行创建表 SPRING_AI_CHAT_MEMORY表#官方暂时未提供schema-mysql.sql 初始化脚本。当配置 platform=mysql 时,框架会尝试加载 schema-mysql.sql 文件,但该文件在官方 JAR 包中不存在,因此会导致启动失败。所以推荐使用 platform=mariadb。#而MariaDB 的脚本与 MySQL 高度兼容,可以作为替代方案使用。spring.ai.chat.memory.repository.jdbc.platform=mariadb#确保每条 JDBC 连接都使用 utf8mb4 字符集并指定 utf8mb4_unicode_ci 排序spring.datasource.hikari.connection-init-sql=SETNAMES utf8mb4 COLLATE utf8mb4_unicode_ci# 配置 Deepseek URL、API Key、模型spring.ai.deepseek.base-url=https://api.deepseek.com spring.ai.deepseek.api-key=your_api_key spring.ai.deepseek.chat.options.model=deepseek-chat@ConfigurationpublicclassConfig{//准备 ChatMemory 对象,将聊天记忆放在 Mysql 中@BeanpublicChatMemorychatMemory(JdbcChatMemoryRepositoryrepo){returnMessageWindowChatMemory.builder().chatMemoryRepository(repo).maxMessages(20).build();}@BeanpublicChatClientchatClient(ChatModelchatModel,ChatMemorymemory){ChatClientclient=ChatClient.builder(chatModel)//设置 chatMemory.defaultAdvisors(MessageChatMemoryAdvisor.builder(memory).build()).build();returnclient;}}- CassandraChatMemoryRepository:使用 Cassandra,可配置 TTL(例如 3 年)实现自动过期。
- Neo4jChatMemoryRepository:基于图数据库 Neo4j存储消息。
Advisor是 Spring AI 中用于增强方法执行逻辑的组件,类似于一个拦截器,通过 Advisor将 ChatMemory 集成到ChatClient中。
importorg.springframework.ai.chat.client.ChatClient;importorg.springframework.ai.chat.memory.ChatMemory;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/ai")publicclassChatMemoryInMySQLController{@AutowiredprivateChatClientchatClient;@GetMapping("/chatMemory")publicStringchatMemory(@RequestParam(value="cid")StringconversationID,@RequestParam(value="message",defaultValue="你是谁?")Stringmessage){Stringresult=chatClient.prompt().user(message)//加入聊天的会话.advisors(advisorSpec->advisorSpec.param(ChatMemory.CONVERSATION_ID,conversationID)).call().content();returnresult;}}使用ChatClient时传入对话ID,与模型交互时通过CONVERSATION_ID标记会话,advisor会自动上下文管理。