完成聊天 Demo
聊天 Demo 的目标是把前面几天的知识串起来,做出一个能运行、能调用、能说明清楚的最小 AI 聊天项目。
Demo 是什么
Demo,全称是 Demonstration,中文一般翻译为“演示”或“示例项目”。
这里的 ai-chat-demo 不是完整产品。
它只需要证明一条链路可以跑通:
用户输入
-> Spring Boot 接口
-> Spring AI ChatClient
-> 大模型 API
-> 返回回答
结论:
Day07 的重点不是加很多功能,而是把一个最小聊天项目整理到可以复现、可以演示、可以提交。
项目范围
先控制范围。
必须完成:
普通聊天接口
统一请求响应对象
基础异常处理
README
接口文档
Git 提交
可以暂时不做:
登录权限
数据库存储
多轮上下文
前端页面
RAG
Tool Calling
复杂流式输出
范围越清楚,Demo 越容易完成。
建议项目结构
ai-chat-demo
├── pom.xml
├── README.md
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com/example/aichat
│ │ │ ├── AiChatDemoApplication.java
│ │ │ ├── controller
│ │ │ │ └── ChatController.java
│ │ │ ├── dto
│ │ │ │ ├── ChatRequest.java
│ │ │ │ └── ChatResponse.java
│ │ │ └── service
│ │ │ └── AiChatService.java
│ │ └── resources
│ │ └── application.yml
│ └── test
│ └── java
└── docs
└── api.md
结构不要一开始就拆得太复杂。
先保证 Controller、Service、DTO 清楚。
依赖准备
Maven,中文一般翻译为“项目构建和依赖管理工具”。
pom.xml 里至少需要:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
</dependencies>
这里使用 spring-ai-starter-model-openai 是因为阿里云百炼 DashScope 支持 OpenAI 兼容接口。
Spring AI 版本建议通过 BOM 管理。
BOM,全称是 Bill of Materials,中文一般翻译为“物料清单”或“依赖版本清单”。
它的作用是统一依赖版本。
具体版本以当前项目和官方文档为准。
配置文件
application.yml:
server:
port: 8080
spring:
application:
name: ai-chat-demo
ai:
openai:
api-key: ${DASHSCOPE_API_KEY}
base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
chat:
options:
model: qwen3.5-flash
temperature: 0.3
后续示例默认使用通义千问 qwen3.5-flash。
原则:
密钥不进代码
密钥不进 Git
模型名可配置
参数可配置
请求和响应对象
请求:
import jakarta.validation.constraints.NotBlank;
public record ChatRequest(
@NotBlank(message = "message 不能为空")
String message
) {
}
响应:
public record ChatResponse(
String answer
) {
}
如果后面要扩展,可以再加字段:
requestId
model
usage
elapsedMs
学习阶段先保留最小字段。
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 builder) {
this.chatClient = builder
.defaultSystem("你是一个严谨的 Java 后端工程师,回答要简洁。")
.build();
}
public String chat(String message) {
return chatClient.prompt()
.user(message)
.call()
.content();
}
}
这里把默认 system prompt 放在 ChatClient 初始化时。
也可以每次调用时传入:
chatClient.prompt()
.system("你是一个严谨的 Java 后端工程师。")
.user(message)
.call()
.content();
Controller
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/chat")
public class ChatController {
private final AiChatService aiChatService;
public ChatController(AiChatService aiChatService) {
this.aiChatService = aiChatService;
}
@PostMapping
public ChatResponse chat(@Valid @RequestBody ChatRequest request) {
String answer = aiChatService.chat(request.message());
return new ChatResponse(answer);
}
}
接口先保持简单。
如果后面要做前端页面,再补 CORS。
CORS,全称是 Cross-Origin Resource Sharing,中文一般翻译为“跨源资源共享”。
基础异常处理
可以先定义一个统一错误响应:
public record ErrorResponse(
String code,
String message
) {
}
全局异常处理:
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleValidation() {
return new ErrorResponse("BAD_REQUEST", "请求参数不合法");
}
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponse handleException() {
return new ErrorResponse("INTERNAL_ERROR", "服务暂时不可用");
}
}
注意:
不要把 API Key 打到日志里
不要把完整异常堆栈返回给前端
服务端日志保留真实异常
用户侧返回可理解的提示
接口文档
可以先写 docs/api.md。
示例:
# API 文档
## 聊天接口
POST /api/chat
### 请求
```json
{
"message": "什么是 Spring AI?"
}
```
### 响应
```json
{
"answer": "Spring AI 是 Spring 生态中用于接入大模型能力的应用开发框架。"
}
```
### 错误响应
```json
{
"code": "BAD_REQUEST",
"message": "请求参数不合法"
}
```
如果使用接口文档工具,后面可以再接 OpenAPI。
OpenAPI,中文一般翻译为“开放接口规范”。
README 要写什么
README 是别人运行项目的入口。
至少写清楚:
项目说明
技术栈
环境要求
启动方式
环境变量
接口示例
目录结构
常见问题
示例结构:
# ai-chat-demo
## 项目说明
最小 AI 聊天 Demo,基于 Spring Boot 和 Spring AI。
## 环境要求
- JDK 21
- Maven 3.9+
- Spring Boot 3.x
## 环境变量
```bash
export DASHSCOPE_API_KEY=你的阿里云百炼 API Key
```
## 启动
```bash
mvn spring-boot:run
```
## 测试
```bash
curl -X POST http://localhost:8080/api/chat \
-H "Content-Type: application/json" \
-d '{"message":"什么是 Spring AI?"}'
```

提交 Git
Git,中文一般翻译为“分布式版本控制系统”。
先检查不要提交密钥:
git status
.gitignore:
.env
target/
*.log
.idea/
提交:
git add .
git commit -m "feat: add ai chat demo"
提交前检查:
项目能启动
curl 能调用成功
README 能按步骤复现
没有提交 API Key
常见问题
README 为什么重要
Demo 不是只给自己看。
README 能说明项目怎么运行、怎么测试、解决了什么问题。
后面写简历或面试复盘时,也能直接引用。
接口文档要不要很复杂
不需要。
Day07 先写 Markdown 版接口文档即可。
重点是请求、响应、错误格式清楚。
为什么要提交 Git
Git 提交能保留阶段成果。
后面做 RAG、Tool Calling、Agent 时,遇到问题可以回到稳定版本。
练习清单
完成几件事情:
创建 ai-chat-demo 项目
完成 /api/chat 接口
完成 AiChatService
完成 ChatRequest 和 ChatResponse
配置 API Key 环境变量
curl 能调用成功
写 README
写接口文档
提交 Git
小结
Day07 的结论:
聊天 Demo 要做到能启动、能调用、能复现、能提交。
最小交付物:
Spring Boot 项目
聊天接口
README
接口文档
Git 提交
后面所有复杂能力都可以从这个 Demo 往上加。


Comments | 0 条评论