Java大模型应用开发框架——Spring AI
Spring AI官方文档:docs.spring.io/spring-ai/reference
Hyplus目录
1 模型部署
大模型原理简介详见A Survey of Large Language Models
各种部署方式的优缺点对比:
开放大模型API | 云平台部署私有模型 | 本地部署私有模型 | |
---|---|---|---|
优点 | 没有部署和维护成本,按调用收费 简单 |
前期投入成本低 部署和维护方便 网络延迟较低 |
数据完全自主掌控,安全性高 不依赖外部环境 虽然短期投入大,但长期来看成本会更低 |
缺点 | 依赖平台方,稳定性差 长期使用成本较高 数据存储在第三方,有隐私和安全问题 |
数据存储在第三方,有隐私和安全问题 长期使用成本高 |
初期部署成本高 维护困难 |
1.1 开放大模型服务
通常发布大模型的官方、大多数的云平台都会提供开放的、公共的大模型服务。国内常见的大模型服务云平台如下:
云平台 | 公司 | 地址 |
---|---|---|
阿里百炼 | 阿里巴巴 | bailian.console.aliyun.com |
腾讯TI平台 | 腾讯 | cloud.tencent.com/product/ti |
千帆平台 | 百度 | console.bce.baidu.com/qianfan/overview |
SiliconCloud | 硅基流动 | siliconflow.cn/zh-cn/siliconcloud |
火山方舟-火山引擎 | 字节跳动 | www.volcengine.com/product/ark |
GPT-GOD | - | |
... | ... | ... |
KINA | Hyperplasma | ??? |
一般流程:
- 注册账号
- 申请
API_KEY
- 体验模型
1.2 本地部署
使用Ollama(官网:ollama.com)部署和运行大模型(相当于Docker)。
一般流程:
- 下载、安装Ollama,配置环境变量
- 搜索模型
- 运行模型
常用命令:
ollama serve # Start ollama
ollama create # Create a model from a Modelfile
ollama show # Show information for a model
ollama run # Run a model
ollama stop # Stop a running model
ollama pull # Pull a model from a registry
ollama push # Push a model to a registry
ollama list # List models
ollama ps # List running models
ollama cp # Copy a model
ollama rm # Remove a model
ollama help # Help about any command
2 大模型接口规范
大模型开发通过访问模型对外暴露的API接口,实现与大模型的交互。目前大多数大模型都遵循OpenAI的接口规范,均为基于Http协议的接口,故请求路径、参数、返回值信息都类似,具体细微差别请查阅各自的官方API文档。以DeepSeek官方给出的文档为例(Python):
# Please install OpenAI SDK first: `pip3 install openai`
from openai import OpenAI
# 1.初始化OpenAI客户端,要指定两个参数:api_key、base_url
client = OpenAI(api_key="<DeepSeek API Key>", base_url="https://api.deepseek.com")
# 2.发送http请求到大模型,参数比较多
response = client.chat.completions.create(
model="deepseek-chat", # 2.1.选择要访问的模型
messages=[ # 2.2.发送给大模型的消息
{"role": "system", "content": "You are a helpful assistant"},
{"role": "user", "content": "Hello"},
],
stream=False # 2.3.是否以流式返回结果
)
print(response.choices[0].message.content)
接口说明:
- 请求方式:通常为POST,因为要传递JSON风格的参数
- 请求路径:视具体平台而定
- DeepSeek官方平台:
https://api.deepseek.com
- 阿里云百炼平台:
https://dashscope.aliyuncs.com/compatible-mode/v1
- 本地ollama部署的模型:
http://localhost:11434
- GPT-GOD:
https://api.gptgod.online/
(或https://api.gptgod.online/v1
或https://api.gptgod.online/v1/chat/completions
) - ……
- DeepSeek官方平台:
- 安全校验:开放平台都需要提供
API_KEY
来校验权限,本地ollama则不需要 - 请求参数:
model
:要访问的模型名称messages
:发送给大模型的消息(提示词/指令,Prompt),一个消息数组(以此实现会话记忆),包含两个属性:role
:消息对应的角色,有如下三种:system
:优先于user指令之前的指令,即给大模型设定角色和任务背景的系统指令user
:终端用户输入的指令assistant
:由大模型生成的消息,可能是上一轮对话生成的结果
content
:消息内容
stream
:true
表示响应结果流式返回;false
表示响应结果一次性返回,但需要等待temperature
:取值范围[0, 2)
,代表大模型生成结果的随机性,越小随机性越低。(DeepSeek-R1不支持)- ……
3 大模型应用
大模型应用是基于大模型的推理、分析、生成能力,结合传统编程能力,开发出的各种应用。
3.1 传统应用与AI大模型
传统应用和AI大模型应用特点对比:
传统应用 | AI大模型 | |
---|---|---|
核心特点 | 基于明确规则的逻辑设计,确定性执行,可预测结果 | 基于数据驱动的概率推理,擅长处理模糊性和不确定性 |
擅长领域 | 结构化计算(银行转账系统、Excel公式) 确定性任务(排序算法) 高性能低延迟场景(操作系统内核调度、数据库索引查询) 规则明确的流程控制(红绿灯信号切换系统) |
自然语言处理(写作、翻译、客服机器人理解用户意图) 非结构化数据分析(医学影像识别、TTS) 创造性内容生成(图像生成、AI作曲) 复杂模式预测(股票市场趋势预测) |
不擅长领域 | 非结构化数据处理(无法直接理解用户自然语言提问) 模糊推理与模式识别(判断一张图片是"猫"还是"狗") 动态适应性(用户需求频繁变化,例如电商促销规则每天调整) |
精确计算 确定性逻辑验证(验证身份证号码是否符合规则) 低资源消耗场景(嵌入式设备) 因果推理("公鸡打鸣导致日出") |
总结 | 适用于确定性、规则化、高性能,适合数学计算、流程控制等场景 | 适用于概率性、非结构化、泛化性,适合语言、图像、创造性任务 |
两者恰好互补,强强联合能够解决以前难以实现的一些问题:
- 混合系统(Hybrid AI):用传统程序处理结构化逻辑(如支付校验),AI处理非结构化任务(如用户意图识别)。
- 示例:智能客服中,AI理解用户问题,传统代码调用数据库返回结果。
- 增强可解释性:结合规则引擎约束AI输出(如法律文档生成时强制符合条款格式)。
- 低代码/无代码平台:通过AI自动生成部分代码,降低传统开发门槛。
综上所述,大模型应用就是整合传统程序和大模型的能力和优势来开发的一种应用。
3.2 大模型应用开发技术架构
基于大模型开发应用有多种方式,大模型应用开发的技术架构主要有四种:
从开发成本由低到高来看,四种方案排序为:Prompt < Function Calling < RAG < Fine-tuning
在选择技术时通常遵循"在达成目标效果的前提下,尽量降低开发成本"这一首要原则,然后可参考以下流程来思考:
3.2.1 纯Prompt模式
不断雕琢提示词,使大模型能给出最理想的答案的过程称为提示词工程(Prompt Engineering)。很多简单的AI应用,仅仅靠一段足够好的提示词即可实现,这就是纯Prompt模式。
特点:利用大模型推理能力完成应用的核心功能。
应用场景:文本摘要分析、舆情分析、坐席检查、AI对话……
3.2.2 Function Calling
特点:将应用端业务能力与AI大模型推理能力相结合,简化复杂业务开发。
可分为以下步骤:
- 将传统应用中的部分功能封装成若干函数(Function)。
- 构建智能体(Agent):在提示词中描述用户的需求,并且描述清楚每个函数的作用,要求AI理解用户意图,判断何时需要调用(Call)哪个函数,并将任务拆解为多个步骤。
- 当AI执行到某一步需要调用某个函数时,返回要调用的函数名称、函数需要的参数信息。
- 传统应用接收到这些数据后调用本地函数,再将函数执行结果封装为提示词,再次发送给AI。
- 以此类推,逐步执行,直至达成最终结果。
应用场景:旅行指南、数据提取、数据聚合分析、课程顾问……
3.2.3 RAG
检索增强生成(Retrieval-Augmented Generation,RAG)为把信息检索技术和大模型结合的方案。
大模型从知识角度存在很多限制:
- 时效性差:大模型训练比较耗时,其训练数据都是旧数据,无法实时更新
- 缺少专业领域知识:大模型训练数据都是采集的通用数据,缺少专业数据
RAG利用信息检索技术来拓展大模型的知识库,解决大模型的知识限制,总体上可分为两个模块:
- 检索模块(Retrieval):负责存储和检索拓展的知识库
- 文本拆分:将文本按照某种规则拆分为很多片段
- 文本嵌入(Embedding):根据文本片段内容,将文本片段归类存储
- 文本检索:根据用户提问的问题,找出最相关的文本片段
- 生成模块(Generation):
- 组合提示词:将检索到的片段与用户提问组织成提示词,形成更丰富的上下文信息
- 生成结果:调用生成式模型,根据提示词生成更准确的回答
离线步骤:文档加载 → 文档切分 → 文档编码 → 写入知识库
在线步骤:获得用户问题 → 检索知识库中相关知识片段 → 将检索结果和用户问题填入Prompt模板 → 用最终获得的Prompt调用LLM → 由LLM生成回复
应用场景:个人知识库、AI客服助手……
3.2.4 Fine-tuning
Fine-tuning(模型微调)是在预训练大模型的基础上,通过企业自己的数据做进一步的训练,使大模型的回答更符合自己企业的业务需求。该过程通常需要在模型的参数上进行细微的修改,以达到最佳的性能表现。
在进行微调时,通常会保留模型的大部分结构和参数,只对其中的一小部分进行调整。这样做的好处是可以利用预训练模型已经学习到的知识,同时减少了训练时间和计算资源的消耗。微调的过程包括以下几个关键步骤:
- 选择合适的预训练模型:根据任务的需求,选择一个已经在大量数据上进行过预训练的模型。
- 准备特定领域的数据集:收集和准备与任务相关的数据集,这些数据将用于微调模型。
- 设置超参数:调整学习率、批次大小、训练轮次等超参数,以确保模型能够有效学习新任务的特征。
- 训练和优化:使用特定任务的数据对模型进行训练,通过前向传播、损失计算、反向传播和权重更新等步骤,不断优化模型的性能。
模型微调虽然更加灵活、强大,但也存在一些问题:
- 需要大量的计算资源
- 调参复杂性高
- 过拟合风险
故Fine-tuning成本较高,难度较大,并不适合大多数企业。
4 Spring AI
目前Java平台常用的大模型框架有Spring AI和LangChain4j,注意Spring AI要求JDK版本至少为17(LangChain4j则为JDK 8),但LangChain4j暂不支持DeepSeek,故请按实际要求选用。
本文通过4个大模型应用案例介绍Spring AI用法。
4.1 对话机器人
4.1.1 快速入门
初始化Spring AI项目的一般步骤:
- 引入和管理依赖和起步依赖(此处为Ollama平台,可换为其他平台,例如想使用OpenAI则将
<artifactId>
中ollama
改为openai
):
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Ollama -->
<dependency>
<groupId>org.springframework.ai</groupId>
<!-- <artifactId>spring-ai-ollama-spring-boot-starter</artifactId> -->
<artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>
<!-- OpenAI -->
<dependency>
<groupId>org.springframework.ai</groupId>
<!-- <artifactId>spring-ai-openai-spring-boot-starter</artifactId> -->
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
可在新建项目时直接选定以下依赖:Web(Spring Web)、SQL(MySQL Driver)、AI(Ollama、OpenAI)。注意用此种方式引入的Lombok存在bug,请手动在
pom.xml
中引入。
- 配置模型(应配置准确的BaseUrl和ApiKey)
# Ollama
spring:
ai:
ollama:
base-url: https://api.gptgod.online/
chat:
model: gpt-4o-mini
# OpenAI
spring:
ai:
openai:
base-url: https://api.gptgod.online/
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-4o-mini
temperature: 0.8
- 配置客户端:自动装配ChatClient
/* CommonConfiguration.java */
@Configuration
public class CommonConfiguration {
@Bean // 根据所引依赖自动装配模型
public ChatClient chatClient(OpenAiChatModel model) {
return ChatClient.builder(model)
.defaultSystem("你是Hyperplasma的热心可爱的AI助手,名为KINA,请你以友好、热情的语气回答用户的问题。")
.build();
}
}
之后可通过定义Controller来接收前端消息,调用模型。
/* ChatController.java */
// 发送消息
@RequestMapping("/chat")
public String chat(String prompt) {
return chatClient.prompt()
.user(prompt)
.call()
.content();
}
/* ChatController.java */
// 使用stream()方法流式调用,返回结果为Flux(响应式流式技术)
// 需指定编码
@RequestMapping(value = "/chat", produces = "text/html;charset=utf-8")
public Flux<String> chat(String prompt) {
return chatClient.prompt()
.user(prompt)
.stream()
.content();
}
当与前端对接时,可能会发生跨域错误(端口不同),可在MVC配置类中进行相关的配置:
/* MvcConfiguration.java */
@Configuration
public class MvcConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*") // 允许所有来源
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的HTTP方法
.allowedHeaders("*"); // 允许所有请求头
}
}
4.1.2 会话日志
Spring AI利用AOP原理提供了AI会话时的拦截、增强等功能,即Advisor。该接口有3个实现类,分别为SimpleLoggerAdvisor、MessageChatMemoryAdvisor、QuestionAnswerAdvisor。
直接在配置类中装配模型时配置默认Advisors即可。.defaultAdvisors(new SimpleLoggerAdvisor())
可实现会话日志的输出,此外还需在配置文件中添加指定包日志级别的配置:
logging:
level:
# 给指定包添加日志输出
org.springframework.ai.chat.client.advisor: debug
top.hyperplasma.hyprojai: debug
此后进行调用时控制台即会显示debug日志。
4.1.3 会话记忆
Spring AI提供了标准的会话记忆接口ChatMemory,部分源码如下:
public interface ChatMemory {
void add(String conversationId, List<Message> messages);
List<Message> get(String conversationId);
void clear(String conversationId);
}
可通过编写其实现类来实现各种数据库(MySQL、MongoDB等)的持久化操作。以下编写了基于内存记忆的实现类InMemoryChatMemory:
public class InMemoryChatMemory implements ChatMemory {
private final Map<String, List<Message>> memory = new HashMap<>();
@Override
public void add(String conversationId, List<Message> messages) {
memory.computeIfAbsent(conversationId, k -> new ArrayList<>()).addAll(messages);
}
@Override
public List<Message> get(String conversationId) {
return memory.getOrDefault(conversationId, new ArrayList<>());
}
@Override
public void clear(String conversationId) {
memory.remove(conversationId);
}
}
可直接在配置类中自动装配实现类,并在配置默认advisors是设置:
/* CommonConfiguration.java */
@Bean
public ChatMemory chatMemory() {
return new InMemoryChatMemory();
}