spring ai 实战:spring boot + deepseek 实现第一个 ai 聊天接口
我们不再停留在概念层面,而是从 0 创建一个 spring boot 项目,接入 deepseek,并使用 spring ai 提供的 chatclient 实现一个真正可以运行的 ai 聊天接口。
本章最终效果非常简单:
用户 ↓ http 请求 ↓ spring boot ↓ spring ai chatclient ↓ deepseek ↓ ai 返回答案
最终我们可以通过接口:
post /api/ai/chat
发送:
{
"message": "什么是 spring ai?"
}得到:
{
"answer": "spring ai 是 spring 生态面向 ai 应用开发提供的框架和抽象..."
}这也是后续学习 rag、tool calling、agent 等高级能力的基础。
一、本章目标
完成本章之后,我们将拥有一个最基础的 spring ai ai 聊天 服务。
具体完成:
✓ 创建 spring boot 项目 ✓ 添加 spring ai 依赖 ✓ 配置 deepseek ✓ 创建 chatclient ✓ 创建 ai 聊天接口 ✓ 启动项目 ✓ 使用 postman 测试 ✓ 理解 chatclient 调用流程
最终项目结构:
spring-ai-chat-demo
├── pom.xml
└── src
└── main
├── java
│ └── com.example.ai
│ ├── aiapplication.java
│ └── controller
│ └── aichatcontroller.java
│
└── resources
└── application.yml二、先理解一下整体架构
在真正开始写代码之前,我们先搞清楚整个请求是怎么走的。
┌───────────────┐
│ 用户 │
└───────┬───────┘
│
│ http post
↓
┌────────────────────┐
│ spring boot │
│ aichatcontroller │
└────────┬───────────┘
│
↓
┌────────────────────┐
│ spring ai │
│ chatclient │
└────────┬───────────┘
│
↓
┌────────────────────┐
│ spring ai │
│ chatmodel │
└────────┬───────────┘
│
│ api request
↓
┌────────────────────┐
│ deepseek │
│ llm │
└────────┬───────────┘
│
│ ai response
↓
┌────────────────────┐
│ chatclient │
└────────┬───────────┘
↓
┌────────────────────┐
│ spring boot api │
└────────┬───────────┘
↓
用户
这里需要记住一个非常重要的关系:
controller
↓
chatclient
↓
chatmodel
↓
deepseek
后面学习 spring ai 的过程中,这几个概念会反复出现。
三、环境准备
开始之前,需要准备以下环境。
3.1 jdk
建议使用:
jdk 17+
检查 java 版本:
java -version
例如:
java version "17.x.x"
四、创建 spring boot 项目
可以使用 spring initializr 创建项目。
项目基础信息可以设置为:
project: maven language: java spring boot: 当前稳定版本 group: com.example artifact: spring-ai-chat-demo name: spring-ai-chat-demo packaging: jar java: 17
项目创建完成之后,用 intellij idea 打开。
五、添加 spring ai 依赖
spring ai 项目建议通过 spring ai bom 管理版本。
在 pom.xml 中加入对应的 spring ai bom,然后添加 openai 兼容模型相关的 starter。
一个典型配置可以写成:
<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>然后添加:
<dependencies>
<dependency>
<groupid>org.springframework.ai</groupid>
<artifactid>spring-ai-starter-model-openai</artifactid>
</dependency>
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-web</artifactid>
</dependency>
</dependencies>这里需要注意:
spring ai 的版本需要与你使用的 spring boot 版本保持兼容。
不同 spring ai 版本的 starter 名称、配置属性和 api 可能存在差异。
因此实际项目中不要机械复制旧教程里的版本号,应该以你当前使用的 spring ai 版本官方文档为准。
六、为什么使用 openai starter?
这里可能会产生一个疑问:
“我们明明使用的是 deepseek,为什么添加的是 openai starter?”
这是因为很多国产大模型提供了与 openai api 兼容的接口。
也就是说:
spring ai ↓ openai-compatible api ↓ deepseek
因此,我们可以利用 spring ai 的 openai 模型集成能力连接 deepseek。
这也是一个非常实用的设计:
应用层不需要关心底层 api 的大量 http 细节。
七、配置 deepseek
接下来配置 deepseek api。
在:
src/main/resources/application.yml
添加:
spring:
ai:
openai:
api-key: ${deepseek_api_key}
base-url: https://api.deepseek.com
chat:
options:
model: deepseek-chat这里有几个非常重要的配置。
7.1 api-key
api-key: ${deepseek_api_key}不要直接把 api key 写死在代码里面。
不推荐:
api-key: sk-xxxxxxxx
推荐:
api-key: ${deepseek_api_key}
然后通过环境变量提供:
export deepseek_api_key=你的api_key
windows 可以使用对应的环境变量配置方式。
这样做的好处是:
代码 ↓ 不会保存 secret ↓ git 提交 ↓ 不会意外泄露 api key
八、base-url 是什么?
这里:
base-url: https://api.deepseek.com
表示:
spring ai 应该把模型请求发送到哪里。
如果你使用的是 openai,那么对应地址会不同。
如果使用 deepseek,则需要配置 deepseek 的 api 地址。
因此可以理解成:
chatclient ↓ chatmodel ↓ base-url ↓ deepseek api
九、model 是什么?
这里:
model: deepseek-chat
表示:
使用哪个模型处理请求。
模型是 ai 应用非常重要的一个概念。
例如:
deepseek-chat
用于普通聊天场景。
具体可使用哪些模型,需要以 deepseek 当前提供的模型列表和 api 文档为准。
十、创建 spring boot 启动类
创建:
aiapplication.java
代码:
package com.example.ai;
import org.springframework.boot.springapplication;
import org.springframework.boot.autoconfigure.springbootapplication;
@springbootapplication
public class aiapplication {
public static void main(string[] args) {
springapplication.run(aiapplication.class, args);
}
}这是标准的 spring boot 启动类。
启动:
mvn spring-boot:run
如果看到:
started aiapplication
说明 spring boot 已经启动成功。
十一、创建 chatclient
接下来进入 spring ai 最核心的一步。
我们需要创建:
chatclient
可以使用 spring ai 自动配置的 chatclient.builder 创建:
package com.example.ai.controller;
import org.springframework.ai.chat.client.chatclient;
import org.springframework.web.bind.annotation.*;
@restcontroller
@requestmapping("/api/ai")
public class aichatcontroller {
private final chatclient chatclient;
public aichatcontroller(chatclient.builder chatclientbuilder) {
this.chatclient = chatclientbuilder.build();
}
}这里发生了什么?
spring boot 启动时会根据我们配置的模型自动创建相关 ai 组件。
然后 spring ai 提供:
chatclient.builder
我们通过:
chatclientbuilder.build();
得到:
chatclient
于是:
spring boot
↓
自动配置
↓
chatclient.builder
↓
chatclient
十二、实现第一个 ai 聊天接口
接下来添加:
@postmapping("/chat")
public string chat(@requestparam string message) {
return chatclient
.prompt()
.user(message)
.call()
.content();
}完整代码:
package com.example.ai.controller;
import org.springframework.ai.chat.client.chatclient;
import org.springframework.web.bind.annotation.*;
@restcontroller
@requestmapping("/api/ai")
public class aichatcontroller {
private final chatclient chatclient;
public aichatcontroller(chatclient.builder chatclientbuilder) {
this.chatclient = chatclientbuilder.build();
}
@postmapping("/chat")
public string chat(@requestparam string message) {
return chatclient
.prompt()
.user(message)
.call()
.content();
}
}现在我们的第一个 ai 接口就完成了。
十三、这几行代码到底做了什么?
第一次看到:
chatclient
.prompt()
.user(message)
.call()
.content();可能会觉得:
“这么简单?”
没错,spring ai 的目标之一就是把底层复杂度封装起来。
我们拆开来看。
13.1 prompt()
chatclient.prompt()
表示:
创建一次 ai 请求。
可以理解成:
chatclient
↓
创建 prompt 请求
13.2 user()
.user(message)
表示:
设置用户发送给 ai 的消息。
例如:
message = "什么是 spring ai?"
最终形成:
user message
↓
什么是 spring ai?
13.3 call()
.call()
表示:
真正执行模型调用。
前面的:
prompt() user()
主要是在构建请求。
到了:
call()
才真正发起调用。
整体过程:
prompt() ↓ 构建 prompt ↓ user() ↓ 添加 user message ↓ call() ↓ 调用 deepseek
13.4 content()
最后:
.content()
表示:
获取模型返回的文本内容。
所以整个代码可以翻译成人话:
创建一次 ai 请求
↓
告诉 ai 用户的问题
↓
发送请求
↓
获取 ai 返回的文本
十四、启动项目
启动:
mvn spring-boot:run
或者直接使用 intellij idea 启动:
aiapplication ↓ run
默认情况下:
http://localhost:8080
十五、使用 postman 测试
发送:
post http://localhost:8080/api/ai/chat?message=什么是spring%20ai?
如果配置正确,你应该能够得到类似:
spring ai 是 spring 生态面向 ai 应用开发提供的框架和抽象……
这意味着:
spring boot
↓
spring ai
↓
deepseek
↓
ai
已经完整打通。
十六、使用 curl 测试
也可以直接使用:
curl -x post \ "http://localhost:8080/api/ai/chat?message=请介绍一下spring%20ai"
如果调用成功,就会直接看到模型返回结果。
十七、目前的接口有什么问题?
虽然我们的接口已经可以运行,但是它还比较简陋。
现在请求:
post /api/ai/chat?message=你好
返回:
你好!有什么可以帮助你的吗?
这对于 demo 足够了。
但是如果是真正的后端项目,我们一般不会让接口直接返回一个字符串。
更合理的是:
{
"message": "你好",
"answer": "你好!有什么可以帮助你的吗?"
}所以我们可以定义请求对象。
十八、定义 chatrequest
创建:
chatrequest.java
package com.example.ai.dto;
public record chatrequest(
string message
) {
}然后修改 controller:
@postmapping("/chat")
public string chat(@requestbody chatrequest request) {
return chatclient
.prompt()
.user(request.message())
.call()
.content();
}请求:
{
"message": "什么是 spring ai?"
}这样更符合 rest api 的开发习惯。
十九、进一步封装 service
随着项目越来越复杂,我们不建议把 ai 调用逻辑全部放到 controller。
更合理的结构:
controller
↓
service
↓
chatclient
↓
deepseek
创建:
aichatservice.java
package com.example.ai.service;
import org.springframework.ai.chat.client.chatclient;
import org.springframework.stereotype.service;
@service
public class aichatservice {
private final chatclient chatclient;
public aichatservice(chatclient.builder chatclientbuilder) {
this.chatclient = chatclientbuilder.build();
}
public string chat(string message) {
return chatclient
.prompt()
.user(message)
.call()
.content();
}
}controller:
package com.example.ai.controller;
import com.example.ai.dto.chatrequest;
import com.example.ai.service.aichatservice;
import org.springframework.web.bind.annotation.*;
@restcontroller
@requestmapping("/api/ai")
public class aichatcontroller {
private final aichatservice aichatservice;
public aichatcontroller(aichatservice aichatservice) {
this.aichatservice = aichatservice;
}
@postmapping("/chat")
public string chat(@requestbody chatrequest request) {
return aichatservice.chat(request.message());
}
}这样结构就变成:
http ↓ controller ↓ service ↓ chatclient ↓ chatmodel ↓ deepseek
这才是更接近真实项目的写法。
二十、为什么要进行 controller / service 分层?
因为 ai 应用后面会越来越复杂。
现在:
chat(message)
以后可能变成:
chat() ├── prompt ├── memory ├── advisor ├── rag ├── tool calling ├── logging └── exception handling
如果全部写在 controller:
controller ↓ 大量 ai 业务逻辑
代码很容易失控。
所以:
controller ↓ 只负责 http service ↓ 负责业务 spring ai ↓ 负责 ai 能力
职责更加清晰。
二十一、完整项目结构
现在我们的项目可以整理成:
spring-ai-chat-demo
│
├── pom.xml
│
└── src
└── main
├── java
│ └── com.example.ai
│ │
│ ├── aiapplication.java
│ │
│ ├── controller
│ │ └── aichatcontroller.java
│ │
│ ├── service
│ │ └── aichatservice.java
│ │
│ └── dto
│ └── chatrequest.java
│
└── resources
└── application.yml虽然项目很小,但已经有一个基本的分层结构。
二十二、完整请求流程
现在重新看一次整个流程:
用户
│
│ post
↓
┌────────────────────┐
│ aichatcontroller │
└─────────┬──────────┘
│
↓
┌────────────────────┐
│ aichatservice │
└─────────┬──────────┘
│
↓
┌────────────────────┐
│ chatclient │
└─────────┬──────────┘
│
↓
┌────────────────────┐
│ chatmodel │
└─────────┬──────────┘
│
↓
┌────────────────────┐
│ deepseek │
└─────────┬──────────┘
│
↓
ai response
│
↓
用户
这张图非常重要。
后面我们学习 spring ai 的其他能力,本质上都是在这个基础上不断扩展。
二十三、到这里,我们真正学到了什么?
表面上看,这一章只是写了一个:
/api/ai/chat
但实际上我们已经建立了 spring ai 的第一个核心认知:
spring boot
↓
spring ai
↓
chatclient
↓
chatmodel
↓
llm
同时理解了:
prompt() user() call() content()
分别承担什么作用。
二十四、为什么不直接使用 restclient?
这是一个非常值得思考的问题。
当然可以。
你完全可以自己:
restclient ↓ http ↓ deepseek api
但是随着项目变复杂,你还需要自己处理:
模型适配 消息结构 streaming tool calling structured output embedding vector store rag
spring ai 的价值就在这里。
它不是让:
“调用一个 api”
变得多么神奇。
而是希望:
把 ai 应用开发过程中大量重复的基础能力进行抽象,让开发者可以把更多精力放在业务本身。
二十五、常见问题
问题 1:启动时报 api key 错误
例如:
401 unauthorized
首先检查:
echo $deepseek_api_key
确认环境变量是否存在。
然后检查:
spring:
ai:
openai:
api-key: ${deepseek_api_key}是否配置正确。
问题 2:模型不存在
如果出现类似:
model not found
检查:
model: deepseek-chat
是否仍然是当前 deepseek api 支持的模型名称。
模型名称会随着模型服务商的产品变化,因此应以当前官方模型列表为准。
问题 3:为什么 deepseek 使用 openai starter?
因为 deepseek 提供了 openai 兼容接口。
因此 spring ai 可以使用 openai 模型集成能力连接 deepseek。
需要注意:
openai starter 并不意味着你必须使用 openai 模型。
这里主要使用的是兼容的 api 接口规范。
问题 4:为什么不把 api key 写在 application.yml?
不推荐:
api-key: sk-xxxx
因为:
git ↓ 提交代码 ↓ api key 泄露
更推荐:
api-key: ${deepseek_api_key}
使用环境变量或者安全的 secret 管理系统。
二十六、本章总结
这一章我们从 0 开始创建了一个 spring ai 项目,并成功完成:
spring boot
↓
spring ai
↓
chatclient
↓
deepseek
↓
ai response
我们还学习了:
chatclient chatmodel prompt user message call() content()
并最终实现了:
post /api/ai/chat
可以把用户问题发送给 deepseek,并获得 ai 返回结果。
二十七、但是,现在的 ai 还非常“笨”
目前我们只是:
用户 ↓ 问题 ↓ deepseek ↓ 答案
它还不知道:
你的身份 你的业务 你的公司 你的知识库 你的数据库 你的系统能力
例如用户问:
“我们公司的退款规则是什么?”
现在的 ai 很可能无法回答。
那么怎么办?
我们需要学习:
prompt
通过 system prompt,我们可以告诉 ai:
你是谁? 你的职责是什么? 你应该如何回答? 你不能回答什么? 回答需要遵循什么格式?
再进一步,我们还可以让 ai:
读取公司文档 ↓ 查询数据库 ↓ 调用 java 方法 ↓ 检索知识库
这时候 ai 才真正开始进入企业业务系统。
以上就是springboot+deepseek实现第一个ai聊天接口的详细内容,更多关于springboot deepseek ai聊天接口的资料请关注代码网其它相关文章!
发表评论