目录
- 一、项目介绍
-
- [1.1 背景&⽬标](#1.1 背景&⽬标)
- [1.2 核⼼功能](#1.2 核⼼功能)
- 二、环境搭建
- 三、简单对话
-
- [3.1 接口定义](#3.1 接口定义)
- [3.2 实现](#3.2 实现)
- 四、对话记忆
-
- [4.1 Chat Memory](#4.1 Chat Memory)
- [4.2 修改接⼝](#4.2 修改接⼝)
- [4.2 实现](#4.2 实现)
- 四、对话历史
-
- [4.1 存储会话](#4.1 存储会话)
- [4.2 获取会话列表](#4.2 获取会话列表)
- [4.3 获取会话记录](#4.3 获取会话记录)

一、项目介绍
1.1 背景&⽬标
模仿kimi简单实现⼀个智能聊天机器⼈,提升⽤⼾交互体验.
产品⽬标:
- 提供流畅,⾃然的对话体验
- ⽀持多轮对话及上下⽂理解
- 能够回答常⻅问题
- 记录和管理⽤⼾历史对话
1.2 核⼼功能
-
对话
-
- ⽀持⽤⼾与机器⼈进⾏⽂本对话
-
- 实时响应⽤⼾输⼊,输出⾃然语⾔回复
-
多轮对话
-
- 能够理解和处理多轮对话,保持上下⽂连续性
-
- ⽀持基于上下⽂的智能应答
-
历史记录
-
- ⾃动保存⽤⼾与机器⼈的对话历史
-
- ⽀持⽤⼾查看历史对话内
二、环境搭建
创建项目并初始化
创建子项⽬:spring-chat-bot
pom:
xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>1.0.0-M6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-M6</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
启动类
java
package com.spring.chat.bot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringChatBotApplication {
public static void main(String[] args) {
SpringApplication.run(SpringChatBotApplication.class, args);
}
}
添加配置⽂件
yml
spring:
application:
name: spring-chat-bot
ai:
openai:
api-key: sk-key
base-url: https://api.deepseek.com
chat:
options:
model: deepseek-chat
temperature: 0.7
logging:
level:
org.springframework.ai.chat.client.advisor: debug
pattern:
console: "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
file: "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
三、简单对话
3.1 接口定义
java
[请求]
GET/POST/chat/stream
[参数]
prompt(String): ⽤⼾输⼊的消息内容
[响应]
Flux流式返回的机器⼈回复内容(text/html;charset=utf-8)
3.2 实现
配置client:
java
package com.spring.chat.bot.config;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CommonConfiguration {
@Bean
public ChatClient ollamaChatClient(ChatClient.Builder client) {
return client
.defaultSystem("你是一个IKUN,名字叫鸽鸽damn,专门负责的聊天机器人,你可以回答用户的问题.")
.defaultAdvisors(new SimpleLoggerAdvisor())
.build();
}
}
流式响应:
java
package com.spring.chat.bot.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/chat")
@Slf4j
public class ChatController {
@Autowired
private final ChatClient chatClient;
public ChatController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@RequestMapping(value = "/stream", produces = "text/html;charset=utf-8")
public Flux<String> stream(String prompt){
return chatClient.prompt()
.user(prompt)
.stream()
.content();
}
}

四、对话记忆
当前的简单对话,每一次发消息都是重新调接口,是一次新的对话,每次对话之间是没有什么联系的。
"⼤模型的对话记忆"这⼀概念,指的就是模型在与⽤⼾进⾏交互式对话过程中,能够追踪、理解并利⽤先前对话上下⽂的能⼒. 此机制使得⼤模型不仅能够响应即时的输⼊请求,还能基于之前的交流内容能够在对话中记住先前的对话内容,并根据这些信息进⾏后续的响应。
Spring AI ⻆⾊消息类型:
在对话系统(尤其是⼤语⾔模型应⽤)中,SystemMessage、UserMessage和AssistantMessage是三种核⼼⻆⾊消息类型,⽤于构建上下⽂感知的对话框架.
- SystemMessage:系统消息,通常由系统设定或初始化时提供,⽤于设定对话的背景、⻆⾊、⾏为准则等.它不是⽤⼾或助⼿发出的,⽽是系统层⾯的指令或上下⽂信息
- UserMessage:⽤⼾消息,即由⽤⼾输⼊的内容.在对话中,⽤⼾的问题或语句都属于此类.
- AssistantMessage:助⼿消息,即由助⼿(通常是AI模型)⽣成并返回给⽤⼾的响应.
4.1 Chat Memory
⼤型语⾔模型 (LLM) 是⽆状态的,也就是它们不会保留有关以前交互的信息.当开发⼈员希望在多个交互中维护上下⽂或状态时,这可能是⼀个限制.为了解决这个问题,SpringAI提供了对话内存功能,定义了ChatMemory接⼝,允许开发⼈员在与LLM的多次交互中存储和检索信息.
java
public interface ChatMemory {
void add(String conversationId, List<Message> messages);
List<Message> get(String conversationId, int lastN);
void clear(String conversationId);
}
- add(String conversationId, List messages)
说明:将单条或多条对话消息(如⽤⼾输⼊或AI回复)添加到指定会话的记忆库中
参数:
- conversationId: 区分不同会话的唯⼀标识
- Messages:可包含UserMessage/AssistantMessage等类型
- List get(String conversationId, int lastN)
说明:根据会话标识,获取历史消息
参数:
- conversationId: 会话唯⼀标识
- lastN
参数表⽰从指定会话中获取的最新消息数量
- clear(String conversationId)
说明:清空指定会话的记忆存储
参数:
- conversationId: 会话唯⼀标识
Spring AI 会⾃动配置ChatMemory,开发⼈员可以直接在应⽤程序中使⽤这个bean,SpringAI提供了默认实现InMemoryChatMemory ,不需要开发⼈员显⽰的调⽤记录每⼀轮的对话历史.
默认情况下,ChatMemory使⽤内存来存储消息,开发⼈员可以根据⾃⼰的需求,去配置不同的存储库.
4.2 修改接⼝
java
[请求]
GET/POST/chat/stream
[参数]
prompt(String): ⽤⼾输⼊的消息内容
chatId (String) : 会话标识ID, 由前端⽣成, 不重复, 新建会话时, 创建新的chatID
[响应]
Flux流式返回的机器⼈回复内容(text/html;charset=utf-8)
4.2 实现
在配置项中注入ChatMemory
java
@Bean
public ChatMemory chatMemory(){
return new InMemoryChatMemory();
}
配置会话记忆:把ChatMemory注⼊到ChatClient
java
@Bean
public ChatClient ollamaChatClient(ChatClient.Builder client, ChatMemory chatMemory) {
return client
.defaultSystem("你是一个IKUN,名字叫鸽鸽damn,专门负责的聊天机器人,你可以回答用户的问题.")
.defaultAdvisors(new SimpleLoggerAdvisor(), new MessageChatMemoryAdvisor(chatMemory))
.build();
}
向模型发送请求时,传递ChatId:
java
@RequestMapping(value = "/stream", produces = "text/html;charset=utf-8")
public Flux<String> stream(String prompt, String chatId){
return chatClient.prompt()
.user(prompt)
.advisors(spec->spec.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY,chatId))
.stream()
.content();
}
http://127.0.0.1:8080/chat/stream?prompt=我是鸡哥&chatID=123456
http://127.0.0.1:8080/chat/stream?prompt=我是谁&chatID=123456


四、对话历史
ChatMemory提供了get接⼝,可以根据会话ID,获取会话记录.我们可以定义⼀个List,来存储会话ID的列表,然后再根据会话ID获取会话记录。
4.1 存储会话
定义ChatInfo 存储会话记录,存储会话的id和最后一次询问的内容的前15个字作为这个会话的标题展示。
java
package com.spring.chat.bot.entity;
import lombok.Data;
@Data
public class ChatInfo {
private String chatId;
private String title;
public ChatInfo(String chatId, String title) {
this.chatId = chatId;
this.title = title==null?"⽆标题 ": title.length()>15 ? title.substring(0,15):title;
}
}
仿照ChatMemory接口来实现我们自己的接口,也提供添加(add)、获取(get)、删除(clear)会话的方法。
java
package com.spring.chat.bot.repository;
import com.spring.chat.bot.entity.ChatInfo;
import java.util.List;
public interface ChatHistoryRepository {
void add(String chatId, String title);
List<ChatInfo> getChats();
void clearByChatId(String chatId);
}
实现类:
java
package com.spring.chat.bot.repository;
import com.spring.chat.bot.entity.ChatInfo;
import org.springframework.stereotype.Component;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@Component
public class MemoryChatHistoryRepository implements ChatHistoryRepository {
Map<String,String> chatHistoryMap = new LinkedHashMap<>();
@Override
public void add(String chatId, String title) {
chatHistoryMap.put(chatId,title);
}
@Override
public List<ChatInfo> getChats() {
List<ChatInfo> chatInfos = chatHistoryMap.entrySet().stream()
.map(entry -> new ChatInfo(entry.getKey(), entry.getValue()))
.toList();
return chatInfos;
}
@Override
public void clearByChatId(String chatId) {
chatHistoryMap.remove(chatId);
}
}
存储会话: 在前面的简单对话实现中加:
java
@RestController
@RequestMapping("/chat")
@Slf4j
public class ChatController {
@Autowired
private final ChatClient chatClient;
@Autowired
private ChatHistoryRepository memoryChatHistoryRepository;
public ChatController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@RequestMapping(value = "/stream", produces = "text/html;charset=utf-8")
public Flux<String> stream(String prompt, String chatId){
memoryChatHistoryRepository.add(chatId,prompt);
return chatClient.prompt()
.user(prompt)
.advisors(spec->spec.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY,chatId))
.stream()
.content();
}
}
4.2 获取会话列表
获取会话列表:
java
[请求]
GET/POST
[参数]
⽆ /chat/getChatIds
[响应]
会话ID列表 List<ChatInfo>
java
@RequestMapping("/getChatIds")
public List<ChatInfo> getChatIds(){
return memoryChatHistoryRepository.getChats();
}
4.3 获取会话记录
获取会话记录:根据会话ID,从ChatMemory中获取会话记录
将spring自带的Message进行转换,返回自己前端需要的东西。
java
package com.spring.chat.bot.controller;
import lombok.Data;
import org.springframework.ai.chat.messages.Message;
@Data
public class MessageVO {
private String role;
private String content;
public MessageVO(Message message) {
switch (message.getMessageType()) {
case USER -> {
role = "user";
break;
}
case ASSISTANT -> {
role = "assistant";
break;
}
case SYSTEM -> {
role = "system";
break;
}
case TOOL -> {
role = "tool";
break;
}
}
this.content = message.getText();
}
}
```java
[请求]
GET/POST /chat/getChatHistory
[参数]
chatId (String) : 会话标识ID
[响应]
会话历史记录 List<MessageVO>
java
@Autowired
private ChatMemory chatMemory;
@RequestMapping("/getChatHistory")
List<MessageVO> getChatHistory(String chatId){
List<Message> messages = chatMemory.get(chatId, 20);
if (messages==null){
return List.of();
}
return messages.stream().map(MessageVO::new).toList();
}
## 4.4 删除会话记录
```java
[请求]
GET/POST /chat/deleteChat
[参数]
chatId (String) : 会话标识ID
[响应]
true/false
java
@RequestMapping("/deleteChat")
public Boolean deleteChat(String chatId){
try {
chatMemory.clear(chatId);
memoryChatHistoryRepository.clearByChatId(chatId);
}catch (Exception e){
return false;
}
return true;
}
