Spring?AI?+?ollama?本地搭建聊天?AI?功能
Spring AI + ollama 本地搭建聊天 AI
不知道怎么搭建 ollama 的可以查看上一篇Spring AI 初學(xué)。
項(xiàng)目可以查看gitee
前期準(zhǔn)備
添加依賴
創(chuàng)建 SpringBoot 項(xiàng)目,添加主要相關(guān)依賴(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配置文件中添加,也可以在項(xiàng)目中指定模型等參數(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
聊天實(shí)現(xiàn)
主要使用 org.springframework.ai.chat.memory.ChatMemory 接口保存對(duì)話信息。
一、采用 Java 緩存對(duì)話信息
支持功能:聊天對(duì)話、切換對(duì)話、刪除對(duì)話
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)存進(jìn)行對(duì)話 *@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ù)庫存儲(chǔ)會(huì)話和消息 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)建隨機(jī)會(huì)話 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ù)庫保存對(duì)話信息
支持功能:聊天對(duì)話、切換對(duì)話、刪除對(duì)話、撤回消息
實(shí)體類
import lombok.Data; import java.util.Date; @Data public class ChatEntity { private String id; /** 會(huì)話id */ private String sessionId; /** 會(huì)話內(nèi)容 */ private String content; /** AI、人 */ private String type; /** 創(chuàng)建時(shí)間 */ private Date time; /** 是否刪除,Y-是 */ private String beDeleted; /** AI會(huì)話時(shí),獲取人對(duì)話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 實(shí)現(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實(shí)現(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 保存用戶會(huì)話 service 實(shí)現(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ù)庫(緩存)進(jìn)行對(duì)話 *@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; //會(huì)話存儲(chǔ)方式 @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)建隨機(jī)會(huì)話 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"; } //切換會(huì)話,需要傳入 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 是否返回會(huì)話,從而判斷用戶是否可以輸入 chatClient.prompt(param.getUserMsg()).call().content(); //獲取返回最新兩條,一條用戶問題(用戶獲取用戶發(fā)送ID),一條 AI 返回結(jié)果 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); } }
效果圖
總結(jié)
主要實(shí)現(xiàn) org.springframework.ai.chat.memory.ChatMemory 方法,實(shí)際項(xiàng)目過程需要實(shí)現(xiàn)該接口重寫方法。
到此這篇關(guān)于Spring AI + ollama 本地搭建聊天 AI 的文章就介紹到這了,更多相關(guān)Spring AI ollama聊天 AI 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot yaml語法與數(shù)據(jù)讀取操作詳解
YAML 是 “YAML Ain’t Markup Language”(YAML 不是一種標(biāo)記語言)的遞歸縮寫。在開發(fā)的這種語言時(shí),YAML 的意思其實(shí)是:“Yet Another Markup Language”(仍是一種標(biāo)記語言),本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07使用遞歸刪除樹形結(jié)構(gòu)的所有子節(jié)點(diǎn)(java和mysql實(shí)現(xiàn))
下面小編就為大家?guī)硪黄褂眠f歸刪除樹形結(jié)構(gòu)的所有子節(jié)點(diǎn)(java和mysql實(shí)現(xiàn))。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-10-10Spring Boot中整合Spring Security并自定義驗(yàn)證代碼實(shí)例
本篇文章主要介紹了Spring Boot中整合Spring Security并自定義驗(yàn)證代碼實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04SpringBoot基于redis自定義注解實(shí)現(xiàn)后端接口防重復(fù)提交校驗(yàn)
本文主要介紹了SpringBoot基于redis自定義注解實(shí)現(xiàn)后端接口防重復(fù)提交校驗(yàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01JavaWeb監(jiān)聽器Listener實(shí)例解析
這篇文章主要為大家詳細(xì)介紹了JavaWeb監(jiān)聽器Listener實(shí)例,針對(duì)監(jiān)聽器進(jìn)行進(jìn)行細(xì)致分析,感興趣的小伙伴們可以參考一下2016-08-08springboot接口多實(shí)現(xiàn)類選擇性注入解決方案
這篇文章主要為大家介紹了springboot接口多實(shí)現(xiàn)類選擇性注入解決方案的四種方式,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03