借助 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
繁體地址,請注明出處:http://hk.pswp.cn/news/908328.shtml
英文地址,請注明出處:http://en.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.利用…