Inteligência Artificial

Orquestrando IA em produção com Firebase Genkit

Aquele tutorial de IA que parece trivial esconde um emaranhado de responsabilidades que só emerge quando o código encontra o tráfego real.

Orquestrando IA em produção com Firebase Genkit

Integrar IA parece simples: chamar uma API de LLM e ler a resposta. Essa simplicidade é uma ilusão. Em produção, aquela única seta vira um emaranhado de responsabilidades — e é exatamente esse problema que o Firebase Genkit se propõe a resolver, atuando como uma camada de orquestração que desacopla a lógica de negócio da infraestrutura de IA.

A tese central é direta: um framework de orquestração transforma chamadas frágeis de LLM em arquitetura previsível, tipada e observável. O ganho não está em prompts mágicos, mas em contratos rígidos, abstrações maduras e isolamento de infraestrutura.


Parte 1 — Por que chamadas isoladas falham

O fluxo linear que imaginamos — App → LLM → Resposta — se desdobra, na prática, em um conjunto de responsabilidades que ninguém pediu mas todas aparecem em produção: gerência de contexto, retry com backoff, parsing de JSON quebrado, troca de chaves, chunking de documentos e integração com vector store. Quatro problemas estruturais emergem desse acoplamento:

  • Falta de tipagem — sem contratos rígidos, a resposta do modelo é um JSON imprevisível que quebra silenciosamente o consumidor.

  • Prompts hardcoded — a intenção do LLM mora cravada no código de aplicação, impossível de versionar ou testar isoladamente.

  • Vendor lock-in — a lógica de negócio fica amarrada a um modelo proprietário específico.

  • Zero observabilidade — sem visibilidade de latência e custo, cada chamada é uma caixa-preta financeira.

A resposta profissional é decompor a integração antes que ela vire dívida técnica: separar cada preocupação em um módulo com seu próprio contrato. É o que o Genkit organiza em três camadas.


Parte 2 — O modelo operacional em três camadas

O Genkit se organiza em três camadas sobrepostas. A de baixo é agnóstica de provedor; a do meio concentra as abstrações reutilizáveis; a de cima entrega a experiência de desenvolvimento.

As três camadas do GenkitDeveloper experienceTipagem (Zod/Pydantic) · Dev UI · traces · deploy nativoAbstrações centraisFlows · RAG (retrievers & indexers) · ToolsInfraestrutura agnósticaGoogle · OpenAI · Ollama · Pinecone · Chroma · pgvector
As três camadas do Genkit

A ideia-chave dessa arquitetura é escreva a lógica uma vez; troque a infraestrutura com uma linha de código. O runtime de primeira classe é JS/TS, mas o framework também tem SDKs em Go, Python e Dart.

Flows tipados e contratos rígidos

Um Flow é uma operação observável com contratos de entrada e saída definidos por schemas Zod. O segredo está no campo output: { schema }: o Genkit injeta as regras do schema na instrução ao modelo (constrained generation) e valida a resposta de volta.

TYPESCRIPT
import { genkit, z } from 'genkit';import { googleAI } from '@genkit-ai/googleai';const ai = genkit({ plugins: [googleAI()] });// contrato de saída: nada de JSON soltoconst Relatorio = z.object({  titulo: z.string(),  risco: z.enum(['baixo', 'medio', 'alto']),  pontos: z.array(z.string()),});export const gerarRelatorio = ai.defineFlow(  {    name: 'gerarRelatorio',    inputSchema: z.object({ texto: z.string() }),    outputSchema: Relatorio,  },  async ({ texto }) => {    const { output } = await ai.generate({      model: 'googleai/gemini-1.5-flash',      prompt: `Analise e resuma: ${texto}`,      output: { schema: Relatorio },    });    return output!;  });

O enum no campo risco não é decoração: ele elimina toda uma classe de bugs onde o modelo inventaria "risco moderado-alto" e quebraria o switch do consumidor. Trate o schema de saída do LLM com o mesmo rigor de um contrato de API REST.

Dotprompt: prompts como código

Para acabar com prompts hardcoded, o Genkit usa arquivos .prompt versionáveis e testáveis. Um frontmatter YAML declara configuração (modelo, temperatura, schemas, tools) e um template Handlebars define as mensagens, com lógica condicional embutida.

YAML
---model: googleai/gemini-1.5-flashconfig:  temperature: 0.4input:  schema: UserSchemaoutput:  schema: ReportSchematools: [buscarClima]---{{role "system"}}Você é um assistente técnico especializado.{{role "user"}}Analise os dados de {{usuario.nome}} e a imagem:{{media url=imagemPerfil}}{{#if isPremium}}Forneça resposta detalhada.{{/if}}

Ajustar um prompt vira um diff de texto, revisável em pull request, sem recompilar a aplicação.


Parte 3 — RAG, reranking e tools

Pergunte a um LLM sobre um termo interno da sua empresa e ele alucina, porque o dado não existe no treinamento dele. Injetar o documento inteiro resolve a alucinação mas consome ~4.000 tokens por pergunta. A resposta arquitetural é o RAG (Retrieval-Augmented Generation): recuperar apenas os trechos relevantes. O pipeline nativo tem duas vias que compartilham o mesmo vector store.

Pipeline RAG nativo do GenkitQuery do usuárioEmbedderRetrieverVector store (indexado)AugmentationLLM
Pipeline RAG nativo do Genkit

O ganho concreto: o consumo cai de ~4.000 para ~830 tokens por consulta, mantendo a exatidão. Paga-se o custo de vetorizar uma vez, na ingestão, e colhem-se respostas baratas e precisas em toda consulta.

Reranking: two-stage retrieval

Um retriever vetorial é rápido mas barulhento. O padrão two-stage retrieval adiciona um reranker (cross-encoder) que reordena os candidatos pela relevância exata e estreita a saída para o topo — só o que tem maior valor estatístico ocupa a janela de contexto. O primeiro estágio prioriza recall; o segundo, precisão.

Two-stage retrieval (reranking)Retriever básico10–20 docs por similaridade (com ruído)Reranker (cross-encoder)reordena pela relevância exataSaída estreitasó o top 3 vai ao LLM
Two-stage retrieval com reranking

Tools: de LLMs passivos a agentes

As Tools permitem que o modelo peça dados externos. Você envia o catálogo de tools; quando o modelo precisa de algo que não tem, ele pausa a geração, o Genkit intercepta e executa a tool localmente, devolve o resultado e o modelo sintetiza a resposta. Uma tool é só uma função tipada com Zod que o modelo aprende a invocar.

TYPESCRIPT
export const buscarClima = ai.defineTool(  {    name: 'buscarClima',    description: 'Retorna o clima atual de uma cidade',    inputSchema: z.object({ cidade: z.string() }),    outputSchema: z.object({ tempC: z.number() }),  },  async ({ cidade }) => {    // código real: chama uma API de clima    const r = await fetch(`/api/clima?c=${cidade}`);    return { tempC: (await r.json()).temp };  });

Parte 4 — Produção: multimodalidade, segurança e ciclo

Texto bruto, mídia via Handlebars, URLs HTTPS e PDFs em Base64 entram por abstrações uniformes: o framework trata conversão e encoding e monta um array multimodal homogêneo. Antes do deploy, o comando genkit start sobe uma Dev UI local com trace inspector (cascata e tempo de cada etapa), model runner e contagem de tokens.

Na borda, expor endpoints generativos sem proteção é risco financeiro severo. Chaves vão para o Secret Manager (fora do código), a autenticação é nativa via Cloud Functions, e o App Check bloqueia requisições fraudulentas no perímetro (DeviceCheck / Play Integrity), garantindo que só instâncias genuínas do app invoquem cobranças.

O isolamento estrutural também define os alvos de deploy por linguagem — e aqui está a ponte direta para o frontend: Next.js e Angular entram via Firebase App Hosting.

Linguagem

Modelo (ex.)

Vector store

Alvo de deploy

JS / TS

Gemini

Cloud Firestore

Firebase App Hosting (Next.js / Angular)

Go

Claude

Pinecone

Cloud Run (binário)

Python

GPT-4o

pgvector

Cloud Functions

Dart

Ollama (local)

Redis

Telemetria local

Tudo se amarra num ciclo de quatro fases em torno do Genkit:

Ciclo operacionalConstruirTipagem · ToolsIterarDev UI · tracesDeployApp HostingMonitorarTelemetry · avaliaçõesciclo contínuo
Ciclo operacional do Genkit

Conclusão: orquestração no lugar de prompts mágicos

O arco deste material descreve uma maturação que outras disciplinas da computação já viveram: a integração com IA está saindo da fase artesanal — em que o resultado dependia de acertar o prompt — e entrando na fase de engenharia, em que o resultado depende da qualidade do sistema.

Cada abstração apresentada — Flows tipados, Dotprompt, RAG, reranking, Tools, segurança de borda — é uma resposta de engenharia a uma falha específica e reprodutível das chamadas isoladas de LLM. A confiabilidade em produção já não é um golpe de sorte; é um processo orquestrado e observável.