O que é o Laravel AI SDK: guia prático do SDK de IA nativo do Laravel
Você abre um projeto Laravel novo em 2026 e a pergunta deixou de ser "qual pacote eu uso pra falar com a OpenAI". Virou outra: qual das coisas oficiais do Laravel eu instalo — e onde foi parar aquele wrapper que você escreveu em app/Services/OpenAiService.php e nunca mais olhou.
O Laravel AI SDK é o pacote first-party que responde a primeira metade dessa pergunta: ele é o SDK de IA que hoje vem com o framework. Ele serve pra colocar IA dentro do seu app, no fluxo que o seu usuário final toca. Um composer require laravel/ai, uma classe de agente, e você tem geração de texto, tool calling, structured output, streaming, embeddings e busca vetorial falando com OpenAI, Anthropic, Gemini, Groq ou Ollama pela mesma API.
Neste guia a gente vai do zero ao agente rodando: o que o SDK é, o que ele não é, instalação, primeiro agente com tool, saída estruturada, embeddings com pgvector, teste com fake e onde ele ainda dói. Código que roda, não pseudo-código.
TL;DR
- O que é: SDK oficial de IA do Laravel (
laravel/ai), com API unificada para múltiplos provedores e o conceito de agente como classe PHP. - Stack/Modelos: PHP 8.3+, Laravel 12 ou 13, OpenAI, Anthropic, Gemini, Groq, xAI, ElevenLabs, Cohere, Ollama e qualquer endpoint OpenAI-compatible.
- Custo/Acesso: pacote MIT, gratuito. Você paga o token do provedor que escolher.
- Link útil: documentação oficial do AI SDK e o repositório laravel/ai.
O contexto: por que o Laravel AI SDK importa
Antes de fevereiro de 2026, colocar inteligência artificial num projeto Laravel era decisão de arquitetura de cada time. Ou você usava Prism, ou escrevia seu próprio client HTTP, ou saía chamando a API da OpenAI de dentro do controller e rezava. Cada projeto resolvia rate limit, retry, streaming e schema de tool do seu jeito. O resultado é o que todo mundo que auditou um projeto PHP com IA já viu: três formas diferentes de chamar LLM no mesmo repositório.
O SDK oficial foi anunciado em 5 de fevereiro de 2026 e ficou estável junto com o Laravel 13. A motivação, na fala do próprio Taylor Otwell:
"It's such an important part of the dev workflow at this point. It felt like we needed some sort of first-party opinion on interacting with AI providers. Just like we have opinions on sending email or queuing jobs."
É exatamente isso. O Laravel sempre teve opinião sobre e-mail, fila, cache e storage. Agora tem opinião sobre LLM. E opinião de framework, no Laravel, significa três coisas: convenção de pastas, comando de Artisan pra gerar o boilerplate e fake pra testar.
Os números dizem que pegou. O pacote está na v0.10.3, com mais de 5,5 milhões de downloads no Packagist e licença MIT. É a diferença entre usar IA e construir com IA, e é esse segundo tipo de trabalho — arquitetura, avaliação, custo, contexto — que a gente destrincha toda semana ao vivo no Clã Beer and Code, com código rodando na tela. É pago, é assinatura, e é o ambiente que este post descreve na prática.
Pré-requisitos
Checklist antes de rodar qualquer linha:
- [ ] PHP 8.3 ou superior (o
composer.jsondo pacote exige^8.3). - [ ] Laravel 12.x ou 13.x — os componentes
illuminate/*são pinados em^12.0|^13.0. - [ ] Uma chave de API de pelo menos um provedor (
ANTHROPIC_API_KEY,OPENAI_API_KEY,GEMINI_API_KEY,GROQ_API_KEY). - [ ] PostgreSQL com a extensão
pgvector, se você for usar busca vetorial. Para o resto, qualquer banco serve.
Se você nunca mexeu com embeddings e pgvector, vale ler antes o guia de RAG para devs backend — a parte de busca vetorial deste post assume que os conceitos já estão de pé.
Como usar IA no Laravel: do composer require ao primeiro agente
Passo 1: instalar e publicar
composer require laravel/ai
php artisan vendor:publish --provider="Laravel\Ai\AiServiceProvider"
php artisan migrate
O vendor:publish cria o config/ai.php e duas migrations: agent_conversations e agent_conversation_messages. É onde o SDK guarda histórico de conversa quando você liga memória. Se o seu caso é one-shot (resumir texto, classificar, extrair dado), você nunca toca nessas tabelas.
No .env, a chave do provedor:
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
Passo 2: o prompt mais curto possível
Antes da classe, dá pra usar a função helper para um teste rápido no Tinker:
use function Laravel\Ai\agent;
$response = agent(
instructions: 'Você é um assistente técnico. Responda em português, direto.',
)->prompt('Explique o que é idempotência em uma API REST.');
echo (string) $response;
Repare no (string) $response. O objeto de resposta é Stringable, mas carrega mais coisa: ->text, ->usage (tokens gastos) e os eventos da geração. Logar usage desde o primeiro dia é o que te salva da surpresa na fatura no fim do mês.
Passo 3: agente como classe (aqui mora o valor)
Helper serve pra brincar. Em produção, agente é classe:
php artisan make:agent SuporteTecnico
<?php
namespace App\Ai\Agents;
use Laravel\Ai\Attributes\MaxSteps;
use Laravel\Ai\Attributes\Model;
use Laravel\Ai\Attributes\Provider;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\HasTools;
use Laravel\Ai\Enums\Lab;
use Laravel\Ai\Promptable;
use Stringable;
#[Provider(Lab::Anthropic)]
#[Model('claude-sonnet-5')]
#[MaxSteps(8)]
class SuporteTecnico implements Agent, HasTools
{
use Promptable;
public function __construct(public User $user) {}
public function instructions(): Stringable|string
{
return <<<TXT
Você é o suporte técnico da loja. Responda com base nos pedidos
do cliente. Se não achar o dado, diga que não achou.
Nunca invente número de pedido.
TXT;
}
public function tools(): iterable
{
return [
new BuscarPedido($this->user),
];
}
}
Três coisas que valem o preço da entrada aqui:
- Os atributos PHP substituem config espalhada.
#[Provider],#[Model],#[MaxSteps],#[Temperature],#[Timeout]. Trocar de Anthropic pra OpenAI é editar uma linha, não refatorar service. #[UseCheapestModel]e#[UseSmartestModel]existem. Você declara a intenção de custo em vez de chumbar o nome do modelo, e o SDK resolve. Agente que só classifica intenção não precisa do modelo caro.- Injeção de dependência normal. O agente é uma classe PHP com construtor.
SuporteTecnico::make(user: $user)resolve pelo container.
Chamar é uma linha:
$resposta = (new SuporteTecnico($user))->prompt('Cadê meu pedido 8812?');
Passo 4: tools, que é onde o agente vira útil
php artisan make:tool BuscarPedido
<?php
namespace App\Ai\Tools;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Ai\Contracts\Tool;
use Laravel\Ai\Tools\Request;
use Stringable;
class BuscarPedido implements Tool
{
public function __construct(protected User $user) {}
public function description(): Stringable|string
{
return 'Busca um pedido do cliente autenticado pelo número.';
}
public function schema(JsonSchema $schema): array
{
return [
'numero' => $schema->integer()->required(),
];
}
public function handle(Request $request): Stringable|string
{
$pedido = $this->user->orders()->find($request['numero']);
return $pedido
? "Pedido {$pedido->id}: status {$pedido->status}, entrega prevista {$pedido->eta}."
: 'Pedido não encontrado para este cliente.';
}
}
O detalhe de segurança que quase todo tutorial pula: a tool recebe o $user no construtor e filtra por $this->user->orders(). O modelo pede "pedido 8812" e o escopo do Eloquent garante que ele só enxerga o que é daquele cliente. Tool não valida permissão sozinha. Se você fizer Order::find($request['numero']), acabou de construir um IDOR conversacional.
O SDK também traz tools prontas: SimilaritySearch (busca vetorial no seu banco), FileStorage (leitura de disco, com variante readOnly), e as tools de provedor WebSearch, WebFetch e FileSearch.
use Laravel\Ai\Providers\Tools\WebSearch;
(new WebSearch)->max(5)->allow(['laravel.com']);
Passo 5: structured output, pra parar de fazer regex em resposta de LLM
Se o resultado vai pro banco, você não quer texto livre. Quer JSON com contrato:
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Ai\Contracts\HasStructuredOutput;
class ClassificadorDeTicket implements Agent, HasStructuredOutput
{
use Promptable;
public function instructions(): Stringable|string
{
return 'Classifique o ticket de suporte recebido.';
}
public function schema(JsonSchema $schema): array
{
return [
'categoria' => $schema->string()
->enum(['entrega', 'pagamento', 'produto', 'outro'])
->required(),
'urgencia' => $schema->integer()->min(1)->max(5)->required(),
'resumo' => $schema->string()->required(),
];
}
}
$r = (new ClassificadorDeTicket)->prompt($ticket->body);
Ticket::find($ticket->id)->update([
'categoria' => $r['categoria'],
'urgencia' => $r['urgencia'],
]);
O JsonSchema é o mesmo contrato que o Laravel usa em outros pontos do framework, e o enum() fecha o vocabulário. Sem isso você vai receber "Entrega", "entregas" e "problema de entrega" na mesma coluna.
Passo 6: streaming, fila e embeddings
Streaming é retornar o agente direto da rota:
Route::get('/suporte', fn () => (new SuporteTecnico(auth()->user()))
->stream('Cadê meu pedido 8812?'));
Tem suporte ao protocolo do Vercel AI SDK via ->usingVercelDataProtocol(), o que economiza a camada de tradução se o seu front já consome esse formato.
Para tarefa lenta, troque prompt() por queue() e a geração vira job:
(new RelatorioMensal)
->queue('Analise os dados de julho.')
->then(fn ($response) => $report->update(['body' => $response->text]))
->catch(fn ($e) => Log::error('Falhou', ['e' => $e->getMessage()]));
E embeddings ganharam açúcar sintático de Laravel:
use Illuminate\Support\Str;
use Laravel\Ai\Embeddings;
$vetor = Str::of('Napa Valley tem ótimos vinhos.')->toEmbeddings();
$resposta = Embeddings::for($textos)
->dimensions(1536)
->cache(seconds: 3600)
->generate();
Na migration, vector() virou tipo de coluna nativo:
Schema::ensureVectorExtensionExists();
Schema::create('documents', function (Blueprint $table) {
$table->id();
$table->text('content');
$table->vector('embedding', dimensions: 1536)->index();
});
E a query de similaridade é Eloquent normal:
$docs = Document::query()
->whereVectorSimilarTo('embedding', 'melhores vinícolas de Napa', minSimilarity: 0.4)
->limit(10)
->get();
Repare: você passou a string. O SDK gera o embedding da consulta e faz a comparação. Isso é o tipo de coisa que antes custava umas 40 linhas de SQL cru com <=> e cast de array.
Passo 7: testar sem gastar token
O motivo real de o SDK existir como camada em cima de um client HTTP são os fakes:
use Laravel\Ai\Fakes\AgentFake;
it('classifica ticket de entrega', function () {
AgentFake::expect(ClassificadorDeTicket::class)
->toReceivePrompt('Meu pedido não chegou')
->andRespond(['categoria' => 'entrega', 'urgencia' => 4, 'resumo' => '...']);
$ticket = Ticket::factory()->create(['body' => 'Meu pedido não chegou']);
expect(classify($ticket)->categoria)->toBe('entrega');
});
Tem fake para agente, imagem, áudio, transcrição e embeddings. Suíte determinística, custo zero, CI sem chave de API. Se o seu wrapper caseiro não tem isso, é aqui que a migração se paga.
Tutorial te mostra o caminho — no Clã você constrói junto. Aula ao vivo toda semana, projetos reais de Engenharia de IA, ao lado de quem já está em produção.
Entrar no ClãAI SDK, Boost ou MCP: qual é qual
A confusão mais comum de quem chega agora. São três pacotes oficiais e nenhum substitui o outro:
| Pacote | Quem usa | O que faz |
|---|---|---|
| Laravel AI SDK | sua aplicação | adiciona features de IA que o seu usuário final consome |
| Laravel Boost | você, o dev | dá contexto e docs atualizadas pro agente de código escrever Laravel idiomático |
| Laravel MCP | clientes de IA externos | expõe o seu app pro ChatGPT, Claude ou Cursor chamarem |
A régua prática: se a feature aparece na tela do seu cliente, é AI SDK. Se ela só melhora o que o Claude Code gera no seu editor, é Boost. Se um agente de fora precisa executar ação no seu sistema, é MCP.
Limitações e pontos de atenção
Onde você vai se queimar:
Ainda é 0.x. A versão atual é a v0.10.3. Não existe garantia de semver estável em major zero, e o pacote está recebendo release com frequência. Pinne a versão no composer.json e leia o CHANGELOG antes de subir minor em produção.
Context bloat com tool demais. O aviso é do próprio Taylor: "You shouldn't have like 50, 60, 70 tools probably exposed to the LLM because you get what's called context bloat. You also have to send all of the tool definitions, what they do, and you have to send that on every message." A definição de toda tool vai em toda mensagem. Um agente com 40 tools queima contexto antes de o usuário digitar qualquer coisa. Por isso as tools são declaradas por agente e não globalmente: escopo é feature, não limitação.
Migrar do Prism não é automático. A relação entre os dois, na analogia do Taylor, é a de Query Builder e Eloquent: o AI SDK é uma camada de abstração por cima do mesmo tipo de problema, com classes de agente, atributos e teste. Se você já roda Prism em produção, vale olhar o diff real de uma migração de Prism pro SDK oficial antes de decidir.
Prompt injection continua sendo seu problema. O SDK não sanitiza nada. Se a tool escreve no banco, apaga arquivo ou dispara e-mail, trate como endpoint público: valide argumento, escope por usuário e considere a API de aprovação humana (Approvable + Decisions), que permite segurar a execução da tool até alguém aprovar.
Custo é invisível até não ser. $response->usage existe. Use. Um agente com MaxSteps(10) pode fazer dez idas ao modelo em uma única chamada de prompt(), e essa conta não aparece em lugar nenhum se você não logar.
FAQ rápido
Preciso do Laravel 13?
Não. O pacote aceita illuminate/* em ^12.0|^13.0, então Laravel 12 roda. O requisito duro é PHP 8.3 ou superior.
Dá pra usar modelo local, sem mandar dado pra fora?
Dá. O SDK suporta Ollama para inferência local, e você pode apontar qualquer endpoint OpenAI-compatible sobrescrevendo a base URL no config/ai.php. OpenRouter também funciona, se a ideia é uma chave só pra vários provedores.
Ele substitui o Prism? Para projeto novo, sim — é a recomendação oficial. Para projeto existente, é escolha de custo de migração. O Prism continua funcionando e não vai explodir por você não migrar.
Como evito que o usuário espere 40 segundos por uma resposta?
Duas saídas: stream() pra devolver token a token via SSE, ou queue() pra jogar na fila e notificar depois por broadcast. Se você usa fila pra isso, vale desenhar o SLA direito — a gente falou disso no post sobre filas no Laravel em 2026.
O que fazer com isso
O Laravel AI SDK não te ensina engenharia de IA. Ele tira do caminho a parte chata: cliente HTTP, retry, schema de tool, parsing de JSON, mock de teste. O que sobra é o trabalho que sempre foi difícil — decidir qual contexto o modelo recebe, como você mede se a resposta presta, quanto aquilo custa por requisição e o que acontece quando o provedor cai às três da manhã.
Comece pequeno. Um agente, uma tool, structured output, teste com fake. Rode em produção numa feature de baixo risco (resumo, classificação, extração) e olhe o usage por uma semana antes de dar tool de escrita pra qualquer coisa.
O próximo passo do ecossistema já está desenhado: human-in-the-loop nativo, sub-agentes como tool e middleware de agente. Ou seja, o Laravel está tratando agente com o mesmo rigor que trata request HTTP — pipeline, middleware, teste. Isso é bem mais interessante do que qualquer demo de chatbot.
{AI Engineer} — apaixonado por Laravel, arquitetura de software e construir produtos com impacto. Compartilho aqui tutoriais, descobertas e reflexões sobre o dia a dia de engenharia.
Conteúdo é o que não falta. Falta quem desembaralhe: o que importa agora é como implementar do jeito certo. No Clã você tem isso ao vivo, toda semana, com quem já filtrou o ruído.
Entrar no Clã