Docs IA escrevendo Bots

Iteração autônoma do agente

Deixe um agente de programação jogar partidas, ler os resultados, mudar o código e jogar de novo sozinho. Ele precisa de um comando que rode sem supervisão, de um relatório de partida estruturado e de um objetivo claro.

Em Escreva um Bot com um LLM, o passo “jogar uma partida → observar → contar ao modelo” fica por sua conta. Um agente de programação que executa comandos (Claude Code, Codex, o modo agente do Cursor etc.) pode assumir esse passo também e fechar o ciclo:

  muda o código ──► joga uma partida (sem supervisão) ──► lê o relatório ──► acha o ponto que mais pesa ──┐
    ▲                                                                                                     │
    └─────────────────────────────────────────────────────────────────────────────────────────────────────┘

Para esse ciclo convergir de verdade, o agente precisa de três coisas.

1. Um comando que rode sem supervisão

python tools/play.py --bot brains/my_bot.py --speed 200 --minutes 10 --fair
  • --minutes garante que a partida termina (em minutos de relógio real), então o agente nunca fica preso numa partida;
  • --speed 200 usa velocidade 2× para ganhar tempo — mas dentro do Bot, espere pelo relógio do jogo (g.clock()), não com um sleep de relógio real;
  • --fair faz o Bot seguir as regras da Arena desde o primeiro dia: ele só vê o que está no campo de visão;
  • Quando a execução termina, o terminal imprime o motivo do fim, por exemplo 我方没有单位了 (“não temos mais unidades”) ou 到时间了 (“acabou o tempo”); o que o próprio Bot imprime com print também aparece no terminal.

A simulação do jogo para enquanto a janela está minimizada. Faça o agente iniciar o jogo no modo janela padrão e garanta que ele não use o mesmo número de instância que você está usando (--inst).

2. Um relatório de partida estruturado

A saída do terminal é feita para humanos. O que o agente deve ler é um JSON: o que aconteceu, o que não deu certo e por quê. O SDK já entrega toda a matéria-prima — os recibos trazem códigos de motivo, e o fluxo de eventos traz produções concluídas e baixas. Basta juntar tudo:

import collections, json, time
from openwar3 import Bot

class Recorder(Bot):
    """Adiciona um relatório de partida a um Bot. Herde dele e chame super() nos seus próprios on_start / on_event."""

    def on_start(self, g):
        self.rejects = collections.Counter()   # "train hfoo: rejected(人口不够)" -> contagem  (= falta comida)
        self.timeline = []                     # [segundos de jogo, categoria, código de quatro caracteres]: treino / pesquisa / construção / melhoria concluídos
        self.lost = collections.Counter()      # o que nós perdemos
        self.killed = collections.Counter()    # o que nós matamos

    def check(self, r, what):
        """Envolve um comando para registrar motivos de rejeição: self.check(g.train(b, "hfoo"), "train hfoo")"""
        if r is not None and not r:
            self.rejects[f"{what}: {r.reason}"] += 1
        return r

    def on_event(self, g, ev):
        me = g.me()
        if ev.kind == "production.done" and ev.owner == me:
            self.timeline.append([round(ev.clock), ev.done_kind, ev.done_code])
        elif ev.kind == "unit.died":
            (self.lost if ev.owner == me else self.killed)[ev.type] += 1

    def on_end(self, g, reason):
        report = {"reason": reason, "timeline": self.timeline, "lost": self.lost,
                  "killed": self.killed, "rejects": self.rejects.most_common(10)}
        try:                                   # o jogo pode já ter fechado; se não der para ler, deixa para lá
            report |= {"clock": g.clock(), "resources": g.resources(),
                       "army": len(g.my_army()), "workers": len(g.my_workers())}
        except Exception:
            pass
        with open(f"run_{int(time.time())}.json", "w", encoding="utf-8") as f:
            json.dump(report, f, ensure_ascii=False, indent=1)

Perguntas que esse relatório responde:

SinalDe onde vemO que revela
Motivos de rejeição mais frequentesreason / verdict do reciboTravado por comida o tempo todo (3), dando ordens sem ter dinheiro (8 / 9), atacando alvos na névoa de guerra (1001), treinando um herói que já morreu (221)
Linha do tempo de produçãoEventos production.done (com os segundos de jogo gastos)Em que segundo saiu o primeiro herói, em que segundo você subiu de tier, se o Quartel ficou produzindo sem parar; compare com as aberturas de jogadores profissionais
Baixas dos dois ladosEventos unit.diedSe você está entregando unidades o tempo todo, quantas vezes o herói morreu, se o creeping compensou
Motivo do fimon_end(g, reason)我方没有单位了 (“não temos mais unidades”) = derrota; 到时间了 (“acabou o tempo”) = ainda sem vencedor
Exército e recursos finaisUm snapshot lido no on_endOuro acumulado sem gastar = a produção não acompanha; poucos trabalhadores = a economia não decolou

Detectar vitória e derrota por programa é um dos experimentos de fundação da Arena e ainda está no roadmap. Por enquanto, você pode tratar 我方没有单位了 (“não temos mais unidades”) como derrota e aproximar a vitória como “todas as construções inimigas visíveis foram destruídas”.

3. Um objetivo claro e algumas restrições

Entregue o texto abaixo ao agente, ajustado ao seu objetivo:

Objetivo: fazer brains/my_bot.py vencer com consistência o computador no nível "Fácil" em Echo Isles (Humanos contra raça aleatória).

A cada rodada:
1. Rode python tools/play.py --bot brains/my_bot.py --speed 200 --minutes 10 --fair
2. Leia a saída do terminal e o run_*.json mais recente: motivo do fim, linha do tempo de produção, motivos de rejeição mais frequentes, baixas dos dois lados
3. Encontre O problema que mais afeta o resultado e mude só esse ponto; escreva num comentário do código o motivo da mudança e os dados em que ela se baseia
4. Volte ao passo 1. Se não houver melhora por 3 partidas seguidas, pare e me mostre o relatório e a sua avaliação

Restrições:
- Use apenas métodos de docs/api.json; não invente APIs
- Não repita o mesmo comando para a mesma unidade a cada tick; só dê ordens a unidades ociosas
- Mantenha --fair (use só inimigos visíveis no campo de visão)
- Antes de mudar o código, rode python tools/run_tests.py para confirmar que os exemplos não quebraram

Hábitos que fazem o ciclo convergir mais rápido

  • Mude uma coisa por vez. Se você muda três coisas ao mesmo tempo e ganha, não sabe qual ajudou; se perde, não sabe qual estragou.
  • Jogue partidas suficientes para comparar. O mesmo confronto tem muita aleatoriedade; duas partidas só revelam diferenças muito grandes. Avalie “se melhorou” pela tendência de, no mínimo, várias partidas.
  • Corrija as rejeições antes de ajustar a estratégia. O motivo de rejeição mais frequente nos recibos costuma ser o maior bug do Bot.
  • Escreva seu raciocínio nos comentários. O agente da próxima rodada (ou a próxima conversa) consegue ler nos comentários por que o código está assim e não desfaz as correções.
  • Testes offline como rede de segurança. Escreva testes unitários para a lógica principal que não precisem do jogo aberto (os testes dos Bots de exemplo ficam em brains/examples/tests/) e faça o agente rodá-los depois de cada mudança.