借助 Spring AI 和 LM Studio 为业务系统引入本地 AI 能力

  • Spring AI 1.0.0-SNAPSHOT
  • LM Studio 0.3.16
  • qwen3-4b

参考 Unable to use spring ai with LMStudio using spring-ai openai module · Issue #2441 · spring-projects/spring-ai · GitHub 

LM Studio

  1. 下载安装 LM Studio
  2. 下载 qwen3-4b 模型。对于 qwen3 系列模型,测试发现小于 4b 的模型效果不太好。可以根据需要替换为别的模型,遵循原则:Bigger is better。
  3. 加载模型,启动服务
  4. 打开日志,方便观察

业务集成 

依赖

测试使用的是 SpringBoot 3.4.5,JDK 21,实际 SpringBoot 3及以上,JDK 17及以上即可。

<dependencyManagement><dependencies><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>1.0.0</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement><dependencies><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-openai</artifactId></dependency>
</dependencies>

和 LM Studio 交互使用 openai 的依赖。

代码

编写自己的工具,供 AI 调用。这里提供几个简单的示例。

ToolMcp,提供查询当前时间的工具。

import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;import java.text.SimpleDateFormat;
import java.util.Date;@Component
public class ToolMcp {private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");@BeanToolCallbackProvider toolCallbackProvider() {return MethodToolCallbackProvider.builder().toolObjects(this).build();}@Tool(description = "查询当前日期时间")public String queryTime() {System.out.println("查询当前时间");return DATE_FORMAT.format(new Date());}
}

@Tool 注解将普通方法注册为工具,@Bean ToolCallbackProvider 将当前 Bean 注册为 Provider,之后要使用。

HelloMcp,提供打招呼的工具。

import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;@Component
public class HelloMcp {@BeanToolCallbackProvider helloToolCallbackProvider() {return MethodToolCallbackProvider.builder().toolObjects(this).build();}@Tool(description = "say hello")public String hello() {return "hello, devops";}@Tool(description = "say hello to someone", returnDirect = true)public String helloTo(@ToolParam(description = "name of the guy you want to say hello to") String name) {System.out.println("Hello, " + name);return "Hello, " + name;}
}

AIController,提供外部访问的接口

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.model.NoopApiKey;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.client.JdkClientHttpRequestFactory;
import org.springframework.http.client.reactive.JdkClientHttpConnector;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;import java.net.http.HttpClient;
import java.time.Duration;
import java.util.List;@RestController
public class AIController {@Autowiredprivate OpenAiChatModel chatModel;@Autowiredprivate List<ToolCallbackProvider> providerList;@Autowiredprivate ChatMemory chatMemory;// @GetMapping(value = "/ai/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)@GetMapping(value = "/ai/chat")public String generate(@RequestParam(value = "message", defaultValue = "") String message) {OpenAiApi openAiApi = OpenAiApi.builder().baseUrl("http://<LM Studio提供服务的IP>:1234").apiKey(new NoopApiKey()).webClientBuilder(WebClient.builder()// Force HTTP/1.1 for streaming.clientConnector(new JdkClientHttpConnector(HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).connectTimeout(Duration.ofSeconds(200)).build()))).restClientBuilder(RestClient.builder()// Force HTTP/1.1 for non-streaming.requestFactory(new JdkClientHttpRequestFactory(HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).connectTimeout(Duration.ofSeconds(200)).build()))).build();OpenAiChatModel aiChatModel = new OpenAiChatModel.Builder(chatModel.clone()).openAiApi(openAiApi).build();ChatClient chatClient = ChatClient.builder(aiChatModel).defaultSystem("缺少信息时询问用户,不要自己做决定。" +"需要当前时间时调用工具queryTime。时间格式统一使用:yyyy-MM-dd HH:mm:ss").defaultToolCallbacks(providerList.toArray(new ToolCallbackProvider[0])).defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()).build();String conversationId = "007";ChatClient.CallResponseSpec call = chatClient.prompt().user(message).advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId)).call();return call.content();// return chatClient.prompt()//         .user(message)//         .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId))//         .stream()//         .content();}
}

application.yml 添加参数

spring:ai:openai:api-key: sk-anykeybase-url: http://<LM Studio提供服务的IP>:1234chat:options:model: qwen3-4b

