函数调用(Function Calling)

function calling函数调用,也叫Tools工具。

入门案例

大语言模型本身并不擅长数学运算,实际上很多应用场景都会涉及到数学计算,因此我们可以为它提供一个“数学工具”。当我们提出问题时,大语言模型会判断是否使用某个工具

注解介绍

(1)【@Tool注解】通过在方法上添加@Tool注解,并在构建 AI 服务时显式指定这些工具,这样LLM可以根据用户的请求决定是否调用相应的工具方法:

1
2
3
4
5
6
7
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Tool {
String name() default "";

String[] value() default {""};
}

在Tool注解中有两个可选的属性:

  • name(可选):指定工具的名称。如果未提供,默认使用方法名。
  • value(可选):提供工具的描述,有助于LLM更好地理解工具的用途。

(2)【@P注解】此外,还可以使用@P注解为方法参数添加描述,用于增强LLM对参数含义的理解:

1
2
3
4
5
6
7
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface P {
String value();

boolean required() default true;
}

在Tool注解中有两个属性,其中value是必填字段,required是可选字段:

  • <font style="color:rgba(0, 0, 0, 0.8);background-color:rgb(247, 247, 249);">value</font>参数的描述信息,这是必填字段。
  • <font style="color:rgba(0, 0, 0, 0.8);background-color:rgb(247, 247, 249);">required</font>表示该参数是否为必需项,默认值为 true ,此为可选字段。

(3)【@ToolMemoryId注解】当你的AIService方法中有一个参数使用了@MemoryId注解,那么就可以使用@ToolMemoryId注解,作为@Tool注解中的一个参数。@ToolMemoryId注解用于在工具方法的参数上,指定用于关联对话上下文的内存标识符(memoryID)。

提供给AIService方法的memoryID将自动传递给@Tool方法,如果你有多个用户,或每个用户有多个聊天记忆,并且希望在@Tool方法中对它们进行区分,那么这个功能会很有用。

函数调用流程

函数调用的流程如下:

  1. LLM 接收用户输入;
  2. 第一次调用大模型时,判断是否需要调用工具方法;
  3. 如果需要,那么第二次调用大模型,调用相应的@Tool方法,并获取返回结果;
  4. 将工具方法的返回结果作为对话的一部分,继续与用户交互。

创建工具类

新建一个名为tools的包,并在里面定义一个名为MathTools的类,在里面我们定义了三个方法,分别用于求和、求差和求平方根:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Slf4j
@Component
public class MathTools {

@Tool(name = "加法", value = "计算两个数之和")
public double add(@ToolMemoryId int memoryId,
@P(value = "加数1", required = true) double a,
@P(value = "加数2", required = true) double b ){
log.info("调用加法运算,运算的memoryId:{}",memoryId);
return a + b;
}

@Tool(name = "减法", value = "计算两个数之差")
public double sub(@ToolMemoryId int memoryId,
@P(value = "减数", required = true) double a,
@P(value = "被减数", required = true) double b ){
log.info("调用减法运算,运算的memoryId:{}",memoryId);
return a - b;
}

@Tool(name = "平方根", value = "计算给定参数的平方根")
public double sqrt(@ToolMemoryId int memoryId,
double a ){
log.info("调用平方根运算,运算的memoryId:{}",memoryId);
return Math.sqrt(a);
}
}

给Assistant配置工具类

回到SeparateChatAssistant类中,我们修改@AiService注解的值,并在里面设置tools属性,然后添加一个测试方法:

1
2
3
4
5
6
7
8
9
@AiService(wiringMode = AiServiceWiringMode.EXPLICIT,
chatModel = "ollamaChatModel", chatMemoryProvider = "chatMemoryProvider",
tools = "mathTools"
)
public interface SeparateChatAssistant {
@SystemMessage("你是我的好朋友,请用四川话回答问题。")
String chatTool(@MemoryId int memoryId , @UserMessage String userMessage);
}

tools属性的值为mathTools,也就是工具类在Spring容器中Bean的名称,之后新增一个chatTool方法。

创建测试接口

接着在PromptController类中定义一个名为promptTool的方法,里面的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
@RequestMapping("/prompt")
public class PromptController {
@Autowired
private SeparateChatAssistant separateChatAssistant;


/**
* 测试提示词调用函数调用
*/
@GetMapping("/promptTool")
public String promptTool(){
return separateChatAssistant.chatTool(6,"6+9等于多少?98-17等于多少?255的平方根是多少?");
}
}

启动项目进行测试

启动项目,访问如下地址:

1
http://localhost:8080/prompt/promptTool

页面报错了,查看一下控制台,输出如下:

意思就是deepseek-r1:1.5b这个模型不支持函数调用,那么我们可以切换到qwen3:1.7b,修改application.properties配置文件中的参数,然后重新启动项目并进行访问,结果如下所示:

查看一下数据库,可以发现也存有这条记录了,同时显示了提示词的类型: