Function Calling 机制
引言
Function Calling(函数调用)是 LLM 与外部世界交互的标准化接口。各大模型提供商(OpenAI、Anthropic、Google)都提供了原生的函数调用支持,使 LLM 能够以结构化的方式描述需要调用的函数及其参数。
OpenAI Function Calling
基本流程
用户消息 → LLM 判断是否需要调用函数 → 输出函数名和参数(JSON)→ 应用执行函数 → 结果返回给 LLM → LLM 生成最终回答
工具定义
使用 JSON 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"]
}
}
}
]
完整调用示例
from openai import OpenAI
import json
client = OpenAI()
def get_weather(city, unit="celsius"):
"""实际的天气 API 调用"""
# 模拟 API 返回
return {"city": city, "temperature": 22, "unit": unit, "condition": "晴"}
def run_conversation(user_message):
messages = [{"role": "user", "content": user_message}]
# 第一轮:LLM 决定是否调用函数
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools,
tool_choice="auto", # auto/none/required/specific
)
message = response.choices[0].message
if message.tool_calls:
# 执行函数调用
messages.append(message)
for tool_call in message.tool_calls:
func_name = tool_call.function.name
func_args = json.loads(tool_call.function.arguments)
# 执行对应函数
if func_name == "get_weather":
result = get_weather(**func_args)
# 将结果返回给 LLM
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result, ensure_ascii=False)
})
# 第二轮:LLM 基于函数结果生成回答
final_response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
)
return final_response.choices[0].message.content
return message.content
# 使用
print(run_conversation("北京今天天气怎么样?"))
并行函数调用(Parallel Function Calling)
GPT-4o 支持在一次回复中调用多个函数:
# LLM 可能同时返回多个 tool_calls
# 例如:"北京和上海的天气分别是什么?"
# tool_calls: [
# {"function": {"name": "get_weather", "arguments": '{"city": "北京"}'}},
# {"function": {"name": "get_weather", "arguments": '{"city": "上海"}'}},
# ]
Structured Outputs
确保函数参数严格遵循 JSON Schema:
tools = [
{
"type": "function",
"function": {
"name": "create_task",
"description": "创建一个新任务",
"parameters": {
"type": "object",
"properties": {
"title": {"type": "string"},
"priority": {"type": "string", "enum": ["high", "medium", "low"]},
"due_date": {"type": "string", "format": "date"},
},
"required": ["title", "priority"],
"additionalProperties": False, # 严格模式
},
"strict": True # 启用 Structured Outputs
}
}
]
Anthropic Tool Use
工具定义
tools = [
{
"name": "get_weather",
"description": "获取指定城市的当前天气信息",
"input_schema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位"
}
},
"required": ["city"]
}
}
]
调用示例
import anthropic
client = anthropic.Anthropic()
def run_claude_tool_use(user_message):
messages = [{"role": "user", "content": user_message}]
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
tools=tools,
messages=messages,
)
# Claude 返回的内容可能包含文本和工具调用
tool_calls = [block for block in response.content if block.type == "tool_use"]
if tool_calls:
# 执行工具调用
messages.append({"role": "assistant", "content": response.content})
tool_results = []
for tc in tool_calls:
result = execute_tool(tc.name, tc.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": tc.id,
"content": json.dumps(result, ensure_ascii=False)
})
messages.append({"role": "user", "content": tool_results})
# 获取最终回答
final = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
tools=tools,
messages=messages,
)
return final.content[0].text
return response.content[0].text
Claude 的特殊能力
- 思考过程:使用 extended thinking 展示工具选择的推理过程
- 多轮工具使用:自动进行多轮工具调用直到获得足够信息
- 工具使用与文本交织:在同一回复中混合文本和工具调用
Google Gemini Function Calling
工具定义
import google.generativeai as genai
def get_weather(city: str, unit: str = "celsius") -> dict:
"""获取指定城市的天气信息。
Args:
city: 城市名称
unit: 温度单位 (celsius 或 fahrenheit)
Returns:
天气信息字典
"""
return {"city": city, "temperature": 22, "unit": unit}
# Gemini 可以直接从 Python 函数签名和 docstring 生成工具定义
model = genai.GenerativeModel(
model_name="gemini-1.5-pro",
tools=[get_weather]
)
chat = model.start_chat()
response = chat.send_message("东京的天气怎么样?")
# 自动处理函数调用
for part in response.parts:
if part.function_call:
result = get_weather(**part.function_call.args)
response = chat.send_message(
genai.protos.Content(
parts=[genai.protos.Part(
function_response=genai.protos.FunctionResponse(
name=part.function_call.name,
response={"result": result}
)
)]
)
)
平台对比
| 特性 | OpenAI | Anthropic | Google Gemini |
|---|---|---|---|
| 工具定义格式 | JSON Schema | JSON Schema | Python 函数 / JSON |
| 并行调用 | 支持 | 支持 | 支持 |
| 强制调用 | tool_choice: required |
tool_choice: any |
tool_config |
| 指定工具 | tool_choice: {name} |
tool_choice: {name} |
allowed_function_names |
| 结构化输出 | strict: true |
不支持 | 不支持 |
| 流式工具调用 | 支持 | 支持 | 支持 |
| 最大工具数 | 128 | 数百 | 数百 |
| 嵌套对象 | 支持 | 支持 | 支持 |
高级模式
工具选择策略
# 1. 自动选择(默认)
tool_choice = "auto" # LLM 自行决定
# 2. 强制使用工具
tool_choice = "required" # 必须调用至少一个工具
# 3. 指定工具
tool_choice = {"type": "function", "function": {"name": "get_weather"}}
# 4. 禁用工具
tool_choice = "none" # 不调用任何工具
错误处理
def safe_tool_execution(tool_name, arguments, timeout=30):
"""安全的工具执行,带错误处理"""
try:
result = execute_with_timeout(tool_name, arguments, timeout)
return {"status": "success", "data": result}
except TimeoutError:
return {"status": "error", "message": f"工具 {tool_name} 执行超时({timeout}s)"}
except ValidationError as e:
return {"status": "error", "message": f"参数验证失败: {e}"}
except Exception as e:
return {"status": "error", "message": f"执行失败: {str(e)}"}
工具结果的格式化
def format_tool_result(result, max_length=4000):
"""格式化工具结果,防止过长"""
result_str = json.dumps(result, ensure_ascii=False, indent=2)
if len(result_str) > max_length:
# 截断并添加提示
truncated = result_str[:max_length]
return truncated + f"\n... [结果已截断,原始长度: {len(result_str)} 字符]"
return result_str
多轮工具调用循环
def agent_tool_loop(query, tools, llm, max_iterations=10):
"""完整的多轮工具调用循环"""
messages = [{"role": "user", "content": query}]
for i in range(max_iterations):
response = llm.chat(messages, tools=tools)
if not response.tool_calls:
return response.content # 最终回答
messages.append(response.message)
for tool_call in response.tool_calls:
result = safe_tool_execution(
tool_call.function.name,
json.loads(tool_call.function.arguments)
)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": format_tool_result(result)
})
return "达到最大迭代次数,任务未完成。"
实践建议
工具设计清单
- [ ] 工具名称清晰、无歧义
- [ ] 描述详细说明何时使用、何时不使用
- [ ] 参数有清晰的类型和描述
- [ ] 使用 enum 限制参数范围
- [ ] 标注 required 字段
- [ ] 工具返回结构化结果
- [ ] 实现超时和错误处理
常见陷阱
- 工具描述模糊:LLM 无法判断何时使用
- 参数过于复杂:嵌套过深导致参数构造错误
- 忽略错误处理:工具失败时 Agent 无法恢复
- 工具过多:工具选择困难,建议控制在 10-20 个以内
- 结果过长:未截断的大结果浪费上下文空间
参考文献
- OpenAI. "Function Calling" Documentation
- Anthropic. "Tool Use" Documentation
- Google. "Gemini Function Calling" Documentation
- Patil, S. G., et al. (2023). "Gorilla: Large Language Model Connected with Massive APIs"