抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

Function Calling(函数调用)是大语言模型与外部世界交互的关键能力,也是构建AI Agent的核心技术。它让LLM能够调用外部工具、API和函数,从而完成实际任务。本文详细介绍Function Calling的原理和实现方法。

什么是Function Calling

Function Calling让LLM能够识别何时需要调用函数,并生成符合规范的函数调用参数

1
2
3
4
5
6
7
8
9
传统LLM:
用户:"北京今天天气怎么样?"
LLM:"我无法获取实时天气信息..." ← 只能用训练数据回答

Function Calling:
用户:"北京今天天气怎么样?"
LLM:识别需要调用天气API → 生成参数 {city: "北京"}
系统:调用weather_api("北京") → 返回结果
LLM:"北京今天晴,气温15-23℃,适合外出。" ← 基于实时数据回答

工作原理

整体流程

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
┌─────────────────────────────────────────────────────────────────┐
│ Function Calling 流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. 定义函数 │
│ 开发者定义可用的函数及其参数Schema │
│ │
│ 2. 用户提问 │
│ 用户发送消息给LLM │
│ │
│ 3. LLM决策 │
│ LLM判断是否需要调用函数 │
│ ├─ 不需要 → 直接生成文本回答 │
│ └─ 需要 → 生成函数调用(函数名+参数) │
│ │
│ 4. 执行函数 │
│ 开发者的代码执行函数,获取结果 │
│ │
│ 5. 返回结果 │
│ 将函数执行结果发送给LLM │
│ │
│ 6. 生成回答 │
│ LLM基于函数结果生成最终回答 │
│ │
└─────────────────────────────────────────────────────────────────┘

时序图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
用户          应用程序          LLM           外部API
│ │ │ │
│──"查天气"────▶│ │ │
│ │──函数定义────▶│ │
│ │ +用户消息 │ │
│ │ │ │
│ │◀─函数调用────│ │
│ │ get_weather │ │
│ │ {city:"北京"}│ │
│ │ │ │
│ │──────────────────────────────▶│
│ │ │ 调用API │
│ │◀─────────────────────────────│
│ │ │ 返回天气数据 │
│ │ │ │
│ │──函数结果────▶│ │
│ │ │ │
│ │◀─最终回答────│ │
│ │ │ │
│◀──回答────────│ │ │
│ │ │ │

OpenAI 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
from openai import OpenAI

client = OpenAI()

# 1. 定义函数Schema
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的当前天气信息",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,如:北京、上海"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位"
}
},
"required": ["city"]
}
}
}
]

# 2. 发送请求
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "user", "content": "北京今天天气怎么样?"}
],
tools=tools,
tool_choice="auto" # auto, none, 或指定函数
)

# 3. 检查是否需要调用函数
message = response.choices[0].message

if message.tool_calls:
# LLM决定调用函数
tool_call = message.tool_calls[0]
function_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)

print(f"调用函数: {function_name}")
print(f"参数: {arguments}")
# 调用函数: get_weather
# 参数: {"city": "北京"}

完整对话流程

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
import json
from openai import OpenAI

client = OpenAI()

# 模拟的天气API
def get_weather(city: str, unit: str = "celsius") -> dict:
# 实际应用中调用真实API
weather_data = {
"北京": {"temp": 18, "condition": "晴", "humidity": 45},
"上海": {"temp": 22, "condition": "多云", "humidity": 65},
}
data = weather_data.get(city, {"temp": 20, "condition": "未知", "humidity": 50})
if unit == "fahrenheit":
data["temp"] = data["temp"] * 9/5 + 32
return {"city": city, **data, "unit": unit}

# 函数映射
available_functions = {
"get_weather": get_weather
}

# 工具定义
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的当前天气信息",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"default": "celsius"
}
},
"required": ["city"]
}
}
}
]

def chat_with_functions(user_message: str):
messages = [{"role": "user", "content": user_message}]

# 第一次调用:让LLM决定是否调用函数
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools,
tool_choice="auto"
)

assistant_message = response.choices[0].message
messages.append(assistant_message)

# 检查是否有函数调用
if assistant_message.tool_calls:
# 执行所有函数调用
for tool_call in assistant_message.tool_calls:
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)

# 调用函数
function = available_functions[function_name]
result = function(**function_args)

# 将函数结果添加到消息中
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result, ensure_ascii=False)
})

# 第二次调用:让LLM基于函数结果生成回答
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools
)

return response.choices[0].message.content

# 使用
answer = chat_with_functions("北京和上海今天哪个更热?")
print(answer)

并行函数调用

OpenAI支持一次返回多个函数调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 用户问:"北京和上海今天天气怎么样?"

# LLM返回两个并行的函数调用:
tool_calls = [
{
"id": "call_1",
"function": {
"name": "get_weather",
"arguments": '{"city": "北京"}'
}
},
{
"id": "call_2",
"function": {
"name": "get_weather",
"arguments": '{"city": "上海"}'
}
}
]

# 可以并行执行这些函数调用

函数定义最佳实践

JSON Schema规范

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
{
"type": "function",
"function": {
"name": "search_products",
"description": "在商品数据库中搜索商品。支持按名称、类别、价格范围搜索。",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词,如商品名称或描述"
},
"category": {
"type": "string",
"enum": ["电子产品", "服装", "食品", "图书"],
"description": "商品类别"
},
"min_price": {
"type": "number",
"description": "最低价格(元)"
},
"max_price": {
"type": "number",
"description": "最高价格(元)"
},
"sort_by": {
"type": "string",
"enum": ["price_asc", "price_desc", "rating", "sales"],
"default": "rating",
"description": "排序方式"
},
"limit": {
"type": "integer",
"default": 10,
"minimum": 1,
"maximum": 50,
"description": "返回结果数量"
}
},
"required": ["query"]
}
}
}

描述编写技巧

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
# 好的描述
{
"name": "send_email",
"description": """
发送电子邮件。

使用场景:
- 用户明确要求发送邮件时
- 需要通知某人某事时

注意事项:
- 发送前会请求用户确认
- 支持HTML格式的邮件内容
""",
"parameters": {
"type": "object",
"properties": {
"to": {
"type": "string",
"description": "收件人邮箱地址,如:user@example.com"
},
"subject": {
"type": "string",
"description": "邮件主题,简洁明了,不超过100字符"
},
"body": {
"type": "string",
"description": "邮件正文内容,支持纯文本或HTML格式"
}
},
"required": ["to", "subject", "body"]
}
}

# 不好的描述
{
"name": "send_email",
"description": "发邮件", # 太简略
"parameters": {
"properties": {
"to": {"type": "string"}, # 缺少描述
"subject": {"type": "string"},
"body": {"type": "string"}
}
}
}

多函数协作

定义多个函数

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
tools = [
{
"type": "function",
"function": {
"name": "search_flights",
"description": "搜索航班信息",
"parameters": {
"type": "object",
"properties": {
"from_city": {"type": "string", "description": "出发城市"},
"to_city": {"type": "string", "description": "目的城市"},
"date": {"type": "string", "description": "日期,格式:YYYY-MM-DD"}
},
"required": ["from_city", "to_city", "date"]
}
}
},
{
"type": "function",
"function": {
"name": "search_hotels",
"description": "搜索酒店信息",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市"},
"check_in": {"type": "string", "description": "入住日期"},
"check_out": {"type": "string", "description": "退房日期"},
"guests": {"type": "integer", "description": "入住人数"}
},
"required": ["city", "check_in", "check_out"]
}
}
},
{
"type": "function",
"function": {
"name": "book_reservation",
"description": "预订航班或酒店",
"parameters": {
"type": "object",
"properties": {
"type": {"type": "string", "enum": ["flight", "hotel"]},
"reservation_id": {"type": "string", "description": "预订项目ID"},
"passenger_name": {"type": "string", "description": "乘客/住客姓名"}
},
"required": ["type", "reservation_id", "passenger_name"]
}
}
}
]

