一、前言
? ? 在AI大模型百花齊放的時(shí)代,很多人都對新興技術(shù)充滿了熱情,都想嘗試一下。但是,實(shí)際上要入門AI技術(shù)的門檻非常高。除了需要高端設(shè)備,還需要面臨復(fù)雜的部署和安裝過程,這讓很多人望而卻步。不過,隨著開源技術(shù)的不斷進(jìn)步,使得入門AI變得越來越容易。通過使用Ollama,您可以快速體驗(yàn)大語言模型的樂趣,不再需要擔(dān)心繁瑣的設(shè)置和安裝過程。另外,通過集成Spring AI,讓更多Java愛好者能便捷的將AI能力集成到項(xiàng)目中,接下來,跟隨我的腳步,一起來體驗(yàn)一把。
二、術(shù)語
2.1、Spring AI
? ? 是 Spring 生態(tài)系統(tǒng)的一個(gè)新項(xiàng)目,它簡化了 Java 中 AI 應(yīng)用程序的創(chuàng)建。它提供以下功能:
- 支持所有主要模型提供商,例如 OpenAI、Microsoft、Amazon、Google 和 Huggingface。
- 支持的模型類型包括“聊天”和“文本到圖像”,還有更多模型類型正在開發(fā)中。
- 跨 AI 提供商的可移植 API,用于聊天和嵌入模型。
- 支持同步和流 API 選項(xiàng)。
- 支持下拉訪問模型特定功能。
- AI 模型輸出到 POJO 的映射。
2.2、Ollama
? ? 是一個(gè)強(qiáng)大的框架,用于在 Docker 容器中部署 LLM(大型語言模型)。它的主要功能是在 Docker 容器內(nèi)部署和管理 LLM 的促進(jìn)者,使該過程變得簡單。它可以幫助用戶快速在本地運(yùn)行大模型,通過簡單的安裝指令,用戶可以執(zhí)行一條命令就在本地運(yùn)行開源大型語言模型。
? ? Ollama 支持 GPU/CPU 混合模式運(yùn)行,允許用戶根據(jù)自己的硬件條件(如 GPU、顯存、CPU 和內(nèi)存)選擇不同量化版本的大模型。它提供了一種方式,使得即使在沒有高性能 GPU 的設(shè)備上,也能夠運(yùn)行大型模型。
?
三、前置條件
3.1、JDK 17+
? ? 下載地址:https://www.oracle.com/java/technologies/downloads/#jdk17-windows
? ??
? ? 類文件具有錯誤的版本 61.0, 應(yīng)為 52.0
3.2、創(chuàng)建Maven項(xiàng)目
? ? 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、導(dǎo)入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、 科學(xué)上網(wǎng)的軟件
3.5、 安裝Ollama及部署Qwen模型
? ? 參見:開源模型應(yīng)用落地-工具使用篇-Ollama(六)-CSDN博客
四、技術(shù)實(shí)現(xiàn)
4.1、調(diào)用Open AI
4.1.1、非流式調(diào)用
@RequestMapping("/chat")
public String chat(){
String systemPrompt = "{prompt}";
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPrompt);
String userPrompt = "廣州有什么特產(chǎn)?";
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;
}
? ? 調(diào)用結(jié)果:
? ??
4.1.2、流式調(diào)用
@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 = "廣州有什么特產(chǎn)?";
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("流式返回結(jié)果異常",e);
}
});
return emitter;
}
流式輸出返回的數(shù)據(jù)結(jié)構(gòu):
? ? 調(diào)用結(jié)果:
?
4.2、調(diào)用Ollama API
Spring封裝的很好,基本和調(diào)用OpenAI的代碼一致
4.2.1、非流式調(diào)用
@RequestMapping("/chat")
public String chat(){
String systemPrompt = "{prompt}";
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPrompt);
String userPrompt = "廣州有什么特產(chǎn)?";
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;
}
調(diào)用結(jié)果:
Ollam的server.log輸出
4.2.2、流式調(diào)用
@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 = "廣州有什么特產(chǎn)?";
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("流式返回結(jié)果異常",e);
}
});
return emitter;
}
調(diào)用結(jié)果:
五、附帶說明
5.1、OpenAiChatClient默認(rèn)使用gpt-3.5-turbo模型
5.2、流式輸出如何關(guān)閉連接
? ? 不能判斷是否為''(即空字符串),以下代碼將提前關(guān)閉連接
? ? 流式輸出會返回''的情況
? ? ? 應(yīng)該在返回內(nèi)容為字符串null的時(shí)候關(guān)閉
5.3、配置文件中指定的Ollama的模型參數(shù),要和運(yùn)行的模型一致,即
5.4、OpenAI調(diào)用完整代碼
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 = "廣州有什么特產(chǎn)?";
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 = "廣州有什么特產(chǎn)?";
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("流式返回結(jié)果異常",e);
}
});
return emitter;
}
}
5.5、Ollama調(diào)用完整代碼
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 = "廣州有什么特產(chǎn)?";
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 = "廣州有什么特產(chǎn)?";
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("流式返回結(jié)果異常",e);
}
});
return emitter;
}
}
5.6、核心配置文章來源:http://www.zghlxwxcb.cn/news/detail-838278.html
spring:
ai:
openai:
api-key: sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ollama:
base-url: http://localhost:11434
chat:
model: qwen:1.8b-chat
5.7、啟動類文章來源地址http://www.zghlxwxcb.cn/news/detail-838278.html
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);
}
}
到了這里,關(guān)于開源模型應(yīng)用落地-工具使用篇-Spring AI(七)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!