Claude oder GPT-4 als Entscheidungsmodell für deinen Pokerbot
Mit ungefähr 80 Zeilen Python lässt sich Claude oder GPT-4 an einen Pokerbot anbinden. Das Sprachmodell liest die Nachricht your_turn, schlägt eine Entscheidung vor und der Bot führt sie aus. In den hier beschriebenen Versuchen lagen die Kosten mit Claude Haiku bei etwa 0,30 US-Dollar pro 100 Händen und die Entscheidungszeiten bei 600 bis 900 ms. Der Bot spielte besser als eine Calling Station, kam aber nicht an eine abgestimmte heuristische Strategie heran. Das macht ihn zu einem schnellen Einstieg in eine eigene Entscheidungslogik.
Teil des vollständigen Leitfadens zum Bau eines KI-Pokerbots 2026: Frameworks, Entscheidungslogik, Equity, Tests und Wettbewerbe. Probiere diese optionale Lektion aus, sobald dein einfacher Bot zuverlässig eine Hand abschließen kann.
Warum ein Sprachmodell für Pokerentscheidungen verwenden?
Dafür sprechen vor allem drei Gründe.
Schnellere Versuche. Bei einer heuristischen Strategie musst du Starthandbereiche, Einsatzgrößen, Positionsanpassungen und Gegnerprofile im Code abstimmen. Bei einem LLM kannst du zunächst den Prompt ändern und den Bot neu starten. Gerade am Anfang kann das die Entwicklungszeit deutlich verkürzen. Die im ursprünglichen Versuch genannte zehnfache Beschleunigung beschreibt eine Erfahrung, keine feste Zusage für jedes Projekt.
Ungewöhnliche Situationen in Sprache beschreiben. Ein Multiway-Pot mit Suited Connectors, zwei Callern und einem gepaarten Board am Turn lässt sich nur schwer mit wenigen Regeln abdecken. Ein Sprachmodell kann solche Situationen anhand gelernter Pokerkonzepte einordnen, auch wenn du dafür noch keinen eigenen Entscheidungszweig geschrieben hast.
Vorwissen als Ausgangspunkt. Moderne Sprachmodelle kennen Begriffe wie Pot Odds und Position. Du musst diese Grundlagen nicht vollständig selbst implementieren, um erste Entscheidungen zu erhalten. Im beschriebenen Beispiel entsprachen die Kosten etwa 0,003 US-Dollar pro Entscheidung. Ob das Modell sinnvoll spielt, musst du trotzdem anhand seiner Aktionen prüfen.
Die Nachteile: Modellaufrufe brauchen Zeit und kosten Geld. Die hier genannten Größenordnungen liegen bei 600 bis 1.500 ms pro Entscheidung und je nach Modell bei 0,30 bis 3 US-Dollar pro 100 Händen. Eine gut abgestimmte heuristische Strategie kann stärker spielen. Betrachte das LLM deshalb als Ausgangspunkt für Versuche.
Was braucht ein einfacher LLM-Bot?
Drei Bausteine: eine WebSocket-Verbindung zu Open Poker, einen API-Client für das Sprachmodell und einen Prompt, der die Angaben aus your_turn als Entscheidungsaufgabe aufbereitet.
Installiere die Abhängigkeiten:
pip install websockets anthropic
Setze zwei Umgebungsvariablen: OPEN_POKER_API_KEY für die Anmeldung am WebSocket und ANTHROPIC_API_KEY für Claude. Das vollständige Beispiel:
import asyncio
import json
import os
import websockets
from anthropic import AsyncAnthropic
API_KEY = os.environ["OPEN_POKER_API_KEY"]
WS_URL = "wss://openpoker.ai/ws"
client = AsyncAnthropic()
PROMPT = """You are playing 6-max No-Limit Hold'em at 10/20 blinds.
Decide what action to take based on the game state below.
Your hole cards: {hole_cards}
Community cards: {community_cards}
Pot size: {pot}
Your stack: {my_stack}
Your current bet: {my_bet}
Position (0=BTN, 1=SB, 2=BB, 3=UTG, etc): {seat}
Valid actions: {valid_actions}
Respond with ONLY a JSON object: {{"action": "fold|check|call|raise|all_in", "amount": <int or 0>}}
For raise, amount is the raise-to total (not increment). For check/call/fold, amount is 0.
"""
async def decide_action(state, hole_cards):
prompt = PROMPT.format(
hole_cards=hole_cards or "unknown",
community_cards=state.get("community_cards", []),
pot=state.get("pot", 0),
my_stack=state.get("my_stack", 0),
my_bet=state.get("my_bet", 0),
seat=state.get("seat", -1),
valid_actions=state.get("valid_actions", []),
)
msg = await client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=100,
messages=[{"role": "user", "content": prompt}],
)
text = msg.content[0].text.strip()
return json.loads(text)
async def play():
headers = {"Authorization": f"Bearer {API_KEY}"}
hole = None
async with websockets.connect(WS_URL, additional_headers=headers) as ws:
await ws.send(json.dumps({"type": "set_auto_rebuy", "enabled": True}))
await ws.send(json.dumps({"type": "join_lobby", "buy_in": 2000}))
async for raw in ws:
msg = json.loads(raw)
t = msg.get("type")
if t == "hole_cards":
hole = msg["cards"]
elif t == "your_turn":
decision = await decide_action(msg, hole)
await ws.send(json.dumps({
"type": "action",
"action": decision["action"],
"amount": decision.get("amount", 0),
"client_action_id": f"a-{msg['turn_token'][:8]}",
"turn_token": msg["turn_token"],
}))
elif t in ("table_closed", "season_ended"):
await ws.send(json.dumps({"type": "join_lobby", "buy_in": 2000}))
asyncio.run(play())Speichere es als llm_bot.py, setze beide Schlüssel und starte python llm_bot.py. Der Bot verbindet sich, meldet sich für einen Tisch an und setzt die Modellentscheidungen um. Beaufsichtige die ersten Versuche und prüfe insbesondere die erlaubten Aktionen und Raise-Beträge.
Welches Modell eignet sich für den Einstieg?
Die Auswahl ist eine Abwägung zwischen Antwortzeit, Kosten und Spielstärke. Im ursprünglichen Vergleich wurden diese Modelle betrachtet:
| Modell | Kosten pro 100 Hände | Mittlere Antwortzeit (Median) | Einordnung im Versuch |
|---|---|---|---|
| Claude Haiku 4.5 | ca. 0,30 US-Dollar | 600 ms | Solides Mittelfeld |
| Claude Sonnet 4.5 | ca. 1,50 US-Dollar | 900 ms | Stärker bei Sonderfällen |
| GPT-4o-mini | ca. 0,40 US-Dollar | 700 ms | Mit Haiku vergleichbar |
Diese Werte sind grobe Schätzungen aus einigen Hundert Händen pro Modell. Die Kosten hängen von der Prompt-Länge und davon ab, wie stark du den Kontext kürzt. Die Antwortzeit hängt auch von der Auslastung des Anbieters ab. Es handelt sich nicht um aktuelle Preiszusagen.
Für den ersten Versuch wurde Claude Haiku 4.5 verwendet: kurze Antwortzeiten, geringe Kosten und im damaligen Test stärker als die Calling-Station-Referenz. Ein Wechsel zu Sonnet lässt sich später anhand gemessener Ergebnisse und zusätzlicher Kosten beurteilen.
Im öffentlichen Spiel von Open Poker gilt eine Aktionsfrist von 45 Sekunden. Auch ein normalerweise schnelles Modell kann bei einem hängenden Aufruf diese Frist aufbrauchen. Begrenze deshalb die Wartezeit und halte eine schnelle, erlaubte Ersatzaktion bereit. Eine Wiederverbindung startet die Frist nicht neu. Die Dokumentation zu Aktionsfristen beschreibt das Serververhalten.
Wie formulierst du einen brauchbaren Prompt?
Der einfache Prompt oben ist ein Anfang. Drei Maßnahmen machen ihn robuster.
Übernimm valid_actions unverändert. Die Liste enthält die genauen Grenzen für Raises und den exakten Call-Betrag. Eine freie sprachliche Zusammenfassung kann diese Angaben verfälschen. Übergebe das JSON direkt. Im ursprünglichen Versuch wurden bei umformulierten Angaben häufig falsche Einsatzgrößen beobachtet; prüfe die Ausgabe unabhängig von der Formulierung immer selbst.
Verlange JSON und validiere es vor dem Senden. Verlasse dich nicht darauf, dass das Modell ausschließlich gültiges JSON zurückgibt. Fange Parserfehler ab und weiche auf eine erlaubte Ersatzaktion aus:
try:
decision = json.loads(text)
action = decision["action"]
if action not in {"fold", "check", "call", "raise", "all_in"}:
decision = {"action": "fold", "amount": 0}
except (json.JSONDecodeError, KeyError):
decision = {"action": "fold", "amount": 0}Schon ein einmaliges „Hier ist meine Entscheidung:“ vor dem JSON kann einen ungeschützten Parser zum Absturz bringen. Die Prüfung muss daher bei jeder Antwort erfolgen.
Gib die jüngsten Aktionen mit. Der Basis-Prompt enthält keinen Verlauf zum Gegnerverhalten. Die letzten fünf bis zehn player_action-Ereignisse der aktuellen Hand können zusätzlichen Kontext liefern. Übergib sie als kurze Liste. Der gesamte Handverlauf erhöht dagegen die Kosten und enthält oft mehr Informationen, als das Modell für die aktuelle Entscheidung braucht.
Wie schnitt ein LLM-Bot in einer Saison ab?
Der ursprüngliche Artikel beschreibt einen Lauf mit Claude Haiku über eine vollständige Saison. Die ungefähren Ergebnisse waren:
- 3.200 gespielte Hände in 14 Tagen
- Endstand: 7.800 Chips, ausgehend von 5.000 Chips
- 24 Prozent gewonnene Hände
- Etwa +1,4 bb/100, also ein kleiner positiver Wert
- 9,60 US-Dollar Modellkosten für die Saison
Der beste Bot dieser Saison endete zum Vergleich bei ungefähr 18.500 Chips. Der LLM-Bot lag im Mittelfeld. Er vermied einige grobe Fehler einer Calling Station, verlor aber gegen Gegner, die seine vorhersehbaren Einsatzgrößen ausnutzten. Ein solcher Lauf ist ein Erfahrungsbericht, kein Nachweis für eine allgemein gewinnende Strategie.
Die größte Schwäche waren die Einsatzgrößen. Das Modell setzte in vielen Situationen ungefähr die Potgröße. Eine Heuristik, die je nach Board zwischen halbem Pot, drei Vierteln des Pots und Overbets wechselte, schnitt in denselben Begegnungen besser ab.
Die größte Stärke zeigte sich in ungewöhnlichen Situationen. In einem Pot mit vier Spielern, gepaartem Board und zwei möglichen Flush Draws traf es brauchbare Entscheidungen, bei denen einfache feste Regeln an Grenzen kamen. Solche seltenen Boardkonstellationen sind in kleinen Rangetabellen kaum abgedeckt.
Lassen sich LLM und Heuristiken kombinieren?
Ja. Für einen Bot mit Sprachmodell ist eine Kombination oft ein sinnvoller Ansatz.
Erledige günstig berechenbare Entscheidungen mit Regeln: Starthandauswahl, offensichtliche Folds und klare Value Bets. Rufe das Modell nur bei Situationen auf, für die deine Regeln noch keine gute Antwort liefern. Dadurch sinkt die Zahl kostenpflichtiger Anfragen.
Ein einfacher Vorfilter kann triviale Fälle erkennen, etwa 72 offsuit gegen einen Preflop-Raise oder eine klare Value-Situation am River. Ob Check, Raise oder Fold erlaubt ist, entscheidet weiterhin valid_actions. Auch eine scheinbar offensichtliche Entscheidung darf diese Grenzen nicht umgehen.
def is_trivial_spot(state, hole_cards):
# Pre-flop trash → fold
if not state.get("community_cards"):
if hole_cards and rank_strength(hole_cards) < 0.15:
return ("fold", 0)
# Free check available → take it
actions = {a["action"]: a for a in state.get("valid_actions", [])}
if "check" in actions and len(actions) == 1:
return ("check", 0)
return None # not trivial, use LLMIm beschriebenen Test verringerte ein solcher Vorfilter die Zahl der Modellaufrufe um etwa 60 Prozent. Die Saisonkosten sanken von 9,60 auf ungefähr 4,20 US-Dollar. Zugleich übernahm das LLM vor allem Situationen, in denen zusätzlicher Kontext tatsächlich half.
Was lief beim ersten LLM-Bot schief?
Die erste Version hatte weder JSON-Validierung noch Ersatzaktionen oder eine Begrenzung der Anfragen. Innerhalb von 200 Händen stürzte sie zweimal ab, weil Claude dem JSON eine Erklärung voranstellte. Einmal lieferte das Modell amount: 60000, obwohl höchstens 1980 erlaubt war. Außerdem verbrauchten wir mehr API-Tokens als erwartet, weil jede Anfrage den gesamten Handverlauf enthielt.
Die Korrekturen waren einfach, aber notwendig: JSON prüfen, Raise-Beträge auf die erlaubten Grenzen begrenzen und nur den benötigten aktuellen Kontext übergeben. Ohne diese Schutzmaßnahmen muss ein Entwickler ständig eingreifen.
Auch die Modellauswahl war anfangs zu pauschal. Wir starteten mit Sonnet nach der Annahme „stärkeres Modell, besseres Spiel“. In diesem Versuch rechtfertigte die zusätzliche Qualität die etwa fünffachen Kosten nicht. Beginne mit einem günstigen Modell und entscheide anhand vergleichbarer Ergebnisse, ob ein Wechsel sinnvoll ist.
Häufige Fragen
Schlägt ein LLM-Bot einen gut abgestimmten heuristischen Bot? Im beschriebenen Vergleich normalerweise nicht. Eine abgestimmte Strategie mit guter Handauswahl, passenden Einsatzgrößen und einfachen Gegnerprofilen war im 6-max stärker. Ein LLM-Bot lässt sich schneller aufbauen und flexibler anpassen, ist aber nicht automatisch die spielstärkste Lösung.
Was kostet eine Saison mit einem LLM? Der Artikel nennt für ungefähr 3.000 Hände in 14 Tagen mit Haiku etwa 5 bis 10 US-Dollar API-Kosten. Ein Vorfilter senkte die Größenordnung auf 2 bis 5 US-Dollar. GPT-4o-mini lag ähnlich; Sonnet und Opus waren im Vergleich deutlich teurer. Die tatsächlichen Kosten hängen vom Modell, dessen Preis und deinem Kontextumfang ab.
Kann das Modell die verdeckten Karten der Gegner sehen?
Nein. your_turn enthält nur die für deinen Bot vorgesehenen Angaben, etwa Pot, Gemeinschaftskarten, Stacks und erlaubte Aktionen. Gegnerische Karten werden erst beim Showdown über hand_result offengelegt. Das Modell erhält keinen besonderen Informationszugang.
Was passiert, wenn der Modellaufruf zu lange dauert?
Die Aktionsfrist im öffentlichen Spiel beträgt 45 Sekunden. Ohne rechtzeitige gültige Aktion übernimmt der Server. Begrenze asynchrone Modellaufrufe mit asyncio.wait_for() auf beispielsweise fünf bis zehn Sekunden und halte eine schnelle, erlaubte Ersatzentscheidung bereit. Der Leitfaden zur Fehlersuche erklärt die Fristen genauer.
Kann ich stattdessen ein lokales Modell wie Llama oder Mistral nutzen? Ja, wenn es auf deiner Hardware läuft. Du musst Antwortzeit, Ressourcenbedarf und Spielqualität selbst vergleichen. Im ursprünglichen Artikel schnitten kleine Modelle mit sieben Milliarden Parametern schwächer ab; große Modelle erforderten mehr Hardware. Eine bezahlte API kann für kleine Versuche günstiger sein als eigene Inferenzinfrastruktur.
Ein LLM ist ein schneller Weg zu einer ersten Entscheidungslogik und eignet sich besonders für Experimente mit ungewöhnlichen Spielsituationen. Registriere einen Bot, richte den Modellzugang ein und prüfe seine Vorschläge in einer beaufsichtigten Sitzung. Entwickle von dort aus anhand deiner eigenen Ergebnisse weiter.