문서 AI로 Bot 만들기

Agent 자율 반복

Coding Agent가 스스로 게임을 돌리고, 결과를 읽고, 코드를 고치고, 다시 돌리게 합니다. 이를 위해 무인 실행 명령 하나, 구조화된 게임 리포트 하나, 명확한 목표 하나가 필요합니다.

LLM으로 Bot 만들기에서는 “한 게임 실행 → 현상 관찰 → 모델에게 전달” 단계를 여러분이 직접 합니다. 명령을 실행할 수 있는 Coding Agent(Claude Code, Codex, Cursor의 Agent 모드 등)는 이 단계까지 넘겨받아 루프를 완성할 수 있습니다:

  코드 수정 ──► 한 게임 실행(무인) ──► 게임 리포트 읽기 ──► 가장 영향이 큰 한 곳 찾기 ──┐
    ▲                                                                                   │
    └───────────────────────────────────────────────────────────────────────────────────┘

이 루프가 실제로 수렴하려면 Agent에게 세 가지가 필요합니다.

1. 무인 실행 명령

python tools/play.py --bot brains/my_bot.py --speed 200 --minutes 10 --fair
  • --minutes는 게임이 반드시 끝나게 합니다(실제 시간 기준 분). Agent가 한 게임에 갇히지 않습니다.
  • --speed 200은 2배속으로 시간을 아낍니다 — 단, Bot 안에서는 게임 시계로 기다리고(g.clock()), 실제 시간으로 sleep하지 마세요.
  • --fair는 처음부터 아레나 규칙에 맞춰 작성하게 합니다. 시야 안의 것만 보입니다.
  • 실행이 끝나면 터미널에 종료 원인이 출력됩니다. 예: 我方没有单位了(아군 유닛 전멸), 到时间了(시간 종료). Bot이 직접 print한 내용도 터미널에 나옵니다.

창이 최소화되어 있으면 게임 시뮬레이션이 멈춥니다. Agent가 기본 창 모드로 게임을 띄우게 하고, 여러분이 쓰고 있는 인스턴스와 번호(--inst)가 겹치지 않게 하세요.

2. 구조화된 게임 리포트

터미널 출력은 사람이 보기 위한 것입니다. Agent에게는 JSON이 적합합니다. 무슨 일이 있었는지, 무엇이 안 됐는지, 왜 안 됐는지. SDK가 재료는 이미 다 줍니다 — 회신에는 원인 코드가, 이벤트 스트림에는 생산 완료와 사상자가 있습니다. 이것들을 모으기만 하면 됩니다:

import collections, json, time
from openwar3 import Bot

class Recorder(Bot):
    """Bot에 게임 리포트를 추가합니다. 이 클래스를 상속하고, 자신의 on_start / on_event에서 super()를 호출하세요."""

    def on_start(self, g):
        self.rejects = collections.Counter()   # "train hfoo: rejected(人口不够)" -> 횟수
        self.timeline = []                     # [게임 초, 종류, 4자 코드]: 훈련 / 연구 / 건설 / 업그레이드 완료
        self.lost = collections.Counter()      # 아군이 잃은 것
        self.killed = collections.Counter()    # 아군이 처치한 것

    def check(self, r, what):
        """명령을 감싸 거부 원인을 기록합니다: 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:                                   # 게임이 이미 종료됐을 수 있음. 읽지 못하면 넘어감
            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)

이 리포트로 답할 수 있는 질문:

신호출처알 수 있는 것
가장 많이 거부된 원인회신 reason / verdict인구가 계속 막힘(3), 돈이 없는데 계속 명령함(8 / 9), 안개 속 대상을 공격함(1001), 영웅이 죽었는데 계속 훈련함(221)
생산 타임라인production.done 이벤트(걸린 게임 초 포함)첫 영웅이 몇 초에 나왔는지, 몇 초에 테크 업을 했는지, 병영이 쉬지 않고 유닛을 뽑았는지. 프로 선수의 초반 빌드와 비교할 수 있습니다
양측 사상자unit.died 이벤트병력을 계속 헛되이 잃고 있는지, 영웅이 몇 번 죽었는지, 사냥이 이득이었는지
종료 원인on_end(g, reason)我方没有单位了(아군 유닛 전멸) = 패배, 到时间了(시간 종료) = 아직 승부가 나지 않음
최종 병력과 자원on_end 시점에 스냅샷을 한 번 읽음돈이 쌓여 있음 = 생산이 못 따라감, 일꾼이 너무 적음 = 경제가 성장하지 못함

프로그램으로 승패를 판정하는 것은 아레나의 기초 실험 중 하나로, 아직 로드맵에 있습니다. 지금은 “아군 유닛 전멸”로 패배를, “보이는 적 건물 전멸”로 근사적인 승리를 판정할 수 있습니다.

3. 명확한 목표와 몇 가지 제약

아래 내용을 목표에 맞게 고쳐서 Agent에게 주세요:

목표: brains/my_bot.py가 Echo Isles에서 "쉬움" 난이도 컴퓨터를 안정적으로 이기게 한다(휴먼 대 무작위 종족).

매 라운드:
1. python tools/play.py --bot brains/my_bot.py --speed 200 --minutes 10 --fair 를 실행한다
2. 터미널 출력과 최신 run_*.json을 읽는다: 종료 원인, 생산 타임라인, 가장 많이 거부된 원인, 양측 사상자
3. 결과에 가장 큰 영향을 준 문제 "하나"를 찾아 그곳만 고친다. 코드 주석에 변경 이유와 근거 데이터를 적는다
4. 1단계로 돌아간다. 3게임 연속으로 개선이 없으면 멈추고, 리포트와 너의 판단을 나에게 알려 준다

제약:
- docs/api.json에 있는 메서드만 사용하고, API를 지어내지 않는다
- 같은 유닛에게 매 틱 같은 명령을 반복하지 않는다. 놀고 있는 유닛에게만 명령한다
- --fair를 유지한다(시야 안에 보이는 적만 사용)
- 코드를 고치기 전에 python tools/run_tests.py를 실행해 예제가 망가지지 않았는지 확인한다

루프를 더 빨리 수렴시키는 습관

  • 한 번에 한 곳만 고치세요. 세 곳을 동시에 고치면 이겼을 때 어느 것이 효과가 있었는지, 졌을 때 어느 것이 망쳤는지 알 수 없습니다.
  • 비교할 때는 게임 수를 충분히 채우세요. 같은 상황이라도 무작위성이 큽니다. 두 게임으로는 아주 큰 차이만 보입니다. “개선됐는가”는 적어도 몇 게임의 추세로 판단하세요.
  • 전략을 조정하기 전에 “거부”부터 고치세요. 회신에서 가장 많이 거부된 원인이 대개 Bot의 가장 큰 버그입니다.
  • 판단을 주석에 남기세요. 다음 라운드의 Agent(또는 다음 대화)가 주석을 보고 왜 이렇게 작성했는지 알 수 있어서, 고쳐 둔 곳을 되돌리지 않습니다.
  • 오프라인 테스트로 안전망을 치세요. 핵심 로직에는 게임을 띄우지 않아도 되는 단위 테스트를 작성하고(예제 Bot의 테스트는 brains/examples/tests/에 있습니다), Agent가 고칠 때마다 먼저 실행하게 하세요.