赞
踩
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能力实现:
- <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>

spring:
ai:
openai:
base-url: # 支持 openai-sb、openai-hk 等中转站点,如用官方则不填
api-key: sk-xxxx
在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 {
-
- @Autowired
- private 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);
- }
- }

创建一个服务类,用于封装提取要素的逻辑。在这个服务类中,可以使用自然语言处理技术来分析用户输入并提取需求和变量。可以使用现有的开源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));
- }
- }

创建一个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;
- }
- }

- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
-
- @Service
- public class CustomFunctionService {
-
- @Autowired
- private OtherAPIService otherAPIService;
-
- public String customFunction(String apiId, String inputParameters) {
- // 根据API的ID筛选需要调用的API
- String 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);
- }
- }
- }

- 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 {
-
- @Autowired
- private 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 {
-
- @Autowired
- private ElementExtractionService elementExtractionService;
-
- @Autowired
- private 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 和具体项目的桥梁 即在开发配置当中我输入我的已有项目的服务的签名 那这个助手能够根据用户的自然语言输入 自动的去调用执行 项目中已有的各种服务 来做各种各样的复杂的数据库查询 等操作
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。