Документация ИИ пишет бота

Автономные итерации агента

Пусть Coding Agent сам играет партии, читает результаты, правит код и запускает снова. Для этого ему нужны команда, работающая без присмотра, структурированный отчёт о партии и чёткая цель.

В разделе Пишем бота с помощью LLM шаг «сыграть партию → посмотреть, что происходит → рассказать модели» выполняете вы. Coding Agent, умеющий запускать команды (Claude Code, Codex, режим Agent в Cursor и т. п.), может взять на себя и этот шаг, и цикл замкнётся:

  правка кода ──► партия (без присмотра) ──► чтение отчёта ──► поиск главной проблемы ──┐
    ▲                                                                                  │
    └──────────────────────────────────────────────────────────────────────────────────┘

Чтобы такой цикл действительно сходился, агенту нужны три вещи.

1. Команда, работающая без присмотра

python tools/play.py --bot brains/my_bot.py --speed 200 --minutes 10 --fair
  • --minutes гарантирует, что партия закончится (в минутах реального времени), и агент не застрянет в ней;
  • --speed 200 экономит время за счёт скорости 2× — но в боте ждите по игровым часам (g.clock()), а не через sleep по реальному времени;
  • --fair с первого дня заставляет писать по правилам Арены: видно только то, что в зоне обзора;
  • по окончании запуска терминал печатает причину завершения, например 我方没有单位了 («у нас не осталось юнитов») или 到时间了 («время вышло»); всё, что бот выводит через print, тоже попадает в терминал.

Когда окно свёрнуто, симуляция игры стоит. Пусть агент запускает игру в оконном режиме по умолчанию и не занимает номер экземпляра, которым пользуетесь вы (--inst).

2. Структурированный отчёт о партии

Вывод терминала рассчитан на человека. Агенту нужен JSON: что произошло, что не получилось и почему. SDK уже даёт всё сырьё — у квитанций есть коды причин, в потоке событий есть завершение производства и потери. Остаётся только это собрать:

import collections, json, time
from openwar3 import Bot

class Recorder(Bot):
    """Добавляет Bot отчёт о партии. Унаследуйтесь от него и вызывайте super() в своих on_start / on_event."""

    def on_start(self, g):
        self.rejects = collections.Counter()   # "train hfoo: rejected(人口不够)" -> число раз
        self.timeline = []                     # [игровые секунды, категория, четырёхсимвольный код]: найм / исследование / постройка / улучшение завершены
        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. Чёткая цель и несколько ограничений

Передайте агенту следующий текст, подправив цель под себя:

Цель: добиться, чтобы 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, не выдумывай интерфейсы
- Не отдавай одному и тому же юниту одну и ту же команду каждый тик; приказывай только бездельничающим юнитам
- Сохраняй --fair (только враги, которых видно в зоне обзора)
- Перед правкой кода запусти python tools/run_tests.py и убедись, что примеры не сломаны

Привычки, которые ускоряют сходимость цикла

  • Меняйте что-то одно за раз. Если поменять три вещи сразу, то при победе неизвестно, что сработало, а при поражении — что сломалось.
  • Сравнивайте на достаточном числе партий. В одной и той же ситуации случайность велика; по двум партиям видна только очень большая разница. Чтобы судить о прогрессе, смотрите на тренд хотя бы по нескольким партиям.
  • Сначала чините отказы, потом настраивайте стратегию. Самая частая причина отказа в квитанциях — зачастую и есть главный баг бота.
  • Записывайте выводы в комментарии. Агент в следующем раунде (или в следующем диалоге) поймёт из комментариев, почему код написан именно так, и не откатит уже исправленное.
  • Страхуйтесь офлайн-тестами. Напишите для ключевой логики модульные тесты, которым не нужна запущенная игра (тесты примеров ботов лежат в brains/examples/tests/), и пусть агент прогоняет их после каждой правки.