autoresearch v1.1: fechando o loop propor-aplicar-medir

LLM
agents
Python
optimization
opensource
O que mudou no autoresearch v1.1: o loop passa a chamar um agente de código sem cabeça em cada iteração e elimina a etapa humana que ficava entre propor e medir. Com uma run ilustrativa de TSP, as duas posturas de permissão, e os backends de critic plugáveis no mesmo release.
Autor

Pedro Carvalho Brom

Data de Publicação

4 de junho de 2026

Code repositorypcbrom/autoresearch

O padrão autoresearch proposto por Andrej Karpathy é um loop pequeno com uma reivindicação grande: entregue a um critic LLM um único arquivo mutável, uma métrica e um orçamento de tempo, e o critic é capaz de propor edições úteis, uma iteração por vez. O detalhe, na forma original do padrão, é que uma pessoa ainda precisava aplicar a edição proposta antes que o loop medisse o próximo resultado. O ciclo tinha um buraco no meio.

O autoresearch v1.1 fecha esse buraco. O loop passa a chamar um agente de código sem cabeça (headless) em cada iteração, entrega a proposta do critic, e aguarda o agente reescrever o arquivo mutável antes de medir. Este post percorre o que mudou, o que o loop ainda detém, uma run ilustrativa em um pequeno problema do caixeiro viajante (TSP), e os backends de critic plugáveis que vêm no mesmo release. Não é um benchmark de coders; mostra o formato do loop e o que ele produz.

O que muda na v1.1

O contrato do loop é o mesmo de antes. O runner ainda precisa de quatro coisas:

  1. Uma única métrica que o runner extrai do stdout.
  2. Um orçamento de tempo por iteração para que as runs sejam comparáveis.
  3. Um único arquivo mutável que o agente edita, mais arquivos somente-leitura que definem dados, avaliação e constantes.
  4. Um branch git dedicado a uma sessão.

O que a v1.1 adiciona é um flag de configuração, coder.enabled: true. Com esse flag, o loop é dono do ciclo de ponta a ponta. O critic, um LLM exposto via endpoint compatível com OpenAI, lê a melhor versão anterior e retorna um JSON estruturado com thought_process, alternativas, hypothesis, code_pseudocode e risk_level. Em seguida o loop pede ao coder configurado, Claude Code, Codex ou OpenCode, que aplique a proposta no arquivo mutável. O coder escreve; o loop roda o experimento dentro do orçamento de tempo; o resultado é classificado como keep, discard ou crash; o branch git avança ou reseta.

A posse continua com o loop. O agente só edita o arquivo mutável, nada além. Commits, estado do branch, avaliação e a decisão de manter ou descartar pertencem ao loop. Se o agente falha em produzir uma edição válida, o loop registra a falha e volta para o modo passivo, aguardando intervenção humana.

Duas posturas de permissão

A configuração expõe duas posturas para o agente de código. A primeira restringe o agente a edições do arquivo mutável e nada mais; nenhuma escrita em outro caminho do sistema, nenhum comando de shell. É o default e basta para dirigir o loop com raio de explosão pequeno: o pior caso de um erro do agente é um único arquivo que o loop já versiona. A segunda postura permite autonomia mais ampla, incluindo uso de ferramentas arbitrárias e arquivos auxiliares. É útil quando a otimização precisa que o agente inspecione o ambiente, mas só deve ser selecionada em ambientes confiáveis e por configuração explícita.

As duas compartilham o mesmo fallback: se o agente trava, estoura tempo, ou retorna proposta inaplicável, o loop registra a falha e volta para espera passiva. O agente nunca compromete o experimento. O pior caso é uma iteração perdida.

Uma run ilustrativa, TSP de 50 cidades

Para mostrar o loop em movimento, a v1.1 traz um exemplo que roda um pequeno problema do caixeiro viajante. O estado inicial é um tour aleatório de 50 cidades com comprimento 25,25. O arquivo mutável é uma função Python que devolve um tour. A métrica é comprimento do tour.

Em uma run de três iterações com o backend padrão (modelo da família Gemma 3 servido por Ollama local), o critic propôs 2-opt na primeira iteração; o coder aplicou; na iteração seguinte o critic propôs simulated annealing sobre o 2-opt; o coder aplicou; a terceira iteração apertou o schedule. O melhor tour aterrissou em 5,63, zero toques humanos entre o comando de iniciar o loop e o número final.

O exemplo do TSP é ilustrativo, não um benchmark. O mesmo comprimento de tour é alcançável por uma pessoa cuidadosa rodando os mesmos prompts manualmente. O que a run mostra é que o ciclo não tem mais a etapa humana no meio. Se o loop é útil em um dado problema é decidido pelo contrato (métrica única, arquivo mutável, constantes somente-leitura, orçamento de tempo) mais do que pelo LLM no comando.

Backends de critic plugáveis

A outra entrega da v1.1 é desacoplar o critic de um provedor específico. Um campo provider em problem.yaml seleciona o backend sem tocar no código do loop. Cinco endpoints são suportados no release:

  • ollama, para qualquer modelo local que honre respostas em JSON Schema (Gemma 3 e 4, Qwen 2.5, Llama 3, etc.).
  • vllm, para inferência local de alto throughput.
  • llama.cpp, para serving portável em CPU ou híbrido CPU/GPU.
  • openrouter, para roteamento entre múltiplos provedores hospedados atrás de uma única chave de API.
  • openai, para o endpoint canônico de Chat Completions da OpenAI.

Os cinco compartilham uma interface compatível com OpenAI. Credenciais de provedor são resolvidas por variáveis de ambiente. O mesmo loop roda local, na nuvem ou em modo híbrido sem qualquer mudança no código.

Trade-offs para ler com olhos abertos

Fechar o loop tem custos. Com o coder no ciclo, uma iteração custa mais tempo de relógio do que uma chamada pura ao LLM, porque o agente lê, planeja e escreve antes do experimento rodar. O custo de tokens também não é trivial quando o agente é um endpoint pago: uma sessão de 10 iterações pode usar vários milhares de tokens de contexto do agente por iteração, além dos do critic. Orçar de acordo.

Um segundo custo é observabilidade. Com o agente aplicando mudanças, o diff entre iterações é a interpretação do agente sobre a proposta do critic, não o texto do critic diretamente. O loop registra os dois, mas um operador cuidadoso deve ler ambos, especialmente nas primeiras iterações de um problema novo, antes de confiar no loop para rodar a noite inteira.

Como testar

pip install -e git+https://github.com/pcbrom/autoresearch
ollama pull gemma3:12b
cd autoresearch/examples/tsp
autoresearch run --problem problem.yaml

O repositório traz três exemplos: TSP, ajuste de hiperparâmetros de XGBoost e um problema de escalarização multi-métrica. Cada um tem um sample_run/results.tsv que registra o que uma sessão real produziu.

Citação

Brom, P. C. (2026). autoresearch v1.1. Zenodo. DOI 10.5281/zenodo.20544957. Licença MIT.