目录
[03-为什么要学习 Spring AI Alibaba](#03-为什么要学习 Spring AI Alibaba)
[04-Java AI 应用开发框架的选择](#04-Java AI 应用开发框架的选择)
[05-Spring AI Alibaba 介绍](#05-Spring AI Alibaba 介绍)
[07-Spring AI Alibaba 小试牛刀_1:项目初始化与配置](#07-Spring AI Alibaba 小试牛刀_1:项目初始化与配置)
[08-Spring AI Alibaba 小试牛刀_2:编写第一个对话接口](#08-Spring AI Alibaba 小试牛刀_2:编写第一个对话接口)
[09-Spring AI Alibaba 小试牛刀_3:使用 ChatModel 进行更细粒度控制](#09-Spring AI Alibaba 小试牛刀_3:使用 ChatModel 进行更细粒度控制)
[11-默认的模型名和 baseUrl](#11-默认的模型名和 baseUrl)
[13-Ollama 的介绍&下载](#13-Ollama 的介绍&下载)
[14-Ollama 的安装](#14-Ollama 的安装)
[15-使用 Ollama 本地部署大模型](#15-使用 Ollama 本地部署大模型)
[16-Java 微服务对接本地大模型](#16-Java 微服务对接本地大模型)
[17-ChatClient 与 ChatModel 的对比](#17-ChatClient 与 ChatModel 的对比)
[18-ChatClient 小试牛刀](#18-ChatClient 小试牛刀)
[24-对话记忆手动实现 1](#24-对话记忆手动实现 1)
[25-对话记忆手动实现 2](#25-对话记忆手动实现 2)
[26-对话记忆手动实现 3](#26-对话记忆手动实现 3)
[34-Milvus Standalone 搭建](#34-Milvus Standalone 搭建)
[35-attu 可视化工具安装](#35-attu 可视化工具安装)
[36-Milvus 核心概念介绍](#36-Milvus 核心概念介绍)
[37-Milvus 代码实操-创建 Collection 和 Schema](#37-Milvus 代码实操-创建 Collection 和 Schema)
[38-Milvus 代码实操-插入数据](#38-Milvus 代码实操-插入数据)
[39-Milvus 代码实操-查询数据&删除数据](#39-Milvus 代码实操-查询数据&删除数据)
[41-RAG 系统全流程架构图](#41-RAG 系统全流程架构图)
摘要部分
本文是一份基于用户提供的课程目录整理的 Spring AI Alibaba 详细学习教程。教程涵盖了从 AI 基础认知、框架选择、Spring AI Alibaba 核心功能(模型调用、流式输出、多模型共存、提示词工程、对话记忆、多模态、向量化与 RAG)到本地模型部署(Ollama)、向量数据库(Milvus)集成以及完整 RAG 系统实战的全流程。文中将穿插关键代码示例、官方文档链接及学习资源渠道,旨在帮助 Java 开发者系统掌握大模型应用开发技能。
01-前置知识&速成课推荐
在学习 Spring AI Alibaba 之前,建议具备以下基础知识:
- Java 基础:熟悉 Java 8+ 特性,了解 Maven/Gradle 项目管理工具。
- Spring Boot:掌握 Spring Boot 基础,能独立创建 Web 应用。
- RESTful API:理解 HTTP 协议及 API 调用方式。
- AI 基础概念:了解大模型、Token、Embedding、RAG 等术语。
速成课推荐:
- 【狂神说】SpringBoot 最新教程(快速上手 Spring Boot)
- 【尚硅谷】大模型入门导论(建立 AI 认知)
- Spring AI 官方文档(核心框架参考)
02-程序员近况&未来趋势
当前,AI 大模型技术正深刻改变软件开发范式。传统 CRUD 开发岗位竞争加剧,而掌握 AI 应用开发能力的"AI 原生程序员"需求激增。未来趋势包括:
- AI 赋能传统软件:为现有系统添加智能问答、内容生成、数据分析能力。
- Agent(智能体)开发:构建能自主理解、规划、执行复杂任务的 AI 应用。
- 垂直领域深挖:金融、医疗、法律、教育等行业的专业化 AI 解决方案。
- 工程化与落地:关注成本、性能、安全、可观测性,让 AI 应用稳定服务于业务。
学习 Spring AI Alibaba 等框架,正是 Java 开发者拥抱这一趋势、提升竞争力的关键一步。
03-为什么要学习 Spring AI Alibaba
Spring AI Alibaba 是阿里巴巴基于 Spring AI 生态提供的增强实现,针对国内开发者进行了深度优化:
- 开箱即用:简化通义千问、DeepSeek 等国内主流大模型的接入配置。
- 符合国情:网络、鉴权、计费方式更贴合国内云服务环境。
- 生态整合:与 Spring Cloud Alibaba、Nacos、Sentinel 等阿里云原生组件无缝集成。
- 降低门槛 :提供高层级的抽象(如
ChatClient),让开发者更关注业务逻辑而非底层通信。
04-Java AI 应用开发框架的选择
Java 生态中主要的 AI 应用开发框架/库对比如下:
| 框架 | 特点 | 适用场景 |
|---|---|---|
| Spring AI | Spring 官方出品,抽象统一,生态强大,学习曲线平缓。 | 企业级应用,需要与 Spring 生态深度集成。 |
| Spring AI Alibaba | 基于 Spring AI,针对国内模型和阿里云优化。 | 国内团队,使用通义千问、DeepSeek 等模型。 |
| LangChain4j | Java 版 LangChain,链式编排能力强,社区活跃。 | 需要复杂 AI 工作流编排(如 RAG、Agent)。 |
| 直接调用 SDK | 使用各模型厂商提供的官方 SDK(如阿里云 DashScope SDK)。 | 简单调用,无需框架抽象,追求极致控制。 |
对于大多数希望快速构建稳定企业级 AI 应用的 Java 团队,Spring AI Alibaba 是平衡效率、稳定性和国内适配性的优选。
05-Spring AI Alibaba 介绍
Spring AI Alibaba 项目地址:GitHub - alibaba/spring-ai-alibaba。
核心特性:
- 多模型支持:通义千问、DeepSeek、智谱 AI、百度文心等。
- 统一抽象 :通过
ChatClient、ChatModel等接口屏蔽模型差异。 - 便捷配置 :通过
application.yml轻松配置 API Key、Base URL、模型参数。 - 流式响应:支持 Server-Sent Events (SSE) 流式输出,提升用户体验。
- 扩展组件:集成提示词模板、对话记忆、向量存储、函数调用等高级功能。
06-大模型的使用
使用大模型的核心步骤:
- 获取 API 密钥:在相应模型平台(如阿里云百炼、DeepSeek 开放平台)注册并获取。
- 明确输入与输出:设计清晰的提示词(Prompt),定义你希望模型完成的任务。
- 调用与解析:通过框架或 SDK 发送请求,并处理返回的响应。
- 评估与优化:根据结果调整提示词或模型参数,以达到最佳效果。
07-Spring AI Alibaba 小试牛刀_1:项目初始化与配置
1. 创建 Spring Boot 项目
使用 Spring Initializr 或 IDE 创建项目,依赖选择:
- Spring Web
- Spring AI Alibaba(需手动添加依赖)
2. 添加依赖(Maven 示例)
xml
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-spring-boot-starter</artifactId>
<version>最新版本</version> <!-- 请查看GitHub获取最新版本 -->
</dependency>
3. 配置 application.yml
yaml
spring:
ai:
alibaba:
chat:
enabled: true
options:
api-key: your-api-key-here # 替换为你的API Key
model: qwen-turbo # 模型名称,例如 qwen-turbo, deepseek-chat
base-url: https://dashscope.aliyuncs.com/compatible-mode/v1 # 通义千问兼容端点
08-Spring AI Alibaba 小试牛刀_2:编写第一个对话接口
创建一个简单的 REST 控制器,使用 ChatClient 进行对话。
java
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@GetMapping("/chat")
public String chat(@RequestParam String message) {
return chatClient.prompt()
.user(message)
.call()
.content();
}
}
启动应用,访问 http://localhost:8080/chat?message=你好,请介绍一下你自己,即可获得模型回复。
09-Spring AI Alibaba 小试牛刀_3:使用 ChatModel 进行更细粒度控制
ChatModel 提供了比 ChatClient 更底层的控制,可以设置系统提示词、调整温度等参数。
java
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class AdvancedChatController {
private final ChatModel chatModel;
public AdvancedChatController(ChatModel chatModel) {
this.chatModel = chatModel;
}
@GetMapping("/chat/advanced")
public String advancedChat() {
SystemMessage systemMessage = new SystemMessage("你是一个专业的Java技术专家,回答要简洁准确。");
UserMessage userMessage = new UserMessage("Spring Boot 的核心优势是什么?");
Prompt prompt = new Prompt(List.of(systemMessage, userMessage));
// 可以在这里设置更多参数,如 temperature, topP 等
// prompt.getOptions().setTemperature(0.7);
return chatModel.call(prompt).getResult().getOutput().getContent();
}
}
10-流式输出
流式输出允许服务器将响应内容分块发送给客户端,实现打字机效果,提升用户体验。Spring AI Alibaba 支持 Server-Sent Events (SSE)。
java
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
public class StreamChatController {
private final ChatClient chatClient;
public StreamChatController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamChat(@RequestParam String message) {
return chatClient.prompt()
.user(message)
.stream()
.content();
}
}
前端可以使用 EventSource 来接收流式响应。
11-默认的模型名和 baseUrl
Spring AI Alibaba 为不同模型提供了默认配置,减少冗余配置。常见模型的默认值:
| 模型提供商 | 默认 spring.ai.alibaba.chat.options.model |
默认 spring.ai.alibaba.chat.options.base-url |
|---|---|---|
| 通义千问 (DashScope) | qwen-turbo |
https://dashscope.aliyuncs.com/compatible-mode/v1 |
| DeepSeek | deepseek-chat |
https://api.deepseek.com |
在 application.yml 中,如果只配置 api-key 和 model,框架会自动使用对应的默认 base-url。
12-多模型共存
在实际项目中,可能需要同时连接多个模型(例如,一个用于创意生成,一个用于代码审查)。Spring AI Alibaba 支持通过定义多个 ChatModel Bean 来实现。
java
import org.springframework.ai.alibaba.chat.AlibabaChatModel;
import org.springframework.ai.alibaba.chat.AlibabaChatOptions;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MultiModelConfig {
@Bean
public AlibabaChatModel qwenChatModel() {
AlibabaChatOptions options = AlibabaChatOptions.builder()
.apiKey("your-qwen-api-key")
.model("qwen-max")
.build();
return new AlibabaChatModel(options);
}
@Bean
public AlibabaChatModel deepSeekChatModel() {
AlibabaChatOptions options = AlibabaChatOptions.builder()
.apiKey("your-deepseek-api-key")
.model("deepseek-chat")
.baseUrl("https://api.deepseek.com")
.build();
return new AlibabaChatModel(options);
}
}
在 Service 中,可以通过 @Qualifier 注入指定的模型 Bean 进行调用。
13-Ollama 的介绍&下载
Ollama 是一个用于在本地运行、管理和部署大型语言模型(LLM)的开源工具。它简化了模型下载、运行和提供 API 的过程。
核心优势:
- 本地运行:数据不出境,隐私和安全有保障。
- 模型丰富:支持 Llama 3、Mistral、Gemma、Qwen 等众多开源模型。
- 易于使用:命令行工具,一键拉取和运行模型。
- 提供 API:提供兼容 OpenAI API 的接口,便于集成。
下载地址 :Ollama 官网,支持 macOS、Linux、Windows。
14-Ollama 的安装
以 macOS 为例:
bash
# 1. 使用 Homebrew 安装(推荐)
brew install ollama
2. 启动 Ollama 服务
ollama serve
3. 打开新的终端窗口,拉取一个模型,例如 Llama 3
ollama pull llama3
4. 运行模型进行对话
ollama run llama3
安装完成后,Ollama 会在本地 http://localhost:11434 提供一个 API 服务。
15-使用 Ollama 本地部署大模型
Ollama 运行后,即可通过其 API 调用本地模型。Spring AI Alibaba 可以配置为连接 Ollama。
配置 application.yml:
yaml
spring:
ai:
alibaba:
chat:
enabled: true
options:
api-key: "ollama" # Ollama不需要真正的API Key,但属性需要存在
model: llama3 # 你本地运行的模型名称
base-url: http://localhost:11434/v1 # Ollama 的 OpenAI 兼容端点
之后,你的 ChatClient 或 ChatModel 就会将请求发送到本地 Ollama 服务。
16-Java 微服务对接本地大模型
将上述配置集成到 Spring Boot 微服务中,即可实现业务系统与本地大模型的对接。关键步骤:
- 在微服务的配置中心(如 Nacos)中管理 Ollama 的
base-url和模型名称。 - 使用 Spring Cloud LoadBalancer(如果有多台模型服务器)或直接配置。
- 在业务 Service 中注入
ChatClient,封装具体的 AI 能力(如智能客服、内容审核、代码生成)。 - 添加熔断、降级、限流(通过 Sentinel)等保障措施,防止模型服务不稳定影响主业务。
这样,你的微服务就具备了本地 AI 能力,且完全可控。
17-ChatClient 与 ChatModel 的对比
在 Spring AI 中,ChatClient 和 ChatModel 是两个核心的对话抽象,但它们的定位和使用方式有明显区别。简单来说,ChatModel 是底层模型调用接口,而 ChatClient 是构建在其之上的高层流式 API,提供了更友好的链式调用体验。
| 特性 | ChatClient | ChatModel |
|---|---|---|
| 定位 | 高层流式 API,面向业务开发,提供链式调用 | 底层模型抽象,直接封装模型调用逻辑 |
| 易用性 | 高,通过 prompt()、user()、call() 链式构建请求 |
较低,需要手动构造 Prompt 和消息列表 |
| 流式输出 | 内置支持,通过 stream() 返回 Flux |
需要额外调用 stream() 方法并处理响应流 |
| 提示词模板 | 内置 PromptTemplate 支持,可快速填充变量 |
需要手动创建 PromptTemplate 并渲染 |
| 对话记忆 | 可通过 ChatMemory 自动管理历史消息 |
需要手动维护消息列表并传入 Prompt |
| 参数控制 | 通过 options() 设置,但封装较深 |
更直接,可精细控制 temperature、topP 等参数 |
| 适用场景 | 快速开发对话接口、智能客服、内容生成等业务功能 | 需要深度定制模型行为、研究模型特性、多模型切换 |
选择建议 :日常业务开发优先使用 ChatClient,它更简洁、更符合 Spring 生态的声明式风格;当需要精细控制模型参数、研究模型行为或构建底层能力时,再使用 ChatModel。
18-ChatClient 小试牛刀
下面通过一个完整的示例,演示 ChatClient 的常用能力,包括基础对话、流式输出、提示词模板和参数设置。
1. 基础对话
java
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ChatClientDemoController {
private final ChatClient chatClient;
public ChatClientDemoController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@GetMapping("/demo/chat")
public String chat(@RequestParam String message) {
return chatClient.prompt()
.user(message)
.call()
.content();
}
}
2. 流式输出
java
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
public class ChatClientStreamController {
private final ChatClient chatClient;
public ChatClientStreamController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@GetMapping(value = "/demo/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream(@RequestParam String message) {
return chatClient.prompt()
.user(message)
.stream()
.content();
}
}
3. 使用提示词模板
java
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
public class ChatClientTemplateController {
private final ChatClient chatClient;
public ChatClientTemplateController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@GetMapping("/demo/template")
public String template(@RequestParam String topic) {
PromptTemplate template = new PromptTemplate("请用三句话介绍{topic},要求通俗易懂。");
return chatClient.prompt(template.create(Map.of("topic", topic)))
.call()
.content();
}
}
4. 设置模型参数
java
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ChatClientOptionsController {
private final ChatClient chatClient;
public ChatClientOptionsController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@GetMapping("/demo/options")
public String options(@RequestParam String message) {
return chatClient.prompt()
.user(message)
.options(org.springframework.ai.chat.client.ChatClient.ChatClientRequestSpec
.defaults()
.getOptions())
.call()
.content();
}
}
以上示例展示了 ChatClient 的核心用法。实际项目中,你可以根据业务需求组合使用这些能力,快速构建功能丰富的 AI 应用。
19-提示词介绍
提示词(Prompt)是与大模型交互的核心。一个精心设计的提示词,能显著提升模型输出的质量和准确性。提示词通常包含以下要素:
- 角色设定:告诉模型它应该扮演什么角色,例如"你是一名资深 Java 架构师"。
- 任务描述:明确说明需要模型完成的任务,例如"请解释 Spring 的依赖注入机制"。
- 上下文信息:提供必要的背景资料,帮助模型理解问题。
- 输出格式:指定期望的输出格式,例如"请用列表形式回答"。
- 约束条件:限制回答的范围、长度或风格。
在 Spring AI 中,提示词通过 Prompt 对象封装,可以包含系统消息、用户消息和模型参数。
20-提示词角色代码展示
在 Spring AI 中,消息分为系统消息(SystemMessage)、用户消息(UserMessage)和助手消息(AssistantMessage)。下面演示如何组合使用这些角色。
java
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class PromptRoleController {
private final ChatModel chatModel;
public PromptRoleController(ChatModel chatModel) {
this.chatModel = chatModel;
}
@GetMapping("/prompt/roles")
public String roles() {
SystemMessage systemMessage = new SystemMessage("你是一位严谨的代码审查专家,擅长发现潜在问题并给出改进建议。");
UserMessage userMessage = new UserMessage("请审查以下代码:\npublic int divide(int a, int b) { return a / b; }");
Prompt prompt = new Prompt(List.of(systemMessage, userMessage));
return chatModel.call(prompt).getResult().getOutput().getContent();
}
}
通过合理设置系统消息,可以引导模型以特定角色和风格回答问题,从而获得更符合预期的输出。
21-提示词模版实操
提示词模板(PromptTemplate)允许你定义带占位符的模板,运行时动态填充变量,避免重复拼接字符串。
java
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
public class PromptTemplateController {
private final ChatModel chatModel;
public PromptTemplateController(ChatModel chatModel) {
this.chatModel = chatModel;
}
@GetMapping("/prompt/template")
public String template(@RequestParam String language, @RequestParam String topic) {
String templateText = "请用{language}写一段关于{topic}的简介,要求不超过100字。";
PromptTemplate template = new PromptTemplate(templateText);
Prompt prompt = template.create(Map.of("language", language, "topic", topic));
return chatModel.call(prompt).getResult().getOutput().getContent();
}
}
模板中的 {language} 和 {topic} 会被 Map 中的值替换。这种方式让提示词更易维护和复用。
22-提示词模版提取文件实操
在实际项目中,提示词模板通常存放在资源文件中,便于统一管理和修改。下面演示如何从 classpath 加载模板文件。
1. 创建模板文件 src/main/resources/prompts/summary.st
text
你是一名专业的内容编辑。请阅读以下文本,并生成一段简洁的摘要。
文本内容:
{content}
要求:
摘要不超过三句话。
保留核心信息。
使用中文回答。
2. 加载并渲染模板
java
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
@RestController
public class PromptFileController {
private final ChatModel chatModel;
public PromptFileController(ChatModel chatModel) {
this.chatModel = chatModel;
}
@GetMapping("/prompt/file")
public String file(@RequestParam String content) throws IOException {
ClassPathResource resource = new ClassPathResource("prompts/summary.st");
String templateText = new String(resource.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
PromptTemplate template = new PromptTemplate(templateText);
Prompt prompt = template.create(Map.of("content", content));
return chatModel.call(prompt).getResult().getOutput().getContent();
}
}
将模板外置到文件后,业务人员可以直接修改提示词而无需改动代码,大大提升了协作效率。
23-对话记忆概念介绍
大模型本身是无状态的,每次调用都是独立的。要实现多轮对话,需要将历史消息一并传给模型。对话记忆(Chat Memory)就是管理这些历史消息的机制。
对话记忆的核心价值:
- 上下文连贯:让模型理解对话的来龙去脉,回答更准确。
- 个性化体验:记住用户偏好,提供更贴心的服务。
- 多轮任务:支持需要多步交互的复杂任务。
在 Spring AI 中,对话记忆可以通过手动管理消息列表,或使用 ChatMemory 自动管理。
24-对话记忆手动实现 1
最基础的对话记忆实现,是在每次请求时手动拼接历史消息。
java
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class ManualMemoryController {
private final ChatModel chatModel;
private final List<org.springframework.ai.chat.messages.Message> history = new ArrayList<>();
public ManualMemoryController(ChatModel chatModel) {
this.chatModel = chatModel;
}
@GetMapping("/memory/manual1")
public String chat(@RequestParam String message) {
history.add(new UserMessage(message));
Prompt prompt = new Prompt(history);
String response = chatModel.call(prompt).getResult().getOutput().getContent();
history.add(new AssistantMessage(response));
return response;
}
}
这种方式简单直接,但存在两个问题:一是历史消息无限增长,可能超出模型上下文窗口;二是并发场景下存在线程安全问题。
25-对话记忆手动实现 2
针对历史消息无限增长的问题,可以限制保留的消息数量,只保留最近 N 轮对话。
java
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
@RestController
public class ManualMemoryController2 {
private final ChatModel chatModel;
private final Deque<org.springframework.ai.chat.messages.Message> history = new ArrayDeque<>();
private static final int MAX_MESSAGES = 10;
public ManualMemoryController2(ChatModel chatModel) {
this.chatModel = chatModel;
}
@GetMapping("/memory/manual2")
public String chat(@RequestParam String message) {
history.addLast(new UserMessage(message));
while (history.size() > MAX_MESSAGES) {
history.removeFirst();
}
Prompt prompt = new Prompt(List.copyOf(history));
String response = chatModel.call(prompt).getResult().getOutput().getContent();
history.addLast(new AssistantMessage(response));
while (history.size() > MAX_MESSAGES) {
history.removeFirst();
}
return response;
}
}
通过 ArrayDeque 和最大消息数限制,避免了上下文无限膨胀的问题。
26-对话记忆手动实现 3
更进一步,可以为每个用户维护独立的对话历史,实现多用户隔离。
java
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@RestController
public class ManualMemoryController3 {
private final ChatModel chatModel;
private final Map<String, Deque<org.springframework.ai.chat.messages.Message>> sessions = new ConcurrentHashMap<>();
private static final int MAX_MESSAGES = 10;
public ManualMemoryController3(ChatModel chatModel) {
this.chatModel = chatModel;
}
@GetMapping("/memory/manual3")
public String chat(@RequestParam String sessionId, @RequestParam String message) {
Deque<org.springframework.ai.chat.messages.Message> history = sessions.computeIfAbsent(sessionId, k -> new ArrayDeque<>());
history.addLast(new UserMessage(message));
while (history.size() > MAX_MESSAGES) {
history.removeFirst();
}
Prompt prompt = new Prompt(List.copyOf(history));
String response = chatModel.call(prompt).getResult().getOutput().getContent();
history.addLast(new AssistantMessage(response));
while (history.size() > MAX_MESSAGES) {
history.removeFirst();
}
return response;
}
}
使用 ConcurrentHashMap 按 sessionId 隔离对话历史,支持多用户并发访问。
27-对话记忆自动实现
Spring AI 提供了 ChatMemory 接口和 MessageWindowChatMemory 实现,可以自动管理对话历史,无需手动维护消息列表。
java
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Configuration
public class ChatMemoryConfig {
@Bean
public ChatMemory chatMemory() {
return MessageWindowChatMemory.builder()
.maxMessages(20)
.build();
}
}
@RestController
public class AutoMemoryController {
private final ChatClient chatClient;
public AutoMemoryController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@GetMapping("/memory/auto")
public String chat(@RequestParam String sessionId, @RequestParam String message) {
return chatClient.prompt()
.user(message)
.memory(chatMemory -> chatMemory.id(sessionId))
.call()
.content();
}
}
通过 ChatMemory,框架会自动保存和加载指定会话的历史消息,开发者无需关心消息列表的维护细节。
28-多模态模型概念说明
多模态模型(Multimodal Model)能够同时处理和理解多种类型的数据,例如文本、图像、音频和视频。相比纯文本模型,多模态模型可以:
- 图像理解:识别图片内容、分析图表、提取文字信息。
- 图文生成:根据文本描述生成图像,或为图像生成描述。
- 音频处理:语音识别、语音合成、音频内容分析。
- 视频理解:分析视频内容,提取关键帧和语义信息。
在 Spring AI Alibaba 中,可以通过统一的接口调用支持多模态的模型(如通义千问 VL 系列),实现图文混合输入。
29-多模态模型代码实现
下面演示如何使用 Spring AI Alibaba 调用多模态模型,实现图像理解功能。
java
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.ai.chat.messages.Media;
import org.springframework.core.io.ByteArrayResource;
import java.net.URI;
import java.util.List;
@RestController
public class MultimodalController {
private final ChatModel chatModel;
public MultimodalController(ChatModel chatModel) {
this.chatModel = chatModel;
}
@GetMapping("/multimodal/image")
public String analyzeImage(@RequestParam String imageUrl, @RequestParam String question) throws Exception {
URI uri = URI.create(imageUrl);
byte[] imageBytes = uri.toURL().openStream().readAllBytes();
Media media = new Media(ByteArrayResource.class, imageBytes, "image/png");
UserMessage userMessage = new UserMessage(question, List.of(media));
Prompt prompt = new Prompt(List.of(userMessage));
return chatModel.call(prompt).getResult().getOutput().getContent();
}
}
通过 Media 对象将图片数据附加到用户消息中,模型即可结合图片和文本进行回答。
30-向量和向量化解释
向量(Vector)是数学中表示方向和大小的一组数值。在 AI 领域,向量化(Embedding)是将文本、图像等非结构化数据转换为固定长度的数值数组的过程。
向量化的核心思想:
- 语义编码:语义相近的内容,其向量在空间中的距离也更近。
- 维度固定:无论原始文本多长,向量维度通常是固定的(如 1024 维)。
- 计算相似度:通过余弦相似度等算法,可以量化两个向量的语义相似程度。
向量化是 RAG(检索增强生成)、语义搜索、推荐系统等应用的基础。
31-为什么要向量化
向量化的价值主要体现在以下几个方面:
- 语义搜索:传统关键词搜索无法理解同义词和语义关联,向量搜索可以找到"苹果"和"水果"这类语义相近的内容。
- 知识库检索:在 RAG 系统中,将文档向量化后存入向量数据库,查询时通过向量相似度召回最相关的片段。
- 去重与聚类:通过向量距离判断内容相似度,实现文本去重、主题聚类。
- 推荐系统:将用户和物品向量化,通过向量运算实现个性化推荐。
简单来说,向量化让机器能够"理解"语义,而不仅仅是匹配字符。
32-向量化代码实操
在 Spring AI Alibaba 中,可以通过 EmbeddingModel 接口调用向量化模型。
java
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.embedding.EmbeddingRequest;
import org.springframework.ai.embedding.EmbeddingResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class EmbeddingController {
private final EmbeddingModel embeddingModel;
public EmbeddingController(EmbeddingModel embeddingModel) {
this.embeddingModel = embeddingModel;
}
@GetMapping("/embedding")
public List<Double> embed(@RequestParam String text) {
EmbeddingRequest request = new EmbeddingRequest(List.of(text), org.springframework.ai.embedding.EmbeddingOptions.EMPTY);
EmbeddingResponse response = embeddingModel.call(request);
return response.getResult().getOutput();
}
}
调用后返回一个数值数组,即文本的向量表示。你可以将多个文本向量化后,计算它们之间的余弦相似度来实现语义搜索。
33-向量数据库作用
向量数据库(Vector Database)专门用于存储和检索高维向量数据。它解决了传统数据库无法高效进行向量相似度搜索的问题。
向量数据库的核心能力:
- 高效检索:使用 ANN(近似最近邻)算法,在海量向量中快速找到最相似的记录。
- 元数据过滤:支持按标签、时间等元数据过滤后再进行向量搜索。
- 混合查询:同时支持向量相似度和传统结构化查询。
- 水平扩展:支持分布式部署,应对大规模数据。
常见的向量数据库包括 Milvus、Qdrant、Weaviate、Pinecone 等。在 RAG 系统中,向量数据库是知识库的核心存储组件。
34-Milvus Standalone 搭建
Milvus 是一个开源的分布式向量数据库。下面演示如何通过 Docker 搭建 Milvus Standalone 单机版。
1. 下载 docker-compose 文件
bash
wget https://github.com/milvus-io/milvus/releases/download/v2.4.0/milvus-standalone-docker-compose.yml -O docker-compose.yml
2. 启动 Milvus
bash
docker-compose up -d
3. 验证服务
bash
docker ps
# 确认 milvus-standalone、etcd、minio 三个容器正常运行
Milvus 默认监听 http://localhost:19530,可以通过 attu 可视化工具或 SDK 连接。
35-attu 可视化工具安装
Attu 是 Milvus 的图形化管理界面,可以方便地查看集合、执行查询、管理数据。
1. 通过 Docker 安装 Attu
bash
docker run -p 8000:3000 -e MILVUS_URL=localhost:19530 zilliz/attu:latest
2. 访问控制台
浏览器打开 http://localhost:8000,在连接配置中填写 Milvus 地址 localhost:19530,点击连接即可。
Attu 支持集合管理、向量检索、数据导入导出等功能,是调试 Milvus 的得力工具。
36-Milvus 核心概念介绍
使用 Milvus 前,需要理解以下核心概念:
- Collection(集合):类似于关系数据库中的表,用于存储一组向量及其元数据。
- Schema(模式):定义集合的字段结构,包括主键、向量字段、标量字段等。
- Field(字段):集合中的列,分为向量字段和标量字段。
- Index(索引):为向量字段建立的索引,用于加速相似度检索。
- Partition(分区):将集合按某个字段划分为多个分区,提升查询效率。
- Entity(实体):集合中的一行数据,包含向量和标量字段值。
理解这些概念,是正确使用 Milvus 进行向量检索的前提。
37-Milvus 代码实操-创建 Collection 和 Schema
下面演示如何使用 Java SDK 连接 Milvus 并创建集合。
java
import io.milvus.client.MilvusServiceClient;
import io.milvus.param.ConnectParam;
import io.milvus.param.collection.CreateCollectionParam;
import io.milvus.param.collection.FieldType;
import io.milvus.param.collection.CollectionSchemaParam;
import io.milvus.common.clientenum.ConsistencyLevelEnum;
import io.milvus.param.IndexType;
import io.milvus.param.MetricType;
public class MilvusCreateCollection {
public static void main(String[] args) {
MilvusServiceClient client = new MilvusServiceClient(
ConnectParam.newBuilder()
.withHost("localhost")
.withPort(19530)
.build());
FieldType idField = FieldType.newBuilder()
.withName("id")
.withDataType(io.milvus.grpc.DataType.Int64)
.withPrimaryKey(true)
.withAutoID(false)
.build();
FieldType vectorField = FieldType.newBuilder()
.withName("embedding")
.withDataType(io.milvus.grpc.DataType.FloatVector)
.withDimension(1024)
.build();
FieldType textField = FieldType.newBuilder()
.withName("text")
.withDataType(io.milvus.grpc.DataType.VarChar)
.withMaxLength(65535)
.build();
CollectionSchemaParam schema = CollectionSchemaParam.newBuilder()
.withName("knowledge_base")
.withDescription("知识库集合")
.addFieldType(idField)
.addFieldType(vectorField)
.addFieldType(textField)
.build();
CreateCollectionParam createParam = CreateCollectionParam.newBuilder()
.withCollectionName("knowledge_base")
.withSchema(schema)
.withConsistencyLevel(ConsistencyLevelEnum.STRONG)
.build();
client.createCollection(createParam);
System.out.println("集合创建成功");
client.close();
}
}
创建集合后,还需要为向量字段建立索引,才能进行高效的相似度检索。
38-Milvus 代码实操-插入数据
创建集合后,可以向其中插入向量数据。
java
import io.milvus.client.MilvusServiceClient;
import io.milvus.param.ConnectParam;
import io.milvus.param.dml.InsertParam;
import java.util.Arrays;
import java.util.List;
public class MilvusInsertData {
public static void main(String[] args) {
MilvusServiceClient client = new MilvusServiceClient(
ConnectParam.newBuilder()
.withHost("localhost")
.withPort(19530)
.build());
List&lt;Long&gt; ids = Arrays.asList(1L, 2L);
List&lt;List&lt;Float&gt;&gt; vectors = Arrays.asList(
generateVector(1),
generateVector(2)
);
List&lt;String&gt; texts = Arrays.asList("Spring AI 是 Spring 官方推出的 AI 应用开发框架。", "Milvus 是一个高性能的向量数据库。");
InsertParam insertParam = InsertParam.newBuilder()
.withCollectionName("knowledge_base")
.withFields(Arrays.asList(
new InsertParam.Field("id", ids),
new InsertParam.Field("embedding", vectors),
new InsertParam.Field("text", texts)
))
.build();
client.insert(insertParam);
System.out.println("数据插入成功");
client.close();
}
private static List<Float> generateVector(int seed) {
Float[] vector = new Float[1024];
for (int i = 0; i < 1024; i++) {
vector[i] = (float) Math.sin(seed * 0.01 + i);
}
return Arrays.asList(vector);
}
}
插入数据时,向量维度必须与集合 Schema 中定义的维度一致。
39-Milvus 代码实操-查询数据&删除数据
下面演示如何根据向量相似度查询数据,以及如何删除指定数据。
java
import io.milvus.client.MilvusServiceClient;
import io.milvus.param.ConnectParam;
import io.milvus.param.dml.SearchParam;
import io.milvus.param.dml.DeleteParam;
import io.milvus.response.SearchResultsWrapper;
import java.util.Arrays;
import java.util.List;
public class MilvusQueryDelete {
public static void main(String[] args) {
MilvusServiceClient client = new MilvusServiceClient(
ConnectParam.newBuilder()
.withHost("localhost")
.withPort(19530)
.build());
// 1. 向量相似度查询
List&lt;Float&gt; queryVector = generateVector(1);
SearchParam searchParam = SearchParam.newBuilder()
.withCollectionName("knowledge_base")
.withVectors(Arrays.asList(queryVector))
.withVectorFieldName("embedding")
.withTopK(2)
.withMetricType(io.milvus.param.MetricType.COSINE)
.build();
SearchResultsWrapper results = client.search(searchParam).getData();
System.out.println("查询结果:" + results);
// 2. 删除数据
DeleteParam deleteParam = DeleteParam.newBuilder()
.withCollectionName("knowledge_base")
.withExpr("id in [1]")
.build();
client.delete(deleteParam);
System.out.println("数据删除成功");
client.close();
}
private static List<Float> generateVector(int seed) {
Float[] vector = new Float[1024];
for (int i = 0; i < 1024; i++) {
vector[i] = (float) Math.sin(seed * 0.01 + i);
}
return Arrays.asList(vector);
}
}
通过 SearchParam 指定查询向量和检索参数,即可返回最相似的 TopK 条记录。
40-RAG&知识库的引入
RAG(Retrieval-Augmented Generation,检索增强生成)是一种将信息检索与大模型生成相结合的技术。它解决了大模型的两个核心问题:
- 知识时效性:大模型训练数据有截止日期,无法回答最新信息。
- 领域知识缺失:大模型缺乏企业内部的私有知识。
RAG 的核心流程:
- 文档入库:将企业文档切分、向量化后存入向量数据库。
- 检索召回:用户提问时,将问题向量化,在向量库中检索最相关的文档片段。
- 增强生成:将检索到的片段与用户问题一起交给大模型,生成基于事实的回答。
RAG 让大模型能够"基于你的数据回答问题",是当前企业落地 AI 应用的主流方案。
41-RAG 系统全流程架构图
下面用 Mermaid 展示一个完整的 RAG 系统架构: