4 min read
Enhanced Function Calling in GPT-4 Turbo
Enhanced Function Calling in GPT-4 Turbo
GPT-4 Turbo brings significant improvements to function calling, including parallel function calls and better accuracy. Let’s explore how to leverage these enhancements.
Parallel Function Calling
The model can now request multiple function calls simultaneously:
from openai import OpenAI
import json
client = OpenAI()
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["location"]
}
}
},
{
"type": "function",
"function": {
"name": "get_stock_price",
"description": "Get current stock price",
"parameters": {
"type": "object",
"properties": {
"symbol": {"type": "string", "description": "Stock ticker symbol"}
},
"required": ["symbol"]
}
}
}
]
response = client.chat.completions.create(
model="gpt-4-1106-preview",
messages=[
{"role": "user", "content": "What's the weather in Tokyo and the current price of AAPL and MSFT?"}
],
tools=tools,
tool_choice="auto"
)
# Handle multiple parallel function calls
if response.choices[0].message.tool_calls:
for tool_call in response.choices[0].message.tool_calls:
print(f"Function: {tool_call.function.name}")
print(f"Arguments: {tool_call.function.arguments}")
print("---")
Building a Complete Function Calling System
from typing import Callable, Dict, Any
import json
class FunctionRegistry:
def __init__(self):
self.functions: Dict[str, Callable] = {}
self.schemas: list = []
def register(self, schema: dict):
"""Decorator to register a function with its schema."""
def decorator(func: Callable):
name = schema["function"]["name"]
self.functions[name] = func
self.schemas.append(schema)
return func
return decorator
def execute(self, name: str, arguments: str) -> str:
"""Execute a registered function."""
if name not in self.functions:
return json.dumps({"error": f"Unknown function: {name}"})
try:
args = json.loads(arguments)
result = self.functions[name](**args)
return json.dumps(result)
except Exception as e:
return json.dumps({"error": str(e)})
# Create registry and register functions
registry = FunctionRegistry()
@registry.register({
"type": "function",
"function": {
"name": "search_database",
"description": "Search the product database",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string"},
"category": {"type": "string"},
"max_results": {"type": "integer", "default": 10}
},
"required": ["query"]
}
}
})
def search_database(query: str, category: str = None, max_results: int = 10) -> dict:
# Simulated database search
return {
"results": [
{"id": 1, "name": f"Product matching '{query}'", "price": 29.99},
{"id": 2, "name": f"Another {query} item", "price": 49.99}
][:max_results],
"total": 2
}
@registry.register({
"type": "function",
"function": {
"name": "create_order",
"description": "Create a new order",
"parameters": {
"type": "object",
"properties": {
"product_id": {"type": "integer"},
"quantity": {"type": "integer"},
"customer_email": {"type": "string", "format": "email"}
},
"required": ["product_id", "quantity", "customer_email"]
}
}
})
def create_order(product_id: int, quantity: int, customer_email: str) -> dict:
return {
"order_id": "ORD-12345",
"status": "confirmed",
"product_id": product_id,
"quantity": quantity,
"email_sent_to": customer_email
}
@registry.register({
"type": "function",
"function": {
"name": "get_order_status",
"description": "Get the status of an order",
"parameters": {
"type": "object",
"properties": {
"order_id": {"type": "string"}
},
"required": ["order_id"]
}
}
})
def get_order_status(order_id: str) -> dict:
return {
"order_id": order_id,
"status": "shipped",
"tracking_number": "1Z999AA10123456784",
"estimated_delivery": "2023-11-15"
}
Conversation Loop with Function Calling
def run_conversation(user_message: str, registry: FunctionRegistry) -> str:
"""Run a complete conversation with function calling."""
messages = [{"role": "user", "content": user_message}]
while True:
response = client.chat.completions.create(
model="gpt-4-1106-preview",
messages=messages,
tools=registry.schemas,
tool_choice="auto"
)
assistant_message = response.choices[0].message
messages.append(assistant_message)
# Check if we need to call functions
if not assistant_message.tool_calls:
return assistant_message.content
# Execute all function calls in parallel (in real app, use asyncio)
for tool_call in assistant_message.tool_calls:
function_name = tool_call.function.name
function_args = tool_call.function.arguments
print(f"Calling {function_name} with {function_args}")
result = registry.execute(function_name, function_args)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": result
})
# Example usage
result = run_conversation(
"Search for laptops and then create an order for product ID 1, quantity 2, for customer@example.com",
registry
)
print(result)
Forcing Specific Function Calls
# Force a specific function to be called
response = client.chat.completions.create(
model="gpt-4-1106-preview",
messages=[{"role": "user", "content": "I want to know about laptops"}],
tools=registry.schemas,
tool_choice={"type": "function", "function": {"name": "search_database"}}
)
# Disable function calling for a specific request
response = client.chat.completions.create(
model="gpt-4-1106-preview",
messages=[{"role": "user", "content": "What's your opinion on laptops?"}],
tools=registry.schemas,
tool_choice="none" # Don't call any functions
)
Error Handling Best Practices
def robust_function_execution(name: str, arguments: str) -> str:
"""Execute function with comprehensive error handling."""
try:
args = json.loads(arguments)
except json.JSONDecodeError as e:
return json.dumps({
"error": "Invalid JSON arguments",
"details": str(e)
})
if name not in registry.functions:
return json.dumps({
"error": "Function not found",
"available_functions": list(registry.functions.keys())
})
try:
result = registry.functions[name](**args)
return json.dumps({"success": True, "data": result})
except TypeError as e:
return json.dumps({
"error": "Invalid arguments",
"details": str(e)
})
except Exception as e:
return json.dumps({
"error": "Function execution failed",
"type": type(e).__name__,
"details": str(e)
})
Conclusion
Enhanced function calling in GPT-4 Turbo makes it easier to build powerful AI agents that can interact with external systems. Parallel calls improve efficiency, while better accuracy reduces errors. Tomorrow, we’ll dive into Microsoft Fabric GA - the biggest announcement from Ignite 2023!