[IA] autoresearch v1.0: um loop autônomo de pesquisa com crítico LLM local
1. A anomalia dos 22%
Buscas por "agente de IA" cresceram 22% no Brasil em 2026, segundo levantamento recente da Locaweb noticiado pela IstoÉ Dinheiro em 23 de abril. O mesmo levantamento mostra que "o que é um agente de IA" e "como construir um agente de IA" permanecem entre os termos mais consultados. O hiato entre intenção de adoção e entendimento operacional é amplo e visível.
Este artigo preenche parte desse hiato. Não com definições copiadas de páginas de produto, mas com um artefato funcional: um loop autônomo de pesquisa que qualquer pessoa com laptop, Ollama e git pode rodar em cinco minutos. O artefato é o autoresearch v1.0, depositado no Zenodo sob DOI 10.5281/zenodo.19772195. Ele generaliza um padrão lançado por Andrej Karpathy, retirando-o de um único repositório de pretraining de LLM e levando-o a um pequeno pacote Python utilizável para qualquer problema de otimização que satisfaça quatro condições.
As condições são simples: uma métrica escalar única, um orçamento de tempo fixo, um único arquivo mutável e uma branch git. O restante do artigo mostra por que essas quatro condições bastam, o que o harness faz entre iterações e como três problemas distintos convergem sob ele.
2. Definição operacional: agente como loop, não como chat
Uma confusão comum trata agentes como chatbots mais inteligentes. O levantamento da Locaweb é consistente com essa confusão: a maioria das consultas é operacional ("agente de IA para WhatsApp", "para atendimento ao cliente"), mas as consultas fundacionais ainda são definicionais ("o que é um agente", "como construir um").
A definição operacional que escala com a tecnologia merece ser declarada com precisão:
Um agente é um loop critique-propose-execute-decide com estado persistente, não uma resposta única a um único prompt.
Cada palavra carrega peso.
- Critique implica acesso ao resultado da tentativa anterior e uma noção de melhor e pior. Sem medição não há crítica.
- Propose implica saída estruturada, não texto livre. A proposta deve ser aplicável ao sistema sob mudança.
- Execute implica que a proposta aterrissa em algum lugar com consequência mensurável (mudança de código, atualização de configuração, chamada de API) e roda sob recursos delimitados.
- Decide implica regra de classificação sobre o resultado e função de próximo estado. Manter, descartar, retentar, parar.
- Estado persistente implica memória das tentativas, que é o que permite o loop convergir em vez de vagar.
Esta definição não depende de tamanho de modelo, fornecedor ou tier de marketing. É mecânica. Um modelo local pequeno rodando este loop é um agente no sentido estrito; um modelo de fronteira respondendo uma pergunta por vez não é.
3. A arquitetura do harness
O autoresearch implementa o loop com um conjunto deliberadamente pequeno de partes móveis. O diagrama em prosa:
problem.yaml ┐
contrato read-only │
dados e avaliação │ → crítico LLM (Ollama) → proposta JSON
│ │
│ ↓
│ harness aplica proposta a solution.py
│ │
│ ↓
│ runner executa prepare.py + evaluate.py
│ sob timeout duro (cap no orçamento de recurso)
│ │
│ ↓
│ classificador: keep | discard | crash
│ │
│ ↓
│ git: avança branch (keep) ou reseta (discard/crash)
│ │
│ ↓
solution.py (mutável) ┘ audit log: results.tsv + AUDIT_LOG.md + critic_logs/
Três componentes carregam o design.
O crítico é um LLM local rodando em Ollama (família Gemma por default; qualquer endpoint OpenAI-compatível funciona). Sua saída é restringida por um JSON Schema com sete campos:
thought_process: o rastro da hipótese de trabalho.alternatives_considered: os movimentos vice-líderes que foram rejeitados.hypothesis: a alegação concreta e testável sobre o efeito esperado.expected_delta: a melhoria predita na métrica.justification: a fundamentação em propriedades conhecidas do problema.code_pseudocode: a mudança proposta em nível de pseudo-código.risk_level: a probabilidade que o próprio crítico atribui à degradação.
JSON Schema não é decoração. Força o crítico a se comprometer com uma mudança específica e prever seu efeito, ambos verificáveis post hoc. Propostas em texto livre não permitem isso.
O harness aplica a proposta ao único arquivo mutável (solution.py), faz commit da mudança na branch de pesquisa ativa, e dispara o runner.
O runner executa o prepare.py (configuração de dados) e o evaluate.py (computação da métrica), ambos read-only, sob timeout duro. O timeout faz parte do contrato, não é rede de proteção. Torna runs comparáveis entre propostas e entre máquinas distintas. O classificador lê o stdout do runner, faz parse da métrica e emite uma das três etiquetas:
keepse a métrica melhorou em relação ao melhor corrente.discardse a métrica não melhorou, ou melhorou ao custo de uma restrição declarada peloevaluate.py.crashse o runner falhou (timeout, exceção, saída malformada).
Toda iteração escreve uma linha em results.tsv e um bloco JSONL em critic_logs/. O audit log é o estado persistente que torna o loop convergente em vez de aleatório.
4. Três estudos de caso
O autoresearch v1.0 ship com três exemplos funcionais, cada um contendo um diretório sample_run completo gravado durante o desenvolvimento. O gráfico abaixo resume a convergência dos três.
[Figura 1: posts/images/PROJ-003.png. Painéis de convergência para os três exemplos shippados. Iteração no eixo x, métrica no eixo y, propostas mantidas em verde, descartadas em vermelho, trajetória do melhor-até-agora em linha azul.]
4.1 Busca heurística para TSP
O problema é um Caixeiro Viajante euclidiano de 30 cidades com coordenadas aleatórias, avaliado por comprimento total do tour. A solução baseline retorna o tour aleatório inicial. O crítico rodou quatro iterações dentro do orçamento de tempo:
Iter Tour length Status Movimento
---- ----------- ------- --------------------------------------
0 25,251 keep baseline (tour aleatório)
1 5,975 keep busca local 2-opt sobre init aleatório
2 5,893 keep init nearest-neighbor + 2-opt
3 5,893 discard simulated annealing sobre NN init
Melhoria: 76,7% em relação ao baseline. Três de quatro movimentos aceitos. O descarte na iteração 3 é informativo: o crítico propôs simulated annealing sobre NN+2-opt, o runner retornou a mesma métrica dentro do timeout, e o harness corretamente rejeitou o movimento por não demonstrar melhoria. A branch reverteu para a iteração 2.
4.2 Tuning de hiperparâmetros XGBoost
O problema é uma tarefa de regressão com RMSE como objetivo. Seis iterações:
Iter RMSE Status Movimento
---- ------ ------- ------------------------------------------
0 0,4639 keep baseline (n_estimators=500, eta=0,1)
1 0,4503 keep n_estimators=750
2 0,4561 discard eta=0,05
3 0,4485 keep n_estimators=1000
4 0,4449 keep eta=0,08
5 0,4481 discard eta=0,05 (revisitada)
Melhoria: 4,1% em relação ao baseline. Quatro de seis movimentos aceitos. Este é o regime típico de tuning de hiperparâmetros, no qual ganhos são menores e a taxa de descarte informa sobre a geometria local. Os dois descartes envolvem reduções agressivas de eta, sugerindo que o crítico aprendeu que a superfície de perda era plana o suficiente para penalizar shrinking excessivo.
4.3 Escalarização multi-métrica
O problema combina performance de modelo, latência e contagem de parâmetros em um score escalar com pesos predefinidos. Três iterações:
Iter Score Status Movimento
---- ------ ------- --------------------------------------------------
0 0,6532 keep baseline (max_depth=3)
1 0,7061 discard max_depth=2 (menor, mais rápido, predição pior)
2 0,6159 keep max_depth=4
Melhoria: 5,7% em relação ao baseline. Um de três movimentos aceitos, mas o caso é o mais valioso pedagogicamente. A iteração 1 retornou score bruto mais alto em tamanho e latência ao custo da performance preditiva. A escalarização corretamente penalizou o movimento, e o harness o descartou. Sem guarda multi-métrica, o loop teria felizmente aceitado um modelo menor, mais rápido, pior. É exatamente para demonstrar isso que o terceiro estudo de caso existe.
5. Quando o padrão se aplica
autoresearch é um loop, não um modelo. Aplica-se onde quatro condições se mantêm:
- Existe uma métrica escalar única que o runner pode emitir no stdout. Trade-offs multi-objetivo devem ser escalarizados antes de entrarem no loop, como na seção 4.3.
- Existe um orçamento de tempo fixo por iteração. O orçamento torna runs comparáveis e força o crítico a propor mudanças que cabem nele.
- Existe um único arquivo mutável. A proposta estruturada do crítico tem como alvo aquele arquivo. Outros arquivos (dados, avaliação, contrato) são read-only.
- Existe uma branch git dedicada. O estado vive em commits; a convergência vem de reverter commits ruins e avançar commits bons.
Domínios que satisfazem essas condições em produção: roteamento e scheduling sob restrições com janela temporal; calibração de threshold para classificadores em pipelines de produção; configuração de campanha ou pricing com simulação delimitada; tuning de modelo e pipeline onde cada candidato leva minutos em vez de horas.
Domínios que não satisfazem: qualquer coisa que exija julgamento humano na métrica (qualidade subjetiva, percepção de justiça sem operacionalização); qualquer coisa com efeitos colaterais fora do arquivo mutável (escritas irreversíveis em estado compartilhado); qualquer coisa em que uma única iteração seja indeterminada no tempo.
6. Por que local-first
O crítico roda em Ollama por default. A escolha é deliberada.
Custo é o primeiro eixo. Um loop que roda centenas de iterações em endpoint hospedado acumula dinheiro real. Um loop que roda em modelo Gemma local em laptop custa zero por chamada uma vez baixado o modelo. Custo deixa de ser restrição de design e remove uma categoria de otimizações prematuras.
Privacidade é o segundo eixo. Código sob análise, lógica de avaliação e propostas intermediárias nunca saem da máquina. Para organizações com restrições regulatórias ou contratuais sobre saída de dados (jurídico, saúde, contratos governamentais, auditoria interna), isso pesa mais que performance de benchmark. Um loop com crítico local é um loop que essas organizações conseguem rodar de fato.
Replicabilidade é o terceiro eixo. Qualquer leitor com Ollama, Python e git pode rerodar os três exemplos, ver curvas de convergência comparáveis e inspecionar o mesmo audit log do qual este artigo foi construído. O depósito Zenodo contém os diretórios sample_run completos. Não há estado oculto.
Velocidade é o quarto eixo. Sem throttling de quota, sem jitter de API. O gargalo é hardware local, que é previsível e tunável.
O trade-off é teto de capacidade. Um modelo de fronteira hospedado propõe movimentos melhores em problemas duros do que um modelo local pequeno. A resposta certa é "use o crítico local para o loop e traga um modelo mais forte quando o local empacar", o que é direto de fazer com a configuração de endpoint OpenAI-compatível.
7. Dirigindo o loop com agentes de codificação
O pacote foi desenhado para ser agent-agnóstico: qualquer agente de codificação capaz de clonar repositório, instalar dependências Python e editar um arquivo mutável único pode dirigir o loop. Três agentes populares validados para isto:
- Claude Code (Anthropic): aponte para a URL do repositório e ele clona, instala, faz scaffolding de uma run via
autoresearch inite itera. - Codex (OpenAI Codex CLI): mesmo fluxo; o agente lê o README, roda o wizard de pré-checagem e entra no loop.
- OpenCode: agente CLI open-source; mesmo caminho de entrada via URL.
A interação é mínima. Diga a qualquer um destes agentes:
Use o repositório github.com/pcbrom/autoresearch para rodar o exemplo TSP heurístico até convergência.
O agente cuida de instalação, pull do modelo, scaffolding do projeto e o loop iterativo. O audit log persiste no diretório do projeto independente de qual agente rodou; pode-se entregar entre agentes no meio de uma run.
Esse desacoplamento importa: autoresearch é o harness, o crítico LLM é uma parte móvel, e o agente orquestrador é outra. Qualquer das peças é substituível.
8. Genealogia: de karpathy/autoresearch a um pacote generalizado
O padrão tem origem no repositório autoresearch de Andrej Karpathy. Aquela base de código amarrava o harness a um único experimento de pretraining de LLM e era pensada para ser clonada, modificada e rodada nessa única tarefa.
O autoresearch v1.0 extrai o padrão. A superfície mutável agora é um solution.py plugável, definido por problema; o contrato agora é um problem.yaml com métrica, runner e orçamento declarados; o runner agora é uma CLI genérica que consome o contrato; o crítico está desacoplado de qualquer endpoint particular via cliente OpenAI-compatível; e o audit log foi consolidado como artefato navegável em vez de scaffolding por experimento.
O crédito é explícito. O campo related_identifiers no Zenodo declara isDerivedFrom: github.com/karpathy/autoresearch, o README declara a genealogia, e a licença é MIT, alinhada com o upstream.
9. Referências
BROM, P. C.. autoresearch v1.0. Zenodo, 2026. DOI 10.5281/zenodo.19772195.
KARPATHY, A.. autoresearch. Repositório GitHub. Disponível em: github.com/karpathy/autoresearch.
Locaweb (2026). 22% de crescimento em buscas no Google sobre agentes de IA no Brasil. Reportado em IstoÉ Dinheiro, 23 de abril de 2026.
Código: github.com/pcbrom/autoresearch
Licença: MIT
DOI conceito (versão mais recente): 10.5281/zenodo.19772194