大模型 Function Calling 详细解析
·
本文深入解析大模型 Function Calling 的概念、原理、流程及优化方法,并附带代码示例。
一、概念(What)
1.1 什么是 Function Calling?
Function Calling(函数调用) 是大模型(LLM)与外部世界交互的核心能力。它允许大模型在生成自然语言回复的过程中,主动识别出需要调用外部工具/函数,并以结构化的方式输出函数名和参数,而非直接生成函数执行结果。
核心思想:大模型不直接执行代码,而是作为"调度器",决定何时调用哪个函数、传入什么参数。实际执行由外部系统完成。
1.2 为什么需要 Function Calling?
| 大模型的局限 | Function Calling 的解决 |
|---|---|
| 知识有截止日期,无法获取实时信息 | 调用搜索 API 获取最新数据 |
| 无法执行数学计算(易幻觉) | 调用计算器/代码执行工具 |
| 无法访问私有数据库 | 调用数据库查询函数 |
| 无法与物理世界交互 | 调用 IoT 设备控制接口 |
| 无法生成精确结构化数据 | 强制输出 JSON 格式参数 |
1.3 典型应用场景
- 实时信息查询:天气、股价、新闻
- 数学与逻辑计算:复杂公式、统计分析
- 数据库操作:CRUD 操作、SQL 查询
- API 集成:发送邮件、创建日程、调用第三方服务
- 多工具协作:Agent 系统中多个工具的链式调用
二、原理(How It Works)
2.1 核心机制
Function Calling 的本质是结构化输出控制。大模型通过特殊的系统提示(System Prompt)和工具描述(Tool Description),学习在特定场景下输出 JSON 格式的函数调用指令。
┌─────────────────────────────────────────────────────────────┐
│ Function Calling 架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ 工具描述列表 ┌──────────────┐ │
│ │ 用户输入 │ ────────────────────▶ │ 大模型 │ │
│ └──────────────┘ └──────┬───────┘ │
│ │ │
│ ┌───────────────────────────────┘ │
│ ▼ │
│ ┌────────────────────────────────┐ │
│ │ 判断:是否需要调用函数? │ │
│ │ ├─ 否 → 直接生成自然语言回复 │ │
│ │ └─ 是 → 输出 JSON 函数调用指令 │ │
│ └────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ 解析 JSON ┌──────────────┐ │
│ │ 外部执行器 │ ◀─────────────────── │ 应用层 │ │
│ │ (执行函数) │ │ (解析结果) │ │
│ └──────┬───────┘ └──────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ 执行结果 ┌──────────────┐ │
│ │ 函数结果 │ ────────────────────▶ │ 大模型 │ │
│ └──────────────┘ └──────┬───────┘ │
│ │ │
│ ▼ │
│ 生成最终回复 │
│ │
└─────────────────────────────────────────────────────────────┘
2.2 技术实现细节
2.2.1 工具描述格式(Tool Schema)
大模型需要了解每个函数的名称、功能、参数类型和约束。通常使用 JSON Schema 描述:
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的当前天气信息",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,如'北京'、'Shanghai'"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位"
}
},
"required": ["city"]
}
}
}
2.2.2 模型内部决策流程
输入: 用户消息 + 可用工具列表
│
▼
┌─────────────────────┐
│ 1. 意图识别阶段 │
│ 分析用户意图是否 │
│ 需要外部工具支持 │
└──────────┬──────────┘
│
┌─────┴─────┐
▼ ▼
不需要 需要
│ │
▼ ▼
直接回复 ┌─────────────┐
│ 2. 工具选择 │
│ 从工具列表中 │
│ 匹配最合适的 │
└──────┬──────┘
│
▼
┌─────────────┐
│ 3. 参数提取 │
│ 从用户输入中 │
│ 提取参数值 │
└──────┬──────┘
│
▼
┌─────────────┐
│ 4. 结构化输出 │
│ 生成标准JSON │
│ 函数调用格式 │
└─────────────┘
2.3 与普通 Chat Completion 的区别
| 特性 | 普通 Chat | Function Calling |
|---|---|---|
| 输出内容 | 纯文本 | 文本 + 结构化 JSON |
| 交互能力 | 单向生成 | 双向交互(可调用外部) |
| 实时性 | 依赖训练数据 | 可获取实时信息 |
| 准确性 | 计算易出错 | 借助外部工具精确计算 |
| 可控性 | 较低 | 高(强制格式输出) |
三、流程(The Workflow)
3.1 完整调用流程
Step 1: 定义工具
│
▼
Step 2: 发送用户请求 + 工具描述
│
▼
Step 3: 模型判断是否需要调用工具
│
├── 不需要 → 直接返回文本回复
│
└── 需要 → 返回 function_call 对象
│
▼
Step 4: 应用层解析 function_call
│
▼
Step 5: 执行对应函数(本地/远程)
│
▼
Step 6: 将执行结果回传给模型
│
▼
Step 7: 模型基于结果生成最终回复
3.2 代码示例:完整流程
import json
# ========== Step 1: 定义工具 ==========
tools = [
{
"type": "function",
"function": {
"name": "calculate",
"description": "执行数学计算,支持加减乘除和复杂表达式",
"parameters": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "数学表达式,如 '2 + 3 * 4'"
}
},
"required": ["expression"]
}
}
},
{
"type": "function",
"function": {
"name": "get_current_time",
"description": "获取当前时间",
"parameters": {
"type": "object",
"properties": {},
"required": []
}
}
}
]
# ========== Step 2 & 3: 模拟模型调用 ==========
def mock_llm_call(user_message, tools):
"""
模拟大模型的 function calling 决策
实际中应调用 OpenAI/Claude/DeepSeek 等 API
"""
# 模拟模型判断逻辑
if "计算" in user_message or any(op in user_message for op in ['+', '-', '*', '/']):
# 提取表达式(简化示例)
import re
expr = re.search(r'[\d\+\-\*\/\(\)\.\s]+', user_message)
if expr:
return {
"role": "assistant",
"content": None,
"tool_calls": [{
"id": "call_001",
"type": "function",
"function": {
"name": "calculate",
"arguments": json.dumps({"expression": expr.group().strip()})
}
}]
}
elif "时间" in user_message or "几点" in user_message:
return {
"role": "assistant",
"content": None,
"tool_calls": [{
"id": "call_002",
"type": "function",
"function": {
"name": "get_current_time",
"arguments": json.dumps({})
}
}]
}
# 不需要调用工具
return {
"role": "assistant",
"content": "我是AI助手,有什么可以帮您的吗?"
}
# ========== Step 4 & 5: 执行函数 ==========
def execute_function(name, arguments):
"""根据函数名执行对应的本地函数"""
args = json.loads(arguments)
if name == "calculate":
try:
# 注意:实际生产环境应使用安全的计算方式
result = eval(args["expression"])
return str(result)
except Exception as e:
return f"计算错误: {str(e)}"
elif name == "get_current_time":
from datetime import datetime
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
return "未知函数"
# ========== Step 6 & 7: 完整流程演示 ==========
def chat_with_functions(user_message):
"""完整的 Function Calling 流程"""
# 1. 调用模型
response = mock_llm_call(user_message, tools)
# 2. 检查是否需要调用工具
if "tool_calls" in response:
print(f"模型决定调用工具: {response['tool_calls'][0]['function']['name']}")
# 3. 执行所有工具调用
tool_results = []
for tool_call in response["tool_calls"]:
func_name = tool_call["function"]["name"]
func_args = tool_call["function"]["arguments"]
# 执行函数
result = execute_function(func_name, func_args)
tool_results.append({
"tool_call_id": tool_call["id"],
"role": "tool",
"name": func_name,
"content": result
})
print(f"函数 '{func_name}' 执行结果: {result}")
# 4. 将结果回传给模型生成最终回复(简化)
final_response = f"根据计算结果,答案是 {tool_results[0]['content']}"
return final_response
# 不需要调用工具
return response["content"]
# ========== 测试 ==========
if __name__ == "__main__":
# 测试场景1:需要计算
print("=" * 50)
print("用户: 帮我计算 123 * 456")
result = chat_with_functions("帮我计算 123 * 456")
print(f"助手: {result}")
# 测试场景2:需要时间
print("\n" + "=" * 50)
print("用户: 现在几点了?")
result = chat_with_functions("现在几点了?")
print(f"助手: {result}")
# 测试场景3:普通对话
print("\n" + "=" * 50)
print("用户: 你好")
result = chat_with_functions("你好")
print(f"助手: {result}")
3.3 实际 API 调用示例(OpenAI 风格)
from openai import OpenAI
client = OpenAI(api_key="your-api-key")
# 定义工具
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的天气",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名"},
"date": {"type": "string", "description": "日期,格式YYYY-MM-DD"}
},
"required": ["city"]
}
}
}
]
# 第一次调用:让模型决定是否调用工具
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "北京明天天气怎么样?"}],
tools=tools,
tool_choice="auto" # auto / none / specific function
)
message = response.choices[0].message
# 检查是否有工具调用
if message.tool_calls:
tool_call = message.tool_calls[0]
function_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
# 执行函数(假设 get_weather 已实现)
weather_result = get_weather(**arguments)
# 第二次调用:将结果回传
final_response = client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "user", "content": "北京明天天气怎么样?"},
message, # 包含 function_call 的助手消息
{
"role": "tool",
"tool_call_id": tool_call.id,
"content": weather_result
}
]
)
print(final_response.choices[0].message.content)
四、优化结果准确度
4.1 工具描述优化
好的描述
{
"name": "search_products",
"description": "在电商数据库中搜索商品。当用户想要购买、查找、比较商品时使用此工具。支持按关键词、价格区间、品牌筛选。",
"parameters": {
"type": "object",
"properties": {
"keyword": {
"type": "string",
"description": "用户搜索的关键词,如'无线耳机'、'iPhone 15'"
},
"price_min": {
"type": "number",
"description": "最低价格(人民币),如 100"
},
"price_max": {
"type": "number",
"description": "最高价格(人民币),如 1000"
},
"brand": {
"type": "string",
"description": "品牌名称,如'Apple'、'小米'、'华为'"
}
},
"required": ["keyword"]
}
}
差的描述
{
"name": "search",
"description": "搜索东西",
"parameters": {
"type": "object",
"properties": {
"q": {"type": "string"},
"min": {"type": "number"},
"max": {"type": "number"}
}
}
}
优化要点:
- 明确触发条件:描述中说明"何时使用此工具"
- 参数语义清晰:每个参数都要有详细的
description - 枚举值明确:使用
enum限制可选值 - 示例丰富:在描述中给出具体示例
4.2 Few-Shot 示例注入
在系统提示中加入示例,引导模型正确输出:
system_prompt = """你是一个智能助手,可以使用以下工具帮助用户:
## 可用工具
[工具列表...]
## 示例
用户:上海今天多少度?
思考:用户询问天气,需要调用 get_weather 工具。
输出:
```json
{"name": "get_weather", "arguments": {"city": "上海", "date": "2024-01-15"}}
用户:3的平方根是多少? 思考:用户需要数学计算,需要调用 calculate 工具。 输出:
{"name": "calculate", "arguments": {"expression": "math.sqrt(3)"}}
规则
- 如果用户的问题可以直接回答,不要调用工具
- 如果需要调用工具,只输出 JSON,不要输出其他文字
- 参数必须从用户输入中提取,不要编造 """
### 4.3 参数校验与纠错
```python
import jsonschema
def validate_and_fix_arguments(function_name, arguments, tools):
"""校验并修复参数"""
# 找到对应的工具定义
tool = next(t for t in tools if t["function"]["name"] == function_name)
schema = tool["function"]["parameters"]
try:
# 校验参数
jsonschema.validate(arguments, schema)
return arguments
except jsonschema.ValidationError as e:
# 自动修复常见错误
fixed_args = auto_fix_arguments(arguments, schema, e)
return fixed_args
def auto_fix_arguments(args, schema, error):
"""自动修复参数错误"""
fixed = args.copy()
# 修复1:缺少必填参数,使用默认值
required = schema.get("required", [])
for field in required:
if field not in fixed:
prop = schema["properties"].get(field, {})
if "default" in prop:
fixed[field] = prop["default"]
elif "enum" in prop:
fixed[field] = prop["enum"][0] # 使用第一个枚举值
# 修复2:类型转换
for key, value in fixed.items():
prop = schema["properties"].get(key, {})
expected_type = prop.get("type")
if expected_type == "number" and isinstance(value, str):
try:
fixed[key] = float(value)
except ValueError:
pass
elif expected_type == "integer" and isinstance(value, str):
try:
fixed[key] = int(value)
except ValueError:
pass
return fixed
4.4 多轮调用与链式思考
复杂任务可能需要多次工具调用:
def multi_step_agent(user_query, max_steps=5):
"""多步调用 Agent"""
messages = [{"role": "user", "content": user_query}]
for step in range(max_steps):
# 调用模型
response = client.chat.completions.create(
model="gpt-4",
messages=messages,
tools=tools
)
message = response.choices[0].message
messages.append(message)
# 检查是否需要调用工具
if not message.tool_calls:
# 没有工具调用,返回最终结果
return message.content
# 执行所有工具调用
for tool_call in message.tool_calls:
result = execute_function(
tool_call.function.name,
json.loads(tool_call.function.arguments)
)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": str(result)
})
return "达到最大步数限制"
# 示例:复杂查询
# "查一下北京和上海的天气,然后告诉我哪个城市更适合户外活动"
# 这需要两次 get_weather 调用 + 一次比较推理
4.5 工具选择策略
# 策略1:强制调用特定工具
tool_choice = {"type": "function", "function": {"name": "get_weather"}}
# 策略2:强制不调用工具
tool_choice = "none"
# 策略3:自动判断(推荐)
tool_choice = "auto"
# 策略4:并行调用(OpenAI 支持一次返回多个 tool_calls)
# 适用于独立任务,如同时查询多个城市的天气
4.6 错误处理与重试
import time
def robust_function_call(func_name, arguments, max_retries=3):
"""带重试机制的函数调用"""
for attempt in range(max_retries):
try:
result = execute_function(func_name, arguments)
# 检查结果是否有效
if is_valid_result(result):
return result
else:
raise ValueError("Invalid result")
except Exception as e:
if attempt == max_retries - 1:
# 最后一次重试失败,返回错误信息给模型
return f"工具调用失败(已重试{max_retries}次): {str(e)}"
# 指数退避
time.sleep(2 ** attempt)
# 可以在这里让模型重新生成参数
arguments = regenerate_arguments(func_name, arguments, str(e))
4.7 结果缓存
from functools import lru_cache
@lru_cache(maxsize=128)
def cached_api_call(endpoint, params):
"""缓存 API 调用结果,避免重复请求"""
return requests.get(endpoint, params=params).json()
# 使用缓存后的函数替代原始函数
tools_map = {
"get_weather": cached_get_weather,
"search_products": cached_search_products
}
4.8 安全考虑
import ast
def safe_eval(expression):
"""安全的数学表达式计算,替代 eval"""
allowed_nodes = (
ast.Expression, ast.BinOp, ast.UnaryOp, ast.Num,
ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Pow,
ast.Load, ast.Constant
)
tree = ast.parse(expression, mode='eval')
for node in ast.walk(tree):
if not isinstance(node, allowed_nodes):
raise ValueError(f"不安全的表达式: {expression}")
return eval(compile(tree, '<string>', 'eval'))
五、总结对比表
| 优化维度 | 优化前 | 优化后 | 效果 |
|---|---|---|---|
| 工具描述 | 简单一句话 | 详细说明+示例+触发条件 | 工具选择准确率 ↑ 40% |
| 参数校验 | 直接执行 | Schema 校验+自动修复 | 执行成功率 ↑ 60% |
| 错误处理 | 直接报错 | 重试+回退+错误信息回传 | 用户体验大幅提升 |
| 调用策略 | 串行调用 | 并行调用+缓存 | 响应速度 ↑ 50% |
| 多轮交互 | 单轮调用 | 多步 Agent | 复杂任务完成率 ↑ 70% |
六、参考资源
作者注:Function Calling 是大模型从"聊天机器人"进化为"智能代理"的关键技术。掌握其核心原理和优化方法,是构建高效 AI Agent 的基础。
更多推荐


所有评论(0)