I’m having trouble getting multiple function calls to work reliably.
Problem Description
When I ask the model to perform multiple tasks that should each trigger a separate function call, it only calls one function instead of both. For example, with this user message:
What’s the weather like in London today and can you send that information to [email protected]?
I expect the model to:
- Call
get_weather
to get the weather in London - Call
send_email
to send that information to the email address
However, the model only calls the get_weather
function and then returns None
as the final response, instead of incorporating the weather information into an email or explicitly calling the email function.
What I’ve Tried
- Setting
tool_choice="required"
to force function calls - Setting
parallel_tool_calls=True
to explicitly enable multiple function calls - Using different model versions
- Adding detailed function descriptions
- Rewriting the user query to be more explicit
Nothing seems to work consistently. The model calls just one function, this is true for gpt-4o and o3-mini, both models which i’d expect to be able to handle this.
Minimal Reproducible Example
I’ve attached a simple script that demonstrates the issue. It’s a minimal version that defines two functions (weather and email) and tries to get the model to use both in sequence.
Questions
- Is there something wrong with my implementation of function calling?
- Should I be using a different approach entirely for multi-step tasks like this?
Any help would be greatly appreciated!
Code to reproduce:
from openai import OpenAI
import json
import time
# Initialize OpenAI client
client = OpenAI()
# Define the functions
def get_weather(location):
"""Mock weather function"""
return f"Current weather in {location}: 15°C, partly cloudy"
def send_email(to, subject, body):
"""Mock email function"""
print(f"\nWould send email to: {to}")
print(f"Subject: {subject}")
print(f"Body: {body}\n")
return f"Email sent to {to} successfully!"
def call_function(name, args):
"""Router function to call the appropriate function"""
if name == "get_weather":
return get_weather(args.get("location", ""))
elif name == "send_email":
return send_email(args.get("to", ""), args.get("subject", ""), args.get("body", ""))
else:
return f"Function {name} not implemented"
# Define tools for the model
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name, e.g., London, UK"
}
},
"required": ["location"],
"additionalProperties": False
},
"strict": True
}
},
{
"type": "function",
"function": {
"name": "send_email",
"description": "Send an email to a recipient",
"parameters": {
"type": "object",
"properties": {
"to": {
"type": "string",
"description": "Recipient email address"
},
"subject": {
"type": "string",
"description": "Email subject line"
},
"body": {
"type": "string",
"description": "Email body content"
}
},
"required": ["to", "subject", "body"],
"additionalProperties": False
},
"strict": True
}
}
]
# Main test function
def test_multiple_functions():
print("Testing multiple function calls...")
user_message = "What's the weather like in London today and can you send that information to [email protected]?"
print(f"User message: '{user_message}'")
messages = [{"role": "user", "content": user_message}]
# First completion to trigger function calls
print("\n=== First API Call ===")
completion = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools,
# Force tool calls
tool_choice="required",
parallel_tool_calls=True
)
assistant_message = completion.choices[0].message
tool_calls = assistant_message.tool_calls
# Add the assistant message to conversation
messages.append(assistant_message)
print(f"messages after first completion: {messages}")
if not tool_calls:
print("No function calls were made by the model.")
return
print(f"Number of function calls: {len(tool_calls)}")
# Process all tool calls
for i, tool_call in enumerate(tool_calls):
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
print(f"\nFunction call {i+1}: {function_name}")
print(f"Arguments: {function_args}")
# Execute the function
function_response = call_function(function_name, function_args)
print(f"Function result: {function_response}")
# Add result to messages
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": function_response
})
print(f"messages after tool calls: {messages}")
# Second completion to get final response
print("\n=== Second API Call ===")
second_completion = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools,
tool_choice="auto" # Dont need tool calls for final response
)
final_response = second_completion.choices[0].message.content
print(f"\nFinal response: {final_response}")
if __name__ == "__main__":
test_multiple_functions()
I’m missing something fundamental so any help/pointers would be greatly appreciated!