Como Integrar LLMs em Aplicações Laravel: 6 Padrões Que Uso em Produção
Padrões reais de integração de Claude, GPT e Gemini em apps Laravel. Cache, fallback, custo por requisição, prompt versioning e os erros mais caros de iniciante.
Cada vez que um time decide “colocar IA no sistema” sem padrão definido, o que aparece em produção é o mesmo: chamadas síncronas no controller, prompt no meio do código, custo mensal explodindo e nenhum jeito de testar. Quatro semanas depois alguém pergunta por que o LLM “ficou ruim” — e ninguém sabe responder porque ninguém versionou o prompt.
Esse artigo é o que faço pra evitar isso. Seis padrões que aplico em toda integração Laravel + LLM que entrego, com código real, custo aproximado e os erros que já tropecei pessoalmente.
1. Service Layer dedicada (e nunca chamada direto do Controller)
A regra é simples: LLM nunca toca controller. Sempre passa por uma Service própria.
// app/Services/AI/CompletionService.php
class CompletionService
{
public function __construct(
private LLMProvider $provider,
private PromptRegistry $prompts,
private CompletionCache $cache,
private CompletionLogger $logger,
) {}
public function generate(string $promptKey, array $vars): CompletionResult
{
$prompt = $this->prompts->render($promptKey, $vars);
if ($cached = $this->cache->get($prompt)) {
return $cached;
}
$result = $this->provider->call($prompt);
$this->cache->put($prompt, $result, ttl: 3600);
$this->logger->record($promptKey, $prompt, $result);
return $result;
}
}
Por que isso importa: troca de provider (Anthropic ↔ OpenAI ↔ Gemini) vira uma linha. Sem isso, troca vira refactor de semanas.
2. Prompt Registry: trate prompt como código
Prompts em string literal espalhada pelo codebase é dívida garantida. Eu mantenho um PromptRegistry que carrega prompts de arquivos versionados, com versão semântica.
// resources/prompts/classificacao-os.v2.md
Você é um classificador de ordens de serviço contábil.
Categorias disponíveis: {{categorias}}
Entrada: {{descricao}}
Responda APENAS o nome da categoria, sem explicação.
$result = $completion->generate('classificacao-os@v2', [
'categorias' => $cats->pluck('nome')->implode(', '),
'descricao' => $os->descricao,
]);
Versão muda só quando o prompt muda. Posso comparar resultados v1 vs v2 em paralelo antes de migrar.
3. Cache agressivo (mas seguro)
LLM custa dinheiro real. Em um ERP que mantenho em produção, 41% das chamadas eram idênticas entre si — mesma entrada, mesma resposta esperada.
Cache key: hash do prompt renderizado (já com vars substituídas). TTL varia por tipo de tarefa:
| Tipo de tarefa | TTL recomendado | Cache hit médio |
|---|---|---|
| Classificação determinística | 7 dias | 65% |
| Resumo de documento | 30 dias | 80% |
| Geração criativa (e-mail, copy) | 1 hora | 15% |
| Q&A com contexto dinâmico | Sem cache | — |
Só cuidado: nunca cacheie resultado de prompt que tem dado pessoal não-hashado na key. Você acaba retornando dado de cliente A pro cliente B.
4. Fallback entre providers (não fique refém)
Anthropic caiu? GPT está com latência alta? Gemini está bloqueado na sua região? Quem tem 1 provider tem 0 providers.
class MultiProviderLLM implements LLMProvider
{
public function __construct(
private array $providers,
private array $maxLatency = ['anthropic' => 8000, 'openai' => 6000],
) {}
public function call(string $prompt): CompletionResult
{
foreach ($this->providers as $provider) {
try {
return $provider->callWithTimeout($prompt, $this->maxLatency[$provider->name()] ?? 10000);
} catch (ProviderUnavailable | TimeoutException $e) {
Log::warning("Provider {$provider->name()} falhou", ['err' => $e->getMessage()]);
continue;
}
}
throw new AllProvidersFailed();
}
}
A ordem dos providers vira política de negócio. Para tarefas de qualidade alta: Claude primeiro, GPT como fallback. Para tarefas de custo baixo: Gemini primeiro, Claude como fallback.
5. Queue Jobs para chamadas longas
Nunca chame LLM dentro de request HTTP que o usuário está esperando. Latência de LLM varia entre 800ms e 30 segundos dependendo do modelo, prompt e provider. Coloca isso na frente de um usuário e o sistema parece quebrado.
Eu sempre uso fila:
// app/Jobs/ProcessAICompletion.php
class ProcessAICompletion implements ShouldQueue
{
public int $tries = 3;
public int $timeout = 60;
public array $backoff = [10, 30, 60];
public function handle(CompletionService $completion): void
{
$result = $completion->generate($this->promptKey, $this->vars);
event(new AICompletionFinished($this->correlationId, $result));
}
}
No frontend, Inertia.js + router.reload ou broadcast event via Pusher/Reverb avisa quando terminou. O usuário continua usando o sistema enquanto a IA trabalha.
6. Telemetria de custo POR usuário, POR feature
A pior surpresa é a fatura no fim do mês. Eu loguei tokens de entrada, tokens de saída e custo estimado por requisição, agrupado por:
- Usuário (quem chamou)
- Feature (qual módulo do sistema)
- Modelo usado
- Prompt key
Tabela:
CREATE TABLE ai_usage_log (
id BIGINT PRIMARY KEY,
user_id BIGINT,
feature VARCHAR(64),
prompt_key VARCHAR(64),
model VARCHAR(64),
input_tokens INT,
output_tokens INT,
cost_usd DECIMAL(10,6),
latency_ms INT,
cache_hit BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP,
INDEX idx_user_feature (user_id, feature, created_at)
);
Em um sistema que monitoro hoje, esse log me mostrou que um único feature de geração de relatório consumia 73% do custo total de IA do mês. Bastou colocar cache de 30 dias nesse fluxo específico para o custo cair de US$ 412 para US$ 89.
Erros mais caros que já cometi
- Streaming de resposta direto pro browser sem timeout. Cliente fica com conexão aberta, server espera por minutos.
- Prompt com dado sensível sem mascarar. CPF, e-mail, número de cartão vazaram em logs de provider.
- Retry sem idempotência. Sistema cobrou o cliente 3 vezes porque o webhook do gateway falhou e o job retry chamou o LLM 3x pra “decidir” se cobrava.
- Confiar 100% na resposta do LLM. Sempre valido o output (regex, schema JSON, lista finita de categorias) antes de gravar.
- Esquecer rate limit do provider. Anthropic tem RPM por tier. Em pico, eu tomei 429 e o usuário viu erro.
Onde a Sakaguchi IA entra
Esses 6 padrões viraram biblioteca interna que reuso em todo projeto Laravel + IA que entrego. Se você está começando uma integração de LLM em produção ou se já tem uma que está doendo (custo, latência, erro), a gente pode te ajudar a estruturar isso direito. Fale com nosso time.