Function calling

OpenAI API · Practical · 15 min

Function calling lets the model use your data and actions; it is the core of every agent.

With function calling you describe functions your program can run, and the model decides when to call one. The API never runs your code; it asks you to.

A function tool is a dict with type: "function", a name, a description (be specific; the model uses it to decide), parameters as a JSON Schema, and usually strict: True so the arguments always match the schema.

The loop:

  • Call responses.create with tools=[...] and your input list.
  • Add response.output to the input list. Look for items with type == "function_call"; each has a name, a call_id and arguments, a JSON string you must json.loads.
  • Run your function and append {"type": "function_call_output", "call_id": ..., "output": "..."} for every call.
  • Call the API again with the longer input. Repeat until no more function calls come back, then read output_text.

The model can request several calls in one response, so handle them all. With reasoning models, keep the reasoning items from response.output in the input too (appending the whole output does this), or use previous_response_id.

tool_choice controls the decision ("auto" by default, "required", "none", or a specific function), and parallel_tool_calls=False limits it to one call at a time. Validate arguments before doing anything with side effects, such as payments or deletes.

Example

import json
from openai import OpenAI

client = OpenAI()
tools = [{
    "type": "function", "name": "get_weather", "strict": True,
    "description": "Current weather for a city.",
    "parameters": {"type": "object", "properties": {"city": {"type": "string"}},
                   "required": ["city"], "additionalProperties": False},
}]
def get_weather(city): return f"{city}: 18C, light rain"   # fake lookup

items = [{"role": "user", "content": "Do I need an umbrella in Oslo?"}]
while True:
    r = client.responses.create(model="gpt-6-astra", tools=tools, input=items)
    items += r.output
    calls = [i for i in r.output if i.type == "function_call"]
    if not calls:
        break
    for c in calls:
        args = json.loads(c.arguments)
        items.append({"type": "function_call_output", "call_id": c.call_id,
                      "output": get_weather(**args)})
print(r.output_text)

Remember

  • Tools: type function + name + description + parameters (+ strict)
  • Run each function_call; reply with function_call_output by call_id
  • arguments is a JSON string; keep reasoning items in the input

Watch out for

  • Forgetting json.loads on arguments and passing a string where a dict is expected.

Check yourself

  1. How does the API know which function result answers which call?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: developers.openai.com/api/docs/guides/function-calling