Building Your First AI Agent with Function Calling

Function calling is what turns an LLM from a text generator into something that can actually take action — checking a database, calling an API, running a calculation — by letting the model request that a specific function be called with specific arguments, which your code then executes.

Defining Tools the Model Can Call

from openai import OpenAI
client = OpenAI()

def get_weather(city: str) -> str:
    # In reality, this would call a real weather API
    return f"It's 18°C and cloudy in {city}."

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Get the current weather for a city",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "City name"}
            },
            "required": ["city"]
        }
    }
}]

The Agent Loop

import json

def run_agent(user_message):
    messages = [{"role": "user", "content": user_message}]

    response = client.chat.completions.create(
        model="gpt-4o-mini", messages=messages, tools=tools
    )
    message = response.choices[0].message

    if message.tool_calls:
        messages.append(message)
        for call in message.tool_calls:
            args = json.loads(call.function.arguments)
            result = get_weather(**args)  # actually execute the requested function
            messages.append({
                "role": "tool",
                "tool_call_id": call.id,
                "content": result,
            })

        # Send the tool's result back so the model can form a final answer
        final = client.chat.completions.create(model="gpt-4o-mini", messages=messages)
        return final.choices[0].message.content

    return message.content

print(run_agent("What's the weather like in Berlin?"))

Why This Pattern Matters

The model never actually executes anything itself — it just decides which function to call and with what arguments, based on the conversation. Your code retains full control over what actually happens, which is exactly the safety boundary that makes tool-calling agents practical to deploy: you can validate, rate-limit, or reject any requested action before it runs.

Scaling Up: Multi-Step Agents

Real agentic systems loop this pattern — model requests a tool call, your code executes it, the result goes back to the model, which may then request another tool call based on that result, continuing until it has enough information to give a final answer. This is the core mechanism behind essentially every “AI agent” product on the market today.