ローカルLLMで『Slay the Spire 2』エージェントを構築:学びと未解決課題

✍️ OpenClawRadar📅 公開日: March 26, 2026🔗 Source
ローカルLLMで『Slay the Spire 2』エージェントを構築:学びと未解決課題
Ad

ある開発者が、KoboldCPP/Ollamaを介したローカルLLMを使用して『Slay the Spire 2』をプレイするエージェントを構築しました。ゲームはコミュニティ製MODを通じてREST APIとして公開されており、エージェントは中間に位置して動作します:ゲーム状態を読み取る → ツール付きでLLMを呼び出す → アクションを実行する → 繰り返す。

セットアップとパフォーマンス

セットアップでは、KoboldCPP経由でRTX 4090上でQwen3.5-27B(Q4_K_M)を使用しています。パフォーマンス指標:アクションあたり約10秒、アクション成功率約88%。最高の結果:Act 1のボスを倒すこと。プロジェクトはGitHubで公開されています:https://github.com/Alex5418/STS2-Agent

機能している点

  • 状態ベースのツールルーティング — 20以上のツールを一度に公開する代わりに、現在のゲーム状態に関連する1〜3つのツールのみを提供します。戦闘ではplay_cardend_turnuse_potionを、マップ画面ではchoose_map_nodeを提供します。これにより、幻覚的なツール呼び出しが劇的に減少しました。
  • シングルツールモード — 小規模モデルは、アクション後のゲーム状態の変化(例:カードをプレイした後のカードインデックスのシフト)を予測できません。そのため、応答ごとに最初のツール呼び出しのみを実行し、その後ゲーム状態を再取得してモデルに再度問い合わせます。遅くなりますが、はるかに信頼性が高まります。
  • テキストベースのツール呼び出しパーサー(フォールバック) — KoboldCPPは、構造化されたJSONではなくテキストとしてツール呼び出しを出力することがよくあります。複数パターンの正規表現フォールバックにより、json [{"name": "play_card", "arguments": {...}}]Made a function call ... to play_card with arguments = {...}play_card({"card_index": 1, "target": "NIBBIT_0"})、およびend_turnのような引数なしツールの単純な言及などの形式を捕捉します。これにより、本来失われるであろうアクションの約15〜20%を回復できます。
  • エネルギーガード — 残りのエネルギーをクライアント側で追跡します。モデルが支払えないカードをプレイしようとすると、API呼び出しがブロックされ、ターンが自動的に終了します。これにより、最も一般的なエアループ(モデルが同じ支払えないカードを3回以上再試行する)を防止します。
  • 敵ターン中のスマートウェイト — 敵のターン中、ゲーム状態は「Play Phase: False」と表示されます。これに対してLLM呼び出しを無駄にする代わりに、エージェントはプレイヤーのターンに戻るまで1秒ごとにポーリングします。
Ad

未解決の問題

  • モデルがシステムプロンプトのルールに一貫して従わない — システムプロンプトには「敵の意図が攻撃の場合は、まず防御カードをプレイする」などの指示があります。モデルがこれに従うのはおそらく30%の時間です。残りの70%では、関係なく攻撃をプレイします。試みられた解決策:より強い表現(「あなたはまずブロックしなければならない」)、プロンプト内の少数ショット例、計算されたヒントの注入(「警告:15の受けるダメージ」)。どれも信頼性がありません。質問:小規模モデルに条件付きルールを遵守させるためのより良いプロンプト戦略はありますか?それともこれは27Bでの根本的な制限ですか?
  • KoboldCPPでのツール呼び出しの信頼性 — テキストフォールバックパーサーを使用しても、応答の約12%は使用可能なツール呼び出しを生成しません。モデルは時々空の<think></think>ブロックに続いて不正なJSONを出力します。OllamaのOpenAI互換レイヤーも、argumentsを辞書ではなく文字列として返すことがあります。質問:14〜30B範囲でツール呼び出しに特に信頼性の高いモデルを見つけた人はいますか?開発者はPhi-4(14B)を簡単に試しましたが、適切な比較は行っていません。Mistral-SmallやCommand-Rを検討中です。
  • コンテキストウィンドウの管理 — 各ゲーム状態はマークダウンとして約800〜1500トークンです。システムプロンプト(約500トークン)と会話履歴を考慮すると、コンテキストはすぐに埋まります。現在は最後の5回のやり取りのみを保持し、状態遷移(戦闘→マップなど)で履歴をリセットしています。しかし、モデルは戦闘間で記憶を持たないため、失敗から学ぶことができません。質問:ローリングサマリーアプローチは機能しますか?例えば、最後の戦闘を「あなたはJaw Wormと戦った。ターン2にブロックしなかったため15のダメージを受けた。4ターンで勝利した」と要約する方法です。
  • ローカルモデルからのより良い構造化出力 — 核心の問題は、モデルにJSONツール呼び出しを出力させる必要があることですが、モデルが本当にやりたいことは最初に自然言語で考えることです。Qwen3.5は<think>ブロックを使用しますが、これは除去されます。ただし、思考とツール呼び出しが絡み合うことがあります。質問:2段階アプローチの方がより効果的でしょうか?ステージ1:「ゲーム状態を分析し、何をするか決定する」(自由テキスト)。ステージ2:「正確に1つのツール呼び出しを出力する」(制約付き)。これにより遅延は倍増しますが、信頼性が向上するかもしれません。このパターンを試した人はいますか?
  • モデル間でのA/Bテスト — 開発者は、比較用にアクションを記録するJSONLロギングシステムを持っています。

📖 完全なソースを読む: r/LocalLLaMA

Ad

👀 See Also

ClaudeとMCPを使用したLinkedInリード選定ワークフローの構築
Use Cases

ClaudeとMCPを使用したLinkedInリード選定ワークフローの構築

ある開発者がClaudeとMCPサーバー統合を活用し、LinkedInプロフィールデータを抽出し、リードを1〜10でスコアリングし、スコア閾値に基づいてフィルタリングし、手動レビューなしで接続リクエストを送信する自動化パイプラインを作成しました。

OpenClawRadar
AIエージェントの失敗管理:リトライ制限と失敗予算
Use Cases

AIエージェントの失敗管理:リトライ制限と失敗予算

6つのAIエージェントを運用するプロダクションチームは、あるエージェントがレート制限タスクを319回再試行した後、3ストライク失敗予算を導入しました。これにより、数時間分のコンピュートリソースが無駄になる事態を防ぎました。また、ハートビートタイムアウト、誤ったタスク完了報告、楽観的ロック競合といった問題にも対応しました。

OpenClawRadar
Claudeを使ってPainSignalを構築:1,000件の実ビジネス課題のデータベース
Use Cases

Claudeを使ってPainSignalを構築:1,000件の実ビジネス課題のデータベース

開発者がClaude Codeを使用してPainSignalを構築しました。このプラットフォームは、トラック輸送や清掃などの業界から1,000件の実際のビジネス課題を整理しています。Claudeはデータ分類、機会クラスタリング、アプリコンセプト生成を担当しました。

OpenClawRadar
非技術系の創業者がClaude Codeで本番マーケットプレイスを構築
Use Cases

非技術系の創業者がClaude Codeで本番マーケットプレイスを構築

非技術系の創業者が、Claude Codeをターミナルで使用して、Next.js、TypeScript、Supabase、Tailwind、Vercelデプロイメントを用いた双方向マーケットプレイスを構築しました。完全な認証、RLS、データベース移行を含みます。

OpenClawRadar