开源模子应用落地-工具利用篇-Spring AI(七)
一、前言在AI大模子百花齐放的时代,许多人都对新兴技能充满了热情,都想尝试一下。但是,现实上要入门AI技能的门槛非常高。除了必要高端装备,还必要面临复杂的部署和安装过程,这让许多人望而却步。不过,随着开源技能的不断进步,使得入门AI变得越来越容易。通过利用**Ollama**,您可以快速体验大语言模子的乐趣,不再必要担心繁琐的设置和安装过程。别的,通过集成Spring AI,让更多Java爱好者能便捷的将AI能力集成到项目中,接下来,跟随我的脚步,一起来体验一把。
二、术语
2.1、Spring AI
是 Spring 生态系统的一个新项目,它简化了 Java 中 AI 应用程序的创建。它提供以下功能:
[*]支持所有重要模子提供商,例如 OpenAI、Microsoft、Amazon、Google 和 Huggingface。
[*]支持的模子类型包罗“聊天”和“文本到图像”,还有更多模子类型正在开发中。
[*]跨 AI 提供商的可移植 API,用于聊天和嵌入模子。
[*]支持同步和流 API 选项。
[*]支持下拉访问模子特定功能。
[*]AI 模子输出到 POJO 的映射。
2.2、Ollama
是一个强大的框架,用于在 Docker 容器中部署 LLM(大型语言模子)。它的重要功能是在 Docker 容器内部署和管理 LLM 的促进者,使该过程变得简单。它可以资助用户快速在本地运行大模子,通过简单的安装指令,用户可以执行一条命令就在本地运行开源大型语言模子。
Ollama 支持 GPU/CPU 肴杂模式运行,答应用户根据自己的硬件条件(如 GPU、显存、CPU 和内存)选择不同量化版本的大模子。它提供了一种方式,使得纵然在没有高性能 GPU 的装备上,也能够运行大型模子。
三、前置条件
3.1、JDK 17+
下载地点:https://www.oracle.com/java/technologies/downloads/#jdk17-windows
https://i-blog.csdnimg.cn/blog_migrate/cdaa3c52bf6bf6ef0f51c729131d7021.png
类文件具有错误的版本 61.0, 应为 52.0
3.2、创建Maven项目
SpringBoot版本为3.2.3
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
3.3、导入Maven依赖包
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.8.24</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>0.8.0</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
<version>0.8.0</version>
</dependency>
3.4、 科学上网的软件
3.5、 安装Ollama及部署Qwen模子
参见:开源模子应用落地-工具利用篇-Ollama(六)-CSDN博客
四、技能实现
4.1、调用Open AI
4.1.1、非流式调用
@RequestMapping("/chat")
public String chat(){
String systemPrompt = "{prompt}";
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPrompt);
String userPrompt = "广州有什么特产?";
Message userMessage = new UserMessage(userPrompt);
Message systemMessage = systemPromptTemplate.createMessage(MapUtil.of("prompt", "you are a helpful AI assistant"));
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
List<Generation> response = openAiChatClient.call(prompt).getResults();
String result = "";
for (Generation generation : response){
String content = generation.getOutput().getContent();
result += content;
}
return result;
}
调用效果:
https://i-blog.csdnimg.cn/blog_migrate/ab23dcfb6016205f14b31dfc2b44fe67.png
4.1.2、流式调用
@RequestMapping("/stream")
public SseEmitter stream(HttpServletResponse response){
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
SseEmitter emitter = new SseEmitter();
String systemPrompt = "{prompt}";
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPrompt);
String userPrompt = "广州有什么特产?";
Message userMessage = new UserMessage(userPrompt);
Message systemMessage = systemPromptTemplate.createMessage(MapUtil.of("prompt", "you are a helpful AI assistant"));
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
openAiChatClient.stream(prompt).subscribe(x -> {
try {
log.info("response: {}",x);
List<Generation> generations = x.getResults();
if(CollUtil.isNotEmpty(generations)){
for(Generation generation:generations){
AssistantMessage assistantMessage =generation.getOutput();
String content = assistantMessage.getContent();
if(StringUtils.isNotEmpty(content)){
emitter.send(content);
}else{
if(StringUtils.equals(content,"null"))
emitter.complete(); // Complete the SSE connection
}
}
}
} catch (Exception e) {
emitter.complete();
log.error("流式返回结果异常",e);
}
});
return emitter;
}
流式输出返回的数据结构:
https://i-blog.csdnimg.cn/blog_migrate/b19ebae3054eab6306c66b2edcdd454b.png
调用效果:
https://i-blog.csdnimg.cn/blog_migrate/4973191cfb0b0653a7606c864faa010e.png
https://i-blog.csdnimg.cn/blog_migrate/240d6a689afb7b9c958049a21e4f57d3.png
4.2、调用Ollama API
Spring封装的很好,根本和调用OpenAI的代码一致
4.2.1、非流式调用
@RequestMapping("/chat")
public String chat(){
String systemPrompt = "{prompt}";
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPrompt);
String userPrompt = "广州有什么特产?";
Message userMessage = new UserMessage(userPrompt);
Message systemMessage = systemPromptTemplate.createMessage(MapUtil.of("prompt", "you are a helpful AI assistant"));
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
List<Generation> response = ollamaChatClient.call(prompt).getResults();
String result = "";
for (Generation generation : response){
String content = generation.getOutput().getContent();
result += content;
}
return result;
}
调用效果:
Ollam的server.log输出
https://i-blog.csdnimg.cn/blog_migrate/dda4377d4bf2a02123fb83d2bfd37669.png
https://i-blog.csdnimg.cn/blog_migrate/498eaa30425e158df59017b70eea51f5.png
4.2.2、流式调用
@RequestMapping("/stream")
public SseEmitter stream(HttpServletResponse response){
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
SseEmitter emitter = new SseEmitter();
String systemPrompt = "{prompt}";
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPrompt);
String userPrompt = "广州有什么特产?";
Message userMessage = new UserMessage(userPrompt);
Message systemMessage = systemPromptTemplate.createMessage(MapUtil.of("prompt", "you are a helpful AI assistant"));
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
ollamaChatClient.stream(prompt).subscribe(x -> {
try {
log.info("response: {}",x);
List<Generation> generations = x.getResults();
if(CollUtil.isNotEmpty(generations)){
for(Generation generation:generations){
AssistantMessage assistantMessage =generation.getOutput();
String content = assistantMessage.getContent();
if(StringUtils.isNotEmpty(content)){
emitter.send(content);
}else{
if(StringUtils.equals(content,"null"))
emitter.complete(); // Complete the SSE connection
}
}
}
} catch (Exception e) {
emitter.complete();
log.error("流式返回结果异常",e);
}
});
return emitter;
}
调用效果:
https://i-blog.csdnimg.cn/blog_migrate/a4d454a5bb463b4e3d6d10dca3ca2cc6.png
https://i-blog.csdnimg.cn/blog_migrate/6a925f91973dc4635a155faa7b7aaf27.png
五、附带分析
5.1、OpenAiChatClient默认利用gpt-3.5-turbo模子
https://i-blog.csdnimg.cn/blog_migrate/592a4bfa356526554e7711f58bcd7dd7.png
5.2、流式输出如何关闭连接
不能判断是否为’'(即空字符串),以下代码将提前关闭连接
https://i-blog.csdnimg.cn/blog_migrate/e86f0038dc3f189ba52428b7891d7c8b.png
流式输出会返回’'的环境
https://i-blog.csdnimg.cn/blog_migrate/a62cb6aee639adbef0300585faa2e638.png
应该在返回内容为字符串null的时候关闭https://i-blog.csdnimg.cn/blog_migrate/0c8732c67e8fea76e0db16ea75c27a41.png
https://i-blog.csdnimg.cn/blog_migrate/3f8ad8669bfae003bc075ca8414bacc6.png
5.3、配置文件中指定的Ollama的模子参数,要和运行的模子一致
https://i-blog.csdnimg.cn/blog_migrate/22f49c8eff33984e9317c5b1d9f88872.png
https://i-blog.csdnimg.cn/blog_migrate/5d9e991c7c8a07a19a177eeec2527f65.png
5.4、OpenAI调用完整代码
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
import org.springframework.ai.openai.OpenAiChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/api")
public class OpenaiTestController {
@Autowired
private OpenAiChatClient openAiChatClient;
// http://localhost:7777/api/chat
@RequestMapping("/chat")
public String chat(){
String systemPrompt = "{prompt}";
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPrompt);
String userPrompt = "广州有什么特产?";
Message userMessage = new UserMessage(userPrompt);
Message systemMessage = systemPromptTemplate.createMessage(MapUtil.of("prompt", "you are a helpful AI assistant"));
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
List<Generation> response = openAiChatClient.call(prompt).getResults();
String result = "";
for (Generation generation : response){
String content = generation.getOutput().getContent();
result += content;
}
return result;
}
@RequestMapping("/stream")
public SseEmitter stream(HttpServletResponse response){
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
SseEmitter emitter = new SseEmitter();
String systemPrompt = "{prompt}";
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPrompt);
String userPrompt = "广州有什么特产?";
Message userMessage = new UserMessage(userPrompt);
Message systemMessage = systemPromptTemplate.createMessage(MapUtil.of("prompt", "you are a helpful AI assistant"));
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
openAiChatClient.stream(prompt).subscribe(x -> {
try {
log.info("response: {}",x);
List<Generation> generations = x.getResults();
if(CollUtil.isNotEmpty(generations)){
for(Generation generation:generations){
AssistantMessage assistantMessage =generation.getOutput();
String content = assistantMessage.getContent();
if(StringUtils.isNotEmpty(content)){
emitter.send(content);
}else{
if(StringUtils.equals(content,"null"))
emitter.complete(); // Complete the SSE connection
}
}
}
} catch (Exception e) {
emitter.complete();
log.error("流式返回结果异常",e);
}
});
return emitter;
}
}
5.5、Ollama调用完整代码
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
import org.springframework.ai.ollama.OllamaChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/api")
public class OllamaTestController {
@Autowired
private OllamaChatClient ollamaChatClient;
@RequestMapping("/chat")
public String chat(){
String systemPrompt = "{prompt}";
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPrompt);
String userPrompt = "广州有什么特产?";
Message userMessage = new UserMessage(userPrompt);
Message systemMessage = systemPromptTemplate.createMessage(MapUtil.of("prompt", "you are a helpful AI assistant"));
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
List<Generation> response = ollamaChatClient.call(prompt).getResults();
String result = "";
for (Generation generation : response){
String content = generation.getOutput().getContent();
result += content;
}
return result;
}
@RequestMapping("/stream")
public SseEmitter stream(HttpServletResponse response){
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
SseEmitter emitter = new SseEmitter();
String systemPrompt = "{prompt}";
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPrompt);
String userPrompt = "广州有什么特产?";
Message userMessage = new UserMessage(userPrompt);
Message systemMessage = systemPromptTemplate.createMessage(MapUtil.of("prompt", "you are a helpful AI assistant"));
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
ollamaChatClient.stream(prompt).subscribe(x -> {
try {
log.info("response: {}",x);
List<Generation> generations = x.getResults();
if(CollUtil.isNotEmpty(generations)){
for(Generation generation:generations){
AssistantMessage assistantMessage =generation.getOutput();
String content = assistantMessage.getContent();
if(StringUtils.isNotEmpty(content)){
emitter.send(content);
}else{
if(StringUtils.equals(content,"null"))
emitter.complete(); // Complete the SSE connection
}
}
}
} catch (Exception e) {
emitter.complete();
log.error("流式返回结果异常",e);
}
});
return emitter;
}
}
5.6、核心配置
spring:
ai:
openai:
api-key: sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ollama:
base-url: http://localhost:11434
chat:
model: qwen:1.8b-chat
5.7、启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AiApplication {
public static void main(String[] args) {
System.setProperty("http.proxyHost","127.0.0.1");
System.setProperty("http.proxyPort","7078"); // 修改为你代理软件的端口
System.setProperty("https.proxyHost","127.0.0.1");
System.setProperty("https.proxyPort","7078"); // 同理
SpringApplication.run(AiApplication.class, args);
}
}
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]