Function Calling是AI Agent的"手"。如果说LLM的推理能力是"大脑",那么Function Calling就是让AI从"只会说"变成"能做事"的关键能力。本文从Schema设计到安全防护,全面解析2026年的最佳实践。
一、Function Calling的本质
1.1 不只是"调API"
Function Calling(FC)的核心不是"让LLM调用函数"——而是让LLM*理解工具的能力边界,选择正确的工具,构造正确的参数*。
# LLM不执行函数,它只生成调用意图
user_input = "北京明天天气怎样?"
# LLM输出的不是天气数据,而是:
llm_output = {
"function_name": "get_weather",
"arguments": {"city": "Beijing", "date": "2026-03-04"}
}
# 应用层负责实际执行
result = get_weather(**llm_output["arguments"])
这个"理解→选择→构造"的过程,就是FC的核心智能。
1.2 2026年FC生态现状
| 特性 | GPT-4o | Claude 3.5 | Gemini 1.5 | Nova Pro | Llama 3.1 |
|---|---|---|---|---|---|
| 并行FC | ✅ | ✅ | ✅ | ✅ | ⚠️ 部分 |
| 嵌套调用 | ✅ | ✅ | ✅ | ❌ | ❌ |
| Streaming FC | ✅ | ✅ | ✅ | ✅ | ❌ |
| Forced FC | ✅ | ✅ | ✅ | ✅ | ✅ |
| Schema复杂度 | 高 | 高 | 中 | 中 | 低 |
二、Schema设计:FC成败的关键
2.1 好的Schema长什么样
# ❌ 差的Schema——LLM无法理解该何时使用
tools = [{
"type": "function",
"function": {
"name": "process",
"description": "处理数据",
"parameters": {
"type": "object",
"properties": {
"input": {"type": "string"}
}
}
}
}]
# ✅ 好的Schema——清晰、具体、有约束
tools = [{
"type": "function",
"function": {
"name": "query_sales_data",
"description": "查询指定时间范围和区域的销售数据。返回JSON格式的销售额、订单量和同比增长率。当用户询问销售业绩、收入、营收等相关问题时使用。",
"parameters": {
"type": "object",
"properties": {
"start_date": {
"type": "string",
"format": "date",
"description": "查询起始日期,格式YYYY-MM-DD"
},
"end_date": {
"type": "string",
"format": "date",
"description": "查询结束日期,格式YYYY-MM-DD"
},
"region": {
"type": "string",
"enum": ["north", "south", "east", "west", "all"],
"description": "区域筛选,默认all"
},
"granularity": {
"type": "string",
"enum": ["daily", "weekly", "monthly"],
"default": "monthly",
"description": "数据粒度"
}
},
"required": ["start_date", "end_date"]
}
}
}]
2.2 Schema设计对准确率的影响
关键发现:
- 详细描述比简单描述提升16%准确率
- 添加示例再提升7%
- 添加约束(enum、format)再提升3%
- 完全优化的Schema可达93%准确率
2.3 Schema设计5条铁律
1. 函数名用动词+名词(query_sales, create_ticket, delete_user)
2. 描述要说明"何时使用",不只是"做什么"
3. 参数用enum约束可选值
4. required只包含真正必要的参数
5. 每个参数都要有description
三、并行Function Calling
3.1 并行FC原理
# 串行:3个API调用 = 3 * 0.8s = 2.4s
# 并行:3个API调用 = max(0.8s, 0.7s, 0.9s) = 0.9s
# OpenAI并行FC示例
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "比较北京、上海、广州的天气"}],
tools=tools,
parallel_tool_calls=True # 开启并行
)
# LLM一次返回3个tool_calls
for tool_call in response.choices[0].message.tool_calls:
print(f"{tool_call.function.name}({tool_call.function.arguments})")
# get_weather({"city": "Beijing"})
# get_weather({"city": "Shanghai"})
# get_weather({"city": "Guangzhou"})
3.2 并行执行最佳实践
import asyncio
from concurrent.futures import ThreadPoolExecutor
async def execute_parallel_fc(tool_calls):
"""并行执行所有Function Call"""
async def execute_one(tc):
fn = tool_registry[tc.function.name]
args = json.loads(tc.function.arguments)
return await fn(**args) if asyncio.iscoroutinefunction(fn) else fn(**args)
results = await asyncio.gather(
*[execute_one(tc) for tc in tool_calls],
return_exceptions=True # 单个失败不影响其他
)
return [
{"tool_call_id": tc.id, "output": str(r) if not isinstance(r, Exception) else f"Error: {r}"}
for tc, r in zip(tool_calls, results)
]
四、错误处理
4.1 常见FC错误类型
| 错误类型 | 占比 | 解决方案 |
|---|---|---|
| 参数错误 | 30% | 更详细的Schema描述+示例 |
| 选错函数 | 25% | 函数描述差异化+few-shot |
| 缺少参数 | 15% | 合理设置required+默认值 |
| 幻觉函数 | 12% | strict模式+白名单校验 |
| 类型不匹配 | 10% | 强类型Schema+运行时校验 |
4.2 防御性编程
class SafeFunctionExecutor:
def __init__(self, tools: dict, max_retries: int = 2):
self.tools = tools
self.max_retries = max_retries
async def execute(self, tool_call) -> dict:
fn_name = tool_call.function.name
# 1. 白名单校验
if fn_name not in self.tools:
return {"error": f"Unknown function: {fn_name}", "retry": True}
# 2. 参数解析+校验
try:
args = json.loads(tool_call.function.arguments)
except json.JSONDecodeError:
return {"error": "Invalid JSON arguments", "retry": True}
# 3. Schema校验
schema = self.tools[fn_name].schema
errors = validate_args(args, schema)
if errors:
return {"error": f"Validation: {errors}", "retry": True}
# 4. 执行+重试
for attempt in range(self.max_retries + 1):
try:
result = await asyncio.wait_for(
self.tools[fn_name].execute(**args),
timeout=30 # 超时保护
)
return {"result": result}
except Exception as e:
if attempt == self.max_retries:
return {"error": str(e), "retry": False}
await asyncio.sleep(1 * (attempt + 1)) # 退避
五、Function Calling vs MCP
| 维度 | Function Calling | MCP |
|---|---|---|
| 标准化 | 各厂商API不同 | 统一协议(JSON-RPC) |
| 可移植性 | 绑定特定LLM厂商 | 跨模型、跨平台 |
| 安全性 | 应用层自行处理 | 协议层权限声明 |
| 动态发现 | 编译时定义 | 运行时发现工具 |
| 生态 | 成熟(2年+) | 快速增长(9700万SDK下载) |
| 性能 | 极低延迟 | 有协议开销 |
| 适用场景 | 单应用、简单集成 | 跨应用、Agent生态 |
5.1 选择建议
你的场景是什么?
├── 单一LLM + 内部工具 → Function Calling(简单直接)
├── 多LLM + 外部工具生态 → MCP(可移植性)
├── Agent框架集成 → 两者都用(FC做内部,MCP做外部)
└── 企业级部署 → MCP优先(安全+审计+标准化)
5.2 混合使用模式
# 内部工具用FC(低延迟)
internal_tools = [
{"type": "function", "function": {"name": "query_db", ...}},
{"type": "function", "function": {"name": "send_email", ...}},
]
# 外部工具用MCP(可移植+安全)
async with MCPClient("http://external-tools:8000/mcp") as mcp:
external_tools = await mcp.list_tools()
# 合并两类工具
all_tools = internal_tools + [convert_mcp_to_fc(t) for t in external_tools]
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=all_tools
)
六、成本优化
6.1 成本控制策略
class CostAwareFCRouter:
"""根据任务复杂度选择不同模型"""
def __init__(self):
self.models = {
"simple": "groq/llama-3.1-8b", # $0.27/1K calls
"medium": "aws/nova-pro", # $0.80/1K calls
"complex": "openai/gpt-4o", # $3.00/1K calls
}
def route(self, tools, message):
if len(tools) <= 3:
return self.models["simple"]
elif len(tools) <= 10:
return self.models["medium"]
else:
return self.models["complex"]
6.2 FC结果缓存
import hashlib
from datetime import timedelta
class FCCache:
def __init__(self, redis_client, default_ttl=timedelta(hours=1)):
self.redis = redis_client
self.ttl = default_ttl
def cache_key(self, fn_name, args):
content = f"{fn_name}:{json.dumps(args, sort_keys=True)}"
return f"fc:{hashlib.sha256(content.encode()).hexdigest()}"
async def get_or_execute(self, fn_name, args, executor):
key = self.cache_key(fn_name, args)
cached = await self.redis.get(key)
if cached:
return json.loads(cached)
result = await executor(fn_name, args)
await self.redis.setex(key, self.ttl, json.dumps(result))
return result
七、安全最佳实践
7.1 输入校验
# 防止SQL注入
@tool
def query_database(query: str) -> str:
"""执行只读SQL查询"""
# 白名单校验
if any(kw in query.upper() for kw in ["DROP", "DELETE", "UPDATE", "INSERT"]):
return "Error: Only SELECT queries are allowed"
# 参数化查询
return db.execute_readonly(query)
7.2 权限控制
class PermissionedToolExecutor:
def __init__(self, user_role: str):
self.permissions = {
"viewer": ["query_data", "get_report"],
"editor": ["query_data", "get_report", "update_record"],
"admin": ["query_data", "get_report", "update_record", "delete_record"],
}
self.allowed = set(self.permissions.get(user_role, []))
async def execute(self, tool_call):
if tool_call.function.name not in self.allowed:
return {"error": "Permission denied"}
return await self._execute(tool_call)
7.3 Strict模式
# OpenAI strict模式——强制LLM输出符合Schema的参数
tools = [{
"type": "function",
"function": {
"name": "create_user",
"strict": True, # 开启strict模式
"parameters": {
"type": "object",
"properties": {
"name": {"type": "string"},
"email": {"type": "string", "format": "email"},
"role": {"type": "string", "enum": ["user", "admin"]}
},
"required": ["name", "email", "role"],
"additionalProperties": False # strict要求
}
}
}]
八、总结
Function Calling是Agent AI的基础能力。2026年的关键变化:
- FC + MCP融合——内部用FC,外部用MCP
- 并行FC成为标配——5x延迟降低
- Strict模式普及——100%参数合规
- 开源模型追赶——Llama 3.1 FC准确率已达78%
- 成本下降——Groq/Together让FC成本降到$0.27/1K
核心建议:Schema设计投入的每一分钟,都能在生产中节省十倍的调试时间。
作者:JiaDe Wu | AWS Solutions Architect | sample-OpenClaw-on-AWS-with-Bedrock Owner | GitHub: github.com/JiaDe-Wu






Top comments (0)