Tema 2 · Integración HTTP

2. Consumo de APIs de LLMs en PHP (cURL/Guzzle) y Node/Browser (Fetch)

Aprende a estructurar peticiones HTTP profesionales a endpoints de chat completions: cabeceras de autorización, roles y control de temperatura.

El Estándar "Chat Completions": La Estructura Universal

Casi todos los proveedores modernos de Inteligencia Artificial (OpenAI, Anthropic Claude mediante adaptadores, Google Gemini vía REST, Groq, Mistral y DeepSeek) han convergido hacia un estándar común para enviar y recibir conversaciones: la especificación Chat Completions basada en un arreglo de mensajes con roles diferenciados.

En lugar de enviar un único bloque de texto sin estructura, el payload organiza la conversación cronológicamente usando tres roles primordiales:

  • system: Define la identidad, límites, tono y reglas inviolables del asistente (invisible para el usuario final).
  • user: Representa la entrada o pregunta enviada por el visitante del sitio web.
  • assistant: Respuestas previas generadas por el modelo, necesarias para mantener memoria y contexto multi-turno.

Hiperparámetros Clave: ¿Cómo Calibrar el Modelo?

Al configurar una petición HTTP, el comportamiento del LLM depende directamente de los siguientes parámetros de inferencia:

Parámetro Rango Típico Uso Recomendado en Desarrollo Web
temperature 0.0 a 2.0 0.0 a 0.2: Para extracción de datos, SQL, JSON y respuestas deterministas.
0.7 a 1.0: Para redacción de blogs, chatbots conversacionales y lluvia de ideas.
max_tokens 1 a 16,384+ Límite máximo de tokens que el modelo puede generar en la respuesta. Protege contra bucles y controla el costo de cada llamada.
top_p (Nucleus Sampling) 0.0 a 1.0 Alternativa a la temperatura. Con 0.1 solo considera el 10% de tokens con mayor probabilidad acumulada. Se recomienda alterar temperature o top_p, pero no ambos simultáneamente.
seed Entero (ej. 42) Permite obtener respuestas pseudo-deterministas para pruebas unitarias y depuración consistente.
💡 Recomendación Pro: Para salidas estructuradas en JSON o generación de código SQL/PHP, mantén siempre temperature = 0.0. Esto minimiza el riesgo de creatividad no deseada y asegura máxima reproducibilidad.

⚡ Consumo de la API con PHP 8.3 Tipado y Manejo de Errores HTTP

Servicio PHP que encapsula llamadas al endpoint /v1/chat/completions con timeout, validación estricta y control de tokens.

call-llm-api.php
Solo lectura
<?php
declare(strict_types=1);

namespace App\Services;

class OpenAIService
{
    private string $apiKey;
    private string $baseUrl;

    public function __construct(string $apiKey, string $baseUrl = 'https://api.openai.com/v1')
    {
        $this->apiKey = $apiKey;
        $this->baseUrl = rtrim($baseUrl, '/');
    }

    /**
     * Envía una lista de mensajes al modelo de chat y retorna el texto generado.
     *
     * @param array<int, array{role: string, content: string}> $messages
     * @param string $model
     * @param float $temperature
     * @param int $maxTokens
     * @return array{content: string, prompt_tokens: int, completion_tokens: int, total_tokens: int}
     * @throws \RuntimeException
     */
    public function chatCompletion(
        array $messages,
        string $model = 'gpt-4o-mini',
        float $temperature = 0.2,
        int $maxTokens = 800
    ): array {
        $endpoint = $this->baseUrl . '/chat/completions';

        $payload = [
            'model'       => $model,
            'messages'    => $messages,
            'temperature' => $temperature,
            'max_tokens'  => $maxTokens,
        ];

        $ch = curl_init($endpoint);
        curl_setopt_array($ch, [
            CURLOPT_POST           => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER     => [
                'Content-Type: application/json',
                'Authorization: Bearer ' . $this->apiKey,
                'User-Agent: Kilopolis-Web-App/1.0',
            ],
            CURLOPT_POSTFIELDS     => json_encode($payload, JSON_THROW_ON_ERROR),
            CURLOPT_TIMEOUT        => 25,
            CURLOPT_CONNECTTIMEOUT => 5,
        ]);

        $rawResponse = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $curlError = curl_error($ch);
        curl_close($ch);

        if ($curlError !== '') {
            throw new \RuntimeException("Falla de conexión de red con el LLM: {$curlError}");
        }

        if ($httpCode !== 200 || !is_string($rawResponse)) {
            throw new \RuntimeException("Respuesta de API inválida (HTTP {$httpCode}): {$rawResponse}");
        }

        $decoded = json_decode($rawResponse, true, 512, JSON_THROW_ON_ERROR);

        return [
            'content'           => $decoded['choices'][0]['message']['content'] ?? '',
            'prompt_tokens'     => $decoded['usage']['prompt_tokens'] ?? 0,
            'completion_tokens' => $decoded['usage']['completion_tokens'] ?? 0,
            'total_tokens'      => $decoded['usage']['total_tokens'] ?? 0,
        ];
    }
}
🧠
Mini-Quiz de Retención
Comprueba tu comprensión antes de pasar a la siguiente lección
1 Pregunta
Pregunta 1
¿Qué valor de temperatura (temperature) es el más adecuado si tu aplicación web necesita que el LLM genere un objeto JSON estricto o una consulta SQL sin inventar campos?
🦉
¿Tienes dudas con este tema?
Sócrates AI puede darte analogías y explicarte este código paso a paso.

🚀 ¿Quieres dominar IA Aplicada a Desarrollo Web: APIs, RAG & Agentes y crear proyectos reales?

Aprende arquitectura limpia, desarrollo web moderno, buenas prácticas y despliegue en producción con nuestro Bootcamp Fullstack Developer con mentorías en vivo.

🏛️ Autoría y Propiedad Intelectual: Obra educativa original de David Amador para Kilopolis Academia (Solucionáticos), estructurada sobre las especificaciones y estándares abiertos del W3C, el estándar vivo de WHATWG y la documentación oficial de código abierto. © Todos los derechos reservados.