项目背景
随着⼈⼯智能技术的快速发展和⼤模型开源趋势的兴起,智能聊天机器⼈在客服、知识问答、⽣活助⼿ 等领域得到了⼴泛应⽤,我们接下来模仿这些应用实现一个智能的聊天机器人
核心功能
1.对话
- 支持用户和机器人之间的对话
- 实时响应用户的输入,进行回答
2.多轮对话
- 能够理解和处理多轮对话,保持上下文的连续性
- 支持基于上下文的智能应答
3.历史记录
- 自动保存用户和机器人之间的对话历史
- 支持用户查看历史的对话内容
页面设计
我们通过ollama搭建本地的大模型
首先添加本项目所需要的依赖
<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-ollama-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
配置.yml文件
spring:
application:
name: spring-ai-chatRobot
ai:
# ollama
ollama:
base-url: http://127.0.0.1:11434
chat:
model: deepseek-r1:1.5b
logging:
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"
level:
org.springframework.ai.chat.client.advisor: debug # 只针对 Spring AI 的 advisor
写出启动类
@SpringBootApplication
public class SpringChatRobotApplication {
public static void main(String[] args) {
SpringApplication.run(SpringChatRobotApplication.class,args);
}
}
对话功能
定义请求的内容
、
@Bean
public ChatClient chatClient(OllamaChatModel ollamaChatModel, ChatMemory chatMemory){
return ChatClient.builder(ollamaChatModel)
.defaultSystem("你的名字是小瑞,你是一个智能聊天机器人")
.defaultAdvisors(new SimpleLoggerAdvisor(), new MessageChatMemoryAdvisor(chatMemory))
.build();
}
@RequestMapping(value = "/stream",produces = "text/html;charset=utf-8")
public Flux<String> stream(@RequestParam String prompt,String chatId){
if (prompt == null || prompt.trim().isEmpty()) {
return Flux.just(" message 参数不能为空!");
}
memoryChatHistoryRepository.save(prompt, chatId);
return chatClient.prompt()
.user(prompt)
.advisors(spec -> spec.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, chatId))
.stream()
.content();
}
}
对话记忆(ChatMemory)
⼤型语⾔模型是⽆状态的,也就是它们不会保留有关以前交互的信息.当开发⼈员希望在多个交 互中维护上下⽂或状态时,这可能是⼀个限制.为了解决这个问题,SpringAI提供了对话内存功能,定义 了ChatMemory接⼝,允许开发⼈员在与⼤型语⾔模型的多次交互中存储和检索信息.
定义ChatMemory将其注入到ChatClient中
@Bean
public ChatMemory chatMemory(){
return new InMemoryChatMemory();
}
向模型发送请求时,传递ChatId
历史对话
我们需要定义一个实体类表示历史对话
@Data
public class ChatInfo {
private String title;
private String chatId;
public ChatInfo(String title, String chatId) {
this.title = title==null?"无标题" : title.length()>15?title.substring(0,15):title;
this.chatId = chatId;
}
}
实现存储,查询全文,删除三个方法
用LinkedHashMap是因为历史记录是有序的,
private Map<String,String> chatInfos=new LinkedHashMap<>();
模仿ChatMemory中的方法,实现存储,查询全文,删除三个方法
public interface ChatHistoryRepository {
void save(String prompt,String chatId);
List<ChatInfo> getChats();
void clearByChatId(String chatId);
}
@Repository
public class MemoryChatHistoryRepository implements ChatHistoryRepository{
// 用LinkedHashMap<>是因为是有序的
private Map<String,String> chatInfos=new LinkedHashMap<>();
@Override
public void save(String title, String chatId) {
chatInfos.put(chatId, title);
}
@Override
public List<ChatInfo> getChats() {
return chatInfos.entrySet().stream()
.map(entry->new ChatInfo(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
}
@Override
public void clearByChatId(String chatId) {
chatInfos.remove(chatId);
}
}
注入实现的方法对象
@Autowired
private MemoryChatHistoryRepository memoryChatHistoryRepository;
存储会话
获得会话列表
定义接口
// 获得会话列表
@RequestMapping("/getChatIds")
public List<ChatInfo> getChatIds(){
return memoryChatHistoryRepository.getChats();
}
获取会话记录
定义接口
根据会话的Id,获得记录
// 活得会话记录
@RequestMapping("/getChatHistory")
public List<MessageVo> getChatHistory(String chatId){
List<Message> messages = chatMemory.get(chatId, 20);
return messages.stream().map(MessageVo::new).collect(Collectors.toList());
}
为了让前端处理起来更加的简单,我们用VO统一成固定的格式
@Data
public class MessageVo {
private String role;
private String content;
public MessageVo(Message message){
switch (message.getMessageType()){
case USER -> {this.role = "user"; break;}
case ASSISTANT -> {this.role = "assistant"; break;}
case SYSTEM -> {this.role = "system"; break;}
case TOOL -> {this.role = "tool"; break;}
}
this.content = message.getText();
}
}
删除会话记录
定义接口
// 删除会话记录
@RequestMapping("/deleteChat")
public boolean clearChat(String chatId){
try {
memoryChatHistoryRepository.clearByChatId(chatId);
chatMemory.clear(chatId);
}catch (Exception e){
return false;
}
return true;
}
将Ollama切换到DeepSeek
添加依赖和修改.yml文件
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
openai:
api-key: sk-493d502eee42490f92d3015479aa8f47
base-url: https://api.deepseek.com
chat:
options:
model: deepseek-chat
temperature: 0.7
定义ChatClient
@Bean
public ChatClient chatClient(OpenAiChatModel openAiChatModel, ChatMemory chatMemory){
return ChatClient.builder(openAiChatModel)
.defaultSystem("你的名字小瑞,是一个智能机器人")
.defaultAdvisors(new SimpleLoggerAdvisor(), new MessageChatMemoryAdvisor(chatMemory))
.build();
}
希望能对大家有所帮助!!!!!