当前位置:   article > 正文

开源模型应用落地-工具使用篇-Spring AI(七)_spring ai maven

spring ai maven

一、前言

    在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

    

    类文件具有错误的版本 61.0, 应为 52.0

3.2、创建Maven项目

    SpringBoot版本为3.2.3

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-parent</artifactId>
  4. <version>3.2.3</version>
  5. <relativePath/> <!-- lookup parent from repository -->
  6. </parent>

3.3、导入Maven依赖包

  1. <dependency>
  2. <groupId>org.projectlombok</groupId>
  3. <artifactId>lombok</artifactId>
  4. <optional>true</optional>
  5. </dependency>
  6. <dependency>
  7. <groupId>ch.qos.logback</groupId>
  8. <artifactId>logback-core</artifactId>
  9. </dependency>
  10. <dependency>
  11. <groupId>ch.qos.logback</groupId>
  12. <artifactId>logback-classic</artifactId>
  13. </dependency>
  14. <dependency>
  15. <groupId>cn.hutool</groupId>
  16. <artifactId>hutool-core</artifactId>
  17. <version>5.8.24</version>
  18. </dependency>
  19. <dependency>
  20. <groupId>org.springframework.ai</groupId>
  21. <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
  22. <version>0.8.0</version>
  23. </dependency>
  24. <dependency>
  25. <groupId>org.springframework.ai</groupId>
  26. <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
  27. <version>0.8.0</version>
  28. </dependency>

3.4、 科学上网的软件

3.5、 安装Ollama及部署Qwen模型

    参见:开源模型应用落地-工具使用篇-Ollama(六)-CSDN博客


四、技术实现

4.1、调用Open AI

4.1.1、非流式调用

  1. @RequestMapping("/chat")
  2. public String chat(){
  3. String systemPrompt = "{prompt}";
  4. SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPrompt);
  5. String userPrompt = "广州有什么特产?";
  6. Message userMessage = new UserMessage(userPrompt);
  7. Message systemMessage = systemPromptTemplate.createMessage(MapUtil.of("prompt", "you are a helpful AI assistant"));
  8. Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
  9. List<Generation> response = openAiChatClient.call(prompt).getResults();
  10. String result = "";
  11. for (Generation generation : response){
  12. String content = generation.getOutput().getContent();
  13. result += content;
  14. }
  15. return result;
  16. }

    调用结果:

    

4.1.2、流式调用

  1. @RequestMapping("/stream")
  2. public SseEmitter stream(HttpServletResponse response){
  3. response.setContentType("text/event-stream");
  4. response.setCharacterEncoding("UTF-8");
  5. SseEmitter emitter = new SseEmitter();
  6. String systemPrompt = "{prompt}";
  7. SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPrompt);
  8. String userPrompt = "广州有什么特产?";
  9. Message userMessage = new UserMessage(userPrompt);
  10. Message systemMessage = systemPromptTemplate.createMessage(MapUtil.of("prompt", "you are a helpful AI assistant"));
  11. Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
  12. openAiChatClient.stream(prompt).subscribe(x -> {
  13. try {
  14. log.info("response: {}",x);
  15. List<Generation> generations = x.getResults();
  16. if(CollUtil.isNotEmpty(generations)){
  17. for(Generation generation:generations){
  18. AssistantMessage assistantMessage = generation.getOutput();
  19. String content = assistantMessage.getContent();
  20. if(StringUtils.isNotEmpty(content)){
  21. emitter.send(content);
  22. }else{
  23. if(StringUtils.equals(content,"null"))
  24. emitter.complete(); // Complete the SSE connection
  25. }
  26. }
  27. }
  28. } catch (Exception e) {
  29. emitter.complete();
  30. log.error("流式返回结果异常",e);
  31. }
  32. });
  33. return emitter;
  34. }

流式输出返回的数据结构:

    调用结果:

 

4.2、调用Ollama API

Spring封装的很好,基本和调用OpenAI的代码一致

4.2.1、非流式调用

  1. @RequestMapping("/chat")
  2. public String chat(){
  3. String systemPrompt = "{prompt}";
  4. SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPrompt);
  5. String userPrompt = "广州有什么特产?";
  6. Message userMessage = new UserMessage(userPrompt);
  7. Message systemMessage = systemPromptTemplate.createMessage(MapUtil.of("prompt", "you are a helpful AI assistant"));
  8. Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
  9. List<Generation> response = ollamaChatClient.call(prompt).getResults();
  10. String result = "";
  11. for (Generation generation : response){
  12. String content = generation.getOutput().getContent();
  13. result += content;
  14. }
  15. return result;
  16. }

