【Ai生態開發】Spring AI上架,打造專屬業務大模型,AI開發再也不是難事!

大家好 這里是蘇澤 后端是工作 ai是興趣?

對于ai的產生我的立場是擁抱ai的? 是希望拿他作為提升能力的工具? 那么這一篇帶大家來學習如何使用ai打造一個專屬的業務大模型?

需求 就是說假設現在有一個 商城系統 里面有查詢訂單的api和獲取商品購買方式的api? ?用戶只需要輸入 “幫我看看我前幾天買過最便宜的衣服”? 經過語言處理 ai就能夠調用 查詢訂單的api并在里面自動的添加查詢條件以及 排序條件??這是我們的目標? 本文就是來講解實現這樣的目標

Spring AI介紹

Spring AI 是 AI 工程師的一個應用框架,它提供了一個友好的 API 和開發 AI 應用的抽象,旨在簡化 AI 應用的開發工序。

提供對常見模型的接入能力,目前已經上架 https://start.spring.io/,提供大家測試訪問。(請注意雖然已經上架 start.spring.io,但目前還是在 Spring 私服,未發布至 Maven 中央倉庫)

基本知識講解:

函數調用

函數調用(Function Calling)是OpenAI在2023年6月13日對外發布的新能力。根據OpenAI官方博客描述,函數調用能力可以讓大模型輸出一個請求調用函數的消息,其中包含所需調用的函數信息、以及調用函數時所攜帶的參數信息。這是一種將大模型(LLM)能力與外部工具/API連接起來的新方式。

比如用戶輸入:

What’s the weather like in Tokyo?

使用function calling,可實現函數執行get_current_weather(location: string),從而獲取函數輸出,即得到對應地理位置的天氣情況。這其中,location這個參數及其取值是借助大模型能力從用戶輸入中抽取出來的,同時,大模型判斷得到調用的函數為get_current_weather

開發人員可以使用大模型的function calling能力實現:

  • 在進行自然語言交流時,通過調用外部工具回答問題(類似于ChatGPT插件);
  • 將自然語言轉換為調用API調用,或數據庫查詢語句;
  • 從文本中抽取結構化數據
  • 其它

實現步驟

1. 添加依賴

 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-openai-spring-boot-starter</artifactId></dependency><!-- 配置 Spring 倉庫 --><repositories><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository></repositories>

2. 配置 OpenAI 相關參數

spring:
??ai:
????openai:
??????base-url:?#?支持?openai-sb、openai-hk?等中轉站點,如用官方則不填
??????api-key:?sk-xxxx
?

3.創建一個Spring Controller處理HTTP請求。

在Spring項目中創建一個Controller類,用于處理提取要素的HTTP請求和生成調用的API和變量集合。

import com.google.gson.Gson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.Map;@RestController
public class ElementExtractionController {@Autowiredprivate ElementExtractionService elementExtractionService;@PostMapping("/extract-elements")public ResponseEntity<Map<String, Object>> extractElements(@RequestBody String userInput) {Map<String, Object> result = elementExtractionService.extractElements(userInput);return ResponseEntity.ok(result);}
}

3.創建一個ElementExtractionService服務類來提取要素

創建一個服務類,用于封裝提取要素的邏輯。在這個服務類中,可以使用自然語言處理技術來分析用戶輸入并提取需求和變量。可以使用現有的開源NLP庫或API,如NLTK、SpaCy、Stanford CoreNLP、Google Cloud Natural Language API等
這里使用NLTK庫來進行文本分析和實體識別,以提取用戶輸入中的需求和變量:

import org.springframework.stereotype.Service;
import edu.stanford.nlp.simple.Document;
import edu.stanford.nlp.simple.Sentence;import java.util.HashMap;
import java.util.List;
import java.util.Map;@Service
public class ElementExtractionService {public Map<String, Object> extractElements(String userInput) {// 使用NLTK庫進行文本分析和實體識別Document doc = new Document(userInput);List<Sentence> sentences = doc.sentences();// 提取需求String requirement = extractRequirement(sentences);// 提取變量Map<String, String> variables = extractVariables(sentences);// 構建結果Map<String, Object> result = new HashMap<>();result.put("api", requirement);result.put("variables", variables);return result;}private String extractRequirement(List<Sentence> sentences) {// 在這里根據實際需求,從句子中提取需求// 可以使用關鍵詞提取、模式匹配等方法// 這里示例直接返回第一句話作為需求if (!sentences.isEmpty()) {return sentences.get(0).text();}return "";}private Map<String, String> extractVariables(List<Sentence> sentences) {// 在這里根據實際需求,從句子中提取變量// 可以使用實體識別、關鍵詞提取等方法// 這里示例直接從第一句話中提取名詞作為變量Map<String, String> variables = new HashMap<>();if (!sentences.isEmpty()) {Sentence sentence = sentences.get(0);for (String word : sentence.words()) {if (isNoun(word)) {variables.put(word, "true");}}}return variables;}private boolean isNoun(String word) {// 在這里根據實際需求,判斷一個詞是否為名詞// 可以使用詞性標注、詞典匹配等方法// 這里示例簡單判斷是否以大寫字母開頭,作為名詞的判斷條件return Character.isUpperCase(word.charAt(0));}
}

那么下一步 :


4.封裝一個API來操作open ai的Assistants API

創建一個Spring Service來操作OpenAI Assistants API。

創建一個服務類,用于封裝操作OpenAI Assistants API的邏輯。

import com.google.gson.Gson;
import okhttp3.*;import org.springframework.stereotype.Service;import java.io.IOException;@Service
public class OpenAIAssistantsService {public String callOpenAIAssistantsAPI(String prompt) {OkHttpClient client = new OkHttpClient();MediaType mediaType = MediaType.parse("application/json");JsonObject requestBody = new JsonObject();requestBody.addProperty("prompt", prompt);requestBody.addProperty("max_tokens", 32);requestBody.addProperty("stop", null);RequestBody body = RequestBody.create(mediaType, requestBody.toString());Request request = new Request.Builder().url(OPENAI_API_URL).post(body).addHeader("Authorization", "Bearer " + OPENAI_API_KEY).build();try {Response response = client.newCall(request).execute();if (response.isSuccessful()) {String responseBody = response.body().string();JsonObject jsonObject = new Gson().fromJson(responseBody, JsonObject.class);return jsonObject.getAsJsonObject("choices").get(0).getAsJsonObject().get("text").getAsString();} else {System.out.println("OpenAI Assistants API調用失敗: " + response.code() + " - " + response.message());}} catch (IOException e) {System.out.println("OpenAI Assistants API調用異常: " + e.getMessage());}return null;}
}

創建一個自定義函數簽名。

創建一個函數,它將調用其他項目中的API,并返回結果。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class CustomFunctionService {@Autowiredprivate OtherAPIService otherAPIService;public String customFunction(String apiId, String inputParameters) {// 根據API的ID篩選需要調用的APIString apiEndpoint = getApiEndpoint(apiId);// 調用其他項目中的API,并進行處理String result = otherAPIService.callOtherAPI(apiEndpoint, inputParameters);// 對結果進行處理,并返回return "處理后的結果:" + result;}private String getApiEndpoint(String apiId) {//這里還會有很多具體業務的api就不一一列舉了// 根據API的ID獲取相應的API的URL或其他信息// 這里可以根據實際情況進行實現if (apiId.equals("api1")) {return "https://api.example.com/api1";} else if (apiId.equals("api2")) {return "https://api.example.com/api2";} else {throw new IllegalArgumentException("無效的API ID: " + apiId);}}
}

創建一個Spring Controller來調用自定義函數。

創建一個Controller類,它將調用自定義函數,并返回結果。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.Map;@RestController
public class CustomFunctionController {@Autowiredprivate CustomFunctionService customFunctionService;@PostMapping("/call-custom-function")public ResponseEntity<String> callCustomFunction(@RequestBody String userInput) {String result = customFunctionService.customFunction(userInput);return ResponseEntity.ok(result);}
}

在上面提取要素的服務(ElementExtractionService)的基礎上,我們可以再封裝一個Assistants服務,它將接受用戶的請求并調用提取要素的服務。然后,Assistants服務將提取的要素和變量(uid)作為輸入傳遞給封裝了OpenAI的服務(OpenAIAssistantsService),并根據要素選擇適當的API進行調用,并返回對應的結果。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.Map;@Service
public class AssistantsService {@Autowiredprivate ElementExtractionService elementExtractionService;@Autowiredprivate OpenAIAssistantsService openAIAssistantsService;public String processUserRequest(String userInput) {// 提取要素Map<String, Object> elements = elementExtractionService.extractElements(userInput);// 獲取要素和變量String requirement = (String) elements.get("api");Map<String, String> variables = (Map<String, String>) elements.get("variables");String uid = (String) elements.get("uid");// 調用OpenAI Assistants服務String result = openAIAssistantsService.callOpenAIAssistantsAPI(requirement, variables, uid);return result;}
}

AssistantsService類接受用戶的請求,并調用ElementExtractionService來提取要素。然后,它獲取要素、變量和uid,并將它們作為參數傳遞給OpenAIAssistantsService的callOpenAIAssistantsAPI方法。該方法根據要素選擇適當的API進行調用,并返回結果。

具體的業務實現“提取要素”的邏輯部分

請注意,為了實現這個過程,還需要修改ElementExtractionService中提取要素的邏輯,以確保這個服務能符合具體業務的邏輯? 例如我提到的 “幫我看看我買過最便宜的衣服”

import org.springframework.stereotype.Service;
import edu.stanford.nlp.simple.Document;
import edu.stanford.nlp.simple.Sentence;import java.util.HashMap;
import java.util.List;
import java.util.Map;@Service
public class ElementExtractionService {public Map<String, Object> extractElements(String userInput) {// 使用NLTK庫進行文本分析和實體識別Document doc = new Document(userInput);List<Sentence> sentences = doc.sentences();// 提取需求String requirement = extractRequirement(sentences);// 提取變量Map<String, String> variables = extractVariables(sentences);// 構建結果Map<String, Object> result = new HashMap<>();result.put("api", requirement);result.put("variables", variables);return result;}private String extractRequirement(List<Sentence> sentences) {// 在這里根據實際需求,從句子中提取需求// 可以使用關鍵詞提取、模式匹配等方法// 這里示例直接返回第一句話作為需求if (!sentences.isEmpty()) {return sentences.get(0).text();}return "";}private Map<String, String> extractVariables(List<Sentence> sentences) {// 在這里根據實際需求,從句子中提取變量// 可以使用實體識別、關鍵詞提取等方法// 這里示例從第一句話中提取名詞作為變量,并根據特定模式進行匹配Map<String, String> variables = new HashMap<>();if (!sentences.isEmpty()) {Sentence sentence = sentences.get(0);List<String> words = sentence.words();for (int i = 0; i < words.size() - 1; i++) {String currentWord = words.get(i);String nextWord = words.get(i + 1);if (isNoun(currentWord) && nextWord.equals("的")) {variables.put(currentWord, "true");}}}return variables;}private boolean isNoun(String word) {// 在這里根據實際需求,判斷一個詞是否為名詞// 可以使用詞性標注、詞典匹配等方法// 這里示例簡單判斷是否以大寫字母開頭,作為名詞的判斷條件return Character.isUpperCase(word.charAt(0));}
}

我將extractVariables方法進行了修改。現在它從第一句話中提取名詞作為變量,并且根據特定模式進行匹配。特定模式是判斷當前詞是否為名詞,以及下一個詞是否為"的"。如果匹配成功,則將當前詞作為變量存儲。

這樣我們就基本實現了一開始的那個目標:

假設現在有一個 商城系統 里面有查詢訂單的api和獲取商品購買方式的api? ?用戶只需要輸入 “幫我看看我前幾天買過最便宜的衣服”? 經過語言處理 ai就能夠調用 查詢訂單的api并在里面自動的添加查詢條件以及 排序條件??這是我們的目標? 本文就是來講解實現這樣的目標

更長遠的目標:

希望能夠開發出一款中間件(作為一個服務被注冊到項目當中)?能夠作為open ai 和具體項目的橋梁? 即在開發配置當中我輸入我的已有項目的服務的簽名?? 那這個助手能夠根據用戶的自然語言輸入 自動的去調用執行 項目中已有的各種服務 來做各種各樣的復雜的數據庫查詢 等操作

本文所受啟發 參考文獻:

  1. Function calling and other API updates:?https://openai.com/blog/function-calling-and-other-api-updates
  2. OpenAI assistants in LangChain:?https://python.langchain.com/docs/modules/agents/agent_types/openai_assistants
  3. Multi-Input Tools in LangChain:?https://python.langchain.com/docs/modules/agents/tools/multi_input_tool
  4. examples/Assistants_API_overview_python.ipynb:?https://github.com/openai/opena...
  5. The Spring Boot Actuator is the one dependency you should include in every project (danvega.dev)
  6. Assistants API won't allow external web request - API - OpenAI Developer Forum

?

本文只是簡單提供一個可行的思路做參考 真正做出可拓展性的ai開發插件道路還很長 先在這立個小flag吧? 希望今年能夠完成這個小目標? 如果有一起開發這個項目的伙伴可以跟我來討論哦

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/711751.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/711751.shtml
英文地址,請注明出處:http://en.pswp.cn/news/711751.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C++二叉搜樹的實現(遞歸和非遞歸)

目錄 1.什么是二叉搜索樹 2.二叉搜索樹的查找 3.二叉搜索樹插入 4.二叉搜索樹的刪除 1.刪除的節點只有左子樹或者右子樹 2.刪除節點左右子樹都有的情況 5.代碼 1.什么是二叉搜索樹 左節點的值小于根節點 右節點大于根節點 左右子樹也滿足上面兩個條件 例&#xff1a;…

平臺工程與安全

平臺工程不是為了取代DevOps&#xff0c;而是DevOps的進一步演進和發展。本文介紹了DevOps和平臺工程&#xff0c;以及對于安全的意義。原文: Platform Engineering and Security: A Very Short Introduction 中國云南大理的日落 我是一名 DevOps 工程師&#xff0c;個人還是希…

c# 調用存儲過程

1.調用返回OUT的存儲過程 a.調用OUT字符串的存儲過程&#xff1b; #region 連接數據庫/// <summary>/// 連接數據庫/// </summary>/// <param name"connStatus"></param>/// <param name"dbNode"></param>/// <ret…

Android WebView訪問網頁+自動播放視頻+自動全屏+切換橫屏

一、引言 近期&#xff0c;我發現電視家、火星直播等在線看電視直播的軟件都已倒閉&#xff0c;而我奶奶也再無法通過這些平臺看電視了。她已六十多歲&#xff0c;快七十歲啦。這些平臺的倒下對我來說其實沒有多大的影響&#xff0c;但是對于文化不多的她而言&#xff0c;生活中…

Linux下的時間同步,以及ntp時間服務器配置流程

Linux下的時間同步&#xff0c;以及ntp時間服務器配置流程 概論常見時間操作命令Linux下的系統時間配置Linux硬件的時間的設置系統時間和硬件時間的同步NTP服務器時間的同步NTP服務的安裝NTP的時間同步定時任務里的時間同步配置文件同步時間 概論 但在Linux下&#xff0c;系統…

SpringBoot中間件簡介

Spring Boot是一個Java框架&#xff0c;它提供了一系列中間件來簡化應用程序的開發和集成。以下是一些常見的Spring Boot中間件&#xff1a; Web中間件&#xff1a; Servlet容器&#xff08;內嵌Tomcat、Jetty或Undertow&#xff09; Spring MVC&#xff08;用于構建Web應用程…

HBuilderX創建uniapp項目使用 tailwindcss

文章目錄 一、創建package.json文件二、打開終端 yarn / npm 安裝依賴三、創建 vue.config.js文件四、創建postcss.config.js文件五、創建tailwind.config.js文件六、App.vue文件的style中引入tailwindcss 一、創建package.json文件 {"devDependencies": {"aut…

藍橋杯算法 一.

分析&#xff1a; 本題記錄&#xff1a;m個數&#xff0c;異或運算和為0&#xff0c;則相加為偶數&#xff0c;后手獲勝。 分析&#xff1a; 369*99<36500&#xff0c;369*100>36500。 注意&#xff1a;前綴和和后綴和問題

知識(202402)

1.Conditional Conditional來源于spring-context包下的一個注解。Conditional中文是條件的意思&#xff0c;Conditional注解它的作用是按照一定的條件進行判斷&#xff0c;滿足條件給容器注冊bean。 可以控制一個配置類是否注入到容器中&#xff0c;比如控制xxl-job不自動注冊…

【wpf】關于綁定的一點明悟

背景簡介 軟件功能為&#xff0c;讀取一個文件夾下的所有子文件夾&#xff0c;每個文件夾對自動對應生成 一組 “按鍵四個勾選” 按鍵點擊觸發&#xff0c;可以發送與其對應文件夾中的一些內容。這個綁定的過程我在之前的文章有過詳細的介紹&#xff0c;非常的簡單。 這里回顧…

3月1日做題總結(靜態庫與動態庫)

前言 最近學到了靜態庫和動態庫的相關知識&#xff0c;就順便整理了一下相關題目。如果對靜態庫和動態庫知識不熟悉的同學&#xff0c;推薦看這篇文章——《靜態庫與動態庫》&#xff0c;講的很詳細。 第一題 關于靜態庫與動態庫的區別&#xff0c;以下說法錯誤的是&#xff…

mac jupyter使用現有的python環境

mood&#xff1a;python 編程真的是在反復的與自己和解啊 本來超級的畏難情緒 讀會兒書 計算機博士的書 感覺還是要堅強的。《研磨記》--一位博士生的回憶錄 作者技術真的強啊 正文開始&#xff1a; 聚焦搜索&#xff0c;打開終端激活虛擬環境&#xff1a;conda activate pyt…

力扣爆刷第83天之hot100五連刷1-5

力扣爆刷第83天之hot100五連刷1-5 文章目錄 力扣爆刷第83天之hot100五連刷1-5一、1. 兩數之和二、49. 字母異位詞分組三、128. 最長連續序列四、283. 移動零五、11. 盛最多水的容器 一、1. 兩數之和 題目鏈接&#xff1a;https://leetcode.cn/problems/two-sum/description/?…

javascript中使用‘use strict’和不使用的區別

錯誤處理&#xff1a; 嚴格模式使得 JavaScript 對某些可能的問題拋出錯誤&#xff0c;而在非嚴格模式下&#xff0c;這些問題可能會被忽略。例如&#xff0c;未聲明的變量&#xff08;即全局變量&#xff09;在非嚴格模式下會被隱式地創建為全局變量&#xff0c;而在嚴格模式…

十一、 二進制位運算

描述 Python有位運算&#xff0c;是直接將數字看成二進制&#xff0c;直接對二進制數字的每一位進行運算。現輸入兩個十進制整數x、y&#xff0c;請計算它們的位與、位或&#xff0c;輸出按照十進制的形式。 輸入描述&#xff1a; 一行輸入兩個整數x、y&#xff0c;以空格間…

git:合并兩個不同倉庫的代碼

有兩個代碼倉庫&#xff1a;代碼倉庫A、代碼倉庫B&#xff0c;其中一個倉庫的代碼是為了新項目拉取的新分支&#xff0c;所以分支的部分修改歷史是相同的 現在要將代碼倉庫B 的代碼合并到代碼倉庫A 實現思路&#xff1a;分支合并 實現步驟&#xff1a; # 1、clone代碼倉庫A…

外匯天眼:ASIC 獲得針對前 Blockchain Global 董事的臨時出行限制令

澳大利亞證券與投資委員會&#xff08;ASIC&#xff09;已經針對前Blockchain Global Limited&#xff08;清算中&#xff09;董事梁國&#xff08;又名Allan Guo&#xff09;獲得了臨時旅行限制令。這些命令在其他方面&#xff0c;阻止郭先生在2024年8月20日或進一步命令之前離…

(done) 如何計算 Hessian Matrix 海森矩陣 海塞矩陣

參考視頻1&#xff1a;https://www.bilibili.com/video/BV1H64y1T7zQ/?spm_id_from333.337.search-card.all.click 參考視頻2&#xff08;正定矩陣&#xff09;&#xff1a;https://www.bilibili.com/video/BV1Ag411M76G/?spm_id_from333.337.search-card.all.click&vd_…

【JGit】 AddCommand 新增的文件不能添加到暫存區

執行git.add().addFilepattern(".").setUpdate(true).call() 。新增的文件不能添加到暫存區&#xff0c;為什么&#xff1f; 在 JGit 中&#xff0c;setUpdate(true) 方法用于在調用 AddCommand 的 addFilepattern() 方法時&#xff0c;將已跟蹤文件標記為需要更新。…

C語言基礎—習題及代碼(一)

1.讀取一個65到122之間的整型數&#xff0c;然后以字符形式輸出它&#xff0c;比如讀取了97&#xff0c;輸出字符a #include <stdio.h> int main(){int n;scanf("%d",&n);if(n>65 && n<122){printf("%c\n",n);} } 2.判斷某個年份…