Spring AI如何调用Function Calling
在 AI 智能体开发的过程中,RAG(Retrieval-Augmented Generation) 和 功能调用(Function Calling) 已经成为两种至关重要的模式。RAG 通过结合检索技术和生成模型的强大能力,使智能体能够实时从外部数据源获取信息,并在生成过程中增强其知识深度和推理能力。Function Calling模式为智能体提供了调用外部工具的能力,极大地扩展了其应用范围。智能体可以通过调用外部工具(如数据库操作、业务规则执行、算法工具调用等),完成更为复杂的任务和操作。这种灵活性使得智能体在各种实际场景中都能表现得更为高效和精确。本文将介绍使用Spring AI 开源框架,验证AI大模型如何调用外部函数。
1、什么是Function Calling
功能调用(Function Calling)允许大型语言模型(LLM)在必要时调用一个或多个可用的工具,这些工具通常由开发者定义。工具可以是任何东西:网页搜索、对外部 API 的调用,或特定代码的执行等。
功能调用是AI应用与模型交互中一个非常典型的范式,它可以辅助模型更好的回答用户问题。我们在给模型输入的过程中,附带上可用的函数列表(包含函数名、函数描述等),模型在收到问题和函数列表后,根据对问题的推理在必要的时候发起对函数的调用。
SpringAI 帮我们规范了函数定义、注册等过程,并在发起模型请求之前自动将函数注入到 Prompt 中,而当模型决策在合适的时候去调用某个函数时,Spring AI 完成函数调用动作,最终将函数执行结果与原始问题再一并发送给模型,模型根据新的输入决策下一步动作。这其中涉及与大模型的多次交互过程,一次函数调用就是一次完成的交互过程。
2、开发示例前提条件
- JDK为17以上版本,本人使用的jdk21版本;
- SpringBoot版本为3.x以上,本项目使用的是SpringBoot 3.3.3版本;
- 本文采用了阿里巴巴的Qwen大模型进行实验与验证。若选用阿里巴巴的AI服务,则需首先在阿里云平台上开通相应的大型模型服务,并获取所需的API密钥,以便在后续代码中调用。具体的开通与配置步骤,请参阅阿里云大模型服务平台“百炼”的相关文档和指南如何获取API Key_大模型服务平台百炼(Model Studio)-阿里云帮助中心。这样可以确保您能够顺利地集成和使用这些先进的AI资源。
3、添加maven依赖
创建springboot工程后,在pom.xml文件里引入ai相关sdk包,本示例使用了spring-ai-alibaba框架和Qwen模型,而spring-ai-alibaba又是基于spring-ai开发的,所以引入spring-ai-alibaba即可。
Pom.xml完整内容如下:
4.0.0 com.yuncheng spring-ai-demo 1.0-SNAPSHOT 21 21 UTF-8 1.0.0-M5 1.0.0-M5.1 org.springframework.boot spring-boot-starter-parent 3.3.3 org.springframework.boot spring-boot-starter-web com.alibaba.cloud.ai spring-ai-alibaba-starter ${spring-ai-alibaba.version} spring-milestones Spring Milestones https://repo.spring.io/milestone false spring-snapshots Spring Snapshots https://repo.spring.io/snapshot false aliyun aliyun Repository http://maven.aliyun.com/nexus/content/groups/public/ false
3、配置yml文件
#配置访问阿里巴巴AI服务的api-key,并指定了使用qwen-plus模型
server: port: 8080 spring: application: name: spring-ai-demo ai: dashscope: api-key: sk-b90ad31bb3eb4a158524928354x39dc5 chat: options: model: qwen-plus
4、创建自定义的FunctionTools
自定义函数需要提供一个 name、description 和 function call signature,以便模型知道函数能做什么、期望的输入参数。Spring AI 使这一过程变得简单,只需定义一个返回 java.util.Function 的 @Bean 定义,并在调用 ChatModel 时将 bean 名称作为选项进行注册。在底层,Spring 会用适当的适配器代码包装你的 POJO(即函数),以便与 AI 模型进行交互,免去了编写繁琐的样板代码。FunctionCallback.java 接口和配套的 FunctionCallbackWrapper.java 工具类包含了底层实现代码,它们是简化 Java 回调函数的实现和注册的关键。
以下是自定义了模拟算术运算的函数,可以执行加法和乘法运算,在实际的业务中,你可以在自定义函数中执行数据库操作、调用HTTP服务、执行业务逻辑等。
import java.util.function.Function; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Description; @Configuration public class FunctionTools { private static final Logger logger = LoggerFactory.getLogger(FunctionTools.class); public record AddOperationRequest(int d1, int d2) { } public record MulOperationRequest(int d1, int d2) { } @Bean @Description("加法运算") public Function addOperation() { return request -> { logger.info("加法运算函数被调用了:" + request.d1 + "," + request.d2 ); return request.d1 + request.d2; }; } @Bean @Description("乘法运算") public Function mulOperation() { return request -> { logger.info("乘法运算函数被调用了:" + request.d1 + "," + request.d2 ); return request.d1 * request.d2; }; } }
如何定义&注册函数:
您可以在应用程序上下文中定义 @Beans,就像定义任何其他 Spring 管理对象一样。
在内部,Spring AI ChatModel 将创建一个 FunctionCallbackWrapper 包装器的实例,该包装器添加通过 AI 模型调用它的逻辑。@Bean 的名称作为 ChatOption 传递。
@Description 注释是可选的,它提供了函数描述,可帮助模型了解何时调用该函数。这是一个重要的属性,可帮助 AI 模型确定要调用哪个客户端函数。
5、创建ChatClient并启用自定义函数
以下代码创建一个简单的Controller类来测试验证。
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatModel; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/ai") public class FunctionCallController { private static final Logger log = LoggerFactory.getLogger(FunctionCallController.class); @Autowired private ChatModel chatModel; @GetMapping(value = "/chat", produces = MediaType.APPLICATION_STREAM_JSON_VALUE) public String ragJsonText(@RequestParam(value = "userMessage") String userMessage){ return ChatClient.builder(chatModel) .build() .prompt() .system(""" 您是算术计算器的代理。 您能够支持加法运算、乘法运算等操作,其余功能将在后续版本中添加,如果用户问的问题不支持请告知详情。 在提供加法运算、乘法运算等操作之前,您必须从用户处获取如下信息:两个数字,运算类型。 请调用自定义函数执行加法运算、乘法运算。 请讲中文。 """) .user(userMessage) .functions("addOperation", "mulOperation") .call() .content(); } }
为了让模型知道并调用您的自定义函数,您需要在 Prompt 请求中启用它,如上述代码,在functions("addOperation", "mulOperation")中告知ChatClient要使用这两个自定义函数。
另外指定了System Prompt:要求AI 模型被设定为一个算术计算器代理,能够执行加法和乘法运算,并且要求用户提供两个数字和运算类型。这个提示词内容很关键,如何让AI按照自己的意图去执行,要不断测试提示词内容。
6、测试验证Function Calling
以下测试通过4次提问进行验证AI大模型如何调用自定义函数Function:
第1次提问:打招呼
http://localhost:8080/ai/chat?userMessage=你好
返回信息如下:
你好!我是一个算术计算器的代理,目前我可以帮助你进行加法运算和乘法运算。请提供两个数字以及你想要进行的运算类型。
通过返回信息说明了,给ChatClient设置的system提示词生效了,按照默认角色输出了打招呼内容。
第2次提问:算加法
http://localhost:8080/ai/chat?userMessage=3加5等于几
返回信息如下:
3加5等于8。
此时查看后台日志输出,springai.functioncall.FunctionTools : 加法运算函数被调用了:3,5
通过返回信息说明了,证明确实调用了自定义加法函数
第3次提问:算乘法
http://localhost:8080/ai/chat?userMessage=2乘9等于多少
返回信息如下:
2乘9等于18。如果您还有其他数学问题,欢迎继续提问。
此时查看后台日志输出,springai.functioncall.FunctionTools : 乘法运算函数被调用了:2,9
通过返回信息说明了,证明确实调用了自定义乘法函数
第4次提问:算减法
http://localhost:8080/ai/chat?userMessage=5减3等于多少
返回信息如下:
目前我只能协助进行加法和乘法运算。减法功能将在后续版本中添加,敬请期待。如果您有其他运算需求,欢迎告诉我。
由于该示例中没有自定义减法运算函数,所以AI大模型还是按照提示词理解输出的,没有调用其它工具进行输出。
7、验证DeepSeek是否支持Function Calling
修改yml配置文件中的model值,改成deepseek-v3或deepseek-r1,重启springboot后再次测试。
spring:
ai:
dashscope:
api-key: sk-b90ad31bb3eb4a158524928354x39dc5
chat:
options:
model: deepseek-v3
再次请求http://localhost:8080/ai/chat?userMessage=3加5等于几
后台报错:
InternalError.Algo.InvalidParameter: The tool call is not supported
说明deepseek-v3和deepseek-r1目前不支持function calling功能。