使用SpringAI快速实现离线/本地大模型应用
前言
大模型(LLM),Large Language Model作为当前比较热门的技术,最近在年在各行各业中都得到了广泛的应用。
在我们目前使用较多的AI产品中,几乎都是使用的互联网(云端)上的AI工具,即:需要先把数据传输给AI平台,由AI平台处理后,再将信息回馈到我们的本地应用。
然而在许多领域,由于大模型的数据没有采集到更细化的信息,亦或者出于安全原因某些数据不能对外公开,这时使用离线大模型来实现信息的生成与检索则变得非常重要。
很久没用Spring的我,最近看到Spring官网出了Spring AI这个框架,出于好奇体验了下感觉非常不错,在此写篇博文记录下。
本文重点为:如何快速从0到1搭建一个离线大模型,并使用SpringAI进行交互调用。
ollama介绍与安装
ollama作为一个工具(且开源),让大模型的安装与运行变得非常简单。
ollama支持多种操作系统,为了方便可以直接使用Docker运行。
下载命令一行搞定:
sudo docker pull ollama/ollama:latest
ollama上手
ollama下载好后,直接运行
#运行ollama,并指定挂载目录和映射端口
docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama
#进入ollama容器
docker exec -it ollama bash
#运行ollama命令pull一个大模型,这里拉取具有图像识别能力的minicpm-v
ollama pull minicpm-v
ollama支持的大模型非常多,如google的gemma、facebook的llama、阿里的qwen通通都有,按需所取。
模型仓库地址为:https://ollama.com/library
大模型下载好了后,就可以使用ollama run
命令运行对应的模型,并可以进行命令行的文本交互
ollama run minicpm-v
open-webui安装
为了能获得更好的体验,可以使用开源的open-webui进行来访问离线大模型,UI界面和ChatGPT的非常像。
docker下拉取命令:
sudo docker pull ghcr.io/open-webui/open-webui:main
拉取好后直接运行:
docker run -d --network=host -v open-webui:/app/backend/data -e OLLAMA_BASE_URL=http://127.0.0.1:11434 --name open-webui --restart always ghcr.io/open-webui/open-webui:main
这里笔者为了方便使用的是host网络,默认暴露的端口为8080,指定了webui存储数据的目录为/app/backend/data
,并指定了ollama的后端地址为http://127.0.0.1:11434
基本功能体验
通过上面几行命令安装好了ollama与open-webui,之后就可以体验下离线大模型的常用功能了。
访问方式:直接访问open-webui的ip+port就行。
如我这里的docker物理机为192.168.140.8,则访问地址为http://192.168.140.8:8080
智能对话
首次访问open-webui需要先本地注册,随便填一个账号信息就行,并且首次注册的账号也是管理员账号。
由于我前面下载的大模型为minicpm-v,它是支持中文,效果如下:
图片识别
如minicpm-v的介绍所述
minicpm-v模型支持图像和视频识别,传一个兴隆湖的照片让它试一下。
识别效果也还可以
文生图
文生图方面比较知名的开源大模型为StableDiffusion,为了方便我们可以使用在StableDiffusion基础上进行二次开发的ComfyUI。
关于comfyui的安装方式可以参考此链接https://comfyui-wiki.com/zh-CN进行学习。
要注意的是,要让open-webui能成功调用ComfyUI进行文生图,需要确保ComfyUI的监听端口能访问到,且开启了DevMode。
之后再进入open-webui的设置界面的图像栏进行设置,在其中填入ComfyUI的接口地址与模型配置即可。
配置好了之后再回到对话界面,如有符合可以生成图片的提示词界面上就会出点一个图像按钮,点一下即可生成图片
以生成一个小女孩的需求为例,演示一下:
上面例子中输入的关键字为:回复提示词"A girl",仅回复提示词,不要回复别的信息
如想让提示词更加完善,可以使用https://ollama.com/impactframes/llama3_ifai_sd_prompt_mkr_q4km这个模型,它可以自动完善提示词以让生成的图像更丰富。
ollama run impactframes/llama3_ifai_sd_prompt_mkr_q4km
用它生成图像时,就可以这样进行对话:
Write prompts for “A smiling girl,in bikini.”. Only response the prompts for the image and nothing else.
在OpenWebUi中调用文生图,其最终都会调用到本地的ComfyUI里,大量生成图片、动画、视频的需求更建议直接使用ComfyUI,效果更佳
spring-ai
前面介绍了如何使用ollama与open-webui快速搭建一个离线大模型平台,并体验了AI的相关功能。
但在实际业务场景中,前端程序往往与后端平台进行对接,与大模型的交互由后端程序来负责接入则更为合适。
站在这一维度考虑,使用Spring AI来接入大模型则是一个不错的选择。
关于Spring AI的详细介绍可参考:https://spring.io/projects/spring-ai
Spring AI is an application framework for AI engineering. Its goal is to apply to the AI domain Spring ecosystem design principles such as portability and modular design and promote using POJOs as the building blocks of an application to the AI domain.
翻译一下:Spring AI是一个AI工程框架。它的目标是将Spring生态的设计原则应用到AI领域,比如可移植性和模块化,并推广使用POJO来构建AI生态。
换句话讲:Spring AI不生产AI,只是AI的搬运工
快速接入
尽管Spring AI目前还处于开发阶段,但已经提供了使用文档,以让我们能体验并使用Spring AI的基本功能。接入方式说明可参考:getting-started
Spring AI支持接入的大模型较多,这里以接入离线的ollama为例,记录下接入过程
pom依赖
新建一个maven项目,其pom.xml内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.3.0</version><relativePath/></parent><groupId>com.haiyang</groupId><artifactId>spring-ai-demo</artifactId><version>0.0.1-SNAPSHOT</version><name>spring-ai-demo</name><description>spring-ai-demo</description><properties><java.version>17</java.version><spring-ai.version>1.0.0-SNAPSHOT</spring-ai.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-ollama-spring-boot-starter</artifactId></dependency></dependencies><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><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build><repositories><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository><repository><id>spring-snapshots</id><name>Spring Snapshots</name><url>https://repo.spring.io/snapshot</url><releases><enabled>false</enabled></releases></repository></repositories>
</project>
需要了解到的是:目前Spring AI还没有正式版,所以需要添加repository标签以能从snapshot仓库下载到依赖。
大模型接口配置
在SpringBoot项目的配置文件中添加配置项,在application.properties
中新增:
#配置ollama接口地址
spring.ai.ollama.base-url=http://192.168.140.8:11434/
#配置使用的ollama模型
spring.ai.ollama.chat.options.model=gemma:7b
启动类与接口实现
在Application类中创建ChatClient
@SpringBootApplication
public class SpringAiDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringAiDemoApplication.class, args);}@Beanpublic ChatClient chatClient(ChatClient.Builder builder) {return builder.build();}
}
在controller中添加一个接口
@RestController
public class AIController {@Resourceprivate ChatClient chatClient;@GetMapping("/ai")public Map<String, String> completion(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {return Map.of("generation",Objects.requireNonNull(chatClient.prompt().user(message).call().content()));}
}
以上,一个基于集成了gemma的离线大模型后端就实现好了。是不是非常简单
对话接口验证
访问http://127.0.0.1:8080/ai
试一下
试一下携带参数,message=成都有哪些好玩的地方
stream消息接口实现
我们知道智能对话的底层是AIGC,也就是它的结果是一个字一个字冒出来的,为了视觉上更好的直观体验,我们也一般采用实时更新的方式将冒出的字词一个个通知到前端。
用于后端向前端实时通信的方案有很多,为了实现性能好开销低,推荐使用SSE(Server-Sent Events)的方式进行字符(token)的推送
HTTP 短轮询、HTTP 长轮询、WebSocket、SSE 的顺序重新排列的对比表格
在Spring AI中,使用自带的ChatModel就可以实现。示例接口代码如下:
@GetMapping("/ai/generateStream")public Flux<ChatResponse> generateStream(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {Prompt prompt = new Prompt(new UserMessage(message));return this.chatModel.stream(prompt);}
使用stream返回数据时,则可以看到前端收到的消息则是一个字一个字发下去的了。
再搭配Vaadin实现一个前端界面,源码为:
@Route("")
public class HaiyangAIView extends VerticalLayout {public HaiyangAIView(ChatService chatService) {String chatId = chatService.establishChat();var messageList = new VerticalLayout();var messageInput = new MessageInput();messageInput.setWidthFull();messageInput.addSubmitListener(e -> submitPromptListener(chatId, e, chatService, messageList));addClassName("centered-content");add(messageList, messageInput);}private static void submitPromptListener(String chatId, MessageInput.SubmitEvent e, ChatService chatService, VerticalLayout messageList) {var question = e.getValue();var userMessage = new MarkdownMessage(question, "You", Color.AVATAR_PRESETS[6]);var assistantMessage = new MarkdownMessage("haiyangAI", Color.AVATAR_PRESETS[0]);messageList.add(userMessage, assistantMessage);chatService.chat(chatId, question).map(res -> Optional.ofNullable(res.getResult().getOutput().getContent()).orElse("")).subscribe(assistantMessage::appendMarkdownAsync);}
}
再看一下效果,应答内容就动起来了
总结
使用Spring AI与ollama进行离线大模型平台的搭建又快又简单。
为Spring AI点赞,向ollama与各种开源大模型致敬!
本文源码地址:https://github.com/puhaiyang/springai-demo