Volver al Blog

Cómo Implementar ChatGPT en tu Aplicación Laravel: Guía Práctica 2025

Cómo Implementar ChatGPT en tu Aplicación Laravel: Guía Práctica 2025

Seamos honestos: integrar ChatGPT en Laravel suena fácil en los tutoriales de YouTube. "Solo instala el paquete, configura la API key y listo", dicen. Mentira. Me tomó 3 días, 47 cafés y casi $20 en tokens desperdiciados hasta que finalmente funcionó como debía.

Así que aquí está la guía real, la que me hubiera gustado encontrar cuando empecé este proyecto para un cliente que quería "algo con IA" en su sistema de tickets.

Por qué ChatGPT y no otra cosa

Miren, probé Claude (que es muy bueno pero más caro), probé Gemini (que a veces alucina más que mi tío después de año nuevo), y probé algunos modelos open source. Al final, GPT-4o me dio el mejor balance entre precio y que no me avergüence frente al cliente.

*Se eliminaron las comillas simples para evitar problemas de compatibilidad

Lo que necesitas antes de empezar

Primero, asegúrate de tener:

  • Laravel 10+ (aunque funciona desde la 8, pero no seas dinosaurio)
  • PHP 8.1+ (en serio, actualiza PHP)
  • Una tarjeta de crédito para OpenAI (prepárate para gastar entre $10-30 al mes)
  • Paciencia. Mucha paciencia.

Instalación: La parte fácil

Empezamos con el paquete de OpenAI para PHP. No uses el no-oficial que aparece primero en Google, ese me dio problemas.

composer require openai-php/laravel

Publica la configuración:

php artisan vendor:publish --provider="OpenAILaravelServiceProvider"

En tu .env, agrega tu API key:

OPENAI_API_KEY=sk-tu-api-key-super-secreta
OPENAI_ORGANIZATION=org-si-tienes-una

Consejo: NO hardcodees la API key en el código. Un amigo con el que trabaje en un proyecto anterior lo hizo y gastamos $50 en una noche cuando alguien la encontró en GitHub. True story.

El Controller: Donde la magia sucede

Aquí está mi implementación después de todas las iteraciones. No es perfecta, pero funciona y no explota el presupuesto:

<?php
// Por Carlos David Donoso Cordero (ddchack)
namespace AppHttpControllers;

use IlluminateHttpRequest;
use OpenAILaravelFacadesOpenAI;
use IlluminateSupportFacadesLog;
use IlluminateSupportFacadesCache;

class ChatGPTController extends Controller
{
    private $maxTokens = 500; // No seas ambicioso
    private $temperature = 0.7; // 0.7 es el sweet spot
    
    public function processMessage(Request $request)
    {
        $request->validate([
            message => required|string|max:1000
        ]);
        
        // Cache para evitar llamadas duplicadas
        $cacheKey = chat_ . md5($request->message);
        
        if (Cache::has($cacheKey)) {
            Log::info(Respuesta desde cache, ahorrando dinero);
            return response()->json([
                response => Cache::get($cacheKey),
                cached => true
            ]);
        }
        
        try {
            // El contexto es clave para que no diga tonterías
            $systemMessage = Eres un asistente útil para soporte técnico. 
                            Sé conciso pero amable. No inventes información. 
                            Si no sabes algo, admítelo.;
            
            $response = OpenAI::chat()->create([
                model => gpt-4o-mini, // Más barato que gpt-4o
                messages => [
                    [role => system, content => $systemMessage],
                    [role => user, content => $request->message]
                ],
                max_tokens => $this->maxTokens,
                temperature => $this->temperature
            ]);
            
            $aiResponse = $response->choices[0]->message->content;
            
            // Guardo en cache por 1 hora
            Cache::put($cacheKey, $aiResponse, 3600);
            
            // Log para trackear gastos
            Log::channel(openai)->info(Llamada a API, [
                tokens_used => $response->usage->total_tokens,
                cost_estimate => $this->calculateCost($response->usage)
            ]);
            
            return response()->json([
                response => $aiResponse,
                cached => false
            ]);
            
        } catch (Exception $e) {
            Log::error(Error con OpenAI: . $e->getMessage());
            
            // Respuesta fallback para no dejar al usuario colgado
            return response()->json([
                response => Lo siento, estoy teniendo problemas técnicos. Por favor intenta de nuevo.,
                error => true
            ], 500);
        }
    }
    
