Itération autonome d'un agent
Laissez un agent de code jouer ses propres parties, lire les résultats, corriger le code et recommencer : il lui faut une commande qui tourne sans surveillance, un rapport de partie structuré et un objectif clair.
Dans Écrire un Bot avec un LLM, l’étape « jouer une partie → observer → le dire au modèle » vous revient. Un agent de code capable d’exécuter des commandes (Claude Code, Codex, le mode Agent de Cursor, etc.) peut aussi prendre cette étape en charge et boucler la boucle :
corriger le code ──► jouer une partie (sans surveillance) ──► lire le rapport ──► trouver le point décisif ──┐
▲ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Pour que cette boucle converge vraiment, l’agent a besoin de trois choses.
1. Une commande qui tourne sans surveillance
python tools/play.py --bot brains/my_bot.py --speed 200 --minutes 10 --fair
--minutesgarantit que la partie se termine (en minutes réelles), l’agent ne restera pas bloqué dans une partie ;--speed 200fait gagner du temps en vitesse ×2 — mais dans le Bot, attendez selon l’horloge du jeu (g.clock()), pas avec unsleepen temps réel ;--fairl’oblige dès le premier jour à respecter les règles de l’Arène : il ne voit que ce qui est dans son champ de vision ;- À la fin de l’exécution, le terminal affiche la raison de la fin, par exemple
我方没有单位了(« plus aucune unité de notre côté ») ou到时间了(« temps écoulé ») ; ce que le Bot affiche lui-même avecprintapparaît aussi dans le terminal.
Quand la fenêtre est réduite, la simulation du jeu est arrêtée. Faites lancer le jeu par l’agent en mode fenêtré par défaut, et évitez qu’il utilise le même numéro d’instance que celle que vous utilisez (--inst).
2. Un rapport de partie structuré
La sortie du terminal est faite pour les humains. Pour l’agent, il faut un JSON : ce qui s’est passé, ce qui a échoué, et pourquoi. Le SDK vous fournit déjà toute la matière première : les reçus portent des codes de motif, le flux d’événements contient les fins de production et les pertes. Il suffit de les rassembler :
import collections, json, time
from openwar3 import Bot
class Recorder(Bot):
"""Ajoute un rapport de partie au Bot. Héritez-en, puis appelez super() dans vos propres on_start / on_event."""
def on_start(self, g):
self.rejects = collections.Counter() # "train hfoo: rejected(人口不够)" (= nourriture insuffisante) -> nombre d'occurrences
self.timeline = [] # [secondes de jeu, catégorie, code à quatre caractères] : entraînement / recherche / construction / amélioration terminés
self.lost = collections.Counter() # ce que nous avons perdu
self.killed = collections.Counter() # ce que nous avons tué
def check(self, r, what):
"""Enveloppe une commande pour noter la raison du rejet : 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: # le jeu est peut-être déjà fermé ; si la lecture échoue, tant pis
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)
Les questions auxquelles ce rapport répond :
| Signal | Source | Ce qu’il révèle |
|---|---|---|
| Raisons de rejet les plus fréquentes | reason / verdict du reçu | Nourriture constamment bloquée (3), ordres donnés sans assez d’argent (8 / 9), attaques sur des cibles dans le brouillard (1001), entraînement d’un héros déjà mort au lieu de le ressusciter (221) |
| Chronologie de production | Événements production.done (avec le nombre de secondes de jeu nécessaires) | À quelle seconde sort le premier héros, à quelle seconde vous passez de tier, si la caserne produit en continu ; comparable aux ouvertures des joueurs pros |
| Pertes des deux camps | Événements unit.died | Si vous offrez des unités en continu, combien de fois le héros est mort, si le creeping a été rentable |
| Raison de la fin | on_end(g, reason) | 我方没有单位了 (plus aucune unité) = défaite ; 到时间了 (temps écoulé) = pas encore de vainqueur |
| Armée et ressources finales | Un instantané lu dans on_end | De l’argent qui dort = la production ne suit pas ; trop peu d’ouvriers = l’économie n’a pas décollé |
La détermination programmatique du vainqueur fait partie des expériences fondatrices de l’Arène et figure encore sur la feuille de route. Pour l’instant, vous pouvez considérer « plus aucune unité de notre côté » comme une défaite, et approcher la victoire par « tous les bâtiments ennemis visibles sont détruits ».
3. Un objectif clair et quelques contraintes
Confiez le texte suivant à l’agent, en l’adaptant à votre objectif :
Objectif : faire en sorte que brains/my_bot.py batte de façon fiable l'ordinateur en difficulté « facile » sur Echo Isles (Humains contre race aléatoire).
À chaque itération :
1. Exécute python tools/play.py --bot brains/my_bot.py --speed 200 --minutes 10 --fair
2. Lis la sortie du terminal et le run_*.json le plus récent : raison de la fin, chronologie de production, raisons de rejet les plus fréquentes, pertes des deux camps
3. Trouve « un seul » problème, celui qui pèse le plus sur le résultat, et ne corrige que celui-là ; note en commentaire dans le code la raison du changement et les données sur lesquelles il s'appuie
4. Reviens à l'étape 1. Après 3 parties d'affilée sans progrès, arrête-toi et communique-moi le rapport et ton analyse
Contraintes :
- Utilise uniquement les méthodes de docs/api.json, n'invente pas d'interface
- Ne redonne pas le même ordre à la même unité à chaque tick ; ne donne des ordres qu'aux unités inactives
- Garde --fair (uniquement les ennemis visibles dans le champ de vision)
- Avant de modifier le code, exécute python tools/run_tests.py pour vérifier que les exemples ne sont pas cassés
Quelques habitudes pour faire converger la boucle plus vite
- Une seule modification à la fois. Si vous changez trois choses en même temps, vous ne saurez ni laquelle a fait gagner, ni laquelle a tout cassé.
- Jouez assez de parties pour comparer. Une même situation comporte beaucoup d’aléa ; deux parties ne révèlent que les très grands écarts. Pour juger d’un progrès, regardez au moins la tendance sur plusieurs parties.
- Corrigez les « rejets » avant d’ajuster la stratégie. La raison de rejet la plus fréquente dans les reçus est souvent le plus gros bug du Bot.
- Écrivez votre raisonnement dans les commentaires. L’agent de l’itération suivante (ou de la conversation suivante) comprendra pourquoi le code est ainsi et ne défera pas ce qui a été corrigé.
- Des tests hors ligne comme filet de sécurité. Écrivez pour la logique critique des tests unitaires qui n’ont pas besoin de lancer le jeu (les tests des Bots d’exemple sont dans
brains/examples/tests/), et faites-les exécuter par l’agent après chaque modification.