一、前言
本篇教程是《Java驱动AI革命:Spring AI八篇进阶指南——从架构基础到企业级智能系统实战》系列的第二篇,将手把手教你如何使用 Spring AI 的核心三剑客:ChatClient
、PromptTemplate
和 Memory
。它们是构建任何一个人工智能聊天应用的基础。
最重要的是,本篇博文将完全抛开网上各种教程对国外服务的依赖,全程使用免费、开源的本地大模型运行平台 Ollama 和国内优秀的 Qwen (通义千问) 模型。这意味着你不需要注册付费的API,就能在你自己的电脑上体验到大模型的神奇之处,是不是比现在流行的各种网课给力?话不多说,现在咱们就在本地局域网内,用Java编码免费把大模型玩起来。
你将学到:
- 如何在本地安装和运行 Ollama。
- 如何在 Ollama 中下载并运行 Qwen 模型。
- 如何从零开始搭建一个 Spring Boot 项目,并集成 Spring AI。
- 如何使用
ChatClient
发送你的第一个请求。 - 如何使用
PromptTemplate
灵活地构建你的提问。 - 如何使用
Memory
让你的AI记住之前的对话,实现多轮交流。
二、本地环境准备:Ollama 与 Qwen 模型
在使用 Spring AI 之前,我们首先需要在本地搭建一个大模型运行环境。这里我们选择 Ollama,它是一个非常方便的本地大模型运行平台。
具体也可参考之前写过的《Ollama 深度使用指南:在本地玩转大型语言模型》
1. 安装 Ollama
- 下载 Ollama: 访问 Ollama 的官方网站:https://ollama.com/download。
- 选择你的操作系统: 根据你的电脑系统(Windows、macOS、Linux),下载对应的安装包。
- 安装:
- Windows/macOS: 双击下载的安装包,按照提示一步步完成安装。安装过程非常简单,就像安装普通软件一样。
- Linux: 按照官网的命令行指示进行安装。
安装完成后,Ollama 会在后台默默运行,你通常不会看到一个单独的应用程序窗口,它作为一个服务在你的系统上运行。
2. 下载 Qwen 模型
Ollama 安装好后,我们就可以通过命令行下载 Qwen 模型了。Qwen 是阿里云开源的一系列优秀大模型。
-
打开命令行/终端:
- Windows: 按
Win + R
键,输入cmd
或powershell
,然后回车。 - macOS/Linux: 打开“终端”应用程序。
- Windows: 按
-
下载模型: 在命令行中输入以下命令并回车。这里我们以下载
qwen:7b
模型为例,7b
表示70亿参数,对电脑配置要求不高,适合初学者。ollama pull/run qwen:7b
这个过程可能需要一些时间,因为它会从互联网上下载模型文件,文件通常比较大(几个GB)。请耐心等待下载完成。下载完成后,Ollama 服务就已经可以使用
qwen:7b
模型了!注意: Ollama 默认运行在
http://localhost:11434
。如果你的电脑防火墙或网络设置有问题,可能需要检查。但通常情况下,安装后即可直接使用。
三、Spring Boot 项目初始化与配置
现在本地大模型环境已经准备好了,我们来创建 Spring Boot 项目并集成 Spring AI。
1. 创建 Spring Boot 项目
如上图所示,推荐使用 Spring Initializr (https://start.spring.io/) 来快速创建项目,当然如果你是idea的旗舰版用户也可以本地快速创建。
- 访问地址: 打开浏览器,访问 https://start.spring.io/
- 项目设置:
- Project:
Maven Project
(或Gradle Project
,这里我们用Maven) - Language:
Java
- Spring Boot: 选择最新的
3.5.3
版本,参考官网描述,最低3.4.x - Group:
com.example
(可以自定义,例如com.yourcompany
) - Artifact:
spring-ai-ollama-demo
(项目名称,可以自定义) - Java: 选择
17
或更高版本 (建议Java 17 LTS)
- Project:
- 添加依赖 (Dependencies):
- 点击 “Add Dependencies…” 按钮
- 搜索并添加:
Spring Web
(可选,但通常用于构建Web应用,方便测试)Lombok
(可选,但非常推荐,可以大大简化Java代码)Spring AI Ollama Starter
(这个是关键!用于集成Ollama)
- 生成项目: 点击 “Generate” 按钮,下载项目的压缩包。
- 解压并导入IDE: 将下载的
spring-ai-ollama-demo.zip
解压到你喜欢的位置,然后使用你的Java集成开发环境 (IDE),例如 IntelliJ IDEA 或 Eclipse,导入这个Maven项目。
2. pom.xml
依赖配置
导入项目后,检查 pom.xml
文件,确保它包含了所有必要的依赖。
对于spring-ai版本选择,由于进化很快,这里咱用最新的1.0.0-M5来演示,参考如下图
下面是一个完整的 pom.xml
示例,请确保你的文件内容与此相似:
<?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/> </parent>
<groupId>com.example</groupId>
<artifactId>spring-ai-ollama-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-ai-ollama-demo</name>
<description>Demo project for Spring AI with Ollama</description>
<properties>
<java.version>17</java.version>
<spring-ai.version>1.0.0-M5</spring-ai.version> </properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
3. application.properties
配置
在 src/main/resources/
目录下找到 application.properties
文件,并添加以下内容。这些配置告诉 Spring AI 如何连接到 Ollama 服务,以及使用哪个模型。
# Spring AI Ollama 配置
# Ollama 服务的地址,默认是 http://localhost:11434
spring.ai.ollama.chat.base-url=http://localhost:11434
# 指定要使用的模型名称,这里我们使用之前下载的qwen:7b
spring.ai.ollama.chat.options.model=qwen:7b
# 如果需要,可以调整温度参数(控制AI回答的创造性)
# spring.ai.ollama.chat.options.temperature=0.7
注意: 如果你的 Ollama 服务没有运行在 http://localhost:11434
(例如,你在WSL中运行或配置了其他端口),你需要修改 spring.ai.ollama.chat.base-url
为正确的地址。
四、ChatClient:对话的统一入口
ChatClient
是 Spring AI 提供的最核心接口,它充当了你与大模型对话的统一入口。无论你底层用的是 Ollama、OpenAI 还是其他模型,你都通过 ChatClient
来发送提问并接收回答。
1. 核心作用与基本使用
Spring AI 会根据你的配置(这里是 Ollama)自动为你创建一个 ChatClient
的 Bean,你只需要通过 Spring 的 @Autowired
注解就可以把它注入到你的代码中并使用。
现在,我们创建一个主要的 Spring Boot 应用程序类 SpringAiOllamaDemoApplication.java
,并添加第一个使用 ChatClient
的示例。
文件路径: src/main/java/com/example/springaiollamademopackage/SpringAiOllamaDemoApplication.java
(请将 package
名称替换为你的实际项目包名)
// src/main/java/com/example/springaiollamademodemo/SpringAiOllamaDemoApplication.java
package com.example.springaiollamademodemo; // 请替换为你的实际项目包名
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication // 这是一个Spring Boot应用
public class SpringAiOllamaDemoApplication {
public static void main(String[] args) {
// Spring Boot 应用的启动入口
SpringApplication.run(SpringAiOllamaDemoApplication.class, args);
}
/**
* 示例1: ChatClient 基本使用
*
* @param chatClient Spring 自动注入的 ChatClient 实例
* @return CommandLineRunner,在应用启动后执行其run方法
*/
@Bean
public CommandLineRunner runChatClientBasic(ChatClient chatClient) {
return args -> {
System.out.println("--- ChatClient 基本使用:向大模型提问 ---");
// 使用chatClient.prompt().user("你的问题").call().content() 发送问题并获取回答内容
String responseContent = chatClient.prompt()
.user("解释下量子纠缠的原理")
.call()
.content(); // 获取大模型的回答内容
System.out.println("大模型响应: " + responseContent);
System.out.println("\n------------------------------------------------\n");
};
}
}
如何运行:
- 确保你的 Ollama 服务和
qwen:7b
模型正在运行。 - 在你的IDE中,找到
SpringAiOllamaDemoApplication
类,右键点击并选择 “Run ‘SpringAiOllamaDemoApplication.main()’” (或类似的选项)。 - 程序启动后,你会在IDE的控制台(Console)中看到大模型对“量子纠缠”的解释。
2. 进阶用法:封装多轮上下文 (设置角色)
ChatClient
允许你发送包含多条消息的请求,这对于给AI设置一个特定的“角色”或者提供更丰富的对话背景非常有用。你可以发送 system
(系统) 消息和 user
(用户) 消息。
在 SpringAiOllamaDemoApplication.java
中,继续添加一个 @Bean
方法:
// ... (保留SpringAiOllamaDemoApplication的main方法和之前的runChatClientBasic Bean)
package com.example.springaiollamademodemo; // 请替换为你的实际项目包名
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class SpringAiOllamaDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAiOllamaDemoApplication.class, args);
}
// ... (保留 runChatClientBasic 方法)
/**
* 示例2: ChatClient 多轮上下文 (设置角色)
* 通过系统消息为大模型设定一个角色,让它的回答更符合预期。
*
* @param chatClient Spring 自动注入的 ChatClient 实例
* @return CommandLineRunner
*/
@Bean
public CommandLineRunner runChatClientWithContext(ChatClient chatClient) {
return args -> {
System.out.println("--- ChatClient 多轮上下文 (设置角色): 让大模型成为科普专家 ---");
String responseContent = chatClient.prompt()
.system("你是一个科普专家,擅长用通俗易懂的语言解释科学原理。") // 系统消息:设定AI角色
.user("讲讲黑洞是怎么形成的?") // 用户消息:实际问题
.call()
.content();
System.out.println("大模型(科普专家)响应: " + responseContent);
System.out.println("\n------------------------------------------------\n");
};
}
}
再次运行 SpringAiOllamaDemoApplication
,你会发现大模型会以“科普专家”的口吻来解释黑洞的形成。
五、PromptTemplate:灵活的提示模板引擎
在实际开发中,我们往往需要根据不同的输入动态地生成问题。例如,一个翻译应用需要翻译不同的文本,一个总结工具需要总结不同的文章。PromptTemplate
就是为此而生,它允许你定义带有占位符的模板,然后动态填充数据。
1. 为何需要 Prompt 模板?
想象一下,你每次都要手动拼接字符串来提问,当问题结构复杂、变量多时,代码会变得非常难以维护。PromptTemplate
就像一个填空题,你只需要填入变量的值,它就能帮你生成完整的提问。
2. 定义方式示例与使用
PromptTemplate
可以直接在代码中通过字符串定义,也可以更优雅地与 ChatClient
的链式调用结合。
在 SpringAiOllamaDemoApplication.java
中,继续添加一个 @Bean
方法:
// ... (保留SpringAiOllamaDemoApplication的main方法和之前的ChatClient Beans)
package com.example.springaiollamademodemo; // 请替换为你的实际项目包名
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class SpringAiOllamaDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAiOllamaDemoApplication.class, args);
}
// ... (保留 runChatClientBasic 和 runChatClientWithContext 方法)
/**
* 示例3: PromptTemplate 使用
* 通过占位符定义模板,动态填充变量来构建问题。
*
* @param chatClient Spring 自动注入的 ChatClient 实例
* @return CommandLineRunner
*/
@Bean
public CommandLineRunner runPromptTemplate(ChatClient chatClient) {
return args -> {
System.out.println("--- PromptTemplate 使用:动态提问 ---");
// 场景一:字符串模板示例 - 翻译助手
String textToTranslate = "Hello, Spring AI is awesome!";
// 创建模板并渲染变量
PromptTemplate translationTemplate = new PromptTemplate("请帮我翻译这段内容:{{content}}");
String translatedContent = chatClient.prompt()
.system("你是一名翻译助手,请帮助我翻译以下内容:")
.user(translationTemplate.render(Map.of("content", textToTranslate)))
.call()
.content();
System.out.println("翻译结果: " + translatedContent);
System.out.println("\n--- 另一个PromptTemplate示例:简历优化助手 ---");
// 场景二:模拟结构化模板示例 - 简历优化助手
String resumeContent = "我在一家大型互联网公司担任客户支持,负责日常用户咨询和故障排除。";
PromptTemplate resumeTemplate = new PromptTemplate("请帮我优化这段内容:{{content}}");
String optimizedResume = chatClient.prompt()
.system("你是一名专业的简历优化专家,请帮助我改进简历内容,使其更具吸引力。")
.user(resumeTemplate.render(Map.of("content", resumeContent)))
.call()
.content();
System.out.println("优化后的简历内容: " + optimizedResume);
System.out.println("\n------------------------------------------------\n");
};
}
运行项目,你会看到大模型分别完成了翻译和简历优化任务,且是根据你提供的文本动态生成的。
六、Memory:对话上下文与智能体的记忆机制
在真实的聊天场景中,AI 需要记住之前的对话,才能进行连贯的多轮交流。例如,你问完“北京天气如何?”后,接着问“那需要带伞吗?”,AI需要知道“它”指的是“北京的天气”。ChatMemory
就是用来解决这个问题的,它为 AI 提供了“记忆”能力。
1. 接入示例
Spring AI 提供了 InMemoryChatMemory
(内存记忆) 作为默认实现,它会将对话历史存储在内存中。要让 ChatClient
拥有记忆能力,我们需要用到 Spring AI 提供的 MessageChatMemoryAdvisor
。
为了更好的结构化,我们创建一个新的配置类 ChatMemoryConfig.java
来定义带有记忆功能的 ChatClient
。
文件路径: src/main/java/com/example/springaiollamademodemo/ChatMemoryConfig.java
(请将 package
名称替换为你的实际项目包名)
// src/main/java/com/example/springaiollamademodemo/ChatMemoryConfig.java
package com.example.springaiollamademodemo; // 请替换为你的实际项目包名
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration // 这是一个配置类
public class ChatMemoryConfig {
/**
* 定义一个 InMemoryChatMemory Bean。
* 这是对话历史的存储介质,默认将历史存储在内存中。
* @return InMemoryChatMemory 实例
*/
@Bean
public ChatMemory chatMemory() {
return new InMemoryChatMemory();
}
/**
* 定义一个带有记忆功能的 ChatClient。
* 通过 ChatClient.Builder 添加 MessageChatMemoryAdvisor 来实现记忆功能。
* 给这个 Bean 一个特定的名称 "chatClientWithMemory",以免与默认的 ChatClient 冲突。
*
* @param chatClientBuilder Spring AI 自动配置的 ChatClient.Builder
* @param chatMemory 我们上面定义的 ChatMemory 实例
* @return 带有记忆功能的 ChatClient
*/
@Bean("chatClientWithMemory")
public ChatClient chatClientWithMemory(ChatClient.Builder chatClientBuilder, ChatMemory chatMemory) {
return chatClientBuilder
.defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory)) // 添加记忆顾问
.build();
}
}
现在,回到 SpringAiOllamaDemoApplication.java
,添加一个使用这个带有记忆功能的 ChatClient
的示例。
// ... (保留SpringAiOllamaDemoApplication的main方法和所有之前的Beans)
package com.example.springaiollamademodemo; // 请替换为你的实际项目包名
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Qualifier; // 导入Qualifier注解
@SpringBootApplication
public class SpringAiOllamaDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAiOllamaDemoApplication.class, args);
}
// ... (保留 runChatClientBasic, runChatClientWithContext, runPromptTemplate 方法)
/**
* 示例4: ChatClient 与 Memory 结合 (多轮对话)
* 使用带有记忆功能的 ChatClient 进行多轮对话,AI会记住之前的上下文。
*
* @param chatClientWithMemory 通过 @Qualifier 注解注入我们定义的带有记忆功能的 ChatClient
* @return CommandLineRunner
*/
@Bean
public CommandLineRunner runChatClientWithMemory(@Qualifier("chatClientWithMemory") ChatClient chatClientWithMemory) {
return args -> {
System.out.println("--- ChatClient 与 Memory 结合:实现多轮对话 ---");
System.out.println("用户: 今天天气怎么样?");
String response1 = chatClientWithMemory.prompt()
.user("今天天气怎么样?")
.call()
.content();
System.out.println("大模型响应: " + response1);
// 第二次提问:不直接提及地点,看大模型是否能记住上下文
System.out.println("用户: 那我需要带伞吗?");
String response2 = chatClientWithMemory.prompt()
.user("那我需要带伞吗?")
.call()
.content();
System.out.println("大模型响应: " + response2);
System.out.println("\n------------------------------------------------\n");
};
}
}
重新运行 SpringAiOllamaDemoApplication
,你会发现大模型在回答第二个问题时,能够根据第一个问题的上下文(“今天天气怎样?”)来判断是否需要带伞。
2. 使用标识:通过 ConversationId 管理多会话
在实际应用中,你可能需要为不同的用户或不同的对话主题维护独立的记忆。Spring AI 允许你通过 conversationId
来实现这一点。同一个 ChatMemory
实例可以管理多个 conversationId
对应的对话历史。
我们可以在 ChatClient.Builder
中通过 conversationId()
方法来指定当前的会话ID。
在 SpringAiOllamaDemoApplication.java
中,继续添加一个 @Bean
方法:
// ... (保留SpringAiOllamaDemoApplication的main方法和所有之前的Beans)
package com.example.springaiollamademodemo; // 请替换为你的实际项目包名
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.beans.factory.annotation.Autowired; // 导入Autowired
import org.springframework.beans.factory.annotation.Qualifier; // 导入Qualifier
@SpringBootApplication
public class SpringAiOllamaDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAiOllamaDemoApplication.class, args);
}
// ... (保留 runChatClientBasic, runChatClientWithContext, runPromptTemplate, runChatClientWithMemory 方法)
@Autowired // 注入 ChatClient.Builder 以便我们构建不同配置的ChatClient
private ChatClient.Builder chatClientBuilder;
@Autowired // 注入我们之前定义的 ChatMemory 实例
private ChatMemory chatMemory;
/**
* 示例5: 通过 ConversationId 管理多会话
* 为不同的用户或场景设置独立的会话ID,即使使用同一个 ChatMemory 实例,也能保持对话独立。
*
* @return CommandLineRunner
*/
@Bean
public CommandLineRunner runChatClientWithSpecificConversationId() {
return args -> {
System.out.println("--- ChatClient 与特定 ConversationId 结合 ---");
String userId1 = "user-alice-session-001"; // 模拟第一个用户的会话ID
ChatClient aliceChatClient = chatClientBuilder
.chatMemory(chatMemory) // 使用同一个 ChatMemory 实例
.conversationId(userId1) // 指定会话ID
.build();
System.out.println("Alice (会话ID: " + userId1 + "): 中国的首都是哪里?");
aliceChatClient.prompt()
.user("中国e的首都是哪里?")
.call()
.content(); // 发送问题,会话历史存入 'user-alice-session-001'
System.out.println("Alice (会话ID: " + userId1 + "): 它的简称是什么?");
String responseAlice = aliceChatClient.prompt()
.user("它的简称是什么?")
.call()
.content();
System.out.println("大模型响应 (Alice): " + responseAlice); // 大模型能识别“它”指代的是北京
System.out.println("\n--- 切换到另一个用户的会话 ---");
String userId2 = "user-bob-session-002"; // 模拟第二个用户的会话ID
ChatClient bobChatClient = chatClientBuilder
.chatMemory(chatMemory) // 同样使用同一个 ChatMemory 实例
.conversationId(userId2) // 指定另一个会话ID
.build();
System.out.println("Bob (会话ID: " + userId2 + "): 世界上最高的山峰是哪座?");
bobChatClient.prompt()
.user("世界上最高的山峰是哪座?")
.call()
.content();
System.out.println("Bob (会话ID: " + userId2 + "): 它的海拔是多少?");
String responseBob = bobChatClient.prompt()
.user("它的海拔是多少?")
.call()
.content();
System.out.println("大模型响应 (Bob): " + responseBob); // 大模型能识别“它”指代的是珠穆朗玛峰
System.out.println("\n------------------------------------------------\n");
};
}
}
运行项目,你会看到两个独立的对话过程,即使是同一个 ChatMemory
实例,由于 conversationId
不同,大模型也能正确地记住各自的上下文。
3. 查看对话历史 (通过注入 ChatMemory
实例)
如果你想在代码中直接查看某个 conversationId
下的对话历史,可以注入 ChatMemory
Bean 并调用其内部方法。请注意:ChatMemory
接口本身在最新版本中主要用于内部集成。如果你需要获取特定ID的历史,可能需要对 InMemoryChatMemory
进行向下转型,或者自定义一个 ChatMemory
实现来提供更方便的查询方法。
下面我们提供一个用于演示如何查看历史的示例,但请记住,直接操作 InMemoryChatMemory
的内部结构可能不是最推荐的公共API用法,更推荐在业务逻辑层封装一个服务来管理和查询记忆。
// ... (保留SpringAiOllamaDemoApplication的main方法和所有之前的Beans)
package com.example.springaiollamademodemo; // 请替换为你的实际项目包名
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemory; // 导入 InMemoryChatMemory
import org.springframework.ai.chat.messages.Message; // 导入 Message 类
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import java.util.List;
import java.util.Map; // 导入 Map
@SpringBootApplication
public class SpringAiOllamaDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAiOllamaDemoApplication.class, args);
}
// ... (保留所有之前的 ChatClient 和 CommandLineRunner Beans)
@Autowired
private ChatMemory chatMemory; // 注入我们定义的 ChatMemory 实例
/**
* 示例6: 查看对话历史
* 演示如何从 ChatMemory 中获取特定会话的对话历史。
* 注意:这里为了演示直接获取 InMemoryChatMemory 的内部结构,实际生产中推荐封装为服务。
*
* @return CommandLineRunner
*/
@Bean
public CommandLineRunner viewChatHistoryFixed() { // 修改方法名以避免冲突
return args -> {
System.out.println("--- 查看对话历史 (最新修复版) ---");
// 我们尝试查询示例5中Alice使用的固定会话ID
String userIdToRetrieve = "fixed-alice-session-001";
System.out.println("--- 尝试查看会话ID: " + userIdToRetrieve + " 的对话历史 ---");
// 直接通过 ChatMemory 接口的 get(String conversationId) 方法获取消息列表
List<Message> messagesForUser = chatMemory.get(userIdToRetrieve);
if (messagesForUser != null && !messagesForUser.isEmpty()) {
System.out.println("会话ID: " + userIdToRetrieve + " 的对话历史:");
for (Message message : messagesForUser) {
// 打印消息类型(如 USER, AI, SYSTEM)和内容
System.out.println(" " + message.getMessageType().name() + ": " + message.getContent());
}
} else {
System.out.println("会话ID: " + userIdToRetrieve + " 没有找到对话历史或历史为空。");
System.out.println("请确保在运行此示例之前,`runChatClientWithSpecificConversationIdFixed` 方法已经运行,并且向此会话ID写入了消息。");
}
System.out.println("\n------------------------------------------------\n");
};
}
运行项目,你将看到指定会话ID的对话历史被打印出来。
七、实战案例:构建多轮问答机器人
结合以上所有组件,我们可以轻松构建一个能够进行多轮对话的问答机器人。
在 SpringAiOllamaDemoApplication.java
中,继续添加最后一个 @Bean
方法:
// ... (保留SpringAiOllamaDemoApplication的main方法和所有之前的Beans)
package com.example.springaiollamademodemo; // 请替换为你的实际项目包名
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.beans.factory.annotation.Qualifier; // 导入Qualifier
@SpringBootApplication
public class SpringAiOllamaDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAiOllamaDemoApplication.class, args);
}
// ... (保留所有之前的 ChatClient 和 CommandLineRunner Beans)
/**
* 示例7: 实战案例:构建多轮问答机器人
* 这是一个完整的例子,展示了如何利用带记忆的 ChatClient 进行连续的、有上下文的对话。
*
* @param chatClientWithMemory 带有记忆功能的 ChatClient
* @return CommandLineRunner
*/
@Bean
public CommandLineRunner runMultiTurnBot(@Qualifier("chatClientWithMemory") ChatClient chatClientWithMemory) {
return args -> {
System.out.println("--- 实战案例:构建多轮问答机器人 ---");
System.out.println("机器人: 你好,我是基于Qwen的问答机器人,有什么可以帮助你的?");
// 第一次对话
String userQuestion1 = "最近有什么好看的科幻电影吗?";
System.out.println("用户: " + userQuestion1);
String botResponse1 = chatClientWithMemory.prompt()
.user(userQuestion1)
.call()
.content();
System.out.println("机器人: " + botResponse1);
// 第二次对话,利用上下文
String userQuestion2 = "那其中有没有关于时间旅行的?";
System.out.println("用户: " + userQuestion2);
String botResponse2 = chatClientWithMemory.prompt()
.user(userQuestion2)
.call()
.content();
System.out.println("机器人: " + botResponse2);
// 第三次对话,继续利用上下文
String userQuestion3 = "它的导演是谁?";
System.out.println("用户: " + userQuestion3);
String botResponse3 = chatClientWithMemory.prompt()
.user(userQuestion3)
.call()
.content();
System.out.println("机器人: " + botResponse3);
System.out.println("\n------------------------------------------------\n");
};
}
}
再次运行 SpringAiOllamaDemoApplication
,你将看到一个完整的、多轮的对话过程,大模型能够基于之前的聊天内容,给出连贯且符合上下文的回答。
八、总结
组件 | 作用 | 场景 |
---|---|---|
ChatClient | 与 LLM 模型对话的统一接口 | 任何交互类任务,发送请求并处理响应 |
PromptTemplate | 组织 Prompt 模板与变量填充 | 多语言翻译、自动回复、Agent 构建,动态生成Prompt |
Memory | 保存上下文历史,提供会话记忆 | 多轮对话、RAG、智能 Agent,维持对话连贯性 |
恭喜你!通过本篇教程,你不仅学会了 Spring AI 的核心组件,还成功在本地搭建了 Ollama 和 Qwen 模型,并用它们运行了你的第一个 AI 应用。这为你探索人工智能的世界打下了坚实的基础。
完整项目截图:
资料
- Spring AI 官方参考文档: https://docs.spring.io/spring-ai/reference/getting-started.html
- Spring AI GitHub 仓库: https://github.com/spring-projects/spring-ai
- Ollama 官方网站: https://ollama.com/
- Spring Initializr: https://start.spring.io/
参考
《Ollama 深度使用指南:在本地玩转大型语言模型》
本文代码:https://gitcode.com/superfreeman/spring-ai-demo
系列教程:《Java驱动AI革命:Spring AI八篇进阶指南——从架构基础到企业级智能系统实战》