DEV Community

Cover image for Além do LLM: como tirar seu projeto de IA do protótipo e colocar em produção
Tiago Vilas Boas (Montanha)
Tiago Vilas Boas (Montanha)

Posted on Edited on

Além do LLM: como tirar seu projeto de IA do protótipo e colocar em produção

Construí o Downshift porque mandar toda tarefa do agente de código para o modelo mais caro é pagar sênior para carimbar envelope. É um hook open-source: quando o Claude Code cria um subagente, ele lê a tarefa e escolhe o modelo. Renomear variável vai pro Haiku; desenhar algoritmo fica no Opus (README).

O difícil foi provar o valor que ele entrega. Meus primeiros números vieram de um dia de uso pessoal. Com um braço de controle e um conjunto de testes separado, o resultado foi mais modesto: em trabalho mecânico delegado, 12 de 12 tarefas passaram nos dois braços e o custo caiu 12%; em correções reais, o roteador rebaixou 3 de 34 subagentes e a diferença de custo não é dele (A/B de 15/10/2026).

A pergunta deixou de ser "qual modelo?" e virou "o que o código garante, e como provo?": o modelo pensa, o código governa, a avaliação prova. O texto reúne decisões e critérios para levar uma feature com LLM à produção. O Downshift é o caso real; o delivery, um exemplo didático. Meus harness e modelos estão neste post.

Índice: 1. Simples primeiro · 2. Provedor isolado · 3. Roteamento · 4. Prompt · 5. RAG · 6. Agentes · 7. Checklist

1. Comece pela solução mais simples

Antes de escolher um modelo, pergunte qual é a solução mais simples que atende aos requisitos de qualidade e segurança. Avance só quando a opção anterior não resolver.

Da esquerda para a direita: regras resolvem casos previsíveis; classificadores tratam intenções conhecidas; tarefas ainda ambíguas seguem para um LLM.

Da esquerda para a direita: regras → classificador → LLM. Encerre o caminho assim que houver uma solução suficiente.

No Downshift, sinais determinísticos definem o tier, e um reforço local por hash FNV pode elevá-lo. O MiniLM externo saiu no PR #142. Um LLM classificador reconheceria vocabulário novo, mas acrescentaria custo e latência a cada spawn.

2. Isole o provedor e valide a saída

Adoto a distinção da Birgitta Böckeler: o harness combina guias, que orientam antes da ação, e sensores, que conferem depois. Teste e schema são sensores computacionais; juiz-LLM é inferencial, mais caro e menos determinístico.

O provedor fica atrás de um adaptador; seu código valida e decide o destino da falha:

package triage

import (
    "encoding/json"
    "errors"
    "fmt"
)

type Triage struct {
    Intent     *string `json:"intent"`
    NeedsHuman *bool   `json:"needs_human"`
}

func Validate(raw []byte) (Triage, error) {
    var result Triage
    if err := json.Unmarshal(raw, &result); err != nil {
        return Triage{}, err
    }
    if result.Intent == nil || result.NeedsHuman == nil {
        return Triage{}, errors.New("required field missing")
    }
    switch *result.Intent {
    case "status", "delay", "refund", "other":
        return result, nil
    default:
        return Triage{}, fmt.Errorf("invalid intent: %q", *result.Intent)
    }
}
Enter fullscreen mode Exit fullscreen mode
  • Valide sempre. Modo JSON garante JSON válido, não o seu schema. Structured Outputs com strict restringe a saída ao schema suportado (OpenAI); o código ainda precisa tratar recusas e respostas incompletas e verificar o conteúdo.
  • Fixe o snapshot. Alias aponta pro snapshot mais novo e muda quando o provedor atualiza (Anthropic). Avalie antes de trocar e teste o fallback com os mesmos casos.

Guia e sensor. O schema no prompt é guia; a validação é sensor computacional, roda em toda chamada. Falha do roteador: o Downshift é fail-open: se o roteador quebra, o subagente roda com o modelo pedido. Essa escolha preserva a execução; autorização e limites de orçamento continuam obrigatórios no sistema que executa a tarefa. Trade-off: adaptador próprio com um ou dois provedores, gateway (LiteLLM, OpenRouter) quando vários times reinventarem retry e log.

