三層の意思決定支援プロトタイプを作って、壊して、直した
初投稿となります、普段はAntigravityを中心に趣味のツールをつくっています。
今回のプロダクトは最初、単純にゲームAIの仕組みを知りたかっただけでした。
AlphaStar は「StarcraftAI」としてなぜあれほど強かったのか。
Pluribus は「テキサスホールデム」をどうやって攻略したのか。
Cicero は「Diplomacy」でなぜ人間と交渉できたのか。
各ゲームAIモデルの検索・分析
A. AlphaStar — APM制約と「認知資本の最適配分」
AlphaStarの最大の設計的面白さは 意図的な制約の導入 にあります。
アテンション管理の数理:
-
Scatter Connection + Transformer:
O(n²)の空間注意計算を、ユニット位置エンコーディングとクロスアテンションでO(n·k)に削減 - APM制約:人間の平均的操作速度(≈400 APM)を上限として課すことで、「どこに認知資源を集中するか」という選択問題が生まれる
- Pointer Network:膨大なアクション空間(~10²⁶)を「どのユニットに・何を・どこへ」の3段階デコードで圧縮
リーグ学習(League Training)の構造:
Population = {MainAgent} ∪ {MainExploiters} ∪ {LeagueExploiters}
- MainAgent:自己対戦 + 全対戦相手に対する一般化
- MainExploiter:MainAgentの穴を突く(多様性強制)
- LeagueExploiter:過去の全ポリシーの穴を突く(歴史的ロバスト性)
構造的気付き: AlphaStarの本質は 「限られた注意資源をいつ・どこに向けるか」の最適化問題 であり、これは情報過負荷に晒された人間の意思決定者の根本課題と同型。
B. Pluribus — MCCFRと「後悔の貨幣化」
Pluribusは6人ポーカーという情報が漏洩しない多人数不完全情報ゲームを解いた最初のシステム。
MCCFR(Monte Carlo Counterfactual Regret Minimization)の核心:
CFR+_t(I, a) = max(0, CFR+_{t-1}(I, a) + (v(I,a) - v(I)))
Strategy_t(I, a) ∝ Σ max(0, CFR+(I, a))
-
I:情報集合(自分が知っている情報の束) -
v(I,a):行動aを取った場合の反事実的価値 - 後悔(Regret):「あの選択をしておけばよかった」の数値化
- MCによるサブゲームサンプリング:全木展開を避け、現在の情報集合から逆算
情報抽象化(Bucketing): 無限に近いカードの組み合わせを、期待ハンド強度(EHS)とヒストグラム距離でk-meansクラスタリングし、戦略的に等価な「バケツ」に圧縮。
構造的気付き: Pluribusは「最適戦略を求める」のではなく 「後悔を最小化し続ける動的プロセス」 として意思決定を実装した。これは不確実な現実のビジネス課題における 逐次的意思決定最適化 に直接転用できる。
C. Cicero — piKLアルゴリズムと「意図と発話の同期問題」
Ciceroの本質的な技術課題は 「言語的裏切りの防止」 。LLMがボードゲームの文脈から切り離された発言を生成すると、戦略的一貫性が崩壊する(例:「協力する」と言いながら軍を移動させる)。
piKL(Policy-iteration with KL-divergence constraint)の数理構造:
π_dialogue ← argmax E[R_dialogue] s.t. KL(π_dialogue || π_strategy) ≤ ε
-
π_strategy:ゲーム盤面から求めたベイズ均衡ポリシー(R-NaD / Regularized Nash Dynamics で導出) -
π_dialogue:LLMが生成する発言の確率分布 - KL制約:両者の「意図のズレ」をεで上限制御する
構造的気付き: Ciceroは「発話生成」を「戦略ポリシーへの射影問題」として定式化した。これは意思決定支援システムにおける アウトプット整合性保証機構 として汎用転用できる。
Geminiを通じて調べているうちに、気になることが少しずつ変わっていきました。
個別のアルゴリズムのすごさよりも、その奥にある「問い」の方が気になってきました。
- AlphaStarが「何を見るか」 (リアルタイム制約下でのアテンション管理と空間・リソース支配)
- Pluribusが「どう選ぶか」 (不完全情報下における後悔最小化と期待値計算)
- Ciceroが「どう伝えるか」 (自然言語による交渉と、人間らしい意図の制御)
この3つを独立したものでなく、意思決定の三分法としてみると単なる機能分割ではなく、ゲーム理論的に異なる問題クラス(注意配分問題 / 不完全情報決定問題 / 発話整合問題)をそれぞれの専門アルゴリズムで解く問題型特化型アーキテクチャといえます。
最大の設計的面白さは「3つの異なる数学的表現空間の橋渡し(Embedding→離散→自然言語)」にあり、そこへのリソース投資がシステム全体の品質を決定することにあります。
つまりこの三層を、人間向けの意思決定支援システム(AIエージェント)に移せないだろうか。
そう思って組み始めたのが Second Brain — Trichotomy Agent Architecture (三分法アーキテクトによる第二の脳)という研究プロトタイプでした。
これはゲームAIの再現プロジェクトではありません。
AlphaStar や Pluribus や Cicero のアルゴリズムをそのまま写したかったわけではなく、それぞれが解いていた問いを、AIエージェント時代の意思決定支援に翻訳したかった、という方が近いです。
何を作ったのか
全体は三層です。
Input: Signals + CIR v1.1 (nodes / edges / belief_tensor)
+ DomainProfile(三層全体を制御する設定ファイル)
│
▼
┌──────────────────────────────────────────┐
│ Observer Layer (AlphaStar哲学) │
│ rank_signals(budget_k) │
│ → selected_edge_ids + edge_salience │
│ → assemble_observation_ir() [CIR v1.1] │
└──────────────────────────────────────────┘
│ selected_edge_ids / edge_salience
▼
┌──────────────────────────────────────────┐
│ Analyzer Layer (Pluribus哲学) │
│ extract_actions(selected_edge_ids) │
│ → bucket_scenarios() [BucketAssumption] │
│ → minimize_regret() │
│ → assemble_decision_ir() [CIR v2.1] │
└──────────────────────────────────────────┘
│ BucketAssumption / policy_slate
▼
┌──────────────────────────────────────────┐
│ Negotiator Layer (Cicero哲学) │
│ compile_intent() → IntentSchema │
│ → render_language() [ModelProfile] │
└──────────────────────────────────────────┘
│
▼
Output: negotiation_brief / decision_memo / talking_points
(OpenAI / Anthropic / Gemini 対応)
Observer は AlphaStar から発想を借りました。
Analyzer は Pluribus です。
Negotiator は Cicero です。
この分け方にしたのは、LLM に全部まとめてやらせたくなかったからです。
観測と判断と表現が一つの生成ステップに押し込まれると、あとで直すのがかなり大変になります。
どこで何が決まったのかが曖昧になるからです。
なので今回は、少なくとも構造としては
- 観測
- 判断
- 言語化
を分けることを優先しました。
アーキテクチャへの概念転写:
| ゲームAI起源 | 転写後の層名 | 核心機能 | 入力 | 出力 |
|---|---|---|---|---|
| AlphaStar | Observer Layer | コンテキスト圧縮・注意配分・リアルタイム観測 | 生の環境信号(テキスト、数値、時間) | 圧縮された情報セット+注意スコア |
| Pluribus | Analyzer Layer | 期待値計算・後悔最小化・リスク定量化 | 情報セット+不確実性パラメータ | 確率重み付き戦略集合+後悔スコア |
| Cicero | Negotiator Layer | 意図→言語変換・関係者マッピング・KL整合保証 | 最適戦略ポリシー+対象者プロファイル | 整合済み自然言語出力+交渉スクリプト |
Observer でやりたかったのは「全部を見ない」こと
Observer Layer は、AlphaStar の APM 制約から発想を得ています。
人間には限界があります。
全部を見て、全部を処理して、全部に反応することはできません。
AlphaStar の面白さは、単に強いことよりも、その制約の中で「何を見るか」を選んでいたことだと私は思っています。
Second Brain ではこれを、signal のランキングとして実装しました。
各 signal には urgency、relevance、cognitive_cost を持たせて、上位 K 件だけを通します。
その結果を ObservationIR v1.1 に入れます。
いまの ObservationIR には、たとえば次のような情報が入ります。
selected_signal_idsselected_node_idsselected_edge_idsnode_salienceedge_salience
ここで重要だったのは、「選んだ」という事実を本当に下流へ渡すことでした。
これは最初できていませんでした。
Observer は signal を選別しているのに、Analyzer に渡る nodes / edges / belief_tensor は全情報のままでした。
見た目だけ Observer がいる。実際には誰も見落としていない。
この指摘を外部監査で受けたとき、かなり面倒くさい気分になりました。
しかし面倒くさい気分になるときほど、たいていAI相手であったとしてもその考えは正しいです。
修正後は selected_edge_ids と edge_salience を ObservationIR に入れて、Analyzer 側の action extraction が本当に変わるようにしました。
ここを直して、ようやく Observer を置く意味が出ました。
Analyzer は、きれいな理論より先に壊れた
Analyzer Layer は Pluribus から発想を借っています。
belief tensor からシナリオを作り、各行動の期待値やリスクを見ながら regret minimization を回して、policy_slate を出す層です。
ここでは分かりやすい失敗を二回やっています。
ひとつは、内部が stringly-typed だったことです。
scenario の仮定を文字列で持って、それを別の場所で regex で読み直していました。
書いているときから、あまり筋がよくないことは分かっていました。
でも「あとで直せばいい」と思って進めてしまった。
こういうところは、だいたいあとで返ってきます。
修正後は BucketAssumption を入れて、ScenarioBucket.assumptions を唯一のソースにしました。
key_assumptions は表示用にだけ残して、ロジックは構造体だけを見るようにしています。
もうひとつは、scenario 生成が全列挙だったことです。
最初の bucket_scenarios() は、itertools.product で全組み合わせを作ってから上位だけ取っていました。
小さいケースでは普通に動きます。
でも少し大きくすると、すぐに終わります。悪い意味で。
理屈では分かっていたはずなのに、実装に落とすと平気でそういうことをやります。
ここは heapq ベースのインクリメンタル構築に変えました。
全空間を持ってから削るのではなく、上位候補だけを前に進める形です。
この変更は地味ですが、プロトタイプとしてはかなり重要でした。
理論の美しさより先に、壊れないことの方が大事だからです。
Negotiator では、LLM を最後に置いた
三層の中で、いちばんうまくいったのはここだと思っています。
多くの LLM システムは、判断と表現を同じ生成ステップに押し込みます。
それは自然です。LLM は文章を出すのがうまいのでつい全部を任せたくなります。
でも今回は、それをやりたくありませんでした。
Analyzer が DecisionIR を作る。
そのあと compile_intent() で IntentSchema に固定する。
そのあとで render_language() に渡す。
この plan → intent → language の順だけはかなり意識して守りました。
LLM には「何を決めるか」を任せない。
「決まったものをどう言うか」だけを担当させる。
もちろん、これで Cicero を再現したことにはなりません。
piKL も今は prompt と temperature による近似に留まっています。
でも少なくとも、判断と発話を別工程にしたことで、修正のしやすさはかなり上がりました。
ここは、作っていて手応えを感じた部分でした。
文章生成が賢いからではなく、構造として無理をしていない感じがあったからです。
schema は飾りではなく、あとで効く境界だった
このプロトタイプでは、層間通信を CIR で揃えています。
ObservationIR v1.1DecisionIR v2.1
最初はここも甘かったです。
schema はある。でも required が弱い。runtime validation もない。
つまり設計図はあるが、境界としてはまだ弱い状態でした。
その後、
- required フィールドの追加
-
Edge.idの必須化 -
ScenarioBucket.assumptionsの必須化 - strict validation mode の追加
まで入れました。
ここをやってみて分かったのは、schema は「きれいな設計」のためのものというより、監査のあとに何を壊してはいけないかを固定するためのものだということでした。
これは実装しているときにはあまり見えていませんでした。
後から効いてくる部分だったと思います。
4回の外部監査を経て、研究プロトタイプになった
このプロトタイプは、実装してはい終わりではありませんでした。
完成後、Claude Cowork を通じて技術監査を 4 回依頼しています。
最初の評価は 3.5 / 5 でした。
Observer attention が下流へ効いていない。
Analyzer が stringly-typed。
off-tree 判定が偽陽性を起こす。
しかも、どれもコードの表面ではなく、設計思想の中心を突いていました。
そこから修正を重ねて、
- Observer → Analyzer の接続修正
-
BucketAssumptionによる typed 化 - off-tree 判定修正
- strict schema mode
- pytest harness 修正
- bucketing の heap 化
まで入れました。
最終的には 4.5 / 5 で、「研究プロトタイプとして完成している」という評価になりました。
ここで面白かったのは、レビューを受けたというより、設計の言い訳を一つずつ剥がされた感じがあったことです。
自分では「意図どおり」と思っているところほど、外(Claude)から見ると危うい。
それをかなりはっきり学びました。
まだ残っている限界
もちろん、全部片付いたわけではありません。
signal→node mapping は未実装です。
MCCFR も近似です。
piKL も prompt-based な近似です。
schema_version の値自体を強く縛っているわけでもありません。
ただ、今の自分はこれらを「失敗」とは見ていません。
ここから先は、実用化フェーズで本気で解くべき問題だと思っています。
プロトタイプでやるべきことは、全部を終わらせることではなく、
どこが次の論点になるかを明確にすることでした。
今回はそこまではできました。
次に持っていくのは、全部ではなく骨組みの方
このまま同じプロジェクトを大きくするより、次のドメインで試したい気持ちがあります。
今考えているのは game_intelligence です。
ゲーム産業のシグナル監視や、ライブサービス型ゲームの変化検知にこの構造を持ち込めないかを見たい。
League of Legends や Path of Exile、 Dota 2 / CS2 を見ていると、情報の流れ方がかなり独特です。
パッチ、求人、決算、イベント、競技シーン、SNS、開発者発言。
全部がつながっているのに、全部を同じ重さで見るとすぐに埋もれます。
だから Observer がいる。
そのあとで、見えている範囲でどう打つかを Analyzer が決める。
最後に、外へどう伝えるかを Negotiator が整える。
この流れは、次のドメインでも試す価値があると思っています。
たぶん次に持っていくのは、今の細かい実装全部ではありません。
持っていくのは三層構造と CIR です。
逆に、domain-specific な部分はかなり捨てるつもりです。
次のドメインでこれが本当に効くのかは、まだ分かりません。
ただ、何を残して何を捨てるかは、前よりはっきりしました。
ゲーム産業向けのプロファイルも開発の工程で定義しています。
ゲームニュースやパッチ情報のシグナルを観察し、パッチ動向やコミュニティ反応をシナリオとして分析し、意思決定としてのAIエージェントとして機能する。
それを「第二の脳」と呼びたいかどうかは、実際に動かしてみてから判断してみたいとおもいます。