AI-Function-Calling

为什么需要function calling,知乎上有一篇介绍文章 ,说的比较清楚了。

硅基流动上有一些具体的使用指引

这样我们有一些大模型不擅长处理的问题就可以交给function来处理再把结果交还给大模型了。

前阵子刚好在测试用思维树引导大模型计算24点,结果不是很稳定,试试用function calling会不会好一些:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
from openai import OpenAI
import json
from itertools import permutations, product
import operator

# 加载API密钥
with open('../keys.json', 'r') as f:
provider = json.load(f)["siliconflow"]

# 定义计算24点的函数
def calc_24(nums):
# 定义运算符映射
op_map = {
'+': operator.add,
'-': operator.sub,
'*': operator.mul,
'/': operator.truediv
}

# 递归函数,用于计算表达式的值
def evaluate_expression(expr):
try:
return eval(expr)
except ZeroDivisionError:
return None

# 穷举所有可能的表达式
for nums_perm in permutations(nums):
for ops_perm in product(list(op_map.keys()), repeat=3):
# 构造所有可能的表达式
expressions = [
f"({nums_perm[0]} {ops_perm[0]} {nums_perm[1]}) {ops_perm[1]} ({nums_perm[2]} {ops_perm[2]} {nums_perm[3]})",
f"({nums_perm[0]} {ops_perm[0]} ({nums_perm[1]} {ops_perm[1]} {nums_perm[2]})) {ops_perm[2]} {nums_perm[3]}",
f"{nums_perm[0]} {ops_perm[0]} (({nums_perm[1]} {ops_perm[1]} {nums_perm[2]}) {ops_perm[2]} {nums_perm[3]})",
f"(({nums_perm[0]} {ops_perm[0]} {nums_perm[1]}) {ops_perm[1]} {nums_perm[2]}) {ops_perm[2]} {nums_perm[3]}",
f"{nums_perm[0]} {ops_perm[0]} ({nums_perm[1]} {ops_perm[1]} ({nums_perm[2]} {ops_perm[2]} {nums_perm[3]}))",
]

# 尝试计算每个表达式
for expr in expressions:
result = evaluate_expression(expr)
if result is not None and abs(result - 24) < 1e-6:
return True, expr.replace('/', '÷')

return False, ""

# 定义检查是否能计算出24点的函数
def can_make_24(cards):
cards = [float(card) for card in cards]
can, steps = calc_24(cards)
if can:
return f"Yes, the calculation steps are: {steps}"
else:
return "No"

# 定义工具列表
tools = [
{
'type': 'function',
'function': {
'name': 'can_make_24',
'description': 'Determine if four numbers can be used to calculate 24',
'parameters': {
'type': 'object',
'properties': {
'cards': {
'type': 'array',
'description': 'Four numbers',
'items': {
'type': 'float',
},
},
},
'required': ['cards'],
},
}
}
]

def function_call_playground(prompt):
messages = [{'role': 'user', 'content': prompt}]
client = OpenAI(api_key=provider["key"], base_url=provider["base"])
response = client.chat.completions.create(
model="deepseek-ai/DeepSeek-V2.5",
messages=messages,
temperature=0.01,
top_p=0.95,
stream=False,
tools=tools)

# 解析函数调用
func1_name = response.choices[0].message.tool_calls[0].function.name
func1_args = response.choices[0].message.tool_calls[0].function.arguments
func1_args = json.loads(func1_args) # 将字符串转换为字典
func1_out = eval(f'{func1_name}(**func1_args)') # 调用函数
print("函数调用结果:",func1_out)

# 将函数调用的结果添加到消息中
messages.append({
'role': 'assistant',
'content':func1_out
})
messages.append({
'role': 'user',
'content':'翻译assistant返回的结果,如果题目是有解的详细解释计算过程,浮点数如果有可能尽量显示为整数'
})

# 再次调用模型以完成对话
response = client.chat.completions.create(
model="deepseek-ai/DeepSeek-V2.5",
messages=messages,
temperature=0.01,
top_p=0.95,
stream=False)
return response.choices[0].message.content

# 测试函数调用
prompt = "用中文回答:对于扑克牌 3, 3, 8, 8 是否能计算出24点?"
print(function_call_playground(prompt))

大概来讲就是要调用2次大模型,第一次让它对着tools里的函数把prompt中的数据转成调用参数字典,然后把参数字典拿出来本地调用一下函数,最后把函数调用结果给回到大模型让它翻译解释结果

只有部分大模型支持function calling,接口似乎也没有完全统一,这里使用的是 deepseek-ai/DeepSeek-V2.5

结果是像这样的:

是的,可以计算出24点。计算步骤如下:
8 ÷ (3 - (8 ÷ 3))
详细解释:

  1. 先计算括号内的部分:8 ÷ 3 = 2.666…
  2. 然后用3减去这个结果:3 - 2.666… = 0.333…
  3. 最后用8除以这个结果:8 ÷ 0.333… = 24
    因此,最终结果是24。

tools接口接受的时候列表,更高级的方法是准备多个函数,让大模型自己决定调用哪个。