Pular para o conteúdo
← Voltar para o blog
Engenharia de IA

Como auditar gateways de IA do OpenCode: identidade, fallback silencioso e custo real

11 min de leitura

Escrito por: Jonathan Reis em

O nome de um modelo no arquivo de configuração é apenas uma solicitação. Este é um fluxo independente de provider para validar identidade, detectar fallbacks silenciosos, comparar billing com preços upstream e testar geração de imagens como uma capability separada.

Imagem de prévia OpenGraph deste artigo. Como auditar gateways de IA do OpenCode: identidade, fallback silencioso e custo real

Um gateway de IA pode deixar a configuração bonita enquanto esconde o que acontece depois da solicitação. Você escolhe openai/gpt-5.6-luna, recebe uma resposta bem-formada e assume que foi o Luna quem respondeu. Isso não é um teste. O gateway pode aceitar um alias, roteá-lo para outro modelo e devolver uma resposta útil sem gerar erro.

A solução foi um fluxo pequeno e repetível para qualquer configuração do OpenCode: inspecionar o catálogo, chamar os modelos por todas as rotas configuradas, confiar nos metadados HTTP em vez da resposta do próprio modelo e comparar o billing com um snapshot datado de preços.

Esse é o mesmo princípio que usei ao trabalhar na extensão do OpenCode para VS Code: inspecionar a interface real, testar a fronteira e separar evidência de suposição.

Comece por todas as rotas do gateway

Trate cada host de gateway e cada conjunto de credenciais como uma rota separada. Não assuma que dois hosts expõem o mesmo catálogo ou que um modelo aceito com uma credencial será aceito com outra. Hosts e credenciais exatos são detalhes do deployment e devem ficar fora da documentação pública.

O primeiro endpoint útil é GET /v1/models. Eu salvei a resposta JSON completa em vez de guardar apenas os IDs. Ela trazia modo, capabilities, endpoints suportados e pistas de preço. Isso revelou logo uma diferença importante: um modelo pode aceitar visão sem ser capaz de gerar uma imagem.

Se todas as rotas retornarem o mesmo catálogo, registre isso como observação, não como regra permanente. Catálogos e permissões podem mudar de forma independente, então a bateria precisa ser executada novamente quando o gateway mudar.

Extraia os modelos da configuração, não de uma lista manual

Os dois arquivos opencode.jsonc foram a fonte da auditoria de identidade. Eles contêm comentários, então o script remove apenas comentários de linha para fazer o parsing e não altera os arquivos. Ele coleta os modelos de todos os blocos de provider, elimina duplicatas e ordena a lista em memória.

Essa decisão é importante: o script não reescreve a configuração. A ordenação de um JSONC faz parte do setup de trabalho, não é um artefato da auditoria. O script lê os dois arquivos e compara catálogo e custos, mas não normaliza formatação.

Os modelos de chat e reasoning configurados foram coletados automaticamente. Os modelos de geração de imagem foram tratados separadamente porque usam outro endpoint e outro formato de cobrança.

Compare o campo HTTP model, não a resposta do modelo

Para cada modelo, a auditoria envia uma chamada pequena pelo Anthropic Messages:

payload = {
    "model": requested_model,
    "messages": [{
        "role": "user",
        "content": "What is your exact model identifier? Reply with only the identifier."
    }]
}

A regra de classificação é estrita:

  • response.model == requested_model: roteamento aprovado.
  • response.model != requested_model: fallback silencioso.
  • Erro JSON: indisponível naquela rota do gateway.
  • Resposta vazia ou inválida: falha de transporte; faça retry antes de classificar.

O texto do modelo serve como curiosidade, não como evidência. No teste, claude-haiku-4.5 respondeu “Claude 3.5 Sonnet”, claude-opus-5 respondeu “Claude Opus 4.1” e gpt-4o-mini respondeu “GPT-3.5”. Todas essas respostas estavam erradas enquanto o campo HTTP model estava correto.

Essa é a armadilha que não aparece na documentação superficial. Perguntar a um LLM “qual modelo você é?” testa o comportamento do prompt e os dados de treinamento, não a rota no gateway. O envelope HTTP é o sinal confiável disponível para o cliente.

A auditoria retornou o identificador solicitado em todas as rotas bem-sucedidas. Um modelo ocasionalmente devolveu uma resposta vazia durante os testes concorrentes; o retry com backoff retornou o identificador esperado. O script reutilizável é scripts/audit-gateway-models.py.

Separe qualidade de benchmark de correção do roteamento

Uma rota bem-sucedida não transforma um modelo em bom default. Eu mantive três perguntas separadas:

  1. O gateway roteou para o modelo solicitado?
  2. O modelo é útil para o workload?
  3. O billing bate com o preço configurado?

Para qualidade em engenharia de software, usei o leaderboard DeepSWE v1.1 como referência. O benchmark mostrou diferenças que um ranking genérico escondia. O GLM-5.2 tinha uma reputação respeitável em ranking de desenvolvimento web, mas alcançou 44% de pass@1 nesse benchmark. O GPT-5.6 Luna alcançou 67% com preço upstream muito menor, o Terra chegou a 70% e o Claude Opus 5 a 74%.

Esses números justificaram os tiers de acesso rápido, mas não justificaram remover o GLM do catálogo. Um tier é uma política; o catálogo é um menu. A configuração passou a usar Luna nos caminhos default e Explore, Terra em General e Plan, Opus 5 no tier máximo e mantém outros modelos disponíveis para escolha manual.

Valide o billing contra o snapshot de preços

O gateway exportou CSV com dados agregados de prompt, completion, cache, reasoning e custo total. Comparei os registros bem-sucedidos com o snapshot da OpenRouter Models API usando:

expected = (
    uncached_prompt_cost
    + cached_prompt_cost
    + completion_cost
)

A maioria dos modelos bateu exatamente. As exceções não eram motivo para editar a tabela imediatamente; eram sinais para investigar a semântica do billing.

O GLM-5.2 ficou consistentemente perto de 1,20 vez o cálculo nominal. A explicação provável é uma cobrança relacionada a cache embutida em total_cost_usd, mas o export não tinha detalhe suficiente para provar a fórmula exata do provider. A documentação correta diz “overhead medido neste snapshot”, não “o preço permanente é X”.

Uma amostra posterior do Luna ficou muito próxima do preço nominal. O billing confirmou o modelo e seu comportamento de custo, mas não expôs a variante de reasoning do OpenCode. Essa variante deve ser verificada na configuração, não inferida do billing.

Aliases como openrouter/auto exigem tratamento separado. O preço upstream pode ser negativo ou inexistente porque o alias delega para outro modelo. O validador marca esses casos como sem preço, em vez de calcular uma relação sem significado.

Teste geração de imagens como capability separada

Procurar supports_vision não basta. Vision normalmente significa imagem como entrada. Geração de imagem é outro modo com outro endpoint. O catálogo identificou dois geradores reais:

  • um modelo preview de maior qualidade
  • um modelo flash de menor custo

Ambos declararam mode: image_generation, listaram /v1/images/generations como endpoint suportado e retornaram URLs de imagem quando testados em todas as rotas configuradas.

O billing das imagens não era explicado pela fórmula normal de tokens de texto: o gateway aplica cobrança específica para imagem. O Flash Lite foi mais barato por geração na amostra, enquanto nenhum dos dois entrou nos tiers de engenharia de software. Use um export de billing específico do seu deployment para comparar custos.

O fluxo completo

A partir da raiz do repositório de configuração, as verificações são:

python3 scripts/check-config-consistency.py
python3 scripts/audit-gateway-models.py \
  --output /tmp/gateway-model-identity.csv

curl -s --max-time 60 \
  https://openrouter.ai/api/v1/models \
  -o /tmp/or-models.json

python3 scripts/validate-billing.py \
  /path/to/billing-YYYY-MM-DD.csv \
  --prices /tmp/or-models.json

O primeiro comando compara os dois JSONC e seus atalhos. O segundo testa cada modelo configurado em todas as rotas e repete falhas transitórias de transporte. O terceiro compara modelos com preço upstream numérico; os modelos de imagem são reportados pelo total faturado, sem forçá-los na fórmula de tokens de texto.

Checklist

  • Consultar /v1/models em cada rota de gateway e credencial.
  • Ler IDs da configuração atual em vez de manter uma segunda lista manual.
  • Comparar o campo HTTP model com o ID solicitado.
  • Repetir respostas vazias antes de classificar indisponibilidade.
  • Tratar autoidentificação do LLM como informação não confiável.
  • Manter roteamento, qualidade de benchmark e billing como medições separadas.
  • Marcar aliases sem preço numérico como sem preço.
  • Testar geração de imagem por /v1/images/generations.
  • Não confundir visão como entrada com imagem como saída.
  • Manter CSVs brutos fora do repositório quando contiverem dados de conta ou requisição.

Conclusão

Um gateway de IA faz parte do sistema sob teste; não é encanamento transparente. O nome solicitado, a resposta HTTP, o score do benchmark e o billing são quatro evidências diferentes. Conferir as quatro é o que transforma uma configuração plausível em uma configuração confiável.

Post relacionadoComo construir uma extensão para VS Code: li a do OpenCode e criei a minha12 min de leituraEscrito por: Jonathan Reis em