调用结果:

Ollam的server.log输出

4.2.2、流式调用

  1. @RequestMapping("/stream")
  2. public SseEmitter stream(HttpServletResponse response){
  3. response.setContentType("text/event-stream");
  4. response.setCharacterEncoding("UTF-8");
  5. SseEmitter emitter = new SseEmitter();
  6. String systemPrompt = "{prompt}";
  7. SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPrompt);
  8. String userPrompt = "广州有什么特产?";
  9. Message userMessage = new UserMessage(userPrompt);
  10. Message systemMessage = systemPromptTemplate.createMessage(MapUtil.of("prompt", "you are a helpful AI assistant"));
  11. Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
  12. ollamaChatClient.stream(prompt).subscribe(x -> {
  13. try {
  14. log.info("response: {}",x);
  15. List<Generation> generations = x.getResults();
  16. if(CollUtil.isNotEmpty(generations)){
  17. for(Generation generation:generations){
  18. AssistantMessage assistantMessage = generation.getOutput();
  19. String content = assistantMessage.getContent();
  20. if(StringUtils.isNotEmpty(content)){
  21. emitter.send(content);
  22. }else{
  23. if(StringUtils.equals(content,"null"))
  24. emitter.complete(); // Complete the SSE connection
  25. }
  26. }
  27. }
  28. } catch (Exception e) {
  29. emitter.complete();
  30. log.error("流式返回结果异常",e);
  31. }
  32. });
  33. return emitter;
  34. }

调用结果:


五、附带说明

5.1、OpenAiChatClient默认使用gpt-3.5-turbo模型

5.2、流式输出如何关闭连接

    不能判断是否为''(即空字符串),以下代码将提前关闭连接

    流式输出会返回''的情况

      应该在返回内容为字符串null的时候关闭

5.3、配置文件中指定的Ollama的模型参数,要和运行的模型一致

5.4、OpenAI调用完整代码

  1. import cn.hutool.core.collection.CollUtil;
  2. import cn.hutool.core.map.MapUtil;
  3. import jakarta.servlet.http.HttpServletResponse;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.apache.commons.lang3.StringUtils;
  6. import org.springframework.ai.chat.Generation;
  7. import org.springframework.ai.chat.messages.AssistantMessage;
  8. import org.springframework.ai.chat.messages.Message;
  9. import org.springframework.ai.chat.messages.UserMessage;
  10. import org.springframework.ai.chat.prompt.Prompt;
  11. import org.springframework.ai.chat.prompt.SystemPromptTemplate;
  12. import org.springframework.ai.openai.OpenAiChatClient;
  13. import org.springframework.beans.factory.annotation.Autowired;
  14. import org.springframework.web.bind.annotation.RequestMapping;
  15. import org.springframework.web.bind.annotation.RestController;
  16. import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
  17. import java.util.List;
  18. @Slf4j
  19. @RestController
  20. @RequestMapping("/api")
  21. public class OpenaiTestController {
  22. @Autowired
  23. private OpenAiChatClient openAiChatClient;
  24. // http://localhost:7777/api/chat
  25. @RequestMapping("/chat")
  26. public String chat(){
  27. String systemPrompt = "{prompt}";
  28. SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPrompt);
  29. String userPrompt = "广州有什么特产?";
  30. Message userMessage = new UserMessage(userPrompt);
  31. Message systemMessage = systemPromptTemplate.createMessage(MapUtil.of("prompt", "you are a helpful AI assistant"));
  32. Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
  33. List<Generation> response = openAiChatClient.call(prompt).getResults();
  34. String result = "";
  35. for (Generation generation : response){
  36. String content = generation.getOutput().getContent();
  37. result += content;
  38. }
  39. return result;
  40. }
  41. @RequestMapping("/stream")
  42. public SseEmitter stream(HttpServletResponse response){
  43. response.setContentType("text/event-stream");
  44. response.setCharacterEncoding("UTF-8");
  45. SseEmitter emitter = new SseEmitter();
  46. String systemPrompt = "{prompt}";
  47. SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPrompt);
  48. String userPrompt = "广州有什么特产?";
  49. Message userMessage = new UserMessage(userPrompt);
  50. Message systemMessage = systemPromptTemplate.createMessage(MapUtil.of("prompt", "you are a helpful AI assistant"));
  51. Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
  52. openAiChatClient.stream(prompt).subscribe(x -> {
  53. try {
  54. log.info("response: {}",x);
  55. List<Generation> generations = x.getResults();
  56. if(CollUtil.isNotEmpty(generations)){
  57. for(Generation generation:generations){
  58. AssistantMessage assistantMessage = generation.getOutput();
  59. String content = assistantMessage.getContent();
  60. if(StringUtils.isNotEmpty(content)){
  61. emitter.send(content);
  62. }else{
  63. if(StringUtils.equals(content,"null"))
  64. emitter.complete(); // Complete the SSE connection
  65. }
  66. }
  67. }
  68. } catch (Exception e) {
  69. emitter.complete();
  70. log.error("流式返回结果异常",e);
  71. }
  72. });
  73. return emitter;
  74. }
  75. }