多轮函数调用

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
def travel_assistant(user_message: str):
messages = [{"role": "user", "content": user_message}]

while True:
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools,
tool_choice="auto"
)

assistant_message = response.choices[0].message
messages.append(assistant_message)

# 如果没有函数调用,返回最终回答
if not assistant_message.tool_calls:
return assistant_message.content

# 执行所有函数调用
for tool_call in assistant_message.tool_calls:
function_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)

# 执行函数
result = available_functions[function_name](**arguments)

messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result, ensure_ascii=False)
})

# 继续循环,让LLM决定下一步

# 使用
response = travel_assistant(
"帮我查一下明天从北京到上海的航班,顺便看看上海有什么好酒店"
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
执行流程:

用户:"帮我查航班和酒店"


LLM决定并行调用两个函数:
├── search_flights(from="北京", to="上海", date="2025-03-08")
└── search_hotels(city="上海", check_in="2025-03-08", ...)


执行函数,返回结果


LLM整合结果,生成回答:
"找到以下航班:... 推荐酒店:..."

强制函数调用

tool_choice参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 自动决定(默认)
tool_choice = "auto"

# 禁止调用函数
tool_choice = "none"

# 必须调用某个函数
tool_choice = {
"type": "function",
"function": {"name": "get_weather"}
}

# 必须调用函数(任意一个)
tool_choice = "required"

使用场景

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
# 场景1:提取结构化信息
extract_tool = {
"type": "function",
"function": {
"name": "extract_contact",
"description": "从文本中提取联系人信息",
"parameters": {
"type": "object",
"properties": {
"name": {"type": "string"},
"email": {"type": "string"},
"phone": {"type": "string"},
"company": {"type": "string"}
},
"required": ["name"]
}
}
}

response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "user", "content": "张三,阿里巴巴,zhangsan@alibaba.com"}
],
tools=[extract_tool],
tool_choice={"type": "function", "function": {"name": "extract_contact"}}
)

# 强制返回结构化数据
# {"name": "张三", "email": "zhangsan@alibaba.com", "company": "阿里巴巴"}

与Agent的关系

Function Calling是构建Agent的基础能力:

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
┌─────────────────────────────────────────────────────────────────┐
│ AI Agent │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ LLM (大脑) │ │
│ │ │ │
│ │ 规划 ──▶ 决策 ──▶ 行动 ──▶ 观察 ──▶ 反思 │ │
│ │ │ │ │
│ │ │ Function Calling │ │
│ │ ▼ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 工具集 (手脚) │ │
│ │ │ │
│ │ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ │
│ │ │ 搜索 │ │ 计算 │ │ 代码 │ │ API │ │ │
│ │ │ 引擎 │ │ 器 │ │ 执行 │ │ 调用 │ │ │
│ │ └────────┘ └────────┘ └────────┘ └────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

错误处理

函数执行错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def safe_function_call(function_name: str, arguments: dict) -> str:
try:
function = available_functions.get(function_name)
if not function:
return json.dumps({"error": f"未知函数: {function_name}"})

result = function(**arguments)
return json.dumps(result, ensure_ascii=False)

except TypeError as e:
return json.dumps({"error": f"参数错误: {str(e)}"})

except Exception as e:
return json.dumps({"error": f"执行失败: {str(e)}"})

参数验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pydantic import BaseModel, Field, validator

class WeatherRequest(BaseModel):
city: str = Field(..., min_length=1, max_length=50)
unit: str = Field(default="celsius", pattern="^(celsius|fahrenheit)$")

@validator('city')
def validate_city(cls, v):
# 自定义验证逻辑
valid_cities = ["北京", "上海", "广州", "深圳"]
if v not in valid_cities:
raise ValueError(f"不支持的城市: {v}")
return v

def get_weather(city: str, unit: str = "celsius"):
# 验证参数
request = WeatherRequest(city=city, unit=unit)
# 执行逻辑...

安全考虑

函数白名单

