AI Engineer Roadmap All lessons Interactive Quiz MernStackDev GitHub

Function Calling

Play the interactive lesson: Tool calling, step by stepRead on GitHub
On this page
  1. Explain it like I'm new
  2. Watch it
  3. How it works
  4. Code example
  5. When to use it / when not to
  6. Common mistakes
  7. Recap
  8. Interview questions
  9. Go deeper

Level: Beginner · Reading time: 8 min · Phase: 4 · Agentic AI

In one line: Function calling is the OpenAI-style name for tool calling: the model returns a function name plus JSON arguments, and your code maps that to a real function and runs it.

Before you start: Tool Calling. You should know that the model outputs a tool name plus JSON arguments, and your code runs the tool.

Explain it like I'm new

"Tool calling" and "function calling" describe the same idea. OpenAI popularized the term function calling; Anthropic and others say tool use. Many people use the words interchangeably.

Picture a restaurant order slip. The waiter (the model) doesn't cook. They write a slip: dish: "pasta", size: "large", extras: ["cheese"]. The kitchen (your code) reads the slip, checks that the dish exists and the fields make sense, and cooks it. The slip must follow a fixed form, or the kitchen can't read it.

This page focuses on the kitchen side: turning the model's slip into a safe, correct function call. That is called dispatch.

Words to know:

Watch it

Tool calling, step by step: The model asks, your code acts.
Tool calling, step by step: The model asks, your code acts.

It plays on a loop. Read the caption in the box, one step at a time. Want to go at your own pace? Step through it with Previous and Next.

How it works

  1. Describe each function with a name, description, and a JSON Schema for its parameters.
  2. The model returns a call: function name and an arguments value. In the OpenAI Chat Completions API, the arguments arrive as a JSON string you must parse.
  3. Dispatch:
    • Look up the function by name in a registry (a dict). Unknown name → readable error.
    • Parse the JSON. Invalid JSON → readable error.
    • Validate against the schema: required fields present, types right, no unexpected fields.
    • Call the function. Catch exceptions and turn them into error strings.
  4. Send the result back, linked by the call's ID (tool_call_id in OpenAI, tool_use_id in Anthropic).
model output                          your dispatcher
------------------------------        ---------------------------------------
name: "multiply"                ->    registry["multiply"]       found?
arguments: '{"a": 6, "b": 7}'   ->    json.loads(...)            valid JSON?
                                      validate(schema, args)     types ok?
                                      multiply(**args)  -> "42"  caught errors?

Worked example

Here is the dispatcher from the code example below, given four different slips. These are real outputs (run dispatch from agent_react.py):

Model's slip (name, arguments)What dispatch doesResult
multiply, {"a": 6, "b": 7}name found, both fields present, both numbers, runs 6 * 742
multiply, {"a": 6, "c": 7}b is required but missingError: missing required argument 'b'
multiply, {"a": "6", "b": 7}a is a string, not a numberError: argument 'a' must be number
divide, {"a": 6, "b": 7}divide is not in the registryError: unknown tool 'divide'. Available: lookup, multiply

Only the first slip runs the function. The other three become short error messages that go back to the model, which can then fix its call and try again. Nothing crashes. (The last message lists the tools in the full file, so it shows two names. The short code below prints a slightly shorter message.)

Strict mode / structured outputs. Some APIs can constrain the model so its arguments always match your schema (for example OpenAI's strict: true for function definitions). This reduces bad calls. You should still validate, because values can be valid JSON and still wrong for your business rules (a negative quantity, or an ID that doesn't exist).

Parallel calls. Models can return several function calls in one turn. Run independent ones (concurrently if you like), and return one result per call ID.

Code example

From agent_react.py: a registry, a tiny JSON Schema validator, and a dispatcher that never crashes.

TOOLS = {
    "multiply": {
        "fn": lambda a, b: str(a * b),
        "schema": {"type": "object",
                   "properties": {"a": {"type": "number"}, "b": {"type": "number"}},
                   "required": ["a", "b"], "additionalProperties": False},
    },
}

def validate_args(schema, args):
    for key in schema.get("required", []):
        if key not in args:
            return f"missing required argument '{key}'"
    for key, value in args.items():
        if key not in schema["properties"]:
            return f"unexpected argument '{key}'"
        if schema["properties"][key]["type"] == "number" and (
                isinstance(value, bool) or not isinstance(value, (int, float))):
            return f"argument '{key}' must be number"
    return None

def dispatch(name, args):
    tool = TOOLS.get(name)
    if tool is None:
        return f"Error: unknown tool '{name}'"
    problem = validate_args(tool["schema"], args)
    if problem:
        return f"Error: {problem}"
    try:
        return tool["fn"](**args)
    except Exception as e:
        return f"Error: {e}"

print(dispatch("multiply", {"a": 6, "b": 7}))    # 42
print(dispatch("multiply", {"a": 6, "c": 7}))    # Error: missing required argument 'b'

In real projects (OpenAI Python SDK, Chat Completions):

import json
from openai import OpenAI

client = OpenAI()
tools = [{
    "type": "function",
    "function": {
        "name": "multiply",
        "description": "Multiply two numbers.",
        "parameters": {"type": "object",
                       "properties": {"a": {"type": "number"}, "b": {"type": "number"}},
                       "required": ["a", "b"]},
    },
}]
messages = [{"role": "user", "content": "What is 6 times 7?"}]
resp = client.chat.completions.create(model="gpt-4o-mini", messages=messages, tools=tools)
msg = resp.choices[0].message
if msg.tool_calls:
    messages.append(msg)
    for call in msg.tool_calls:
        args = json.loads(call.function.arguments)      # arguments is a JSON string
        messages.append({"role": "tool", "tool_call_id": call.id, "content": dispatch(call.function.name, args)})
    print(client.chat.completions.create(model="gpt-4o-mini", messages=messages, tools=tools).choices[0].message.content)

When to use it / when not to

Provider termDefinition fieldResult message
Anthropic "tool use"input_schematool_result block with tool_use_id
OpenAI "function calling"parametersrole: "tool" message with tool_call_id

Common mistakes

Recap

If you remember only three things:

Interview questions

1. Is function calling different from tool calling? <details><summary>Answer</summary> Not in concept. Both mean the model emits a structured request (name + JSON arguments) that your code executes. The naming and message formats differ by provider. </details>

2. What should a dispatcher check before running a function? <details><summary>Answer</summary> That the function name exists in an allowlist/registry, the arguments parse as JSON, they match the schema (required fields, types, no extras), and they pass business rules and permissions. Then run it inside error handling. </details>

3. If strict schema mode guarantees valid JSON, why validate again? <details><summary>Answer</summary> Schema-valid values can still be wrong or dangerous: nonexistent IDs, out-of-range amounts, or actions the user isn't allowed to perform. Business and permission checks must live in your code. </details>

Go deeper

Prev: Tool Calling · Next: ReAct Pattern · Back to phase: Phase 4