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

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

Источник: https://war3ai.com/ru/docs/agent-loop/

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

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

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

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

```bash
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 уже даёт всё сырьё — у квитанций есть коды причин, в потоке событий есть завершение производства и потери. Остаётся только это собрать:

```python title="recorder.py"
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` | Деньги копятся и не тратятся = производство не поспевает; мало рабочих = экономика не развилась |

> **Примечание**
>
> Программное определение победы — один из фундаментальных экспериментов [Арены](https://war3ai.com/ru/arena/), он пока в дорожной карте. Сейчас поражение можно определять по «у нас не осталось юнитов», а победу приблизительно — по «все видимые здания противника уничтожены».

## 3. Чёткая цель и несколько ограничений

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

```text
Цель: добиться, чтобы 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/`), и пусть агент прогоняет их после каждой правки.
