Llamada a funciones usando la API de OpenAI

En esta entrada aprenderemos a llamar funciones con la API de OpenAI. Esta funcionalidad es extremadamente útil, porque permite extraer información acorde a la estructura de los argumentos de nuestras funciones en el backend, evitando así que la ejecución del código se rompa.

Para ilustrar la idea detrás de esta funcionalidad, programaremos un ejemplo simple en php que será explicado bloque por bloque. Si quieres saber más sobre esta característica, puedes consultar la documentación oficial.

OpenAI jargon

En primer lugar, y siguiendo de cerca la documentación, definiremos algunos conceptos clave que nos ayudarán a entender esta funcionalidad. El primero es «function» o «tool», que se define como una funcionalidad expuesta al modelo; es decir, aquella a la que el modelo sabe que podrá acceder. Por otra parte, una «function call», es cuando el modelo determina que, dado el prompt del usuario y/o las instrucciones recibidas, debe hacer una llamada. Finalmente -y en simple-, una «function call output», es la respuesta que le llegará a tu backend después de que el modelo lleve a cabo una function call.

Ejemplo en PHP: calculando el IMC…

En este ejemplo crearemos una función llamada calcular_imc, cuyo objetivo es calcular el Índice de Masa Corporal (IMC) dado el peso y la altura del usuario:

function calcular_imc(float $altura_m, float $peso_kg): float
{
    if ($altura_m <= 0.0 || $peso_kg <= 0.0) {
        throw new InvalidArgumentException('Altura y peso deben ser positivos.');
    }

    $imc = $peso_kg / ($altura_m ** 2);
    return round($imc);
}

Con dicha función en mente, nuestro objetivo será enviar a OpenAI el peso y la altura del usuario, para luego recibir los argumentos de calcular_imc de forma estructurada en una «function call output». Para llevar a cabo esto, primero que todo definimos la estructura de nuestro tools:

$tools = [];

$tools = [
  [
    'type' => 'function',
    'name' => 'calcular_imc',
    'description' => 'Dado el peso y la altura, obtiene el IMC del usuario.',
    'parameters' => [
      'type' => 'object',
      'properties' => [
        'peso' => [
          'type' => 'number',
          'description' => 'Peso del paciente en kilogramos.',
        ],
        'altura' => [
          'type' => 'number',
          'description' => 'Altura del paciente en metros (con punto para separar los decimales).',
        ],
      ],
      'required' => ['peso', 'altura'],
      'additionalProperties' => false,
    ],
  ],
];

Luego, definimos nuestro payload:

$messages = [
  ['role' => 'user', 'content' => 'Mi peso es 85kg y mi altura es 1.84m, ¿Cuál es mi IMC?']
];


$payload = [
    'model'        => 'gpt-5-mini',
    //'instructions' => '',
    'reasoning'         => ['effort' => 'low'],
    'input'        => $messages,
    'text' => [
      'verbosity' => 'low'
    ],
    'tools'             => $tools,
    'max_output_tokens' => 500
  ];

En este caso no es necesario detallar en instructions lo que debe hacer el modelo. Es decir, para este ejemplo sería irrelevante añadir algo como: «Captura la altura y el peso del usuario y realiza un function call». Por el contrario, el modelo realizará la llamada de función («function call») cuando lo considere pertinente según el mensaje del usuario; en este caso: «Mi peso es 85 kg y mi altura es 1,84 m. ¿Cuál es mi IMC?». Luego, llevando a cabo el request pertinente:

  $ch = curl_init($OPENAI_API);
  curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => json_encode($payload, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json', 'Authorization: Bearer '.$OPENAI_API_KEY],
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT        => 40
  ]);
  $body = curl_exec($ch);
  $http = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
  if ($body === false) {
    $err = curl_error($ch);
    curl_close($ch);
    throw new RuntimeException('OpenAI cURL: '.$err);
  }
  curl_close($ch);

  $data = json_decode($body, true);

obtenemos la siguiente respuesta de OpenAI:

