Resumo

  • Quase toda empresa que rodou piloto com IA hoje tem mais pontos de chamada a modelo do que consegue listar de cabeça.
  • Escrevi um script em Python, sem dependências, que varre um repositório e reporta: pontos de chamada por provedor, chaves de API expostas, endpoints de provedor, servidores MCP declarados e chamadas sem timeout.
  • Rodei em três repositórios. No aider, achou 5 imports de SDK e uma chamada de inferência sem timeout, em 0,2 segundo. Num site institucional em Next.js com 92 arquivos, achou zero — que é o resultado certo.
  • O script está no fim do artigo. Roda offline e não envia nada para lugar nenhum.

A pergunta que ninguém responde na hora

Faça o teste na próxima conversa com alguém do seu time: quantas aplicações da empresa chamam um modelo de IA hoje?

Quase ninguém responde na hora. E quando responde, costuma errar para baixo.

Não é desleixo. É como a coisa cresceu. O primeiro caso de uso foi aprovado, discutido, revisado. O quarto foi alguém resolvendo um problema numa sexta à tarde. O sétimo foi um script de automação interna que nunca passou por revisão porque não era "produto". Cada um com a própria chave, cada um decidindo sozinho o que manda pra fora.

O piloto não tinha esse problema porque era um só.

Antes de discutir gateway, política de uso ou anonimização, tem uma etapa mais chata e mais barata: saber o que existe. E isso dá pra automatizar.

O que eu construí

Um script em Python que varre um repositório e reporta cinco coisas:

  1. Pontos de chamada, por provedor. Import de SDK em Python via AST e em JavaScript/TypeScript via análise de import e require. Cobre OpenAI, Anthropic, Google, Vertex, Bedrock, Azure, Cohere, Mistral, Groq, Together, Replicate, Ollama, LiteLLM, LangChain, LlamaIndex, HuggingFace e o AI SDK da Vercel.
  2. Chaves de API expostas. Por formato conhecido (sk-ant-api..., sk-proj-..., AIza..., hf_..., gsk_..., AKIA...) e por variável com cara de chave recebendo valor literal.
  3. Endpoints de provedor, para pegar configuração que não passa por import — base_url apontando para api.openai.com, para um recurso .openai.azure.com, para localhost:11434.
  4. Servidores MCP declarados em .mcp.json, claude_desktop_config.json e afins, com o comando que executam e as variáveis de ambiente que recebem.
  5. Chamadas sem timeout. Chamada de inferência sem timeout trava o processo quando o provedor degrada — e provedor degrada.

Também avisa quando existe .env no repositório sem regra correspondente no .gitignore, que continua sendo o caminho mais comum de vazamento de credencial.

Sem dependências, Python 3.8+. Não faz requisição de rede. Lê arquivo, conta e escreve o relatório no diretório atual.

Condições do teste

  • Executado em 2 de agosto de 2026, Linux, Python 3.
  • Três alvos: uma fixture controlada com achados plantados, o repositório do aider (open source), e um site institucional em Next.js 12.
  • Diretórios de dependência (node_modules, venv, .git, dist, .next) são ignorados na varredura.

O que apareceu

No aider — 49 arquivos lidos, 4 falam com modelo, 1 provedor, 7 achados em 0,2 segundo. Cinco imports de SDK, um endpoint e uma chamada de inferência sem timeout em aider/coders/base_coder.py:649.

No site institucional — 92 arquivos lidos, zero achados em 0,43 segundo. Esse resultado importa tanto quanto o outro: uma ferramenta dessas que grita em repositório limpo não serve para nada. Se ela acusa em tudo, você para de ler o relatório na terceira vez.

Na fixture — pegou as quatro chaves plantadas, os dois servidores MCP com as variáveis de ambiente que recebem, o .env não ignorado, e ignorou corretamente os placeholders (your-api-key-here, os.environ[...]).

Os dois bugs que ele tinha antes de eu confiar nele

Essa parte é mais útil que o resultado, porque é onde esse tipo de ferramenta engana.

Primeiro: a janela de detecção de timeout era larga demais. Eu procurava a palavra timeout numa janela de seis linhas ao redor da chamada. Na fixture, uma função sem timeout passou limpa porque a função logo abaixo tinha um. Uma varredura que não acusa nada dá a sensação exata de estar tudo bem.

O conserto foi ler apenas os argumentos da própria chamada, atravessando parênteses balanceados até fechar. Aí chat.py:10 acusou e chat.py:13 não, que é o correto.

Segundo: falso positivo em string literal. Rodando no aider, apareceram três chamadas sem timeout. Uma era real. As outras duas eram isto, em main.py:

help="Specify the deployment_id arg to be passed to openai.ChatCompletion.create()",

Texto de ajuda mencionando uma chamada. Casa com o padrão e não é chamada nenhuma. Passei a ignorar match dentro de string e de comentário, e os dois sumiram.

Falso positivo em ferramenta de inventário é pior que falso negativo, porque destrói a confiança no relatório inteiro — e é justamente o que faz alguém descartar o resultado sem olhar.

Um terceiro achado, que não era bug. O aider daquele checkout usa openai.ChatCompletion.create(), a API anterior à versão 1.0 do SDK. Meu padrão original só reconhecia a API atual. Isso não é detalhe histórico: base de código de empresa é cheia de chamada no formato antigo, e ferramenta que só conhece a sintaxe nova passa por cima dela sem ver.

O que ele não faz

  • Não executa o código. Análise estática só — chamada montada dinamicamente em runtime não aparece.
  • Não olha histórico do git. Chave removida num commit anterior continua no histórico e o script não vai contar.
  • Não valida se a chave é válida. Ele reporta formato, não faz requisição.
  • Não lê Java, C#, Go, Ruby ou PHP. Hoje é Python e JavaScript/TypeScript, mais arquivos de configuração.
  • Não sabe quanto cada chamada custa. Custo por aplicação é outro problema e exige instrumentação em runtime.

E o principal: rodei em repositório público, não em código de produção de empresa. O que aparece no repositório de um time de quarenta pessoas é outra conversa, e eu ainda não tenho esse número.

Depois de publicar, rodei numa amostra maior — os repositórios em alta do GitHub, 17 clonados, 38.744 arquivos lidos. Dez chamam modelo de IA, sete não.

E o resultado mais útil não foi o que a ferramenta achou. Foi o que ela achou errado.

Na primeira passada, o relatório acusou 66 chaves de API expostas em severidade alta. Um número desses num post rende. Só que eu fui olhar uma por uma, e todas as 66 eram falso positivo:

  • 57 estavam em tests/, __tests__/, *.test.ts — chaves de fixture, inventadas para o teste rodar. Uma delas era literalmente AKIAFAKE...FAKE.
  • 8 estavam em arquivos *.test.mjs, que meu filtro de caminho de teste não reconhecia.
  • E a última era isto:
const CLAUDE_KEY = "OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES";

Nome de feature flag. Meu padrão casou CLAUDE...KEY recebendo um literal e chamou de credencial.

Zero credenciais reais em 17 repositórios. A ferramenta estava gritando.

O que eu mudei:

  • Heurística de segredo de verdade em vez de "tem valor literal": exige entropia mínima, rejeita identificador em SCREAMING_SNAKE, rejeita valor contendo fake, mock, dummy, e rejeita sequência preguiçosa como abcdef ou 0123456789.
  • Classificação de caminho — produção, teste ou vendorizado. Achado em teste continua no relatório, mas rebaixado; não disputa atenção com credencial de produção.
  • Verbo genérico só conta com contexto de provedor. .stream(, .invoke( e .chat( existem em metade das bibliotecas do mundo. Um servidor HTTP com .stream() estava entrando como chamada de modelo. Agora só conta se o arquivo também importa SDK de provedor ou aponta para endpoint de provedor.
  • Endpoint deduplicado por arquivo. Dump de resposta gravado em .json repetia o mesmo host 32 vezes; o que interessa é que o arquivo referencia aquele provedor.
  • Lockfile ignorado.

Resultado: 3.402 achados viraram 2.018, e os 66 de severidade alta viraram 0. Menos ruído, mesma cobertura.

E aí o dado que sobrou é o interessante:

repositório arquivos com IA provedores distintos
diegosouzapw/OmniRoute 249 16
bojieli/ai-agent-book 381 13
andrewyng/aisuite 72 11
moeru-ai/airi 40 9
different-ai/openwork 61 7
pingdotgg/t3code 52 5

Seis de dez projetos que usam IA já falam com cinco provedores ou mais, chamando direto. Mais 657 chamadas de inferência sem timeout aparente e 44 servidores MCP declarados — 43 num único projeto.

Se você quer saber o que fazer com esse tipo de dispersão, escrevi sobre isso em Um gateway de IA custa 10ms.

Como rodar

python3 ai-inventory.py /caminho/do/repositorio

Com relatório em JSON, para comparar entre repositórios ou acompanhar no tempo:

python3 ai-inventory.py . --json inventario.json

Sai com código 1 se houver achado de severidade alta, o que permite usar em CI como verificação.

Se for rodar em repositório que não é seu, três cuidados que não são burocracia:

  • Peça autorização por escrito, mesmo que informal.
  • Rode na máquina de quem é dono do código, não puxe o repositório para a sua.
  • O relatório mostra caminho, contagem e classificação — nunca trecho de código nem o valor completo de uma chave. Mantenha assim.

Para quem isso serve

Se você tem três aplicações chamando modelo e sabe exatamente quais são, rode uma vez por curiosidade e siga a vida. Você não tem esse problema e não precisa de camada nenhuma no meio.

Se você não consegue responder o número de cabeça, rode antes de qualquer decisão de arquitetura. Discutir gateway, roteamento ou anonimização sem saber o que existe é desenhar solução para um problema que você ainda não mediu.

Se apareceu chave em severidade alta, isso é anterior a tudo. Rotacione primeiro, discuta arquitetura depois.

Como ajudamos times a adotar IA

Trabalhamos com times de engenharia que buscam incorporar IA ao dia a dia de desenvolvimento — não como experimento, mas como parte real do fluxo de trabalho.

Isso passa por escolher as ferramentas certas para o contexto do time, configurá-las de forma que façam sentido para a política de dados da empresa e garantir que os desenvolvedores saibam usá-las de um jeito que realmente aumente a produtividade, em vez de gerar mais fricção.

Se você tem um time de desenvolvimento e está tentando colocar IA para trabalhar de forma séria, fale com a gente.