3. Roteie por tarefa, com piso de risco

As regras do Downshift:

  • Sem confiança, preserve o modelo solicitado: a abstenção evita uma troca sem evidência suficiente.
  • Piso de risco: tarefa simples que toca auth, segredo, pagamento, PII ou concorrência sobe no mínimo pro tier intermediário (complexity-weights).

Números (benchmark/report.md):

  • Rede de regressão, 200 tarefas: 69,0% de acerto de tier (IC 95% de 62,5% a 75,5%), 0 de 50 complexas no pequeno, 49 de 100 simples no intermediário: errar para cima foi escolha. É conjunto de ajuste, não prova generalização.
  • Splits só de avaliação (#123): 53,0% (n=100) e 41,7% (n=120, cego). A maioria dos erros é abstenção; confiante, acerta 35 de 37 e 22 de 28.
  • 40 tarefas Go com teste: com modelo fixo, 40/40 (fronteira), 31/40 (intermediário), 27/40 (pequeno). No tier escolhido pelo classificador, 37/40; era 38/40 e caiu de propósito quando o boost semântico parou de inflar prompt sem sinal (#125).

O hook pede, o harness decide. Reescrita observada no Claude Code e no Antigravity; inferida no Codex; não confirmada no Cursor; no KiroCrew o hook só bloqueia e pede retry (harness support).

4. Prompt é contrato, não pedido

Prompt bom diz o que entra, o que sai, o que é proibido e como saber se deu certo. Para conhecimento externo, avalie recuperação antes de fine-tuning. No estudo de Ovadia et al., RAG teve resultado superior para injeção de conhecimento nas condições avaliadas. Isso não estabelece uma regra para toda tarefa: recuperar fontes, ajustar comportamento e executar ferramentas resolvem necessidades diferentes.

Caso (post completo). "Audite a segurança" devolveu 23 alertas, 22 descartáveis. Reescrito como contrato:

- Achado sem arquivo:linha é descartado.
- CWE ou CVE inventado encerra a sessão.
- Denominador obrigatório: "1 de 174 rotas", não "achei um bug".
- Uma invariante falsificável por rodada: "toda rota que devolve pedido filtra pelo user_id da sessão?"
- Silêncio é resultado válido.
Enter fullscreen mode Exit fullscreen mode

Resultado: 1 IDOR real em 174 rotas, corrigido antes de produção. No mesmo formato, dois reports meus de hardening viraram correção dos mantenedores: Formbricks (issue, PR) e Dub (issue, PR).

Guia e sensor. O contrato é guia; descartar achado sem arquivo:linha é sensor computacional.

5. RAG: consultar a base antes de responder

Pense em um atendente que consulta o manual da empresa antes de orientar o cliente. Ele precisa encontrar a regra certa e explicar como ela se aplica àquela situação.

RAG significa Retrieval-Augmented Generation, ou geração aumentada por recuperação. É um padrão de arquitetura que combina busca com geração de respostas. O sistema consulta uma base de conhecimento — manuais, políticas ou FAQs — e entrega os trechos encontrados ao modelo junto da pergunta. Isso fornece contexto sem treinar o modelo de novo (IBM, AWS).

Exemplo didático: um cliente pergunta sobre cancelamento no delivery. O sistema busca a política vigente na região dele e o modelo explica a regra, citando a fonte. Buscar a política de outra região produz uma orientação errada; mesmo com o trecho certo, o modelo pode distorcê-lo.

Para produto: quem mantém a fonte, quando a busca recebe uma atualização e quem pode consultar cada documento? Meça dúvidas resolvidas corretamente, tempo de resposta e custo por atendimento. RAG ajuda a responder com informação específica e verificável, mas não elimina erros (IBM).

Fluxo RAG: documentos divididos em trechos, filtro por metadados, busca lexical e vetorial combinadas, reordenação e resposta com citações. Abaixo, avaliações independentes da busca e da geração.

Acima: preparar trechos → filtrar região, vigência e acesso → buscar → ordenar → responder com fontes. Abaixo: conferir a busca à esquerda e a resposta à direita.

Neste desenho, BM25 busca palavras; embeddings representam o texto em números para buscar por significado; RRF combina as listas; o reranker reordena os candidatos conforme a pergunta (Anthropic). São escolhas de implementação: RAG também pode usar busca por palavras, sem banco vetorial (Microsoft).

O reranker deste desenho avalia 20 candidatos. Se faltar evidência, diga que não sabe ou encaminhe a dúvida. Ajuste esse critério na validação; reserve perguntas inéditas, o holdout, para a medição final.

Duas avaliações, separadas:

  • Busca: encontrou a informação certa? Hit rate@k mede se há ao menos um trecho relevante entre os k primeiros; recall@k mede quantos dos relevantes vieram; MRR considera a posição do primeiro acerto (IR book).
  • Resposta: usou bem o que encontrou? Meça fidelidade às fontes (faithfulness), se a citação sustenta aquela frase e se o sistema admite quando falta informação, sem recusar perguntas que poderia responder (RAGAS).

Caso: um laboratório medido. Central de ajuda de um delivery fictício (25 seções com região e vigência), 44 perguntas rotuladas e 18 novas como holdout. Neste artigo, R@3 é a proporção de buscas que encontram ao menos um trecho relevante entre os três primeiros resultados. No holdout:

Configuração Embedding leve (R@3) Embedding multilíngue (R@3)
Por palavras (BM25) 0,69 0,69
Por significado (embeddings) 0,62 0,75
Palavras + significado 0,69 0,94
  • O embedding decide se a híbrida vale: com o leve, ela não passou do BM25.
  • Metadado decide o 1º lugar. SP e RJ têm políticas quase iguais; sem filtro de região, o acerto regional cai de 100% para 50%.
  • Quase publiquei número inflado. Um glossário escrito olhando as falhas levou o recall@1 de 0,65 para 0,88 no ajuste, e no holdout só de 0,56 para 0,62. Overfitting, a mesma lição do Downshift: 69% no conjunto de ajuste, 53% no cego.
  • Semelhança não garante resposta. O score de cosseno mede proximidade entre as representações dos textos. O limiar que recusa todas as sem resposta recusa 47% das respondíveis.

Geração, medida em 17/10/2026. Busca fixa, 62 perguntas, 174 casos por prompt, prompt ingênuo contra prompt de contrato. Gerador gemini-2.5-flash-lite, juiz gpt-4o-mini, ambos com ZDR.

  • Citação correta, nos 112 casos avaliados para esse critério: 0/112 no ingênuo, 112/112 no contrato, sem ID inventado.
  • Abstenção, nos 62 casos sem evidência: 59/62 (95,2%) nos dois prompts, com 0 recusa indevida nas perguntas respondíveis.
  • A fidelidade às fontes não separou os prompts, por efeito teto. E o juiz concordou com o rótulo humano em só 78,8%: dos 9 "infiel" que ele marcou, 2 eram reais. Juiz sozinho é piso, não medida.
  • Custo: US$ 0,03 por rodada.

Limites. Busca: amostra pequena (uma pergunta muda 6 pontos), sem reranker. Geração: corpus fictício, 1 gerador, 1 juiz, 1 rodada, busca não medida nesta rodada. As 62 perguntas e os 174 casos são unidades diferentes; os resultados de citação e abstenção usam subconjuntos próprios. Para reproduzir a rodada, ainda falta publicar a composição dos casos e o critério operacional de citação correta. Um ID existente, sozinho, não comprova que a fonte sustenta a afirmação.

Quando vale a complexidade? Comece com arquivos organizados e busca textual; para uma base pequena, o conteúdo direto no contexto pode bastar (Anthropic). Busca híbrida e reranking acrescentam preparação, manutenção, custo e latência. Adote quando as avaliações mostrarem ganho suficiente. Compare com as mesmas perguntas; mantenha vigência e controle de acesso em qualquer opção.

6. Agente precisa de parada, humano e segurança fora do prompt

Num workflow, o código decide o próximo passo; num agente, o modelo decide e chama tools em loop (Anthropic). Passo conhecido é código; agente só com ambiguidade real e parada explícita (passos, orçamento, timeout, humano).

Um jeito simples de explicar esse ciclo é o BDI. Aqui, ele é um recurso visual, não uma arquitetura que todo agente segue: crenças são o que o agente sabe sobre a situação; desejos, o que quer alcançar; intenções, o plano que escolheu. Quando a ferramenta responde, o agente atualiza o contexto e decide o próximo passo.

Ciclo BDI de um agente: crenças, desejos e intenções levam a uma ação; o resultado atualiza o contexto para a próxima decisão.

Em produção, esse ciclo precisa de permissão, schema, orçamento, aprovação humana conforme o risco e sensores.

Evite uma ferramenta genérica como execute_sql(query). A ferramenta deve obter o usuário da sessão, validar os argumentos e pedir aprovação humana para escritas de alto risco.

  • Argumento passa por schema antes de executar.
  • Escrita exige idempotência e, conforme o risco, humano (reembolso acima de R$ X).
  • Segurança fora do prompt. "Não revele dados" no system prompt não é controle. Injeção pode vir indireta, num documento do RAG ou e-mail (OWASP LLM01). Autorização no código, segredo fora do contexto.

No Downshift, o hook lê a tarefa só em memória, não guarda prompt, e a telemetria registra o tipo de subagente sem o nome de agente customizado (#124).

Caso. O Agentic Code Review segue isso com sensores computacionais: IDOR alto ou segredo é BLOCK, o diff é dado não confiável e achado sem path:line não sai.

Trade-off. Aprovação frequente, sozinha, não justifica automatizar. Mova a decisão pro código quando houver uma regra verificável, cobertura das exceções e um destino definido para os casos que exigem julgamento humano.

7. Checklist antes de produção

HTTP 200 com resposta inventada é normal em LLM; qualidade se prova com casos fixos e checagem executável a cada troca. Juiz-LLM só onde não há checagem determinística, calibrado contra rótulo humano: ele tem viés de posição e verbosidade (Zheng et al.), e na seção 5 bateu com o humano em só 78,8%.

Checklist copiável: gist.

[ ] Existe uma versão sem LLM para o caso mais frequente?
[ ] Saída validada por schema; falha tem destino (subir modelo ou humano)
[ ] Snapshot do modelo fixado; fallback testado com os mesmos casos
[ ] Timeout, retry com backoff em 429/5xx, idempotência em toda escrita
[ ] Orçamento por chamada e por conversa; limite de passos no agente
[ ] Tools com menor privilégio; user_id da sessão; humano no irreversível
[ ] RAG: busca medida (hit rate@k, MRR) e geração medida (faithfulness, citação, abstenção), em holdout
[ ] Conjunto de avaliação roda num comando, no CI, antes de cada troca
[ ] Trace por execução: modelo, versão do prompt, tokens, latência, resultado
[ ] Métrica de produção: custo por tarefa resolvida, não por token
Enter fullscreen mode Exit fullscreen mode

Da avaliação ao lançamento. Defina os limites antes de avaliar. Exemplo didático para um assistente de FAQ: nenhuma exposição indevida nos casos de segurança, pelo menos 95% de respostas corretas no conjunto acordado e latência p95 de até 5 segundos. Os valores dependem do produto e não foram validados neste laboratório. Regressão bloqueia o lançamento. Comece com uma parcela pequena do tráfego e compare qualidade, latência e custo por tarefa; se os limites forem violados, reverta modelo e prompt para a versão anterior. Quem opera o serviço precisa ser dono desse acompanhamento e do rollback.


Do protótipo ao produto, o que mudou não foi o modelo: foi dar a cada risco uma guia e um sensor, e medir. No RAG, a geração agora tem número; no Downshift, a economia em uso geral ainda não. Gaps abertos: medir busca e geração na mesma rodada, testar outro gerador e calibrar o juiz. A próxima experiência proposta compara geradores com as mesmas perguntas, busca e critérios, registrando qualidade, latência, falhas e custo por tarefa. Para testes de programação no OpenCode, pretendo experimentar MiMo-V2.6-Flash Free, Ling 3.1 Flash Free, Nemotron 3.5 Lightning Free e Big Pickle, conforme a disponibilidade em /models (OpenCode Zen). São candidatos a testar; os resultados acima continuam sendo do Gemini 2.5 Flash Lite como gerador e GPT-4o Mini como juiz.

Para os comentários: no sistema com LLM que você mantém, qual sensor diz que ele funciona? E se você já calibrou juiz-LLM contra rótulo humano, quanto deu a concordância?

Top comments (0)