开源模型应用落地-工具使用篇-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

    

    类文件具有错误的版本 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;
}

    调用结果:

    

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;
}

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

    调用结果:

 

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输出

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;
}

调用结果:


五、附带说明

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

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

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

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

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

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

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 {@Autowiredprivate 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 {@Autowiredprivate 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-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxollama:base-url: http://localhost:11434chat: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);}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/725938.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kap - macOS 开源录屏工具

文章目录 关于 Kap 关于 Kap Kap 是一个使用web技术的开源的屏幕录制工具 官网&#xff1a;https://getkap.cogithub : https://github.com/wulkano/Kap 目前只支持 macOS 12 以上&#xff0c;支持 Intel 和 Apple silicon 你可以前往官网&#xff0c;右上方下载 你也可以使…

案例介绍:信息抽取技术在汽车销售与分销策略中的应用与实践

一、引言 在当今竞争激烈的汽车制造业中&#xff0c;成功的销售策略、市场营销和分销网络的构建是确保品牌立足市场的关键。作为一名经验丰富的项目经理&#xff0c;我曾领导一个专注于汽车销售和分销的项目&#xff0c;该项目深入挖掘市场数据&#xff0c;运用先进的信息抽取…

EasyExcel3.1.1版本上传文件忽略列头大小写

1、背景 项目中使用easyExcel3.1.1版本实现上传下载功能&#xff0c;相关数据DTO以 ExcelProperty(value "dealer_gssn_id") 形式规定其每一列的名称&#xff0c;这样的话easyExcel会完全匹配对应的列名&#xff0c;即用户上传文件时&#xff0c;列名写成Dealer_…

利用websocket +定时器简易的实现一个网络聊天室

其实原理非常简单,就是客户端用户通过websoket来连接websocket服务端。然后服务端,收集每个用户发出的消息, 进而将每条用户的消息通过广播的形式推送到每个连接到服务端的客户端。从而实现用户的实时聊天。 // TODO : 我主要是讲一下实现思路。并未完善其功能。 1.后端 依赖 …

使用数据库实现增删改查

#include<myhead.h>//定义添加数据函数int do_add(sqlite3 *ppDb) {//1.准备sql语句,输入要添加的信息int add_numb; //工号char add_name[20]; //姓名char add_sex[10]; //性别double add_score; //工资printf("请输入要添加的工号:")…

恢复IDEA误删除的git提交,提交被删除,尝试恢复提交

​​​​​​ dgqDESKTOP-JRQ5NMD MINGW64 /f/IdeaProjects/workspace/spzx-parent ((8bb112e...)) $ git reflog 8bb112e (HEAD, origin/master, master) HEAD{0}: checkout: moving from master to 8bb112e5ac18dfe4bbd64adfd06363e46b609f21 8bb112e (HEAD, origin/master, …

微信小程序开发系列(二十一)·wxml语法·setData()修改数组类型数据(增加、修改、删除)

目录 1. 新增数组元素 方法一&#xff1a;push&#xff08;&#xff09; 方法二&#xff1a;concat() 方法三&#xff1a;ES6中的扩展运算符 ... 2. 修改数组元素 样式一&#xff1a;数字 样式二&#xff1a;元素 3. 删除数组元素 方法一&#xff1a;splice&#x…

vue2源码分析-vue入口文件global-api分析

文章背景 vue项目开发过程中,首先会有一个初始化的流程,以及我们会使用到很多全局的api,如 this.$set this.$delete this.$nextTick,以及初始化方法extend,initUse, initMixin , initExtend, initAssetRegisters 等等那它们是怎么实现,让我们一起来探究下吧 源码目录 global-…

Windows下 OracleXE_21 数据库的下载与安装

Oracle 数据库的下载与安装 数据库安装包下载数据库安装访问数据库进行测试Navicat连接数据库 1. 数据库安装包的下载 1.1 下载地址 Oracle Database Express Edition | Oracle 中国 1.2 点击“下载 Oracle Database XE”按钮&#xff0c;进去到下载页面&#xff08;选择对…

Stable diffusion零基础课程

该课程专为零基础学习者设计&#xff0c;旨在介绍和解释稳定扩散的基本概念。学员将通过简单易懂的方式了解扩散现象、数学模型及其应用&#xff0c;为日后更深入的科学研究和工程应用打下坚实基础。 课程大小&#xff1a;3.8G 课程下载&#xff1a;https://download.csdn.ne…

灵魂指针,教给(一)

欢迎来到白刘的领域 Miracle_86.-CSDN博客 系列专栏 C语言知识 先赞后看&#xff0c;已成习惯 创作不易&#xff0c;多多支持&#xff01; 一、内存和地址 1.1 内存 在介绍知识之前&#xff0c;先来想一个生活中的小栗子&#xff1a; 假如把你放在一个有100间屋子的酒店…

第三讲 汇编初步 课程随手记

一、寄存器 32位CPU通用寄存器如下图所示&#xff1a; 因为教材依照的是32位CPU寄存器&#xff0c;而我安装的是64位寄存器&#xff0c;所以找了一下64位的寄存器的资料 PS&#xff1a;一般来说&#xff0c;Intel处理器字节存储顺序为小端法存储&#xff0c;是指数据的高字节保…

基于Skywalking开发分布式监控(四)一个案例

上一篇我们简单介绍了基于SkyWalking自定义增强的基本架构&#xff0c;即通过把Trace数据导入数据加工模块进行加工&#xff0c;进行持久化&#xff0c;并赋能grafana展示。 现在我们给出一个例子&#xff0c;对于量化交易系统&#xff0c;市场交易订单提交&#xff0c;该订单…

关于springboot一个接口请求后,主动取消后,后端是否还在跑

1、最近在思考一个问题&#xff0c;如果一个springboot的请求的接口比较耗时&#xff0c;中途中断该请求后&#xff0c;则后端服务是否会终止该线程的处理&#xff0c;于是写了一个demo RequestMapping(value "/test", method RequestMethod.GET)public BasicResul…

云消息队列 Confluent 版正式上线!

作者&#xff1a;阿里云消息队列 前言 在 2023 年杭州云栖大会上&#xff0c;Confluent 成为阿里云技术合作伙伴&#xff0c;在此基础上&#xff0c;双方展开了深度合作&#xff0c;并在今天&#xff08;3月1日&#xff09;正式上线“云消息队列 Confluent 版”。 通过将 Co…

android基础学习

从上面的描述就可以知道&#xff0c;每一个Activity组件都有一个对应的ViewRoot对象、View对象以及WindowManager.LayoutParams对象。这三个对象的对应关系是由WindowManagerImpl类来维护的。具体来说&#xff0c;就是由WindowManagerImpl类的成员变量mRoots、mViews和mParams所…

【Apache Camel】基础知识

【Apache Camel】基础知识 Apache Camel是什么Apache Camel基本概念和术语CamelContextEndpointsRoutesRouteBuilderComponentsMessageExchangeProcessorsDomain Specific Language&#xff08;DSL&#xff09; Apache Camel 应用执行步骤Apache Camel 示意图参考 Apache Camel…

学习Java的第一天

一、Java简介 Java 是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 面向对象程序设计语言和 Java 平台的总称。由 James Gosling和同事们共同研发&#xff0c;并在 1995 年正式推出。 后来 Sun 公司被 Oracle &#xff08;甲骨文&#xff09;公司收购&#xff0c;Jav…

【AAAI2023】基于神经跨度的持续命名实体识别模型

论文标题&#xff1a;A Neural Span-Based Continual Named Entity Recognition Model 论文链接&#xff1a;https://arxiv.org/abs/2302.12200 代码&#xff1a;https://github.com/Qznan/SpanKL inproceedings{zhang2023spankl,title{A Neural Span-Based Continual Named En…

ElevenLabs用AI为Sora文生视频模型配音 ,景联文科技提供高质量真人音频数据集助力生成逼真音效

随着Open AI公司推出的Sora文生视频模型惊艳亮相互联网&#xff0c;AI语音克隆创企ElevenLabs又为Sora的演示视频生成了配音&#xff0c;所有的音效均由AI创造&#xff0c;与视频内容完美融合。 ElevenLabs的语音克隆技术能够从一分钟的音频样本中创建逼真的声音。为了实现这一…