当前位置: 首页 > news >正文

Spring AI 基本组件详解 —— ChatClient、Prompt、Memory

一、前言

本篇教程是《Java驱动AI革命:Spring AI八篇进阶指南——从架构基础到企业级智能系统实战》系列的第二篇,将手把手教你如何使用 Spring AI 的核心三剑客:ChatClientPromptTemplateMemory。它们是构建任何一个人工智能聊天应用的基础。

最重要的是,本篇博文将完全抛开网上各种教程对国外服务的依赖,全程使用免费、开源的本地大模型运行平台 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 键,输入 cmdpowershell,然后回车。
    • macOS/Linux: 打开“终端”应用程序。
  • 下载模型: 在命令行中输入以下命令并回车。这里我们以下载 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)
  • 添加依赖 (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方法*/@Beanpublic 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");};}
}

如何运行:

  1. 确保你的 Ollama 服务和 qwen:7b 模型正在运行。
  2. 在你的IDE中,找到 SpringAiOllamaDemoApplication 类,右键点击并选择 “Run ‘SpringAiOllamaDemoApplication.main()’” (或类似的选项)。
  3. 程序启动后,你会在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*/@Beanpublic 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*/@Beanpublic 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 实例*/@Beanpublic 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*/@Beanpublic 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 以便我们构建不同配置的ChatClientprivate ChatClient.Builder chatClientBuilder;@Autowired // 注入我们之前定义的 ChatMemory 实例private ChatMemory chatMemory;/*** 示例5: 通过 ConversationId 管理多会话* 为不同的用户或场景设置独立的会话ID,即使使用同一个 ChatMemory 实例,也能保持对话独立。** @return CommandLineRunner*/@Beanpublic CommandLineRunner runChatClientWithSpecificConversationId() {return args -> {System.out.println("--- ChatClient 与特定 ConversationId 结合 ---");String userId1 = "user-alice-session-001"; // 模拟第一个用户的会话IDChatClient 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"; // 模拟第二个用户的会话IDChatClient 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)@Autowiredprivate ChatMemory chatMemory; // 注入我们定义的 ChatMemory 实例/*** 示例6: 查看对话历史* 演示如何从 ChatMemory 中获取特定会话的对话历史。* 注意:这里为了演示直接获取 InMemoryChatMemory 的内部结构,实际生产中推荐封装为服务。** @return CommandLineRunner*/@Beanpublic CommandLineRunner viewChatHistoryFixed() { // 修改方法名以避免冲突return args -> {System.out.println("--- 查看对话历史 (最新修复版) ---");// 我们尝试查询示例5中Alice使用的固定会话IDString 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*/@Beanpublic 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八篇进阶指南——从架构基础到企业级智能系统实战》
在这里插入图片描述

http://www.dtcms.com/a/270006.html

相关文章:

  • 装修水电改造需要注意什么?水电改造有哪些注意事项?
  • C++ 的 copy and swap 惯用法
  • 05每日简报20250708
  • Kafka消息倾斜
  • 机器学习(西瓜书) 第三章 线性模型
  • Java 面向对象三大特性详解:封装、继承与多态,掌握OOP核心思想
  • OSPFv3和v2区别(续)
  • 数字人分身 + 矩阵系统聚合 + 碰一碰发视频:源码搭建 支持 OEM
  • 【网络协议安全】任务14:路由器DHCP_AAA_TELNET配置
  • UE实现路径回放、自动驾驶功能简记
  • 【Python篇】PyCharm 安装与基础配置指南
  • 移动机器人的认知进化:Deepoc大模型重构寻迹本质
  • c语言中的数组I
  • Foundry 依赖库管理实战
  • QML事件处理:鼠标、拖拽与键盘事件
  • HTML5 新特性详解:从语义化到多媒体的全面升级
  • CPP中的List
  • 我的第二份实习,学校附近,但是干前端!
  • 了解 RAC
  • FastAPI通用签名校验模块设计文档
  • 【python基础】python和pycharm的下载与安装
  • 在STM32 FreeRTOS环境中使用mutex和ringbuffer实现多任务的UART同步通信
  • JVM 整体架构详解:线程私有与线程共享内存区域划分
  • 【Android】【input子系统】【Android 焦点窗口问题分析思路】
  • 【linux网络】网络编程全流程详解:从套接字基础到 UDP/TCP 通信实战
  • 【Java安全】RMI基础
  • go go go 出发咯 - go web开发入门系列(二) Gin 框架实战指南
  • WiFi协议学习笔记
  • 点云的无监督语义分割方法
  • 寻找两个正序数组的中位数(C++)