5.5、Ollama调用完整代码

  1. import cn.hutool.core.collection.CollUtil;
  2. import cn.hutool.core.map.MapUtil;
  3. import jakarta.servlet.http.HttpServletResponse;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.apache.commons.lang3.StringUtils;
  6. import org.springframework.ai.chat.Generation;
  7. import org.springframework.ai.chat.messages.AssistantMessage;
  8. import org.springframework.ai.chat.messages.Message;
  9. import org.springframework.ai.chat.messages.UserMessage;
  10. import org.springframework.ai.chat.prompt.Prompt;
  11. import org.springframework.ai.chat.prompt.SystemPromptTemplate;
  12. import org.springframework.ai.ollama.OllamaChatClient;
  13. import org.springframework.beans.factory.annotation.Autowired;
  14. import org.springframework.web.bind.annotation.RequestMapping;
  15. import org.springframework.web.bind.annotation.RestController;
  16. import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
  17. import java.util.List;
  18. @Slf4j
  19. @RestController
  20. @RequestMapping("/api")
  21. public class OllamaTestController {
  22. @Autowired
  23. private OllamaChatClient ollamaChatClient;
  24. @RequestMapping("/chat")
  25. public String chat(){
  26. String systemPrompt = "{prompt}";
  27. SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPrompt);
  28. String userPrompt = "广州有什么特产?";
  29. Message userMessage = new UserMessage(userPrompt);
  30. Message systemMessage = systemPromptTemplate.createMessage(MapUtil.of("prompt", "you are a helpful AI assistant"));
  31. Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
  32. List<Generation> response = ollamaChatClient.call(prompt).getResults();
  33. String result = "";
  34. for (Generation generation : response){
  35. String content = generation.getOutput().getContent();
  36. result += content;
  37. }
  38. return result;
  39. }
  40. @RequestMapping("/stream")
  41. public SseEmitter stream(HttpServletResponse response){
  42. response.setContentType("text/event-stream");
  43. response.setCharacterEncoding("UTF-8");
  44. SseEmitter emitter = new SseEmitter();
  45. String systemPrompt = "{prompt}";
  46. SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPrompt);
  47. String userPrompt = "广州有什么特产?";
  48. Message userMessage = new UserMessage(userPrompt);
  49. Message systemMessage = systemPromptTemplate.createMessage(MapUtil.of("prompt", "you are a helpful AI assistant"));
  50. Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
  51. ollamaChatClient.stream(prompt).subscribe(x -> {
  52. try {
  53. log.info("response: {}",x);
  54. List<Generation> generations = x.getResults();
  55. if(CollUtil.isNotEmpty(generations)){
  56. for(Generation generation:generations){
  57. AssistantMessage assistantMessage = generation.getOutput();
  58. String content = assistantMessage.getContent();
  59. if(StringUtils.isNotEmpty(content)){
  60. emitter.send(content);
  61. }else{
  62. if(StringUtils.equals(content,"null"))
  63. emitter.complete(); // Complete the SSE connection
  64. }
  65. }
  66. }
  67. } catch (Exception e) {
  68. emitter.complete();
  69. log.error("流式返回结果异常",e);
  70. }
  71. });
  72. return emitter;
  73. }
  74. }

5.6、核心配置

  1. spring:
  2. ai:
  3. openai:
  4. api-key: sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  5. ollama:
  6. base-url: http://localhost:11434
  7. chat:
  8. model: qwen:1.8b-chat

5.7、启动类

  1. import org.springframework.boot.SpringApplication;
  2. import org.springframework.boot.autoconfigure.SpringBootApplication;
  3. @SpringBootApplication
  4. public class AiApplication {
  5. public static void main(String[] args) {
  6. System.setProperty("http.proxyHost","127.0.0.1");
  7. System.setProperty("http.proxyPort","7078"); // 修改为你代理软件的端口
  8. System.setProperty("https.proxyHost","127.0.0.1");
  9. System.setProperty("https.proxyPort","7078"); // 同理
  10. SpringApplication.run(AiApplication.class, args);
  11. }
  12. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/596121
推荐阅读
相关标签
  

闽ICP备14008679号