项目背景
随着⼈⼯智能技术的快速发展和⼤模型开源趋势的兴起,智能聊天机器⼈在客服、知识问答、⽣活助⼿ 等领域得到了⼴泛应⽤,我们接下来模仿这些应用实现一个智能的聊天机器人
核心功能
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-chatRobotai:
# ollamaollama:base-url: http://127.0.0.1:11434chat: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);}
}
对话功能
定义请求的内容
、
@Beanpublic 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中
@Beanpublic 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<>();@Overridepublic void save(String title, String chatId) {chatInfos.put(chatId, title);}@Overridepublic List<ChatInfo> getChats() {return chatInfos.entrySet().stream().map(entry->new ChatInfo(entry.getKey(), entry.getValue())).collect(Collectors.toList());}@Overridepublic void clearByChatId(String chatId) {chatInfos.remove(chatId);}
}
注入实现的方法对象
@Autowiredprivate 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-493d502eee42490f92d3015479aa8f47base-url: https://api.deepseek.comchat:options:model: deepseek-chattemperature: 0.7
定义ChatClient
@Beanpublic ChatClient chatClient(OpenAiChatModel openAiChatModel, ChatMemory chatMemory){return ChatClient.builder(openAiChatModel).defaultSystem("你的名字小瑞,是一个智能机器人").defaultAdvisors(new SimpleLoggerAdvisor(), new MessageChatMemoryAdvisor(chatMemory)).build();}
希望能对大家有所帮助!!!!!