本节对应 Github:https://github.com/JCodeNest/JCodeNest-AI-Alibaba/tree/master/spring-ai-alibaba-helloworld
本文将以阿里巴巴的通义大模型为例,通过 Spring AI Alibaba 组件,手把手带你完成从零到一的构建过程:首先,创建一个基础的智能聊天机器人;然后,为其赋予核心的 “记忆” 能力,让它能够理解上下文,进行连贯的多轮对话。
阿里云通义大模型: 这是由阿里云推出的一系列功能强大的语言模型。通过阿里云百炼平台,Java 仔们可以方便地获取 API-KEY,从而在自己的应用中调用这些模型,实现文本生成、对话、嵌入等多种 AI 功能。
1. 构建聊天机器人
我们的第一步是构建一个可以进行单次问答的聊天机器人。它能接收用户的问题,并返回通义模型的回答。
1.1 环境准备
在开始之前,请确保你的开发环境满足以下要求:
- JDK 17 或更高版本: Spring AI 基于 Spring Boot 3.x,因此需要 Java 17+。
- Maven 或 Gradle: 用于项目构建和依赖管理。
- 阿里云 API-KEY: 访问并登录阿里云百炼平台,创建并获取一个有效的 API-KEY。
1.2 项目初始化和依赖配置
首先,我们需要在 pom.xml 中引入 spring-ai-alibaba-starter-dashscope 依赖。它会通过 Spring Boot 的自动装配机制,为我们准备好与通义模型通信所需的核心实例,如 ChatClient。
<!-- 管理 Spring AI Alibaba 的所有依赖版本 -->
<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><!-- 核心依赖:连接通义大模型 -->
<dependencies><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter-dashscope</artifactId></dependency>
</dependencies>
1.3 配置 API-KEY
获取到 API-KEY 后,最安全和推荐的方式是将其配置为环境变量。Spring AI Alibaba 会自动读取该变量。
export AI_DASHSCOPE_API_KEY=${替换为你的有效API-KEY}
或者你可以在 application.yml 中进行如下配置:
spring:ai:dashscope:api-key: <替换为你的有效API-KEY>
1.4 编写核心代码
接下来,我们创建一个 Controller 来处理用户的聊天请求。
package cn.jcodenest.ai.hello.controller;import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import org.springframework.ai.chat.client.ChatClient;
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;/*** 用户聊天 Controller** @author JCodeNest* @version 1.0.0* @since 2025/8/12* <p>* Copyright (c) 2025 JCodeNest-AI-Alibaba* All rights reserved.*/
@RestController
@RequestMapping("/chat")
public class ChatController {private final ChatClient dashScopeChatClient;/*** 通过构造函数注入 ChatClient.Builder*/public ChatController(ChatClient.Builder chatClientBuilder) {this.dashScopeChatClient = chatClientBuilder// 设置默认的系统级提示词(System Prompt),为 AI 设定角色和行为准则.defaultSystem("你是一个博学的智能聊天助手,请根据用户提问,结合上下文进行友好、专业地回答!")// 设置默认的模型参数,如 topP、温度等.defaultOptions(DashScopeChatOptions.builder()// topP 参数,控制生成文本的多样性.withTopP(0.8).build()).build();}/*** 简单问答接口** @param query 用户问题* @return 模型回答*/@GetMapping("/simple")public String simpleChat(@RequestParam(value = "query", defaultValue = "你好,请介绍一下你自己。") String query) {// 使用 prompt() 方法包装用户问题,调用 call().content() 获取模型回答return dashScopeChatClient.prompt(query).call().content();}/*** 流式响应接口,实现打字机效果** @param query 用户问题* @return 模型回答流*/@GetMapping("/stream")public Flux<String> streamChat(@RequestParam(value = "query", defaultValue = "请给我讲一个关于程序员的笑话") String query) {// 调用 stream().content() 返回一个 Flux<String> 对象return dashScopeChatClient.prompt(query).stream().content();}
}
代码剖析:
- 我们通过构造函数注入了
ChatClient.Builder
,这是一个工厂类,用于以链式调用的方式构建和配置ChatClient
实例。 defaultSystem()
: 设置一个系统级的提示词,这对于定义 AI 的角色和行为至关重要。defaultOptions()
: 配置与模型交互时的默认参数。DashScopeChatOptions
提供了丰富的配置项。这些参数也可以在每次调用时动态指定,灵活性非常高。simpleChat()
: 这是最基础的调用方式,一次性返回完整的模型响应。streamChat()
: 通过返回Flux<String>
,实现了流式响应。这在前端可以轻松实现 “打字机” 的实时显示效果,极大地提升了用户体验。
上述简单聊天的完整流程:
基础调用测试结果如下:
流式调用测试结果如下:
2. 为你的机器人赋予记忆
上面的机器人虽然能回答问题,但它是 “健忘” 的。你问它第二个问题时,它完全不记得第一个问题是什么。为了实现真正的对话,我们必须为它增加记忆能力。
例如,当你再问他前面的信息时,他就忘记了:
2.1 为什么需要记忆?
HTTP 协议本身是无状态的,LLM 的 API 调用在默认情况下也是如此。每一次请求都是独立的,模型不会保留之前的对话信息。要让对话持续下去,我们必须在每次请求时,将之前的聊天记录一并发送给模型。
虽然可以手动在代码中维护一个对话历史列表,但这会迅速增加代码的复杂性和维护成本。幸运的是,Spring AI 提供了优雅的解决方案——Chat Memory。
2.2 Spring AI 的记忆解决方案
Spring AI 通过 ChatMemory
接口和 Advisor
(AOP 中的 “通知”)设计模式来解决记忆问题。Advisor
可以在 ChatClient
调用前后执行额外的逻辑,例如:
- 调用前: 从存储中加载历史对话记录,并将其添加到当前的请求中。
- 调用后: 将本次新的问答对(用户问题和模型回答)保存到存储中。
Spring AI Alibaba 提供了多种开箱即用的记忆存储方案,如 jdbc, redis, elasticsearch。下面我们以最通用的 JDBC (使用 MySQL) 为例进行演示。
2.3 使用 JDBC 持久化记忆
在 pom.xml 中追加 jdbc-memory 和 mysql 驱动的依赖。
<dependencies><!-- Spring AI JDBC 记忆组件 --><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter-memory-jdbc</artifactId></dependency><!-- MySQL 数据库驱动 --><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId></dependency>
</dependencies>
在 application.yaml 文件中配置数据库连接信息。
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/your_database?useUnicode=true&characterEncoding=UTF-8username: your_usernamepassword: your_password# 建议开启,让 Hibernate 自动创建或更新表结构jpa:hibernate:ddl-auto: update
注意:
JdbcChatMemoryRepository
在应用启动时会自动检测并创建名为ai_chat_memory
的数据表,用于存储对话记录。确保你的数据库用户有 DDL 权限,或提前手动建表。create table ai_chat_memory (id bigint auto_increment primary key,conversation_id varchar(256) not null,content longtext not null,type varchar(100) not null,timestamp timestamp not null,constraint chk_message_type check (`type` in ('USER', 'ASSISTANT', 'SYSTEM', 'TOOL')) );
现在,我们来改造 ChatController,为其注入记忆能力。
@RestController
@RequestMapping("/chat")
public class ChatController {private final ChatClient dashScopeChatClient;// 注入 JdbcTemplate 用于数据库操作public ChatController(JdbcTemplate jdbcTemplate, ChatClient.Builder chatClientBuilder) {// 1. 构造基于 JDBC 的 ChatMemoryRepositoryChatMemoryRepository chatMemoryRepository = MysqlChatMemoryRepository.mysqlBuilder().jdbcTemplate(jdbcTemplate).build();// 2. 构造一个窗口化的 ChatMemory,它使用上面的 RepositoryChatMemory chatMemory = MessageWindowChatMemory.builder().chatMemoryRepository(chatMemoryRepository).build();// 3. 将 ChatMemory 包装成一个 Advisor,并注册到 ChatClientthis.dashScopeChatClient = chatClientBuilder.defaultSystem("你是一个博学的智能聊天助手,请根据用户提问,结合上下文进行友好、专业地回答!")// 关键:注册 MessageChatMemoryAdvisor.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()).defaultOptions(DashScopeChatOptions.builder().withTopP(0.8).build()).build();}/*** 带有记忆的聊天接口* @param chatId 用于区分不同对话的会话 ID*/@GetMapping("/memory")public String memoryChat(@RequestParam String query,@RequestParam(defaultValue = "default-chat-001") String chatId) {return dashScopeChatClient.prompt(query)// 关键:在每次调用时,通过 Advisor 参数传递当前的会话 ID.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, chatId)).call().content();}
}
代码剖析:
- 我们首先构造了一个
MysqlChatMemoryRepository
,它是ChatMemoryRepository
针对 MySQL 的具体实现。 - 然后,我们创建了一个
MessageWindowChatMemory
实例,它是一种常见的对话记忆策略(例如,只保留最近 N 轮对话)。 - 最核心的一步,是将
chatMemory
包装进MessageChatMemoryAdvisor
,并通过.defaultAdvisors()
方法将其注册到ChatClient
中。从此,这个ChatClient
的所有调用都会经过记忆处理。 - 在
memoryChat
方法中,我们增加了一个chatId
参数。这个 ID 是区分不同用户或不同对话的关键。我们通过.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, chatId))
将其传递给记忆顾问,以便它能为正确的会话加载和保存历史记录。
增加记忆功能后,系统的内部工作流程如下:
现在,你可以连续调用 /chat/memory
接口,使用相同的 chatId
,机器人将能够记住你们之前的对话内容,实现连贯的交流。
第一个问题:请记住,当我问你 “我是谁” 的时候,你只需要答复我,你是 JCode。
此时,可以观察到我们的对话对信息(USER + ASSISTANT)已经持久化到数据库了:
第二个问题:我是谁?
可见,LLM 确实实现了 “记住” 的效果,再观察 ai_chat_memory,新的对话对同样被添加进来了:
3. 总结
可以看到,我们使用 Spring AI Alibaba 构建一个简单的智能聊天机器人就是这么简单。我们从一个简单的问答服务开始,然后通过 Spring AI 强大且易于扩展的 Advisor
和 ChatMemory
机制,为其无缝集成了持久化的对话记忆能力。
这仅仅是 Spring AI 世界的冰山一角。基于这套框架,你还可以进一步探索更高级的功能,例如:
- RAG (Retrieval-Augmented Generation): 结合向量数据库,让机器人能根据你自己的私有知识库回答问题。
- Function Calling: 允许 AI 模型调用你定义的 Java 方法,实现与外部工具和服务的交互。
- 多模态能力: 处理和生成图像等非文本内容。
通过 Spring AI 的抽象设计理念,Java 仔们可以聚焦于业务逻辑创新,而不必深陷于与不同 AI 模型底层 API 对接的泥潭。