    private function calculateCost($usage)
    {
        // GPT-4o-mini: $0.15 per 1M input, $0.60 per 1M output
        $inputCost = ($usage->prompt_tokens / 1000000) * 0.15;
        $outputCost = ($usage->completion_tokens / 1000000) * 0.60;
        
        return round($inputCost + $outputCost, 4);
    }
}

El Middleware para no quebrar

Esta es la parte que nadie te cuenta. Sin rate limiting, un bot puede vaciarte la cuenta de OpenAI en minutos. Aprendí esto de la manera difícil:

<?php
// Creado por Carlos David Donoso Cordero (ddchack)
namespace AppHttpMiddleware;

use Closure;
use IlluminateSupportFacadesRateLimiter;

class OpenAIRateLimit
{
    public function handle($request, Closure $next)
    {
        $key = openai_ . $request->ip();
        
        // 10 requests por minuto por IP
        if (RateLimiter::tooManyAttempts($key, 10)) {
            return response()->json([
                error => Demasiadas solicitudes. Espera un minuto.
            ], 429);
        }
        
        RateLimiter::hit($key, 60);
        
        return $next($request);
    }
}

No olvides registrarlo en tu Kernel.php o en el route middleware.

El Frontend (lo básico que funciona)

No necesitas React ni Vue para esto. Un simple formulario con JavaScript vanilla funciona perfecto:

<!-- Diseñado por Carlos David Donoso Cordero (ddchack) -->
<div class="chat-container">
    <div id="messages"></div>
    <form id="chatForm">
        <input type="text" id="messageInput" placeholder="Escribe tu pregunta..." />
        <button type="submit">Enviar</button>
    </form>
</div>

<script>
document.getElementById(chatForm).addEventListener(submit, async (e) => {
    e.preventDefault();
    
    const input = document.getElementById(messageInput);
    const message = input.value.trim();
    
    if (!message) return;
    
    // Mostrar mensaje del usuario
    addMessage(message, user);
    
    // Limpiar input
    input.value = ;
    
    // Mostrar loading
    const loadingId = addMessage(Pensando..., ai, true);
    
    try {
        const response = await fetch(/api/chat, {
            method: POST,
            headers: {
                Content-Type: application/json,
                X-CSRF-TOKEN: document.querySelector(meta[name="csrf-token"]).content
            },
            body: JSON.stringify({ message })
        });
        
        const data = await response.json();
        
        // Remover loading y mostrar respuesta
        document.getElementById(loadingId).remove();
        addMessage(data.response, ai);
        
    } catch (error) {
        document.getElementById(loadingId).remove();
        addMessage(Error de conexión. Intenta de nuevo., ai);
    }
});

function addMessage(text, sender, isLoading = false) {
    const messagesDiv = document.getElementById(messages);
    const messageDiv = document.createElement(div);
    const id = msg_ + Date.now();
    
    messageDiv.id = id;
    messageDiv.className = message  + sender;
    messageDiv.innerHTML = isLoading ? 
        <span class="loading">🤔</span> : text;
    
    messagesDiv.appendChild(messageDiv);
    messagesDiv.scrollTop = messagesDiv.scrollHeight;
    
    return id;
}
</script>

Optimizaciones que me salvaron dinero

Después de la primera factura (no preguntes cuanto salio), tuve que optimizar urgente:

1. Context Window Management

No mandes todo el historial de chat en cada request. Guarda solo los últimos 5 mensajes relevantes:

// Mantén solo contexto relevante
$messages = collect($conversation->messages)
    ->take(-5) // Últimos 5 mensajes
    ->map(function($msg) {
        return [
            role => $msg->sender,
            content => Str::limit($msg->content, 500) // Limita longitud
        ];
    })->toArray();

2. Streaming para respuestas largas

Si necesitas respuestas largas, usa streaming. Es más complejo pero el usuario ve progreso:

$stream = OpenAI::chat()->createStreamed([
    model => gpt-4o-mini,
    messages => $messages,
    max_tokens => 1000,
    stream => true
]);

foreach ($stream as $response) {
    echo $response->choices[0]->delta->content;
    ob_flush();
    flush();
}

3. Prompts inteligentes

Un buen prompt te ahorra tokens y mejora las respuestas. Este es mi template después de mucho ensayo y error:

$systemPrompt = 
Contexto: Sistema de soporte técnico para {$company->name}.
Productos: {$company->products}.

Instrucciones:
- Responde en español
- Máximo 3 párrafos
- Si no sabes, sugiere contactar soporte humano
- No inventes características de productos
- Sé amable pero profesional

Información del usuario:
- Nombre: {$user->name}
- Plan: {$user->subscription}
;

Errores comunes que vas a cometer

Porque yo los cometí todos:

