开源模型应用落地-工具使用篇-Spring AI-Function Call(八)

ops/2025/1/23 8:21:43/

一、前言

通过“开源模型应用落地-工具使用篇-Spring AI(七)-CSDN博客”文章的学习,已经掌握了如何通过Spring AI集成OpenAI和Ollama系列的模型,现在将通过进一步的学习,让Spring AI集成大语言模型更高阶的用法,使得我们能完成更复杂的需求。


二、术语

2.1、Spring AI

是 Spring 生态系统的一个新项目,它简化了 Java 中 AI 应用程序的创建。它提供以下功能:

  • 支持所有主要模型提供商,例如 OpenAI、Microsoft、Amazon、Google 和 Huggingface。
  • 支持的模型类型包括“聊天”和“文本到图像”,还有更多模型类型正在开发中。
  • 跨 AI 提供商的可移植 API,用于聊天和嵌入模型。
  • 支持同步和流 API 选项。
  • 支持下拉访问模型特定功能。
  • AI 模型输出到 POJO 的映射。

2.2、Function Call

是 GPT API 中的一项新功能。它可以让开发者在调用 GPT系列模型时,描述函数并让模型智能地输出一个包含调用这些函数所需参数的 JSON 对象。这种功能可以更可靠地将 GPT 的能力与外部工具和 API 进行连接。

简单来说就是开放了自定义插件的接口,通过接入外部工具,增强模型的能力。

Spring AI集成Function Call:

Function Calling :: Spring AI Reference


三、前置条件

3.1、JDK 17+

下载地址:Java Downloads | Oracle

3.2、创建Maven项目

SpringBoot版本为3.2.3

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.3</version><relativePath/> <!-- lookup parent from repository -->
</parent>

3.3、导入Maven依赖包

<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-core</artifactId>
</dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId>
</dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-core</artifactId><version>5.8.24</version>
</dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-openai-spring-boot-starter</artifactId><version>0.8.0</version>
</dependency>

3.4、 科学上网的软件


四、技术实现

4.1、新增配置

spring:ai:openai:api-key: sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxchat:options:model: gpt-3.5-turbotemperature: 0.45max_tokens: 4096top-p: 0.9

PS:

  1. openai要替换自己的api-key
  2. 模型参数根据实际情况调整

4.2、新增本地方法类(用于本地回调的function)

import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import lombok.extern.slf4j.Slf4j;import java.util.function.Function;@Slf4j
public class WeatherService implements Function<WeatherService.Request, WeatherService.Response> {/*** Weather Function request.*/@JsonInclude(JsonInclude.Include.NON_NULL)@JsonClassDescription("Weather API request")public record Request(@JsonProperty(required = true,value = "location") @JsonPropertyDescription("The city and state e.g.广州") String location) {}/*** Weather Function response.*/public record Response(String weather) {}@Overridepublic WeatherService.Response apply(WeatherService.Request request) {log.info("location: {}", request.location);String weather = "";if (request.location().contains("广州")) {weather = "小雨转阴 13~19°C";} else if (request.location().contains("深圳")) {weather = "阴 15~26°C";} else {weather = "热到中暑 99~100°C";}return new WeatherService.Response(weather);}
}

4.3、新增配置类

import org.springframework.ai.model.function.FunctionCallback;
import org.springframework.ai.model.function.FunctionCallbackWrapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Description;import java.util.function.Function;@Configuration
public class FunctionConfig {@Beanpublic FunctionCallback weatherFunctionInfo() {return new FunctionCallbackWrapper<WeatherService.Request, WeatherService.Response>("currentWeather", // (1) function name"Get the weather in location", // (2) function descriptionnew WeatherService()); // function code}
}

4.4、新增Controller类

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
import org.springframework.ai.openai.OpenAiChatClient;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;import java.util.List;@Slf4j
@RestController
@RequestMapping("/api")
public class OpenaiTestController {@Autowiredprivate OpenAiChatClient openAiChatClient;@RequestMapping("/function_call")public String function_call(){String systemPrompt = "{prompt}";SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPrompt);String userPrompt = "广州的天气如何?";Message userMessage = new UserMessage(userPrompt);Message systemMessage = systemPromptTemplate.createMessage(MapUtil.of("prompt", "你是一个有用的人工智能助手"));Prompt prompt = new Prompt(List.of(userMessage, systemMessage), OpenAiChatOptions.builder().withFunction("currentWeather").build());List<Generation> response = openAiChatClient.call(prompt).getResults();String result = "";for (Generation generation : response){String content = generation.getOutput().getContent();result += content;}return result;}
}

五、测试

调用结果:

浏览器输出:

idea输出:


六、附带说明

6.1、流式模式不支持Function Call

6.2、更多的模型参数配置

OpenAI Chat :: Spring AI Reference

6.3、qwen系列模型如何支持function call

通过vllm启动兼容openai接口的api_server,命令如下:

python -m vllm.entrypoints.openai.api_server --served-model-name Qwen1.5-7B-Chat --model Qwen/Qwen1.5-7B-Chat 

详细教程参见:

使用以下代码进行测试:

# Reference: https://openai.com/blog/function-calling-and-other-api-updates
import json
from pprint import pprintimport openai# To start an OpenAI-like Qwen server, use the following commands:
#   git clone https://github.com/QwenLM/Qwen-7B;
#   cd Qwen-7B;
#   pip install fastapi uvicorn openai pydantic sse_starlette;
#   python openai_api.py;
#
# Then configure the api_base and api_key in your client:
openai.api_base = 'http://localhost:8000/v1'
openai.api_key = 'none'def call_qwen(messages, functions=None):print('input:')pprint(messages, indent=2)if functions:response = openai.ChatCompletion.create(model='Qwen',messages=messages,functions=functions)else:response = openai.ChatCompletion.create(model='Qwen',messages=messages)response = response.choices[0]['message']response = json.loads(json.dumps(response,ensure_ascii=False))  # fix zh renderingprint('output:')pprint(response, indent=2)print()return responsedef test_1():messages = [{'role': 'user', 'content': '你好'}]call_qwen(messages)messages.append({'role': 'assistant', 'content': '你好!很高兴为你提供帮助。'})messages.append({'role': 'user','content': '给我讲一个年轻人奋斗创业最终取得成功的故事。故事只能有一句话。'})call_qwen(messages)messages.append({'role':'assistant','content':'故事的主人公叫李明,他来自一个普通的家庭,父母都是普通的工人。李明想要成为一名成功的企业家。……',})messages.append({'role': 'user', 'content': '给这个故事起一个标题'})call_qwen(messages)def test_2():functions = [{'name_for_human':'谷歌搜索','name_for_model':'google_search','description_for_model':'谷歌搜索是一个通用搜索引擎,可用于访问互联网、查询百科知识、了解时事新闻等。' +' Format the arguments as a JSON object.','parameters': [{'name': 'search_query','description': '搜索关键词或短语','required': True,'schema': {'type': 'string'},}],},{'name_for_human':'文生图','name_for_model':'image_gen','description_for_model':'文生图是一个AI绘画(图像生成)服务,输入文本描述,返回根据文本作画得到的图片的URL。' +' Format the arguments as a JSON object.','parameters': [{'name': 'prompt','description': '英文关键词,描述了希望图像具有什么内容','required': True,'schema': {'type': 'string'},}],},]messages = [{'role': 'user', 'content': '(请不要调用工具)你好'}]call_qwen(messages, functions)messages.append({'role': 'assistant','content': '你好!很高兴见到你。有什么我可以帮忙的吗?'}, )messages.append({'role': 'user', 'content': '搜索一下谁是周杰伦'})call_qwen(messages, functions)messages.append({'role': 'assistant','content': '我应该使用Google搜索查找相关信息。','function_call': {'name': 'google_search','arguments': '{"search_query": "周杰伦"}',},})messages.append({'role': 'function','name': 'google_search','content': 'Jay Chou is a Taiwanese singer.',})call_qwen(messages, functions)messages.append({'role': 'assistant','content': '周杰伦(Jay Chou)是一位来自台湾的歌手。',}, )messages.append({'role': 'user', 'content': '搜索一下他老婆是谁'})call_qwen(messages, functions)messages.append({'role': 'assistant','content': '我应该使用Google搜索查找相关信息。','function_call': {'name': 'google_search','arguments': '{"search_query": "周杰伦 老婆"}',},})messages.append({'role': 'function','name': 'google_search','content': 'Hannah Quinlivan'})call_qwen(messages, functions)messages.append({'role': 'assistant','content': '周杰伦的老婆是Hannah Quinlivan。',}, )messages.append({'role': 'user', 'content': '用文生图工具画个可爱的小猫吧,最好是黑猫'})call_qwen(messages, functions)messages.append({'role': 'assistant','content': '我应该使用文生图API来生成一张可爱的小猫图片。','function_call': {'name': 'image_gen','arguments': '{"prompt": "cute black cat"}',},})messages.append({'role':'function','name':'image_gen','content':'{"image_url": "https://image.pollinations.ai/prompt/cute%20black%20cat"}',})call_qwen(messages, functions)def test_3():functions = [{'name': 'get_current_weather','description': 'Get the current weather in a given location.','parameters': {'type': 'object','properties': {'location': {'type': 'string','description':'The city and state, e.g. San Francisco, CA',},'unit': {'type': 'string','enum': ['celsius', 'fahrenheit']},},'required': ['location'],},}]messages = [{'role': 'user',# Note: The current version of Qwen-7B-Chat (as of 2023.08) performs okay with Chinese tool-use prompts,# but performs terribly when it comes to English tool-use prompts, due to a mistake in data collecting.'content': '波士顿天气如何?',}]call_qwen(messages, functions)messages.append({'role': 'assistant','content': None,'function_call': {'name': 'get_current_weather','arguments': '{"location": "Boston, MA"}',},}, )messages.append({'role':'function','name':'get_current_weather','content':'{"temperature": "22", "unit": "celsius", "description": "Sunny"}',})call_qwen(messages, functions)def test_4():from langchain.agents import AgentType, initialize_agent, load_toolsfrom langchain.chat_models import ChatOpenAIllm = ChatOpenAI(model_name='Qwen',openai_api_base='http://localhost:8000/v1',openai_api_key='EMPTY',streaming=False,)tools = load_tools(['arxiv'], )agent_chain = initialize_agent(tools,llm,agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,verbose=True,)# TODO: The performance is okay with Chinese prompts, but not so good when it comes to English.agent_chain.run('查一下论文 1605.08386 的信息')if __name__ == '__main__':print('### Test Case 1 - No Function Calling (普通问答、无函数调用) ###')test_1()print('### Test Case 2 - Use Qwen-Style Functions (函数调用,千问格式) ###')test_2()print('### Test Case 3 - Use GPT-Style Functions (函数调用,GPT格式) ###')test_3()print('### Test Case 4 - Use LangChain (接入Langchain) ###')test_4()

http://www.ppmy.cn/ops/152419.html

相关文章

高效建站指南:通过Portainer快速搭建自己的在线网站

文章目录 前言1. 安装Portainer1.1 访问Portainer Web界面 2. 使用Portainer创建Nginx容器3. 将Web静态站点实现公网访问4. 配置Web站点公网访问地址4.1公网访问Web站点 5. 固定Web静态站点公网地址6. 固定公网地址访问Web静态站点 前言 Portainer是一个开源的Docker轻量级可视…

RabbitMQ-消息可靠性以及延迟消息

目录 消息丢失 一、发送者的可靠性 1.1 生产者重试机制 1.2 生产者确认机制 1.3 实现生产者确认 &#xff08;1&#xff09;开启生产者确认 &#xff08;2&#xff09;定义ReturnCallback &#xff08;3&#xff09;定义ConfirmCallback 二、MQ的持久化 2.1 数据持久…

【算法】集合List和队列

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 零&#xff1a;集合&#xff0c;队列的用法 一&#xff1a;字母异位词分组 二&#xff1a;二叉树的锯…

鸿蒙Flutter实战:17-无痛上架审核指南

在上期文章中&#xff0c;我们体验了无痛使用 Flutter 快速启动开发的过程&#xff0c;本期重点聚焦上架审核流程。 无痛打包 提前准备好生产证书。 具体步骤见“鸿蒙Flutter实战&#xff1a;13-鸿蒙应用打包上架流程”&#xff0c;在AGC平台证书栏&#xff0c;“新增证书”成…

Python 模拟真人鼠标轨迹算法 - 防止游戏检测

一.简介 鼠标轨迹算法是一种模拟人类鼠标操作的程序&#xff0c;它能够模拟出自然而真实的鼠标移动路径。 鼠标轨迹算法的底层实现采用C/C语言&#xff0c;原因在于C/C提供了高性能的执行能力和直接访问操作系统底层资源的能力。 鼠标轨迹算法具有以下优势&#xff1a; 模拟…

自动扣webpack框架演示 | 某书 x-xray-traceid 签名算法分析记录

【作者主页】&#xff1a;小鱼神1024 【擅长领域】&#xff1a;JS逆向、小程序逆向、AST还原、验证码突防、Python开发、浏览器插件开发、React前端开发、NestJS后端开发等等 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;不提供完整代码&#…

暑期实习准备:C语言

1.局部变量和全局变量 局部变量的作用域是在变量所在的局部范围&#xff0c;全局变量的作用域是整个工程&#xff1b;局部变量的生命周期是作用域内&#xff0c;全局变量的生命周期是整个程序的生命周期&#xff0c;当两者命名冲突时&#xff0c;优先使用的是局部变量。 2.C语言…

图论 n 皇后问题

首先是对角线的英文是 diagonal &#xff0c;注意线性代数里面说的主对角线是从左上到右下的这条对角线。有时候我们看题解&#xff0c;总是被一些比较小的点卡住。这个小的点可能不是这个题的关键&#xff0c;但是这个小点理解不了就做不了这个题。写算法题或者写其他的题都经…