SpringAI+DeepSeek大模型应用开发
目录
大模型应用开发
模型部署
模型部署方案对比
模型部署-云部署
模型部署-本地部署
调用大模型
AI应用开发技术架构
SpringAI
对话机器人-快速入门
对话机器人-会话日志
对话机器人-会话记忆
一、定义会话存储方式
二、配置会话记忆Advisor
三、添加会话id
对话机器人-会话历史记录功能
大模型应用开发
模型部署
模型部署方案对比
云部署
优点:
- 前期成本低
- 部署维护简单
- 弹性扩展
- 全球访问
缺点:
- 数据隐私性不高
- 网络依赖性强
- 长期成本高
本地部署
优点:
- 数据安全
- 不依赖外部部署
- 长期成本低
- 高度定制
缺点:
- 初始成本高
- 维护复杂
- 部署周期长
开放API
优点:
- 前期成本极低
- 无需部署
- 无需维护
- 全球访问
缺点:
- 数据隐私性不高
- 网络依赖性强
- 定制限制
小型企业开发以及个人学习应用开发可以使用开放API,如果要长期使用的话,用云部署或本地部署较好。
模型部署-云部署
云平台 | 公司 | 地址 |
---|---|---|
阿里百炼 | 阿里巴巴 | https://bailian.console.aliyun.com |
腾讯TI平台 | 腾讯 | https://cloud.tencent.com/product/ti |
千帆平台 | 百度 | https://console.bce.baidu.com/gianfan/overview |
SiliconCloud | 硅基流动 | https://siliconflow.cn/zh-cn/siliconcloud |
火山方舟-火山引擎 | 字节跳动 | https://www.volcengine.com/product/ark |
模型部署-本地部署
本地部署最简单的一种方案就是使用ollama,官网地址:https://ollama.com
打开ollama的官网可以选择系统下载
左上角Models里面会有各个AI大模型
点击你要选择的大模型,下面会有许多版本让你选择,后面的1.5b,7b指的是它的算力,数值越大对电脑配置要求越高,数值最大的就是我们说的满血版,其余都是阉割版,右上角是对应使用的命令,打开命令提示符并输入指令它就会自动为你下载。
调用大模型
下面是DeepSeek官方给出的一段API示例代码:
AI应用开发技术架构
SpringAI
对话机器人-快速入门
1.引入依赖(可以在创建项目时在加入依赖时AI下边勾选对应依赖)
<properties><java.version>17</java.version><spring-ai.version>1.0.3</spring-ai.version></properties><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-ollama</artifactId></dependency><dependencyManagement><dependencies><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>${spring-ai.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>
2.配置大模型
spring:application:name: demoai:ollama:base-url: http://localhost:11434chat:model: deepseek-r1:8b
3.配置客户端
package com.example.config;import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class CommonConfiguration {@Beanpublic ChatClient chatClient(OllamaChatModel model) {return ChatClient.builder(model).build();}
}
都配置好以后就能书写请求然后启动项目发送请求了
@RequiredArgsConstructor
@RestController
@RequestMapping("/ai")
public class ChatController {private final ChatClient chatClient;@RequestMapping("/chat1")
// 阻塞式返回(全部输出完才返回)public String chat(String prompt) {return chatClient.prompt().user(prompt).call().content();}// 流式返回(输出过程中返回)//这里要在返回值加上 produces = "text/html;charset=utf-8",否则会乱码@RequestMapping(value = "/chat2", produces = "text/html;charset=utf-8")public Flux<String> stream(String prompt) {return chatClient.prompt().user(prompt).stream().content();}
}
对话机器人-会话日志
SpringAI利用AOP原理提供了AI会话时的拦截、增强等功能,也就是Advisor。
SpringAI在提示词发送到AI大模型之前(②Before advising)和大模型结果产生后但是发送到用户之前(⑤After advising)进行了环绕增强,我们可以在②位置处记录提示词(作为记忆功能),在⑤处记录输出的内容(记录返回日志)。
SpringAI为我们提供了Advisor的实现,可以直接使用不需要我们自己去写。
配置日志选项并且设置日志级别为DEBUG
@Configuration
public class CommonConfiguration {@Beanpublic ChatClient chatClient(OllamaChatModel model) { //创建ChatClient工厂实例return ChatClient.builder(model).defaultSystem("你是皮卡丘,请以皮卡丘的视角回答").defaultAdvisors(new SimpleLoggerAdvisor()) //配置日志Advisor.build(); //创建ChatClient实例}
}
#配置日志级别为debug
logging:level:org.springframework.ai.chat.client: DEBUGcom.example.demo: DEBUG
对话机器人-会话记忆
大模型是不具备记忆能力的,要想让大模型记住之前聊天的内容,唯一的办法就是把之前聊天的内容与新的提示词一起发给大模型。
一、定义会话存储方式
要定义会话存储方式,可以去实现ChatMemory接口,然后重写对应方法去实现。
SpringAI也提供了一个默认的实现InMemoryChatMemory(最新1.0.3版本已经换成MessageWindowChatMemory了),但这个是存储在内存中的,服务器重启记忆就没了。
二、配置会话记忆Advisor
三、添加会话id
上面advisors里的AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY在最新的1.0.3中已经修改为ChatMemory.CONVERSATION_ID
最后综合代码如下:
@Configuration
public class CommonConfiguration {@Beanpublic ChatMemory chatMemory() {// 创建Memory实例return MessageWindowChatMemory.builder().build();}@Beanpublic ChatClient chatClient(OllamaChatModel model, ChatMemory chatMemory) { //创建ChatClient工厂实例return ChatClient.builder(model).defaultSystem("你是皮卡丘,请以皮卡丘的视角回答").defaultAdvisors(new SimpleLoggerAdvisor(),// 配置MemoryAdvisorMessageChatMemoryAdvisor.builder(chatMemory).build()) //配置日志Advisor.build(); //创建ChatClient实例}
}
// 流式返回(输出过程中返回)//这里要在返回值加上 produces = "text/html;charset=utf-8",否则会乱码@RequestMapping(value = "/chat2", produces = "text/html;charset=utf-8")//再传入一个chatIdpublic Flux<String> stream(String prompt, String chatId) {return chatClient.prompt().user(prompt)//这里的key值是一个固定的参数,不能随便修改.advisors(a -> a.param(CONVERSATION_ID, chatId) ).stream().content();}
}
对话机器人-会话历史记录功能
第一个请求是要查询到对应AI会话业务下的所有会话ID,即上图中左侧列表中的两个就是该请求的返回值。 第二个请求是要根据会话类型和会话id返回用户和AI的对话记录。
我们先要写记录会话ID接口,然后再写查询会话id记录,最后根据会话id查询出具体的会话内容。
先写出记录会话记录和查询会话id记录的方法
public interface ChatHistoryRepository {// 保存会话记录void save(String type, String chatId);// 获取会话ID记录List<String> get(String type);
}
public class InMemoryChatHistoryRepository implements ChatHistoryRepository{private final Map<String, List<String>> chatHistory = new HashMap<>();// 保存会话记录@Overridepublic void save(String type, String chatId) {//先判断该类型会话是否已经存在会话id列表,如果不存在则创建一个空列表if (!chatHistory.containsKey(type)){chatHistory.put(type, List.of());}//从缓存中获取该类型会话的会话id列表List<String> chatHistoryList = chatHistory.get(type);//如果该类型列表中已经存在该会话id,则直接返回if (chatHistoryList.contains(chatId)){return;}//否则将新的会话id添加到该类型列表中chatHistoryList.add(chatId);}// 根据会话类型获取会话ID记录@Overridepublic List<String> get(String type) {List<String> chatHistoryList = chatHistory.get(type);//如果该类型列表不存在,则返回一个空列表return chatHistoryList != null ? chatHistoryList : List.of();}
}
在对话的时候对会话记录进行保存
我们创建实例时用的是spring ai提供的方法,我们对话的具体内容也由它进行存储记录,要查询到会话内容就要看它内部是如何查询的。
Ctrl+左键进去我们可以看到下面的方法,它需要会话id返回Message类型的集合。
进入到Message里面,我们需要看它返回的是否是我们需要的格式,Message是一个接口,里面有一个方法 getMessageType() 并且其继承了Content。
进入到 getMessageType() 方法里,其返回的是对话的角色(类型),我们只需要其中的 user(用户提问)和 assistant(ai回答)。
进入到Content中,里面的getText()方法就是获取对话具体内容的方法。
看完了spring ai提供的方法和返回格式,我们能知道其提供的格式不是我们想要的,因此我们就需要写一个VO类来编写我们需要的返回格式。
@Data
@NoArgsConstructor
public class MessageVO {private String role;private String content;// 构造函数public MessageVO(Message message) {//我们从message中获取role和contentswitch (message.getMessageType()){//保留下来我们需要的角色case USER -> this.role = "user";case ASSISTANT -> this.role = "assistant";default -> this.role = "";}this.content = message.getText();}
}
最后我们把获取会话记录的方法写出来就完成了。
@GetMapping("/{type}/{chatId}")public List<MessageVO> getChatHistory(@PathVariable("type") String type, @PathVariable("chatId") String chatId) {//获取会话记录List<Message> messages = chatMemory.get(chatId);if (messages == null){return List.of();}return messages.stream().map(MessageVO::new).toList();}