Arquitetura e LLMOps em Produção
Um protótipo chama a API direto. Uma empresa com dez aplicações de IA precisa de algo melhor: um ponto único que escolhe o modelo certo para cada tarefa, sobrevive a falhas do provedor, controla o orçamento de cada time, guarda versões de prompt e mostra para onde vai cada centavo. Este módulo constrói essa peça, o gateway de LLM, e as práticas de operação em volta dela.
Ao final do módulo você vai
- Decidir onde usar IA e onde usar regras determinísticas.
- Desenhar a arquitetura de um sistema de IA de produção.
- Implementar roteamento por tarefa, cadeias de reserva e cotas de custo.
- Combinar camadas de cache sem servir respostas erradas.
- Planejar capacidade frente aos limites do provedor.
- Observar custo, latência e qualidade, e lançar mudanças de prompt com canário.
cd o-arco/curso-ia/codigo/modulo-09
python python/01_capacidade.py
cd projeto && npm install && npm test
9.1 IA ou regra determinística?
| Use regra/código quando | Use LLM quando |
|---|---|
| A regra é conhecida e cabe em poucas linhas (validar CPF, calcular frete) | As regras são incontáveis ou difusas (entender a intenção de um e-mail) |
| Precisa ser 100% previsível e auditável | Algum erro é tolerável e detectável |
| Latência de milissegundos, volume enorme | Segundos são aceitáveis |
| O custo por chamada importa mais que a flexibilidade | O valor por chamada paga o custo |
Os melhores sistemas misturam: o LLM interpreta (texto → dados estruturados), o código decide e executa (regras de negócio, cálculos, permissões). Você viu isso no extrator de recibos: o modelo lê, o código valida a soma e os dígitos do CNPJ.
Quiz rápido
Validar se um CPF tem dígitos verificadores corretos é tarefa para:
Se a regra é conhecida e exata, código ganha em custo, latência e previsibilidade. IA entra onde há ambiguidade.
9.2 Arquitetura de referência
Princípios
- Baixo acoplamento: aplicações falam com o gateway e com serviços internos por interfaces estáveis; trocar de modelo ou provedor não exige mudar as aplicações.
- Políticas como dados: rotas, cotas e prompts ficam em configuração versionada (o
politicas.tse a pastaprompts/do projeto). - Segurança no centro: chaves dos provedores só no gateway; aplicações recebem chaves próprias, revogáveis, com cota.
- Onde rodar: serverless (escala a zero, bom para tráfego irregular, cuidado com tempo máximo de execução em respostas longas), contêineres/Kubernetes (controle e conexões longas de streaming), edge (latência, mas ambiente limitado).
9.3 Roteamento de modelos e tiering
export const ROTAS: Record<Tarefa, string[]> = {
classificar: ["claude-haiku-5-5", "claude-sonnet-5-5"], // barato primeiro, reserva depois
extrair: ["claude-sonnet-5-5", "claude-opus-5-5"],
resumir: ["claude-sonnet-5-5", "claude-haiku-5-5"],
geral: ["claude-opus-5-5", "claude-sonnet-5-5"],
};
- Roteamento por tarefa (o do projeto): a aplicação declara a tarefa; o gateway escolhe o modelo. Simples e previsível.
- Roteamento por intenção ou dificuldade: um classificador barato decide se a pergunta precisa do modelo grande. Economiza mais, mas o classificador também erra; meça.
- Cadeia de reserva: em erro transitório (429, 5xx, conexão) ou recusa, tenta o próximo modelo. Em erro do pedido (400), não: o problema não é o modelo.
- Esforço por rota: o mesmo modelo com esforço baixo pode bastar para tarefas simples; compare isso antes de trocar de modelo.
9.4 Camadas de cache
| Camada | O que reaproveita | Risco |
|---|---|---|
| Cache de prompt do provedor | O processamento do prefixo repetido (Módulo 2) | Nenhum: a resposta é nova; só o prefixo é barateado |
| Cache exato de respostas (no projeto) | A resposta inteira para o mesmo pedido (tarefa + versão do prompt + mensagens) | Resposta desatualizada se o mundo mudou; use TTL e desligue para dados dinâmicos |
| Cache semântico | A resposta de um pedido parecido (por embeddings) | Alto: "cancelar o pedido 123" e "cancelar o pedido 124" são parecidos e têm respostas diferentes |
Repare que a chave do cache do projeto inclui a versão do prompt: lançar um prompt novo invalida automaticamente as respostas antigas. Cache semântico só em casos controlados (FAQ sem dados pessoais, com limiar de similaridade alto e avaliado).
9.5 Capacidade, filas e processamento assíncrono
LAB 9.5 — Planejando para o lançamento
python python/01_capacidade.py
TRÁFEGO
chamadas por dia: 120.000
média / pico por segundo: 2.8 / 11.1
chamadas simultâneas no pico: 67 (lei de Little: taxa × latência)
LIMITES DO PROVEDOR (no pico)
requisições/min 667 de 4.000 (17%)
tokens de entrada/min 2.000.000 de 2.000.000 (100%) ⚠ NO LIMITE OU ACIMA: vai receber 429 no pico
CUSTO MENSAL (30 dias)
tudo no claude-opus-5-5 US$ 72.000/mês (100%)
tudo no claude-sonnet-5-5 US$ 36.000/mês (50%)
roteado: 60% haiku, 30% sonnet, 10% opus US$ 19.080/mês (26%)
roteado + 50% da entrada vinda do cache US$ 13.928/mês (19%)
O gargalo é o limite de tokens por minuto, não o de requisições: com 3 mil tokens de entrada por chamada, o pico bate exatamente no teto. Prompts menores e cache resolvem as duas coisas, custo e capacidade. Os limites do exemplo são ilustrativos: consulte os do seu plano.
- Lei de Little: chamadas simultâneas = taxa de chegada × latência. Latências de segundos geram muita concorrência, o que pede pools de conexão e servidores preparados para conexões longas.
- Filas para o que não precisa de resposta imediata: absorvem picos e permitem retentativas com controle.
- Batch (50% mais barato) para processamento noturno.
- Tempo máximo por requisição e cancelamento quando o usuário desiste (Módulo 8).
9.6 Observabilidade
O gateway grava uma linha JSON por requisição (gateway.jsonl):
{"id":"req-118","cliente":"app-suporte","tarefa":"geral","versaoPrompt":"v1",
"modelo":"claude-sonnet-5-5","tentativas":[{"modelo":"claude-opus-5-5","erro":"RateLimitError"},{"modelo":"claude-sonnet-5-5"}],
"cache":false,"tokensEntrada":2400,"tokensSaida":150,"custoUsd":0.0063,"latenciaMs":1840,"status":"ok"}
- Métricas de operação: custo por cliente, modelo e versão de prompt; taxa de cache; latência p50/p95; uso de reserva; erros; recusas por cota. É o que o painel do projeto mostra.
- Traces distribuídos (OpenTelemetry): uma requisição de usuário que passa por RAG, três chamadas de modelo e duas ferramentas vira um trace com todos os passos e tempos. Os SDKs dos provedores e várias plataformas de observabilidade de LLM já emitem esses spans.
- Prompts e respostas nos logs: essenciais para depurar, e são dados pessoais. Decida o que registrar, por quanto tempo e quem acessa; mascare dados sensíveis.
- Qualidade em produção: amostras avaliadas pelo LLM-juiz calibrado e sinais dos usuários (Módulo 3) entram no mesmo painel.
9.7 Versionamento de prompts, canário e rollback
{
"estavel": "v1",
"candidata": "v2",
"fracaoCandidata": 0.1,
"versoes": {
"v1": { "sistema": "Classifique ...", "criadoEm": "2026-09-02", "nota": "versão inicial" },
"v2": { "sistema": "Você classifica ... desempate ...", "criadoEm": "2026-10-20", "nota": "em canário 10%" }
}
}
- A versão nova passa no eval offline (Módulo 3).
- Entra como candidata com 5–10% do tráfego. A escolha é determinística pelo id da requisição.
- O painel compara custo e qualidade de
classificar@v1eclassificar@v2. - Promove (vira estável) ou faz rollback (remove a candidata), mudando um arquivo, sem deploy das aplicações.
O mesmo vale para trocar de modelo: uma rota nova em canário, medida, e então promovida.
Quiz rápido
Qual é a forma mais segura de colocar uma versão nova de prompt em produção?
Canário com métricas comparáveis e rollback limita o estrago de uma regressão que as evals não pegaram.
Projeto do módulo: gateway de LLM
cd projeto && npm install && npm test # 13 testes com provedor falso
GATEWAY_CLIENTES="$(openssl rand -hex 16):app-suporte:5" npm run gateway
# painel em http://127.0.0.1:8787/
| Arquivo | Responsabilidade |
|---|---|
src/politicas.ts | Rotas por tarefa, esforço, preços, clientes |
src/prompts.ts + prompts/ | Prompts versionados com canário determinístico |
src/gateway.ts | Autorização, cota diária, cache, cadeia de reserva, registro |
src/metricas.ts | Agregação para o painel |
src/servidor.ts + public/ | API HTTP com chave por cliente e painel de custos com CSP |
Os testes usam um provedor falso em que cada modelo se comporta como configurado (sucesso, 429, 500, 400 ou recusa) e provam: roteamento, reserva só em erro transitório, cache e TTL, cota diária com virada de dia, autorização por tarefa, proporção do canário e as métricas.
Entregáveis
- Coloque duas aplicações suas (ou os projetos dos Módulos 2 e 4) para chamar o gateway em vez da API.
- Adicione streaming (SSE) ao gateway, com registro de custo ao fim do stream.
- Implemente roteamento por dificuldade com um classificador barato e compare custo e qualidade com o roteamento por tarefa usando a suíte do Módulo 3.
- Exporte traces com OpenTelemetry para uma ferramenta de sua escolha.
- Faça um canário real de uma versão de prompt e escreva a decisão de promover ou não, com números.
Critérios de avaliação
| Critério | Peso |
|---|---|
| Aplicações integradas ao gateway | 20% |
| Streaming com custo registrado | 20% |
| Roteamento por dificuldade medido | 25% |
| Traces | 15% |
| Canário com decisão documentada | 20% |
Recursos para aprofundar
- OpenTelemetry · traces, métricas e logs; veja as convenções semânticas para IA generativa
- LiteLLM · um gateway aberto, para comparar com o seu
- Documentação da API · limites de taxa, cache de prompt e batch
- Livros de SRE do Google · fundamentos de operação que valem para sistemas com IA