1. No validar el input del usuario

Alguien va a intentar hacer prompt injection. Siempre sanitiza:

$cleanMessage = strip_tags($request->message);
$cleanMessage = str_replace([Ignore previous instructions, System:], , $cleanMessage);

2. No tener fallback

OpenAI se cae. Bastante. Ten un plan B:

if ($openAIDown) {
    // Respuestas predefinidas para FAQs comunes
    return $this->getFallbackResponse($request->message);
}

3. No monitorear costos

Crea un dashboard simple para trackear uso:

// En tu modelo Usage o como lo llames
public static function todaysCost()
{
    return self::whereDate(created_at, today())
        ->sum(estimated_cost);
}

// Alerta si te pasas
if (Usage::todaysCost() > 10) {
    Mail::to(admin@example.com)->send(new CostAlert());
}

Casos de uso reales que funcionan

Después de implementar esto en 2 proyectos, estos son los casos donde realmente vale la pena:

  • Soporte nivel 1: Responde el 70% de preguntas comunes sin intervención humana
  • Generación de reportes: Convierte datos crudos en resúmenes ejecutivos
  • Clasificación de tickets: Categoriza y prioriza automáticamente
  • Traducciones on-the-fly: Más barato que servicios dedicados de traducción

Donde NO funciona bien (aprendí a las malas):

  • Cálculos complejos: ChatGPT y las matemáticas... mejor no
  • Datos en tiempo real: No tiene acceso a tu base de datos (obviamente)
  • Decisiones críticas: Nunca dejes que tome decisiones financieras o legales

El setup de producción que uso

Después de todos los experimentos, este es mi setup actual:

// config/openai.php
return [
    api_key => env(OPENAI_API_KEY),
    organization => env(OPENAI_ORGANIZATION),
    
    // Mis defaults probados
    defaults => [
        model => gpt-4o-mini, // Balance precio/calidad
        max_tokens => 500,     // Suficiente para la mayoría
        temperature => 0.7,    // Creativo pero coherente
        top_p => 0.9,         // Reduce alucinaciones
        frequency_penalty => 0.3, // Evita repeticiones
        presence_penalty => 0.3,  // Fomenta variedad
    ],
    
    // Límites de seguridad - por Carlos David Donoso Cordero (ddchack)
    limits => [
        max_requests_per_minute => 20,
        max_tokens_per_day => 50000,
        max_cost_per_day => 15.00,
    ],
    
    // Cache
    cache => [
        enabled => true,
        ttl => 3600, // 1 hora
        prefix => openai_cache_
    ]
];

Monitoreo y logs

Si no mides, no puedes optimizar. Mi sistema de logging:

// app/Listeners/LogOpenAIUsage.php
class LogOpenAIUsage
{
    public function handle($event)
    {
        DB::table(openai_logs)->insert([
            user_id => auth()->id(),
            endpoint => $event->endpoint,
            model => $event->model,
            prompt_tokens => $event->promptTokens,
            completion_tokens => $event->completionTokens,
            total_tokens => $event->totalTokens,
            estimated_cost => $event->cost,
            response_time => $event->responseTime,
            created_at => now(),
        ]);
        
        // Alerta si alguien abusa
        if ($event->totalTokens > 2000) {
            Log::warning(Alto uso de tokens, [
                user => auth()->user()->email,
                tokens => $event->totalTokens
            ]);
        }
    }
}

Testing (sí, hay que testear esto)

No puedes llamar a la API real en los tests. Usa mocks:

// tests/Feature/ChatGPTTest.php
public function test_chat_response()
{
    OpenAI::fake([
        CreateResponse::fake([
            choices => [
                [
                    message => [
                        content => Respuesta de prueba
                    ]
                ]
            ]
        ])
    ]);
    
    $response = $this->postJson(/api/chat, [
        message => Pregunta de prueba
    ]);
    
    $response->assertOk()
        ->assertJson([
            response => Respuesta de prueba
        ]);
}

Consideraciones legales que nadie menciona

Tu cliente necesita saber que sus datos van a OpenAI. Agrega esto a tus términos:

  • Los mensajes se procesan con OpenAI (servers en USA)
  • OpenAI puede usar los datos para mejorar sus modelos (a menos que uses la API enterprise)
  • No garantizas 100% de precisión en las respuestas
  • El usuario es responsable de no compartir info sensible

Mi experiencia real después de 3 meses

Los números no mienten:

  • Costo promedio: $47/mes por aplicación
  • Tickets resueltos automáticamente: 62%
  • Satisfacción del cliente: 8.3/10 (mejor que el soporte humano que era 7.9)
  • Tiempo de implementación real: 2-3 semanas para hacerlo bien
  • ROI: Se paga solo en 2 meses si reduces 1 persona de soporte