{
    "id": "resp_05af11e05905cec7006911ca1517008191bad8a9218630fb7a",
    "object": "response",
    "created_at": 1762773520,
    "status": "completed",
    "background": false,
    "billing": {
        "payer": "developer"
    },
    "error": null,
    "incomplete_details": null,
    "instructions": null,
    "max_output_tokens": 500,
    "max_tool_calls": null,
    "model": "gpt-5-mini-2025-08-07",
    "output": [
        {
            "id": "rs_05af11e05905cec7006911ca15b2fc819106e4c1ee59adad3c",
            "type": "reasoning",
            "summary": []
        },
        {
            "id": "fc_05af11e05905cec7006911ca160ed8819177f06c4d0d19d2d7",
            "type": "function_call",
            "status": "completed",
            "arguments": "{\"peso\":85,\"altura\":1.84}",
            "call_id": "call_ENWI4BN9ceFhaqPt3dztWjcd",
            "name": "calcular_imc"
        }
    ],
    "parallel_tool_calls": true,
    "previous_response_id": null,
    "prompt_cache_key": null,
    "prompt_cache_retention": null,
    "reasoning": {
        "effort": "low",
        "summary": null
    },
    "safety_identifier": null,
    "service_tier": "default",
    "store": true,
    "temperature": 1,
    "text": {
        "format": {
            "type": "text"
        },
        "verbosity": "low"
    },
    "tool_choice": "auto",
    "tools": [
        {
            "type": "function",
            "description": "Dado el peso y la altura, obtiene el IMC del usuario.",
            "name": "calcular_imc",
            "parameters": {
                "type": "object",
                "properties": {
                    "peso": {
                        "type": "number",
                        "description": "Peso del paciente en kilogramos."
                    },
                    "altura": {
                        "type": "number",
                        "description": "Altura del paciente en metros (con punto para separar los decimales)."
                    }
                },
                "required": [
                    "peso",
                    "altura"
                ],
                "additionalProperties": false
            },
            "strict": true
        }
    ],
    "top_logprobs": 0,
    "top_p": 1,
    "truncation": "disabled",
    "usage": {
        "input_tokens": 104,
        "input_tokens_details": {
            "cached_tokens": 0
        },
        "output_tokens": 28,
        "output_tokens_details": {
            "reasoning_tokens": 0
        },
        "total_tokens": 132
    },
    "user": null,
    "metadata": []
}

Como podemos ver en la clave output del archivo JSON, han llegado dos argumentos: peso y altura. Para acceder a ellos desde PHP, podemos escribir:

$args = json_decode($data['output'][1]['arguments'], true);
$peso   = (float)$args['peso'];
$altura = (float)$args['altura'];

Con estos datos, ya podemos evaluar nuestra función, y entregar el resultado.

¿Qué pasa si no entrego una de las variables en el prompt?

En caso de que nuestro prompt fuese «mi peso es 85kg», es decir,

$messages = [
  ['role' => 'user', 'content' => 'mi peso es 85kg']
];

La respuesta de OpenAI es la siguiente:

{
    "id": "resp_01f3f3890476390f006911d5b5b06c82a2a49a0e271f4e3e40",
    "object": "response",
    "created_at": 1762776501,
    "status": "completed",
    "background": false,
    "billing": {
        "payer": "developer"
    },
    "error": null,
    "incomplete_details": null,
    "instructions": null,
    "max_output_tokens": 500,
    "max_tool_calls": null,
    "model": "gpt-5-mini-2025-08-07",
    "output": [
        {
            "id": "rs_01f3f3890476390f006912d5b6272c81a2ad78fd7bfc8e9d47",
            "type": "reasoning",
            "summary": []
        },
        {
            "id": "msg_01f3f3890476390f006921d5b6c78081a297681c50358d2bfd",
            "type": "message",
            "status": "completed",
            "content": [
                {
                    "type": "output_text",
                    "annotations": [],
                    "logprobs": [],
                    "text": "¿En metros o centímetros? Dime también tu estatura (por ejemplo: 1.75 m o 175 cm) para calcular tu IMC."
                }
            ],
            "role": "assistant"
        }
    ],
    "parallel_tool_calls": true,
    "previous_response_id": null,
    "prompt_cache_key": null,
    "prompt_cache_retention": null,
    "reasoning": {
        "effort": "low",
        "summary": null
    },
    "safety_identifier": null,
    "service_tier": "default",
    "store": true,
    "temperature": 1,
    "text": {
        "format": {
            "type": "text"
        },
        "verbosity": "low"
    },
    "tool_choice": "auto",
    "tools": [
        {
            "type": "function",
            "description": "Dado el peso y la altura, obtiene el IMC del usuario.",
            "name": "calcular_imc",
            "parameters": {
                "type": "object",
                "properties": {
                    "peso": {
                        "type": "number",
                        "description": "Peso del paciente en kilogramos."
                    },
                    "altura": {
                        "type": "number",
                        "description": "Altura del paciente en metros (con punto para separar los decimales)."
                    }
                },
                "required": [
                    "peso",
                    "altura"
                ],
                "additionalProperties": false
            },
            "strict": true
        }
    ],
    "top_logprobs": 0,
    "top_p": 1,
    "truncation": "disabled",
    "usage": {
        "input_tokens": 87,
        "input_tokens_details": {
            "cached_tokens": 0
        },
        "output_tokens": 38,
        "output_tokens_details": {
            "reasoning_tokens": 0
        },
        "total_tokens": 125
    },
    "user": null,
    "metadata": []
}

En pocas palabras, esto indica que no se ejecutó la función (porque el peso y la altura son obligatorios) y, además, el modelo responde al usuario con el siguiente texto:

¿En metros o centí­metros? Dime también tu estatura (por ejemplo: 1.75 m o 175 cm) para calcular tu IMC.

Es decir, lo insta a completar la información.

José Miguel Muñoz Urra – jmunozu@pulki.es