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:
- 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.
- 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. - Endpoints de provedor, para pegar configuração que não passa por import —
base_urlapontando paraapi.openai.com, para um recurso.openai.azure.com, paralocalhost:11434. - Servidores MCP declarados em
.mcp.json,claude_desktop_config.jsone afins, com o comando que executam e as variáveis de ambiente que recebem. - 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.
Atualização: 17 repositórios do GitHub trending
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 literalmenteAKIAFAKE...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 contendofake,mock,dummy, e rejeita sequência preguiçosa comoabcdefou0123456789. - 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
.jsonrepetia 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.

