Function Calling 与 Tool Use 架构设计
AI 导读
Function Calling 与 Tool Use 架构设计 核心概念 Function Calling(工具调用)是让大语言模型(LLM)与外部世界交互的关键机制。模型本身不执行函数,而是生成结构化的函数调用请求,由应用层执行后将结果返回给模型。 这个看似简单的机制彻底改变了 AI 应用的架构:LLM 从一个"文本生成器"变成了一个"决策引擎",能够规划、调用工具、处理结果、迭代执行。...
Function Calling 与 Tool Use 架构设计
核心概念
Function Calling(工具调用)是让大语言模型(LLM)与外部世界交互的关键机制。模型本身不执行函数,而是生成结构化的函数调用请求,由应用层执行后将结果返回给模型。
这个看似简单的机制彻底改变了 AI 应用的架构:LLM 从一个"文本生成器"变成了一个"决策引擎",能够规划、调用工具、处理结果、迭代执行。
主流平台实现对比
OpenAI Function Calling
from openai import OpenAI
client = OpenAI()
# 定义工具
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"],
},
},
},
]
# 发送请求
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "北京今天天气怎么样?"}],
tools=tools,
tool_choice="auto", # auto / none / required / 指定函数
)
message = response.choices[0].message
# 检查是否有工具调用
if message.tool_calls:
for tool_call in message.tool_calls:
# 解析函数名和参数
func_name = tool_call.function.name
func_args = json.loads(tool_call.function.arguments)
# 执行函数
result = execute_function(func_name, func_args)
# 将结果传回模型
messages.append(message)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result),
})
# 模型根据工具结果生成最终回复
final_response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
)
OpenAI 的特点:
- 支持并行工具调用(单次返回多个 tool_calls)
tool_choice可精确控制调用行为- Structured Outputs 模式保证 JSON Schema 100% 合规
Anthropic Tool Use
import anthropic
client = anthropic.Anthropic()
tools = [
{
"name": "get_weather",
"description": "获取指定城市的当前天气信息。当用户询问天气时使用此工具。",
"input_schema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称",
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"default": "celsius",
},
},
"required": ["city"],
},
},
]
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
tools=tools,
messages=[{"role": "user", "content": "北京今天天气怎么样?"}],
)
# Anthropic 使用 content blocks 模型
# 一条消息可以包含 text + tool_use 混合内容
for block in response.content:
if block.type == "tool_use":
tool_name = block.name
tool_input = block.input
tool_use_id = block.id
result = execute_function(tool_name, tool_input)
# 将结果传回
messages.append({"role": "assistant", "content": response.content})
messages.append({
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": tool_use_id,
"content": json.dumps(result),
}
],
})
final = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
tools=tools,
messages=messages,
)
Anthropic 的特点:
- Content blocks 模型,文本和工具调用可混合在同一消息中
- 强调工具描述的质量,对 prompt 工程化有更高要求
- 支持
tool_choice: {"type": "any"}和{"type": "tool", "name": "xxx"}
Google Gemini Function Calling
import google.generativeai as genai
# 定义工具(使用 Python 函数签名)
def get_weather(city: str, unit: str = "celsius") -> dict:
"""获取指定城市的当前天气信息。
Args:
city: 城市名称,如 '北京'、'上海'
unit: 温度单位,celsius 或 fahrenheit
"""
return {"temperature": 22, "condition": "晴", "unit": unit}
model = genai.GenerativeModel(
"gemini-2.0-flash",
tools=[get_weather],
)
# Gemini 支持自动执行工具调用
chat = model.start_chat(enable_automatic_function_calling=True)
response = chat.send_message("北京今天天气怎么样?")
# 模型自动调用 get_weather,返回最终结果
print(response.text)
Google 的特点:
- 支持直接传入 Python 函数,自动提取 schema
enable_automatic_function_calling实现全自动循环- 支持 Google Search 和 Code Execution 作为内置工具
三平台对比
| 特性 | OpenAI | Anthropic | |
|---|---|---|---|
| Schema 格式 | JSON Schema | JSON Schema | JSON Schema / Python 函数 |
| 并行调用 | 支持 | 支持 | 支持 |
| 流式工具调用 | 支持 | 支持 | 支持 |
| 强制调用特定工具 | tool_choice: {"name": "xxx"} |
tool_choice: {"type": "tool"} |
function_calling_config: ANY |
| 禁止工具调用 | tool_choice: "none" |
tool_choice: {"type": "none"} |
function_calling_config: NONE |
| 结构化输出保证 | Structured Outputs | 无原生保证 | 无原生保证 |
| 自动执行循环 | 无(需手动实现) | 无 | 支持 |
| 最大工具数 | 128 | 约 50-100 | 128 |
Schema 设计最佳实践
1. 描述是关键
{
"name": "search_products",
"description": "在产品目录中搜索商品。当用户需要查找、浏览或比较产品时使用。不要用于查询订单状态或用户信息。",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词。支持产品名称、类别、品牌。示例:'iPhone 16'、'运动鞋'、'索尼耳机'"
},
"category": {
"type": "string",
"enum": ["electronics", "clothing", "food", "home"],
"description": "产品类别过滤。仅在用户明确提及类别时使用"
},
"price_range": {
"type": "object",
"properties": {
"min": {"type": "number", "description": "最低价格(人民币)"},
"max": {"type": "number", "description": "最高价格(人民币)"}
},
"description": "价格范围过滤。仅在用户提及预算或价格区间时使用"
},
"sort_by": {
"type": "string",
"enum": ["relevance", "price_asc", "price_desc", "rating", "newest"],
"default": "relevance",
"description": "排序方式。默认按相关性排序"
}
},
"required": ["query"]
}
}
2. 防止参数幻觉
# 使用 enum 约束可选值
"status": {
"type": "string",
"enum": ["pending", "processing", "completed", "failed"],
"description": "订单状态筛选"
}
# 使用 pattern 约束格式
"date": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "日期,格式 YYYY-MM-DD"
}
# 使用 minimum/maximum 约束数值范围
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 10,
"description": "返回结果数量"
}
3. 工具编排模式
# 模式一:顺序调用
tools = [
search_database, # 1. 先搜索
analyze_results, # 2. 再分析
generate_report, # 3. 最后生成报告
]
# 模式二:条件分支
tools = [
check_inventory, # 检查库存
place_order, # 有库存 -> 下单
notify_restock, # 无库存 -> 通知补货
]
# 模式三:循环迭代
tools = [
web_search, # 搜索
read_page, # 阅读页面
# 模型判断信息是否充足,不足则继续搜索
summarize, # 汇总
]
错误处理架构
import json
import traceback
from enum import Enum
class ToolError(Enum):
INVALID_PARAMS = "invalid_params"
NOT_FOUND = "not_found"
PERMISSION_DENIED = "permission_denied"
RATE_LIMITED = "rate_limited"
INTERNAL_ERROR = "internal_error"
TIMEOUT = "timeout"
def execute_tool_safely(func_name: str, func_args: dict) -> dict:
"""安全执行工具调用,返回结构化错误信息"""
try:
# 参数验证
if func_name not in TOOL_REGISTRY:
return {
"error": ToolError.INVALID_PARAMS.value,
"message": f"Unknown tool: {func_name}",
"available_tools": list(TOOL_REGISTRY.keys()),
}
func = TOOL_REGISTRY[func_name]
# 执行(带超时)
result = func(**func_args)
return {"status": "success", "data": result}
except ValueError as e:
return {
"error": ToolError.INVALID_PARAMS.value,
"message": str(e),
"hint": "请检查参数格式和取值范围",
}
except PermissionError as e:
return {
"error": ToolError.PERMISSION_DENIED.value,
"message": "当前用户无权执行此操作",
}
except TimeoutError:
return {
"error": ToolError.TIMEOUT.value,
"message": "操作超时,请稍后重试",
}
except Exception as e:
return {
"error": ToolError.INTERNAL_ERROR.value,
"message": "内部错误,请联系管理员",
"debug": traceback.format_exc() if DEBUG else None,
}
重试与降级策略
async def tool_call_with_retry(
client,
messages,
tools,
max_iterations: int = 10,
max_retries_per_tool: int = 2,
):
"""带重试和最大迭代限制的工具调用循环"""
tool_call_counts = {}
iteration = 0
while iteration < max_iterations:
iteration += 1
response = await client.chat.completions.create(
model="gpt-4o",
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:
func_name = tool_call.function.name
# 检查单工具重试次数
tool_call_counts[func_name] = tool_call_counts.get(func_name, 0) + 1
if tool_call_counts[func_name] > max_retries_per_tool:
result = {
"error": "max_retries_exceeded",
"message": f"{func_name} 已重试 {max_retries_per_tool} 次,"
"请尝试其他方法或向用户确认信息",
}
else:
args = json.loads(tool_call.function.arguments)
result = execute_tool_safely(func_name, args)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result, ensure_ascii=False),
})
return "达到最大工具调用次数限制,请简化请求"
安全防护
权限控制
from dataclasses import dataclass, field
@dataclass
class ToolPermission:
"""工具权限定义"""
tool_name: str
allowed_roles: list[str] = field(default_factory=lambda: ["admin"])
requires_confirmation: bool = False
max_calls_per_session: int = 100
sensitive_params: list[str] = field(default_factory=list)
TOOL_PERMISSIONS = {
"search_products": ToolPermission(
tool_name="search_products",
allowed_roles=["user", "admin"],
requires_confirmation=False,
),
"place_order": ToolPermission(
tool_name="place_order",
allowed_roles=["user", "admin"],
requires_confirmation=True, # 下单需确认
sensitive_params=["payment_method"],
),
"delete_user": ToolPermission(
tool_name="delete_user",
allowed_roles=["admin"],
requires_confirmation=True,
max_calls_per_session=5,
),
}
def check_permission(tool_name: str, user_role: str, session) -> tuple[bool, str]:
"""检查工具调用权限"""
perm = TOOL_PERMISSIONS.get(tool_name)
if not perm:
return False, "未注册的工具"
if user_role not in perm.allowed_roles:
return False, f"角色 {user_role} 无权使用 {tool_name}"
call_count = session.get_tool_call_count(tool_name)
if call_count >= perm.max_calls_per_session:
return False, f"{tool_name} 本次会话已达到调用上限 ({perm.max_calls_per_session})"
return True, "ok"
参数注入防护
import re
def sanitize_tool_args(func_name: str, args: dict) -> dict:
"""防止通过工具参数进行注入攻击"""
sanitized = {}
for key, value in args.items():
if isinstance(value, str):
# 防止 SQL 注入
if re.search(r"(;|--|DROP|DELETE|UPDATE|INSERT)\s", value, re.IGNORECASE):
raise ValueError(f"Suspicious SQL pattern in parameter '{key}'")
# 防止命令注入
if re.search(r"[;&|`$(){}]", value):
raise ValueError(f"Suspicious shell pattern in parameter '{key}'")
# 长度限制
if len(value) > 10000:
raise ValueError(f"Parameter '{key}' exceeds maximum length")
sanitized[key] = value
return sanitized
性能优化
并行工具调用
import asyncio
async def execute_parallel_tool_calls(tool_calls):
"""并行执行多个工具调用"""
tasks = []
for tc in tool_calls:
func_name = tc.function.name
func_args = json.loads(tc.function.arguments)
tasks.append(execute_tool_async(func_name, func_args))
results = await asyncio.gather(*tasks, return_exceptions=True)
tool_results = []
for tc, result in zip(tool_calls, results):
if isinstance(result, Exception):
content = json.dumps({"error": str(result)})
else:
content = json.dumps(result, ensure_ascii=False)
tool_results.append({
"role": "tool",
"tool_call_id": tc.id,
"content": content,
})
return tool_results
工具结果缓存
import hashlib
from functools import lru_cache
class ToolCache:
def __init__(self, ttl_seconds=300):
self.cache = {}
self.ttl = ttl_seconds
def cache_key(self, func_name: str, args: dict) -> str:
args_str = json.dumps(args, sort_keys=True)
return hashlib.md5(f"{func_name}:{args_str}".encode()).hexdigest()
def get(self, func_name: str, args: dict):
key = self.cache_key(func_name, args)
entry = self.cache.get(key)
if entry and (time.time() - entry["timestamp"]) < self.ttl:
return entry["result"]
return None
def set(self, func_name: str, args: dict, result):
key = self.cache_key(func_name, args)
self.cache[key] = {
"result": result,
"timestamp": time.time(),
}
# 可缓存的工具标记
CACHEABLE_TOOLS = {"search_products", "get_weather", "get_exchange_rate"}
总结
Function Calling 的架构设计核心要点:
- Schema 设计:描述要详尽,参数要约束,避免模型幻觉
- 错误处理:结构化错误信息帮助模型理解和恢复
- 安全防护:权限控制、参数注入防护、调用频率限制
- 性能优化:并行执行、结果缓存、工具选择精简
- 可观测性:记录每次工具调用的输入输出和延迟,便于调试和优化
Maurice | [email protected]
深度加工(NotebookLM 生成)
基于本文内容生成的 PPT 大纲、博客摘要、短视频脚本与 Deep Dive 播客,用于多场景复用
PPT 大纲(5-8 张幻灯片) 点击展开
Function Calling 与 Tool Use 架构设计 — ppt
幻灯片 1:Function Calling 核心概念与价值
- 角色转变:Function Calling 将大语言模型(LLM)从单纯的“文本生成器”升级为能够与外部世界交互的“决策引擎” [1]。
- 交互机制:模型本身不直接执行函数,而是生成结构化的调用请求,由应用层执行后将结果返回给模型 [1]。
- 核心能力:赋予了 AI 应用进行任务规划、工具调用、结果处理以及迭代执行的完整闭环能力 [1]。
幻灯片 2:主流大模型平台特性对比
- OpenAI:支持并行调用与
tool_choice精确控制,并通过 Structured Outputs 模式保证 JSON Schema 100% 合规 [1, 2]。 - Anthropic (Claude):采用 Content blocks 模型,允许文本和工具调用混合在同一消息中,对 prompt 工程化有更高要求 [2, 3]。
- Google Gemini:支持直接传入 Python 函数并自动提取 Schema,具备独特的全自动执行工具调用循环能力 [3, 4]。
- 平台共性:三大平台均支持 JSON Schema 格式、并行调用以及流式工具调用功能 [4]。
幻灯片 3:Schema 设计最佳实践
- 详尽描述:工具和参数的
description是关键,需明确告知模型何时使用该工具及相关限制条件 [4, 5]。 - 防止参数幻觉:利用
enum约束可选状态值,pattern约束格式(如日期),以及minimum/maximum约束数值范围 [5, 6]。 - 灵活编排模式:工具设计可组合为顺序调用(先搜索再分析)、条件分支(有无库存走不同逻辑)以及循环迭代(搜索并判断信息是否充足) [6]。
幻灯片 4:错误处理与重试降级策略
- 结构化错误返回:在工具执行中捕获异常,并返回结构化的错误信息(如参数无效、权限被拒、超时等),帮助模型理解和恢复 [6, 7]。
- 安全执行机制:通过统一的执行函数校验工具是否注册,并附带超时控制 [6, 7]。
- 重试与降级:设置单次会话的最大迭代次数(如10次)和单工具最大重试次数(如2次),达到限制时提示用户简化请求或尝试其他方法 [7, 8]。
幻灯片 5:安全防护与权限控制架构
- 角色权限隔离:为每个工具定义允许使用的角色(如 admin 或 user),防止越权操作 [8, 9]。
- 高危操作限制:对敏感参数或高危工具(如下单、删除用户)要求用户进行二次确认,并限制单次会话的最大调用频次 [8, 9]。
- 防注入攻击拦截:通过正则匹配清理工具参数,防御 SQL 注入、Shell 命令注入,并严格限制参数的最大长度 [9, 10]。
幻灯片 6:性能优化与可观测性提升
- 并发执行加速:利用异步机制(如 asyncio)并行执行多个工具调用任务,显著缩短系统响应时间 [10, 11]。
- 工具结果缓存:针对天气、汇率等高频且允许延迟的查询工具建立 TTL 缓存,减少重复计算和外部接口调用开销 [11]。
- 系统可观测性:建议记录每次工具调用的输入、输出和延迟时间,以便于持续的调试和架构优化 [11]。
博客摘要 + 核心看点 点击展开
Function Calling 与 Tool Use 架构设计 — summary
SEO 友好博客摘要
本文深度解析大语言模型(LLM)的 Function Calling 与 Tool Use 架构设计,揭秘 AI 与外部世界交互的核心机制[1]。文章横向对比了 OpenAI、Anthropic 和 Google Gemini 三大主流平台的实现差异与特色[1-4],并系统总结了 Schema 设计、防幻觉参数约束及工具编排的最佳实践[4-6]。此外,针对 AI 应用的工程落地,本文还详细梳理了结构化错误处理、防注入安全控制、以及并行调用等性能优化方案[6-8]。这不仅是一篇技术解析,更是开发者构建安全、高效 AI Agent 的必读实战指南。
核心看点
- 三大主流平台解析:横向对比 OpenAI、Anthropic 与 Gemini 在并发及结构化输出等机制的差异[2-4]。
- Schema 设计与防幻觉:通过详尽描述与正则、枚举等参数约束手段,有效防止模型产生参数幻觉[4, 5]。
- 高可用与安全架构:系统讲解结构化错误处理、防注入安全控制、权限管理及并行缓存等优化方案[6-8]。
60 秒短视频脚本 点击展开
Function Calling 与 Tool Use 架构设计 — video
【钩子开场】(13字)
AI从陪聊变决策大脑的秘密!
【核心解说】
它让AI化身决策引擎,生成结构化的请求,指挥外部应用执行任务。[1]
写好Schema是关键,用详尽描述和参数约束,防止AI产生幻觉。[2, 3]
加上权限控制和防注入保安全,配合结构化报错帮模型重试纠错。[4-6]
【收束语】
掌握这套架构设计,让你的AI真正连通并控制外部世界![1]
课后巩固
与本文内容匹配的闪卡与测验,帮助巩固所学知识
延伸阅读
根据本文主题,为你推荐相关的学习资料