因为是使用 LM Studio ,所以 api-key 可以随意填写。base-url 填写 LM Studio 提供服务的 IP 和端口。model 需要填写 LM Studio 加载的模型名称。LM Studio 可以不预先加载模型,它可以根据请求的模型自动加载,详情看 LM Studio 的设置项。

更详细的内容之后补充。

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

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

相关文章

C++学习-入门到精通【13】标准库的容器和迭代器

C学习-入门到精通【13】标准库的容器和迭代器 目录 C学习-入门到精通【13】标准库的容器和迭代器一、标准模板库简介1.容器简介2.STL容器总览3.近容器4.STL容器的通用函数5.首类容器的通用typedef6.对容器元素的要求 二、迭代器简介1.使用istream_iterator输入&#xff0c;使用…

Vue Router的核心实现原理深度解析

1. Vue Router的基本架构 Vue Router的核心功能是实现前端路由&#xff0c;即在不重新加载页面的情况下更改应用的视图。它的基本架构包括&#xff1a; 路由配置&#xff1a;定义路径与组件的映射关系路由实例&#xff1a;管理路由状态和提供导航方法路由视图&#xff1a;渲染…

设计模式——状态设计模式(行为型)

摘要 状态设计模式是一种行为型设计模式&#xff0c;核心在于允许对象在内部状态改变时改变行为。它通过状态对象封装不同行为&#xff0c;使状态切换灵活清晰。该模式包含环境类、抽象状态类和具体状态类等角色&#xff0c;具有避免大量分支判断、符合单一职责和开闭原则等特…

C++ 观察者模式:设计与实现详解

一、引言 在现代软件开发中,组件间的交互与通信是系统设计的核心挑战之一。观察者模式(Observer Pattern)作为一种行为设计模式,提供了一种优雅的解决方案,用于实现对象间的一对多依赖关系。本文将深入探讨 C++ 中观察者模式的设计理念、实现方式及其应用场景。 二、观察…

Windows 账号管理与安全指南

Windows 账号管理与安全指南 概述 Windows 账号管理是系统安全的基础&#xff0c;了解如何正确创建、管理和保护用户账户对于系统管理员和安全专业人员至关重要。本文详细介绍 Windows 系统中的账户管理命令、隐藏账户创建方法以及安全防护措施。 基础账户管理命令 net use…

[蓝桥杯]摆动序列

摆动序列 题目描述 如果一个序列的奇数项都比前一项大&#xff0c;偶数项都比前一项小&#xff0c;则称为一个摆动序列。即 a2i<a2i−1,a2i1 >a2ia2i​<a2i−1​,a2i1​ >a2i​。 小明想知道&#xff0c;长度为 mm&#xff0c;每个数都是 1 到 nn 之间的正整数的…

Python 网络编程 -- WebSocket编程

作者主要是为了用python构建实时网络通信程序。 概念性的东西越简单越好理解,因此,下面我从晚上摘抄的概念 我的理解。 什么是网络通信? 更确切地说&#xff0c;网络通信是两台计算机上的两个进程之间的通信。比如&#xff0c;浏览器进程和新浪服务器上的某个Web服务进程在通…

GM DC Monitor如何实现TCP端口状态监控-操作分享

本节讲解如何通过现有指标提取监控脚本制作自定义的TCP端口监控指标 一、功能介绍 通过提取已有的监控指标的监控命令&#xff0c;来自定义TCP端口的监控指标。 二、配置端口监控 1&#xff09;定位监控脚本 确定脚本及参数如下&#xff1a; check_protocol_tcp.pl --plug…

LabVIEW与Modbus/TCP温湿度监控系统

基于LabVIEW 开发平台与 Modbus/TCP 通信协议&#xff0c;设计一套适用于实验室环境的温湿度数据采集监控系统。通过上位机与高精度温湿度采集设备的远程通信&#xff0c;实现多设备温湿度数据的实时采集、存储、分析及报警功能&#xff0c;解决传统人工采集效率低、环境适应性…

Ntfs!ReadIndexBuffer函数分析之nt!CcGetVirtualAddress函数之nt!CcGetVacbMiss