1
2
3
4
5
6
7
8
9
10
11
12
# 只允许调用预定义的安全函数
ALLOWED_FUNCTIONS = {
"get_weather": get_weather,
"search_products": search_products,
"get_stock_price": get_stock_price,
}

def execute_function(name: str, arguments: dict):
if name not in ALLOWED_FUNCTIONS:
raise PermissionError(f"不允许调用函数: {name}")

return ALLOWED_FUNCTIONS[name](**arguments)

参数过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
import re

def sanitize_arguments(arguments: dict) -> dict:
"""过滤潜在的危险参数"""
sanitized = {}
for key, value in arguments.items():
if isinstance(value, str):
# 移除潜在的注入攻击
value = re.sub(r'[;\'"\\]', '', value)
# 限制长度
value = value[:1000]
sanitized[key] = value
return sanitized

敏感操作确认

1
2
3
4
5
6
7
8
9
10
11
12
SENSITIVE_FUNCTIONS = {"send_email", "delete_file", "make_payment"}

def execute_with_confirmation(name: str, arguments: dict, user_confirm: callable):
if name in SENSITIVE_FUNCTIONS:
# 需要用户确认
confirmed = user_confirm(
f"即将执行敏感操作 {name},参数:{arguments},是否继续?"
)
if not confirmed:
return {"status": "cancelled", "message": "用户取消操作"}

return available_functions[name](**arguments)

实际应用案例

智能客服

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
tools = [
{
"type": "function",
"function": {
"name": "query_order",
"description": "查询订单状态",
"parameters": {
"type": "object",
"properties": {
"order_id": {"type": "string"}
},
"required": ["order_id"]
}
}
},
{
"type": "function",
"function": {
"name": "create_ticket",
"description": "创建客服工单",
"parameters": {
"type": "object",
"properties": {
"issue_type": {
"type": "string",
"enum": ["退款", "换货", "投诉", "咨询"]
},
"description": {"type": "string"}
},
"required": ["issue_type", "description"]
}
}
},
{
"type": "function",
"function": {
"name": "transfer_to_human",
"description": "转接人工客服",
"parameters": {
"type": "object",
"properties": {
"reason": {"type": "string"}
},
"required": ["reason"]
}
}
}
]

数据分析助手

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
tools = [
{
"type": "function",
"function": {
"name": "execute_sql",
"description": "执行SQL查询获取数据",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "SELECT查询语句(只读)"
}
},
"required": ["query"]
}
}
},
{
"type": "function",
"function": {
"name": "create_chart",
"description": "创建数据可视化图表",
"parameters": {
"type": "object",
"properties": {
"chart_type": {
"type": "string",
"enum": ["bar", "line", "pie", "scatter"]
},
"data": {"type": "array"},
"title": {"type": "string"}
},
"required": ["chart_type", "data"]
}
}
}
]

# 用户:"帮我分析上个月的销售数据,做个柱状图"
# LLM会先调用execute_sql获取数据,再调用create_chart生成图表

总结

Function Calling核心要点

1
2
3
4
5
6
┌─────────────────────────────────────────────────────────────────┐
│ 1. 定义清晰的函数Schema(名称、描述、参数) │
│ 2. LLM负责决策"何时调用"和"如何调用" │
│ 3. 应用程序负责实际执行函数 │
│ 4. 将执行结果返回给LLM生成最终回答 │
└─────────────────────────────────────────────────────────────────┘

最佳实践

方面 建议
函数命名 使用动词开头,清晰表达功能
参数描述 详细说明每个参数的用途和格式
错误处理 返回结构化的错误信息
安全性 白名单、参数验证、敏感操作确认
并行调用 善用并行提高效率

与其他技术的关系

1
2
3
4
5
6
7
Function Calling

├──▶ RAG:检索函数获取知识

├──▶ Agent:规划和执行的基础能力

└──▶ 工具使用:连接外部世界的桥梁

Function Calling让LLM从”只会说”变成”能做事”,是构建实用AI应用的关键技术。