Function Calling
--- title: "Function Calling" description: "掌握OpenAI Function Calling机制的原理与实现,理解结构化输出的最佳实践" tags: ["Function Calling", "OpenAI", "结构化输出", "工具调用"] category: "llm" icon: "🧠"
Function Calling
Function Calling简介
Function Calling是OpenAI推出的一项功能,允许GPT模型在响应中调用预定义的函数。这使得模型能够与外部系统交互,获取实时数据,执行复杂计算,从而大大扩展了LLM的应用范围。
与传统的文本生成不同,Function Calling让模型能够输出结构化的函数调用信息,包括函数名和参数,然后由应用程序执行这些函数并将结果返回给模型。
核心机制
1. 函数定义与调用
import json
from typing import List, Dict, Any, Optional
from openai import OpenAI
class FunctionCaller:
def __init__(self, model: str = "gpt-4"):
self.client = OpenAI()
self.model = model
self.functions: List[Dict] = []
def add_function(self, name: str, description: str,
parameters: Dict, required: List[str] = None):
"""添加函数定义"""
function_def = {
"name": name,
"description": description,
"parameters": {
"type": "object",
"properties": parameters,
"required": required or []
}
}
self.functions.append(function_def)
def call(self, messages: List[Dict],
function_call: str = "auto") -> Dict[str, Any]:
"""调用LLM并处理函数调用"""
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
functions=self.functions,
function_call=function_call
)
message = response.choices[0].message
# 检查是否有函数调用
if message.function_call:
return {
"type": "function_call",
"function_name": message.function_call.name,
"arguments": json.loads(message.function_call.arguments),
"message": message
}
else:
return {
"type": "text",
"content": message.content,
"message": message
}
def execute_function(self, function_name: str, arguments: Dict) -> str:
"""执行函数"""
# 这里实现实际的函数逻辑
function_map = {
"get_weather": self._get_weather,
"search_products": self._search_products,
"calculate": self._calculate
}
if function_name in function_map:
return function_map[function_name](**arguments)
else:
return f"Unknown function: {function_name}"
def _get_weather(self, city: str, unit: str = "celsius") -> str:
"""获取天气"""
return json.dumps({
"city": city,
"temperature": 25,
"unit": unit,
"condition": "sunny"
})
def _search_products(self, query: str, max_results: int = 5) -> str:
"""搜索产品"""
return json.dumps({
"query": query,
"results": [
{"name": f"Product {i}", "price": 100 + i * 10}
for i in range(max_results)
]
})
def _calculate(self, expression: str) -> str:
"""计算表达式"""
try:
result = eval(expression)
return json.dumps({"expression": expression, "result": result})
except Exception as e:
return json.dumps({"error": str(e)})
# 使用示例
caller = FunctionCaller()
# 添加函数
caller.add_function(
name="get_weather",
description="获取指定城市的当前天气",
parameters={
"city": {
"type": "string",
"description": "城市名称,如'北京'"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位"
}
},
required=["city"]
)
# 对话
messages = [
{"role": "user", "content": "北京今天天气怎么样?"}
]
result = caller.call(messages)
print(f"调用类型: {result['type']}")
if result['type'] == 'function_call':
print(f"函数名: {result['function_name']}")
print(f"参数: {result['arguments']}")
2. 多轮函数调用
class MultiTurnFunctionCaller:
def __init__(self, model: str = "gpt-4"):
self.client = OpenAI()
self.model = model
self.functions: List[Dict] = []
self.function_implementations: Dict[str, callable] = {}
def register_function(self, name: str, description: str,
parameters: Dict, implementation: callable,
required: List[str] = None):
"""注册函数"""
self.functions.append({
"name": name,
"description": description,
"parameters": {
"type": "object",
"properties": parameters,
"required": required or []
}
})
self.function_implementations[name] = implementation
def chat(self, user_message: str,
max_iterations: int = 5) -> str:
"""多轮对话,支持多次函数调用"""
messages = [{"role": "user", "content": user_message}]
for _ in range(max_iterations):
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
functions=self.functions,
function_call="auto"
)
message = response.choices[0].message
messages.append(message)
# 检查是否有函数调用
if message.function_call:
func_name = message.function_call.name
func_args = json.loads(message.function_call.arguments)
# 执行函数
result = self.function_implementations[func_name](**func_args)
# 添加函数结果到消息
messages.append({
"role": "function",
"name": func_name,
"content": result
})
else:
# 没有函数调用,返回最终回答
return message.content
return "达到最大迭代次数"
# 使用示例
caller = MultiTurnFunctionCaller()
# 注册函数
caller.register_function(
name="get_stock_price",
description="获取股票价格",
parameters={
"symbol": {"type": "string", "description": "股票代码"}
},
implementation=lambda symbol: json.dumps({"symbol": symbol, "price": 150.25})
)
caller.register_function(
name="calculate_portfolio_value",
description="计算投资组合价值",
parameters={
"holdings": {"type": "array", "items": {"type": "object"}},
"prices": {"type": "object"}
},
implementation=lambda holdings, prices: json.dumps({"total_value": 50000})
)
# 多轮对话
response = caller.chat("帮我查一下苹果公司的股票价格,然后计算我的投资组合价值")
print(response)
结构化输出
from pydantic import BaseModel, Field
from typing import List, Optional
class WeatherResponse(BaseModel):
city: str = Field(description="城市名称")
temperature: float = Field(description="温度")
condition: str = Field(description="天气状况")
humidity: int = Field(description="湿度百分比")
class ProductSearchResult(BaseModel):
query: str = Field(description="搜索查询")
total_results: int = Field(description="总结果数")
products: List[Dict] = Field(description="产品列表")
class StructuredOutputCaller:
def __init__(self, model: str = "gpt-4"):
self.client = OpenAI()
self.model = model
def get_structured_output(self, messages: List[Dict],
response_model: BaseModel) -> BaseModel:
"""获取结构化输出"""
# 构建JSON Schema
schema = response_model.model_json_schema()
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
response_format={"type": "json_object"}
)
content = json.loads(response.choices[0].message.content)
# 验证并返回Pydantic模型
return response_model(**content)
def parse_weather(self, query: str) -> WeatherResponse:
"""解析天气查询"""
messages = [
{"role": "user", "content": query}
]
return self.get_structured_output(messages, WeatherResponse)
def parse_product_search(self, query: str) -> ProductSearchResult:
"""解析产品搜索"""
messages = [
{"role": "user", "content": query}
]
return self.get_structured_output(messages, ProductSearchResult)
# 使用示例
caller = StructuredOutputCaller()
weather = caller.parse_weather("北京今天天气怎么样?")
print(f"城市: {weather.city}, 温度: {weather.temperature}°C")
错误处理
class FunctionCallError(Exception):
def __init__(self, message: str, error_type: str = "unknown"):
self.message = message
self.error_type = error_type
super().__init__(self.message)
class RobustFunctionCaller:
def __init__(self, model: str = "gpt-4"):
self.client = OpenAI()
self.model = model
self.functions: List[Dict] = []
self.retry_count = 3
def safe_call(self, messages: List[Dict]) -> Dict:
"""安全调用,包含错误处理"""
for attempt in range(self.retry_count):
try:
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
functions=self.functions if self.functions else None,
function_call="auto"
)
message = response.choices[0].message
if message.function_call:
# 验证函数名和参数
func_name = message.function_call.name
if not self._validate_function_name(func_name):
raise FunctionCallError(f"Invalid function: {func_name}")
try:
args = json.loads(message.function_call.arguments)
self._validate_arguments(func_name, args)
except json.JSONDecodeError as e:
raise FunctionCallError(f"Invalid JSON arguments: {e}")
except ValueError as e:
raise FunctionCallError(f"Invalid arguments: {e}")
return {
"success": True,
"function_name": func_name,
"arguments": args
}
else:
return {
"success": True,
"content": message.content
}
except FunctionCallError as e:
if attempt < self.retry_count - 1:
messages.append({
"role": "system",
"content": f"Error: {e.message}. Please try again."
})
continue
else:
return {
"success": False,
"error": str(e)
}
except Exception as e:
if attempt < self.retry_count - 1:
continue
return {
"success": False,
"error": str(e)
}
def _validate_function_name(self, name: str) -> bool:
"""验证函数名"""
valid_names = [f["name"] for f in self.functions]
return name in valid_names
def _validate_arguments(self, function_name: str, args: Dict):
"""验证参数"""
# 查找函数定义
func_def = next(
(f for f in self.functions if f["name"] == function_name),
None
)
if func_def is None:
raise ValueError(f"Function not found: {function_name}")
# 检查必需参数
required = func_def["parameters"].get("required", [])
for param in required:
if param not in args:
raise ValueError(f"Missing required parameter: {param}")
总结
Function Calling使得LLM能够与外部系统交互,是构建AI应用的核心技术。通过正确设计函数定义、处理多轮调用和错误情况,可以创建强大的智能应用。