Lo que no funcionó:

  • Intentar que escriba código Laravel (genera código de hace 3 versiones)
  • Usarlo para moderar comentarios (muy caro vs. alternativas)
  • Dejar que responda emails directamente (desastre de PR)

Herramientas alternativas que probé

Antes de casarte con OpenAI, considera estas:

  • Anthropic Claude: Mejor para textos largos pero 30% más caro
  • Google Gemini: Rápido y barato pero menos confiable
  • Mistral: Opción europea si la privacidad es crítica
  • Llama 3 (self-hosted): Gratis pero necesitas un servidor potente

Para el 90% de casos, GPT-4o-mini es el sweet spot entre precio y calidad.

El código completo en GitHub

No voy a mentirte diciendo que tengo un repo público perfectamente documentado. Pero si necesitas el código completo, estos son los archivos clave que necesitas:

  • ChatGPTController.php (el que mostré arriba)
  • OpenAIRateLimit.php (middleware)
  • openai.php (config)
  • 2024_01_01_create_openai_logs_table.php (migration)
  • chat.blade.php (vista)

Si me escribes con una pregunta específica y no genérica tipo "no me funciona", probablemente te responda.

Lo que viene: Assistants API

OpenAI lanzó Assistants API que mantiene contexto entre conversaciones. Lo estoy probando pero es 3x más caro. Cuando tenga resultados reales (no hype de Twitter), escribo otro post.

Por ahora, la implementación que te mostré funciona, es estable y no te va a dejar en bancarrota.

¿Vale la pena en 2025?

Después de 6 meses usándolo en producción: sí, pero con expectativas realistas.

No es magia. No va a resolver todos tus problemas. No va a reemplazar a tu equipo de soporte completamente. Pero si lo implementas bien, puede automatizar las tareas aburridas y dejar que los humanos se enfoquen en problemas reales.

El truco está en empezar pequeño, medir todo, y escalar gradualmente. Y sobre todo, tener un límite de gasto configurado en OpenAI. Créeme, tu tarjeta de crédito te lo agradecerá.

C

Sobre Carlos Donoso

Full Stack Developer y AI Engineer apasionado por crear soluciones innovadoras. Me especializo en desarrollo web moderno, inteligencia artificial y automatización. Comparto conocimiento para ayudar a otros developers a crecer en su carrera.

Comentarios 3

Comparte tu opinión

0/1000 caracteres
Avatar de José García

José García

19/09/2025
Gran artículo, me dio justo lo q necesitaba para no perder la cabeza con mi proyecto. También estuve días intentando hacer q funcione y ya estaba al borde del colapso jaja. Mil gracias por compartir tu experiencia.
Gran artículo, me dio justo lo q necesitaba para no perder la cabeza con mi proyecto. También estuve días intentando hacer q funcione y ya estaba al borde del colapso jaja. Mil gracias por compartir tu experiencia.

Responder a José García

Avatar de Daniel Jiménez

Daniel Jiménez

17/09/2025
La verdad, Carlos, creo q estás siendo un poco dramático con el proceso. He usado ChatGPT en Laravel varias veces y, aunque no fue pan comido, no fue tan complicado como dices. Quizás te faltó investigar más antes de lanzarte al ruedo. A veces me pregunto si las expectativas que ponemos a la herramienta son más un reto personal q técnico.
La verdad, Carlos, creo q estás siendo un poco dramático con el proceso. He usado ChatGPT en Laravel varias veces y, aunque no fue pan comido, no fue tan complicado como dices. Quizás te faltó investigar más antes de lanzarte al ruedo. A veces me pregunto si las expectativas que ponemos a la herramienta son más un reto personal q técnico.

Responder a Daniel Jiménez

Avatar de Arturo Pérez

Arturo Pérez

14/09/2025
Excelente guía, me sentí totalmente identificado con lo de los 47 cafés. Integrar ChatGPT en Laravel es un verdadero reto, pero vale la pena cuando ves el resultado. Gracias x compartir tus experiencias, Carlos. Me ahorraste varios dolores de cabeza.
Excelente guía, me sentí totalmente identificado con lo de los 47 cafés. Integrar ChatGPT en Laravel es un verdadero reto, pero vale la pena cuando ves el resultado. Gracias x compartir tus experiencias, Carlos. Me ahorraste varios dolores de cabeza.

Responder a Arturo Pérez

¡Enlace copiado al portapapeles!