Spring?AI?+?ollama?本地搭建聊天?AI?功能
Spring AI + ollama 本地搭建聊天 AI
不知道怎么搭建 ollama 的可以查看上一篇Spring AI 初學。
項目可以查看gitee
前期準備
添加依賴
創(chuàng)建 SpringBoot 項目,添加主要相關依賴(spring-boot-starter-web、spring-ai-ollama-spring-boot-starter)
Spring AI supports Spring Boot 3.2.x and 3.3.x
Spring Boot 3.2.11 requires at least Java 17 and is compatible with versions up to and including Java 23
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-ollama-spring-boot-starter</artifactId> <version>1.0.0-M3</version> </dependency>
配置文件
application.properties、yml配置文件中添加,也可以在項目中指定模型等參數(shù),具體參數(shù)可以參考 OllamaChatProperties
# properties,模型 qwen2.5:14b 根據(jù)自己下載的模型而定 spring.ai.ollama.chat.options.model=qwen2.5:14b #yml spring: ai: ollama: chat: model: qwen2.5:14b
聊天實現(xiàn)
主要使用 org.springframework.ai.chat.memory.ChatMemory 接口保存對話信息。
一、采用 Java 緩存對話信息
支持功能:聊天對話、切換對話、刪除對話
controller
import com.yb.chatai.domain.ChatParam; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; import org.springframework.ai.chat.memory.ChatMemory; import org.springframework.ai.chat.memory.InMemoryChatMemory; import org.springframework.ai.ollama.OllamaChatModel; import org.springframework.ai.ollama.api.OllamaApi; import org.springframework.ai.ollama.api.OllamaOptions; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import java.util.UUID; /* *@title Controller *@description 使用內(nèi)存進行對話 *@author yb *@version 1.0 *@create 2024/11/12 14:39 */ @Controller public class ChatController { //注入模型,配置文件中的模型,或者可以在方法中指定模型 @Resource private OllamaChatModel model; //聊天 client private ChatClient chatClient; // 模擬數(shù)據(jù)庫存儲會話和消息 private final ChatMemory chatMemory = new InMemoryChatMemory(); //首頁 @GetMapping("/index") public String index(){ return "index"; } //開始聊天,生成唯一 sessionId @GetMapping("/start") public String start(Model model){ //新建聊天模型 // OllamaOptions options = OllamaOptions.builder(); // options.setModel("qwen2.5:14b"); // OllamaChatModel chatModel = new OllamaChatModel(new OllamaApi(), options); //創(chuàng)建隨機會話 ID String sessionId = UUID.randomUUID().toString(); model.addAttribute("sessionId", sessionId); //創(chuàng)建聊天client chatClient = ChatClient.builder(this.model).defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory, sessionId, 10)).build(); return "chatPage"; } //聊天 @PostMapping("/chat") @ResponseBody public String chat(@RequestBody ChatParam param){ //直接返回 return chatClient.prompt(param.getUserMsg()).call().content(); } //刪除聊天 @DeleteMapping("/clear/{id}") @ResponseBody public void clear(@PathVariable("id") String sessionId){ chatMemory.clear(sessionId); } }
效果圖
二、采用數(shù)據(jù)庫保存對話信息
支持功能:聊天對話、切換對話、刪除對話、撤回消息
實體類
import lombok.Data; import java.util.Date; @Data public class ChatEntity { private String id; /** 會話id */ private String sessionId; /** 會話內(nèi)容 */ private String content; /** AI、人 */ private String type; /** 創(chuàng)建時間 */ private Date time; /** 是否刪除,Y-是 */ private String beDeleted; /** AI會話時,獲取人對話ID */ private String userChatId; }
confiuration
import com.yb.chatai.domain.ChatEntity; import com.yb.chatai.service.IChatService; import jakarta.annotation.Resource; import org.springframework.ai.chat.memory.ChatMemory; import org.springframework.ai.chat.messages.AssistantMessage; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.MessageType; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.context.annotation.Configuration; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /* *@title DBMemory *@description 實現(xiàn) ChatMemory,注入 spring,方便采用 service 方法 *@author yb *@version 1.0 *@create 2024/11/12 16:15 */ @Configuration public class DBMemory implements ChatMemory { @Resource private IChatService chatService; @Override public void add(String conversationId, List<Message> messages) { for (Message message : messages) { chatService.saveMessage(conversationId, message.getContent(), message.getMessageType().getValue()); } } @Override public List<Message> get(String conversationId, int lastN) { List<ChatEntity> list = chatService.getLastN(conversationId, lastN); if(list != null && !list.isEmpty()) { return list.stream().map(l -> { Message message = null; if (MessageType.ASSISTANT.getValue().equals(l.getType())) { message = new AssistantMessage(l.getContent()); } else if (MessageType.USER.getValue().equals(l.getType())) { message = new UserMessage(l.getContent()); } return message; }).collect(Collectors.<Message>toList()); }else { return new ArrayList<>(); } } @Override public void clear(String conversationId) { chatService.clear(conversationId); } }
services實現(xiàn)類
import com.yb.chatai.domain.ChatEntity; import com.yb.chatai.service.IChatService; import org.springframework.ai.chat.messages.MessageType; import org.springframework.stereotype.Service; import java.util.*; /* *@title ChatServiceImpl *@description 保存用戶會話 service 實現(xiàn)類 *@author yb *@version 1.0 *@create 2024/11/12 15:50 */ @Service public class ChatServiceImpl implements IChatService { Map<String, List<ChatEntity>> map = new HashMap<>(); @Override public void saveMessage(String sessionId, String content, String type) { ChatEntity entity = new ChatEntity(); entity.setId(UUID.randomUUID().toString()); entity.setContent(content); entity.setSessionId(sessionId); entity.setType(type); entity.setTime(new Date()); //改成常量 entity.setBeDeleted("N"); if(MessageType.ASSISTANT.getValue().equals(type)){ entity.setUserChatId(getLastN(sessionId, 1).get(0).getId()); } //todo 保存數(shù)據(jù)庫 //模擬保存到數(shù)據(jù)庫 List<ChatEntity> list = map.getOrDefault(sessionId, new ArrayList<>()); list.add(entity); map.put(sessionId, list); } @Override public List<ChatEntity> getLastN(String sessionId, Integer lastN) { //todo 從數(shù)據(jù)庫獲取 //模擬從數(shù)據(jù)庫獲取 List<ChatEntity> list = map.get(sessionId); return list != null ? list.stream().skip(Math.max(0, list.size() - lastN)).toList() : List.of(); } @Override public void clear(String sessionId) { //todo 數(shù)據(jù)庫更新 beDeleted 字段 map.put(sessionId, new ArrayList<>()); } @Override public void deleteById(String id) { //todo 數(shù)據(jù)庫直接將該 id 數(shù)據(jù) beDeleted 改成 Y for (Map.Entry<String, List<ChatEntity>> next : map.entrySet()) { List<ChatEntity> list = next.getValue(); list.removeIf(chat -> id.equals(chat.getId()) || id.equals(chat.getUserChatId())); } } }
controller
import com.yb.chatai.configuration.DBMemory; import com.yb.chatai.domain.ChatEntity; import com.yb.chatai.domain.ChatParam; import com.yb.chatai.service.IChatService; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; import org.springframework.ai.ollama.OllamaChatModel; import org.springframework.ai.ollama.api.OllamaApi; import org.springframework.ai.ollama.api.OllamaOptions; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.UUID; /* *@title ChatController2 *@description 使用數(shù)據(jù)庫(緩存)進行對話 *@author yb *@version 1.0 *@create 2024/11/12 16:12 */ @Controller public class ChatController2 { //注入模型,配置文件中的模型,或者可以在方法中指定模型 @Resource private OllamaChatModel model; //聊天 client private ChatClient chatClient; //操作聊天信息service @Resource private IChatService chatService; //會話存儲方式 @Resource private DBMemory dbMemory; //開始聊天,生成唯一 sessionId @GetMapping("/start2") public String start(Model model){ //新建聊天模型 // OllamaOptions options = OllamaOptions.builder(); // options.setModel("qwen2.5:14b"); // OllamaChatModel chatModel = new OllamaChatModel(new OllamaApi(), options); //創(chuàng)建隨機會話 ID String sessionId = UUID.randomUUID().toString(); model.addAttribute("sessionId", sessionId); //創(chuàng)建聊天 client chatClient = ChatClient.builder(this.model).defaultAdvisors(new MessageChatMemoryAdvisor(dbMemory, sessionId, 10)).build(); return "chatPage2"; } //切換會話,需要傳入 sessionId @GetMapping("/exchange2/{id}") public String exchange(@PathVariable("id")String sessionId){ //切換聊天 client chatClient = ChatClient.builder(this.model).defaultAdvisors(new MessageChatMemoryAdvisor(dbMemory, sessionId, 10)).build(); return "chatPage2"; } //聊天 @PostMapping("/chat2") @ResponseBody public List<ChatEntity> chat(@RequestBody ChatParam param){ //todo 判斷 AI 是否返回會話,從而判斷用戶是否可以輸入 chatClient.prompt(param.getUserMsg()).call().content(); //獲取返回最新兩條,一條用戶問題(用戶獲取用戶發(fā)送ID),一條 AI 返回結果 return chatService.getLastN(param.getSessionId(), 2); } //撤回消息 @DeleteMapping("/revoke2/{id}") @ResponseBody public void revoke(@PathVariable("id") String id){ chatService.deleteById(id); } //清空消息 @DeleteMapping("/del2/{id}") @ResponseBody public void clear(@PathVariable("id") String sessionId){ dbMemory.clear(sessionId); } }
效果圖
總結
主要實現(xiàn) org.springframework.ai.chat.memory.ChatMemory 方法,實際項目過程需要實現(xiàn)該接口重寫方法。
到此這篇關于Spring AI + ollama 本地搭建聊天 AI 的文章就介紹到這了,更多相關Spring AI ollama聊天 AI 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot yaml語法與數(shù)據(jù)讀取操作詳解
YAML 是 “YAML Ain’t Markup Language”(YAML 不是一種標記語言)的遞歸縮寫。在開發(fā)的這種語言時,YAML 的意思其實是:“Yet Another Markup Language”(仍是一種標記語言),本文給大家介紹的非常詳細,需要的朋友可以參考下2022-07-07使用遞歸刪除樹形結構的所有子節(jié)點(java和mysql實現(xiàn))
下面小編就為大家?guī)硪黄褂眠f歸刪除樹形結構的所有子節(jié)點(java和mysql實現(xiàn))。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10Spring Boot中整合Spring Security并自定義驗證代碼實例
本篇文章主要介紹了Spring Boot中整合Spring Security并自定義驗證代碼實例,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-04-04SpringBoot基于redis自定義注解實現(xiàn)后端接口防重復提交校驗
本文主要介紹了SpringBoot基于redis自定義注解實現(xiàn)后端接口防重復提交校驗,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-01-01springboot接口多實現(xiàn)類選擇性注入解決方案
這篇文章主要為大家介紹了springboot接口多實現(xiàn)類選擇性注入解決方案的四種方式,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步2022-03-03