ポーカーボットのプロ向けアーキテクチャ:耐障害性と評価
プロ向けポーカーボットは、大きな戦略関数ではなく、評価可能なサービスです。推測せずに再接続し、古い処理結果を破棄し、依存先の障害時には安全に機能を縮退させ、どのポリシーが各アクションを生成したか証明できます。戦略の上限も重要ですが、無人運用の成否を決めるのは復旧、可観測性、規律あるリリースです。
ポーカーボット・アーキテクチャシリーズ: 全3回の第3回です。ポーカーボットの基本アーキテクチャで信頼できるコアを構築し、ポーカーボットの高度なアーキテクチャでエクイティとリプレイテストを追加してください。ランタイムの予算設計には2026年のポーカーボット費用もご覧ください。
プロ向けポーカーボットと高度なボットを分けるものは?
プロ向けポーカーボットは、すべてのコンポーネントが故障し得るもの、すべての戦略変更が実験であるものとして扱います。高度なボットはエクイティを計算し、対戦相手へ適応できます。プロ向けボットは、その計算中に接続を失っても、正式なテーブル状態を復元し、古くなった結果を拒否し、安全なフォールバックを選び、一連の流れを説明する監査証跡を残せます。
違いは所有権に表れます。1つのスーパーバイザーがセッション状態機械を所有します。ターンコーディネーターが期限とキャンセルを所有します。ポリシーワーカーは計算を担当しますが、ソケットへは書き込めません。ガードが実行可能なアクションを所有します。テレメトリは経路を変えずに観測します。リリースツールが、署名・バージョン管理されたどのポリシーへトラフィックを流すか決めます。
| 関心事 | 高度な実装 | プロ向け実装 |
|---|---|---|
| 再接続 | ソケットを再度開く | 上限付きバックオフ、再同期、スナップショット再構築 |
| 判断タイムアウト | 関数のタイムアウト | ステージごとの予算とキャンセル |
| モデル障害 | 例外を捕捉 | サーキットブレーカー、フォールバック階層、インシデント通知 |
| 戦略テスト | リプレイとA/B結果 | シャドーポリシー、ペア評価、昇格ゲート |
| ログ | 判断JSON | 対応付けられたイベント、トレース、メトリクス、成果物バージョン |
| デプロイ | プロセスを再起動 | ヘルスチェック、カナリア、ロールバック、状態互換性 |
インフラが多ければ自動的にプロになるわけではありません。追加する各システムには、それが防ぐ障害と、機能していることを証明するメトリクスが必要です。
再接続と再同期はどう機能すべき?
再接続では、戦略を再開する前にサーバーの正しい情報から状態を再構築します。最後のtable_idと処理済みの最大table_seqを保持します。テーブルセッションがまだ存在する可能性がある場合は、同じAPIキーでソケットを開いた後、それらの値を付けてresync_requestを送ります。再送されたイベントを順番に適用し、新しいスナップショットから導出テーブル状態を置き換えます。
Open Pokerは切断された座席を120秒間保持します。これは復旧用の時間枠であり、スリープ時間の目標ではありません。固定間隔で多数のクライアントが同時再接続するとサンダリングハードを起こし得るため、上限付き指数バックオフとジッターで素早く再試行します。ボットのライフサイクルガイドには座席の保持時間が記載され、WebSocketプロトコルにはresync_request、resync_response、table_stateが定義されています。
import asyncio
import random
async def reconnect_forever(connect_and_run):
attempt = 0
while True:
try:
await connect_and_run()
attempt = 0
except asyncio.CancelledError:
raise
except Exception as exc:
cap = min(8.0, 0.25 * (2 ** attempt))
delay = random.uniform(0.0, cap)
record_disconnect(type(exc).__name__, delay)
await asyncio.sleep(delay)
attempt = min(attempt + 1, 6)シーケンス処理は冪等でなければなりません。table_seqが最後に適用した番号以下のイベントは無視します。新しい番号が飛んでいたら、推測で欠落を埋めず再同期を要求します。スナップショットが正式な状態で、イベントログは対戦相手モデルに必要な履歴を提供します。
期限予算は古いアクションをどう防ぐ?
期限予算では、1ターンを計測可能なステージへ分け、検証とネットワーク送信の時間を予約します。Open Pokerでは現在、自動チェックまたはフォールドまで120秒ありますが、プロ向けボットが時間枠をすべて使うべきではありません。停止したモデル呼び出しは有用な復旧を妨げ、1時間当たりのハンド数も減らします。
ランタイムに応じて内部サービス目標を設定します。通常のルールやモデル呼び出しなら、2秒の判断予算を、正規化と特徴量に100 ms、ポリシー処理に1,500 ms、検証に100 ms、送信の予備に300 msと分けるのが妥当な一例です。プラットフォームのタイムアウトは外側の安全網として残ります。
import asyncio
from dataclasses import dataclass
@dataclass(frozen=True)
class ActiveTurn:
hand_id: str
turn_token: str
async def decide_with_budget(snapshot, policy, active_turn):
try:
intent = await asyncio.wait_for(policy(snapshot), timeout=1.5)
except (TimeoutError, ValueError, RuntimeError) as exc:
metric("policy_fallback_total", reason=type(exc).__name__)
intent = safe_fallback(snapshot)
if ActiveTurn(snapshot.hand_id, snapshot.turn_token) != active_turn():
metric("stale_decision_total")
return None
return guard(intent, snapshot)キャンセルは伝播させなければなりません。Pythonのasyncio.wait_forは期限を超えた処理をキャンセルしますが、同期CPUコードはイベントループへ制御を返しません。重いシミュレーションはワーカープロセスで実行するか、上限を設定できるネイティブ実装を使います。Pythonのasyncioタスクドキュメントにはキャンセルの挙動が説明されています。高速な単体テスト用フィクスチャだけでなく、意図的に停止するポリシーでもテストしてください。
障害分離とフォールバックはどう機能すべき?
障害分離により、任意機能である知能部分が必須のプレイ機能を停止させるのを防ぎます。リモートモデル、大規模エクイティシミュレーション、対戦相手ストレージ、分析エクスポーターを、タイムアウト付きの狭いインターフェースの背後に置きます。4つすべてが停止しても、セッションループと合法アクションガードは利用可能でなければなりません。
コストと依存度の順に、次のフォールバック階層を使います。
- 主となる学習済みまたはモデル支援ポリシー。
- 短い計算予算で動くローカルのレンジ・エクイティポリシー。
- 決定的なポジション・価格ルール。
- 合法ならチェック、そうでなければフォールド。
階層を1段下がるたびにラベル付きメトリクスを増やし、判断レコードにも残します。サーキットブレーカーは、障害中のリモートサービス呼び出しをしきい値到達後に止め、クールダウンを待ち、限定的なリクエストで再確認します。1ターン内で同じモデルを3回再試行するより、1回でフォールバックするほうが通常は優れています。再試行はレイテンシを積み上げ、料金も増幅しかねません。
ガードはフォールバック階層の後にも置きます。フォールバックにもバグは入り得ます。アクションが合法リストにあることを検証し、レイズ後合計額をサーバーの現在の最小値と最大値に収め、現在のhand_idとトークンを要求し、新しいclient_action_idを生成します。タイムアウトのデバッグガイドでは、一般的な非同期処理とモデルの障害経路を説明しています。
プロ向けポーカーボットに必要な可観測性は?
プロ向けポーカーボットには、判断単位で対応付けられたログ、メトリクス、トレースが必要です。ポーカーの対応付けキーにhand_id、アクション送信のキーにclient_action_idを使います。セッションID、テーブルID、ターントークンのハッシュ、ポリシーバージョン、特徴量スキーマバージョン、モデルバージョン、プロンプトバージョン、レンジバージョン、コードリビジョンも加えます。
秘密の認証情報を記録したり、生のホールカードを広範な外部テレメトリへ送ったりしないでください。保護された判断ログとリプレイフィクスチャにはホールカードが必要ですが、アクセスと保持期間は意図的に決めるべきです。公開ダッシュボード用のログは集約またはマスキングします。
主要なサービスレベル指標は次のとおりです。
| シグナル | 有用な内訳 |
|---|---|
| 判断レイテンシのヒストグラム | ポリシー、ストリート、フォールバック階層 |
| アクション拒否カウンター | サーバー理由とポリシーバージョン |
| 再接続・再同期カウンター | 原因と復旧結果 |
| シーケンス欠落カウンター | テーブルとクライアントリビジョン |
| フォールバックカウンター | 依存先と例外クラス |
| 古い判断カウンター | ポリシーと経過時間 |
| 完了ハンド数 | ポリシーバージョンとセッション |
| bb/100推定 | ポリシー、対戦相手コホート、信頼区間 |
平均レイテンシよりヒストグラムを優先します。40秒かかったモデル呼び出しが1件あっても低い平均値には埋もれますが、古い応答を引き起こす可能性はあります。OpenTelemetryのメトリクス仕様とトレース仕様には、ベンダー中立な概念が示されています。初日から大規模な可観測性基盤は不要ですが、ダッシュボードが発掘作業にならないよう、安定した名前と単位を保ってください。
シャドーポリシーと反実仮想評価はどう機能する?
シャドーポリシーはアクティブポリシーと同じ不変スナップショットを受け取りますが、アクションを送信できません。提案アクション、サイズ、信頼度、レイテンシ、特徴量をライブ判断と並べて記録します。チップやプロトコルの正しさを危険にさらさず、統合と挙動の違いをテストできます。
シャドー結果は勝率の直接的な証拠ではありません。シャドーがフォールドし、アクティブポリシーがコールした場合、その後に観測されるハンドはコールの分岐を進みます。後の結果をシャドーのフォールドが生んだことにはできません。シャドー運用ではアクション一致率、レイテンシ、スキーマエラー、カバレッジを測ります。反実仮想の価値にはシミュレーター、ソルバー比較、オフポリシー手法を使います。
ローカルのゲーム研究には、OpenSpielが不完全情報ゲーム向けのアルゴリズムと環境を提供しています。2019年の論文にはフレームワークの評価目標が説明されています。ライブ変更では、シャドーチェックをリプレイスイートやカナリアコホートと組み合わせます。
有用な昇格レポートには以下を含めます。
- ストリート・ポジション別のアクション一致率
- サイズの大きな相違
- スキーマおよび合法性の失敗率
- p50、p95、p99レイテンシ
- VPIP、PFR、リバーコール率などの挙動メトリクス
- 不確実性付きのペアシミュレーション結果
- 対戦相手コホートとハンド数を含むライブカナリア結果
説明が賢そうに聞こえるという理由ではポリシーを昇格させません。正しさとレイテンシのゲートを正常に保ちながら、意図した方向へ挙動が変わったことを根拠に昇格させます。
ポリシーのリリースとロールバックはどう機能すべき?
ポリシーリリースは不変で、バージョン管理され、元に戻せる必要があります。コードリビジョン、特徴量スキーマ、レンジデータ、プロンプト本文、モデル識別子、評価器バージョン、設定を1つのリリースマニフェストへまとめます。判断ログにはマニフェストIDを保存し、実際にアクションを行った正確なポリシーで任意のハンドを再現できるようにします。
明示的なゲートを持つリリース経路を使います。
- 合法アクションの不変条件を含む単体・プロパティテストが通る。
- 記録済みハンドのリプレイで、レビュー済みの差分を生成する。
- 長時間シミュレーションでメモリやレイテンシの回帰がない。
- シャドーモードでスキーマ、カバレッジ、レイテンシのゲートを通る。
- 小規模なライブカナリアへ新バージョンを配信する。
- 自動ロールバックで拒否、フォールバック、古い判断、クラッシュのしきい値を監視する。
- 十分なハンドが蓄積してから戦略性能をレビューする。
運用上のロールバックは高速で、ポーカーの分散から独立していなければなりません。不正アクションの急増は直ちにロールバックできます。一方、bb/100の低下は短期サンプルのノイズが大きいため、通常は即時判断できません。厳格なヘルスゲートと時間のかかる性能ゲートを分けます。
状態の互換性には特に注意が必要です。新しい対戦相手モデルで保存特徴量の名前が変わるなら、データを移行するか読み取り側をバージョン管理します。昨日の状態を読めないロールバックは、ロールバックではありません。生の観測値を追記専用で保存し、バージョンごとに導出特徴量を再構築する方式を推奨します。ストレージコストは増えますが、戦略変更による履歴の破損を防げます。
自分を欺かずにポーカー性能を測るには?
ポーカー性能は、不確実性、統制された比較、挙動診断を使って測ります。100ハンド当たりのビッグブラインドを、サンプル数と信頼区間とともに報告します。ポリシーバージョン、テーブル人数、ポジション、対戦相手コホートごとに分割します。シーズンでは生のチップ数も重要ですが、バイインやブラインド負担が異なる実行同士の安定した比較にはなりません。
短期サンプルは嘘をつきます。負の期待値のラインを一貫して選びながら、複数のオールインに勝つこともあります。ショーダウンとオールインの診断を追跡しつつ、調整済みメトリクスも絶対的な正解とは考えないでください。対戦相手モデルはドリフトし、マルチウェイポットは推定を複雑にし、ライブポリシーは後で自らが学習するデータを変えます。
3つの証拠を使います。
| 証拠 | 最適な用途 | 主な限界 |
|---|---|---|
| 決定的リプレイ | 回帰検出と説明 | 既知のハンドのみ |
| ペアシミュレーション | 統制されたディールでのポリシー比較 | シミュレーターとの乖離 |
| ライブボットアリーナ | プロトコル、運用、実際の対戦相手構成 | 大きな分散とドリフト |
結果を見る前に実験上の問いを設定します。「タイムアウトによるフォールバックを増やさず、エクイティが価格を下回るリバーコールを30%削減する」は検証可能です。「ボットをもっとGTOにする」は検証できません。ゼロからリーダーボードまでの計画は反復のペース設計に役立ち、スタック管理ではチップ合計だけでは見えないリスク指標を扱います。
本番運用ランブックに必要なものは?
本番運用ランブックには、認証失敗、再接続ループ、再同期の欠落、アクション拒否の急増、モデル停止、判断遅延、状態破損、残高不足、シーズン移行に対する具体的な手順が必要です。各アラートには担当者が取るべき行動、安全なフォールバック、通常運用へ戻す前に必要な証拠を明記します。
たとえばモデル停止では、1回の呼び出し失敗だけで担当者を呼び出すべきではありません。サーキットブレーカーが開き、ローカルポリシーが引き継ぎ、フォールバック率が一定時間しきい値を超えた場合にアラートを発します。アクション拒否の急増は別です。プロトコル不一致を理解するまで、決定的なフォールバックへ切り替えるか、ロビーへの再参加を停止します。
障害注入でランブックをテストしてください。ターン中にネットワークを切断する。重複イベントを返す。ポリシーを予算以上に遅延させる。モデルに不正なJSONを返させる。hand_startとyour_turnの間でプロセスを再起動する。訓練を生き延びていない文書は推測にすぎません。
コストも運用上の制約です。ハンド当たりのモデル呼び出し回数、入力トークン、シミュレーションCPU、ログ保持期間、再接続試行回数に上限を設けます。ポーカーボット費用ガイドには計画モデルがあります。ごく小さなシミュレーション上の優位性を得ても、上限のないリモート呼び出しを必要とする戦略は無人運用の準備ができていません。
FAQ
プロ向けポーカーボットのアーキテクチャとは?
ポーカーポリシーを包む、耐障害性があり、観測可能で、バージョン管理されたサービスです。正式な再同期、期限によるキャンセル、フォールバック階層、アクションガード、シャドー評価、リリースゲート、インシデント対応ランブックが含まれます。
切断後、ポーカーボットはどう復旧すべき?
上限付きジッターで同じIDを使って再接続し、現在のテーブルと最後に処理したシーケンスを付けたresync_requestを送ります。再送イベントを冪等に適用し、正式なスナップショットから再構築してからアクションします。
AIモデルがタイムアウトしたらどうすべき?
リクエストをキャンセルし、タイムアウトを記録し、ローカルのフォールバック階層を下ります。送信前に、アクティブなハンドとターントークンがまだ一致することを確認します。遅れて返ったモデル結果は決して送信しません。
シャドーモードで新しいポリシーが勝つと証明できる?
いいえ。シャドーモードで証明できるのは、ポリシーが動作し、有効な出力を返し、レイテンシ目標を満たし、既知の形で異なることです。性能の証拠には統制されたシミュレーションとライブカナリアを使います。
ポーカーボットを自動ロールバックすべきタイミングは?
アクション拒否の急増、クラッシュ、古い判断の急増、スキーマ障害、過剰なフォールバックなど、深刻な運用障害でロールバックします。ポーカーの分散により短い期間の結果は信頼できないため、戦略成果はより時間をかけてレビューします。
プロの一手は、さらにモデルを追加することではありません。現在のモデルを交換可能、観測可能、障害時にも安全にすることです。Open Pokerクイックスタートから登録し、最初の障害注入訓練を実施して、任意の依存先をすべて失ってもボットがテーブルの制御を失わないか確かめてください。