第一部分&#xff1a; NtfsMapStream( IrpContext, Scb, LlBytesFromIndexBlocks( IndexBlock, Scb->ScbType.Index.IndexBlockByteShift ), Scb->ScbType.Index.BytesPerIndexBuffer, &am…

vite+vue3项目中,单个组件中使用 @use报错

报错信息&#xff1a; [plugin:vite:css] [sass] use rules must be written before any other rules.use 官方说明 注意事项&#xff1a; https://sass-lang.com/documentation/at-rules/use/ 样式表中的 use 规则必须位于所有其他规则&#xff08;除 forward 外&#xff0…

基于VMD-LSTM融合方法的F10.7指数预报

F10.7 Daily Forecast Using LSTM Combined With VMD Method ​​F10.7​​ solar radiation flux is a well-known parameter that is closely linked to ​​solar activity​​, serving as a key index for measuring the level of solar activity. In this study, the ​​…

React 新项目

使用git bash 创建一个新项目 建议一开始就创建TS项目 原因在Webpack中改配置麻烦 编译方法:ts compiler 另一种 bable 最好都配置 $ create-react-app cloundmusic --template typescript 早期react项目 yarn 居多 目前npm包管理居多 目前pnpm不通用 icon 在public文件夹中…

2025年- H65-Lc173--347.前k个高频元素(小根堆,堆顶元素是当前堆元素里面最小的)--Java版

1.题目描述 2.思路 &#xff08;1&#xff09;这里定义了一个小根堆&#xff08;最小堆&#xff09;&#xff0c;根据元素的频率从小到大排序。小根堆原理&#xff1a;堆顶是最小值&#xff0c;每次插入或删除操作会保持堆的有序结构&#xff08;常用二叉堆实现&#xff09;。 …

VR/AR 显示瓶颈将破!铁电液晶技术迎来关键突破

在 VR/AR 设备逐渐走进大众生活的今天&#xff0c;显示效果却始终是制约其发展的一大痛点。纱窗效应、画面拖影、眩晕感…… 传统液晶技术的瓶颈让用户体验大打折扣。不过&#xff0c;随着铁电液晶技术的重大突破&#xff0c;这一局面有望得到彻底改变。 一、传统液晶技术瓶颈…

【bug】Error: /undefinedfilename in (/tmp/ocrmypdf.io.9xfn1e3b/origin.pdf)

在使用ocrmypdf的时候&#xff0c;需要Ghostscript9.55及以上的版本&#xff0c;但是ubuntu自带为9.50 然后使用ocrmypdf报错了 sudo apt update sudo apt install ghostscript gs --version 9.50 #版本不够安装的版本为9.50不够&#xff0c;因此去官网https://ghostscript.c…

【TinyWebServer】线程同步封装

目录 POSIX信号量 int sem_init(sem_t* sem,int pshared,unsingned int value); int sem_destroy(sem_t* sem); int sem_wait(sem_t* sem); int sem_post(sem_t* sem); 互斥量 条件变量 为了对多线程程序实现同步问题&#xff0c;可以用信号量POSIX信号量、互斥量、条件变…

打造高效多模态RAG系统:原理与评测方法详解

引言 随着信息检索与生成式AI的深度融合&#xff0c;检索增强生成&#xff08;RAG, Retrieval-Augmented Generation&#xff09; 已成为AI领域的重要技术方向。传统RAG系统主要依赖文本数据&#xff0c;但真实世界中的信息往往包含图像、表格等多模态内容。多模态RAG&#xf…

Unity安卓平台开发,启动app并传参

using UnityEngine; using System;public class IntentReceiver : MonoBehaviour {public bool isVR1;void Start(){Debug.LogError("app1111111111111111111111111");if (isVR1){LaunchAnotherApp("com.HappyMaster.DaKongJianVR2");}else{// 检查是否有传…

云计算 Linux Rocky day05【rpm、yum、history、date、du、zip、ln】

云计算 Linux Rocky day05【rpm、yum、history、date、du、zip、ln】 目录 云计算 Linux Rocky day05【rpm、yum、history、date、du、zip、ln】1.RPM包的一般安装位置2.软件名和软件包名3.查询软件信息4.查询软件包5.导入红帽签名信息&#xff0c;解决查询软件包信息报错6.利用…