3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LLMにローグライクを遊ばせて分かった、測り方の落とし穴

3
Last updated at Posted at 2026-10-24

はじめに
ローカルの LLM に、ターン制のローグライク Dungeon Crawl Stone Soup(DCSS)を遊ばせていました。
目標は「生きたまま、できるだけ深い階に着く」。プロンプトを直し、道具を足し、比べる。これを何週間も繰り返しました。
振り返ると、直したことの多くは効いていませんでした。正確に言うと、効いたかどうかを測れていませんでした。
この記事は、その間に引っかかった「測り方の落とし穴」の記録です。LLM エージェントを改良していて、「良くなった気がする」で止まっている人の参考になればと思います。
構成

┌───────────────────────────┐
│ 指揮役(LLM, Ollama)        │  qwen3.6(35B-A3B)、1 判断 ≒ 1.2 秒
│  今できる手の一覧から 1 つ選ぶ   │
└─────────────┬─────────────┘
              │ 状態 / 手の一覧 / 結果
┌─────────────┴─────────────┐
│ 体(Python)                 │
│  ・DCSS を端末で動かし、キーを送る │
│  ・Lua の rc ファイルで状態を取る  │
└───────────────────────────┘

DCSS は Lua で中の状態を読めます(周りの敵、持ち物、地図など)。体はそれを整え、「今できる手」を説明つきで並べます。LLM はその中から 1 つを JSON で選びます。
体は、実行した結果を progressed(進んだ)/ no_op(空振り)などで返します。
比べる時は、ダンジョンの生成を固定する「種(seed)」を揃え、8〜24 種で回しました。
落とし穴 1: ルールを書き直すと、判断は変わるが結果は変わらない
プロンプトには、私が手で書いた戦い方のルールがたくさん入っていました。それを書き直すと、判断の 92% が変わりました。結果(死亡や深さ)は何も変わりませんでした。ルールを丸ごと消すと、むしろ悪くなりました。
一方で、ゲームは多くのことを自分で言ってくれます。
「It was a potion of curing.」(飲んだ薬の正体)
敵の体力の目安、耐性、武器の強さ
アイテムが今の自分に役立たないかどうか(種族・職業・呪いを考慮済み)
記録を見ると、こうした文が 48,278 回分の判断にわたって捨てられていました。
手で書いたルールは、間違いと「使われない選択肢」の出どころでもあります。そこで、ルールを書く前に、ゲームがもう言っていないかを確かめることにしました。
一つだけ例外を置きました。ジャンルの常識です。エージェントは店で一度も買い物をせず(訪問 9 回、購入 0 回)、回復薬を持ったまま戦いで一度も飲みませんでした。「薬は回復する」「店は金で物を売る」といった、どの RPG でも同じ知識が抜けていたのです。そこで、10 行ほどの RPG の基本だけはプロンプトに入れました。DCSS 固有の事実は、引き続きゲームの文から取ります。
落とし穴 2: 少ない事例から作った教訓は害になる
死んだ回を LLM に振り返らせて「教訓」を書かせ、次の回のプロンプトに入れる仕組みがありました。よくある作りだと思います。
数えてみると、ひどいことになっていました。
教訓は、同じ話題で死が 2 回 あればプロンプトに入る作りだった
一方、手元の記録には同じ敵との戦いが数千回あった
敵 記録の戦い 致死率
ribbon worm 3,363 1.0%
adder 3,419 0.5%
kobold 5,114 0.1%
それでも「ribbon worm からはすぐ逃げろ」という教訓が 491 回 発動していました。
振り返りは死んだ回しか読まないので、どの敵も「逃げるべき相手」に見えるのです。
書かれた教訓は 348 個、そのうち 342 個が後で取り下げられ、80 の話題のうち 53 は取り下げ後にまた書き直されていました。発動は 16,941 回、「役に立った」と判定されたのは 79 回です。
教訓をプロンプトに入れるのはやめました。代わりに、最低件数(8 件)を決めて数えた数字だけを渡します。「この状況で戦い続けた場合の致死率は○%(n=○)」のような形です。振り返りの記録は残し、仮説の種として使います。
落とし穴 3: 1 回きりの基準は当てにならない
ある時期、変更を入れるたびに 12 種で回し、基準の回(3,000 ターン)と比べていました。結果は 5 回続けて「230〜350 ターン長く生きた、向きも揃っている」。
ところが、同じ種・同じコードで 6 回回すと、生き残ったターン数はこう散りました。
種 6 回の範囲
58 861〜3050
61 521〜3007
51 1012〜3053
基準の回は、種ごとに 1 回しか 回していませんでした。良くなったように見えた差の大半は、基準の回がたまたま早死にしていた 3 つの種から来ていました。逆に、基準がたまたま長生きした種では、後の回の方が短かった。典型的な平均への回帰です。
「向きが揃っている」のも当然で、5 回とも同じ 1 回きりの基準と比べていたからでした。
種を揃えれば、種ごとの差は消せます。ですが、LLM の回ごとのばらつきは消えず、こちらの方が大きかったのです。
教訓:
基準の側も何回か回し、両方を平均して比べる。基準の種を増やすだけでは弱点が残る
「良くなった」と言う前に、種ごとの表を出し、差が基準の極端な種から来ていないかを見る
何で測るか
ついでに、指標の選び方も書いておきます。24 種で比べた時の解像度はこうでした。
指標 差を見るのに要る種の数
生き残ったターン数 100 ターンの差に 123 種
着いた深さ 半階の差に 55 種(8 種では 1 階未満の差は見えない)
死亡率 20 ポイントの差に 25 種
死亡率は 70〜95% に張り付き、深さはどの設定でも 3 階前後で平らでした。この規模で差が見えたのは、生き残ったターン数だけです。それも落とし穴 3 のとおり、1 回きりの比べでは当てになりませんでした。
落とし穴 4: 結果ではなく、仕組みが動いているかを見る
結局、生存ターンや深さで比べても、何も言い切れませんでした。
一方で、本物の不具合はすべて、仕組みを直接確かめた時に見つかっています。
全アイテムが、ずっと「未鑑定」だった
DCSS の Lua では、アイテムの項目に「値」と「関数」の 2 種類があります。it.name のように関数として呼ぶものと、it.is_identified のように値として読むもの。
体の rc ファイルは、値の方を it:is_identified() と関数として呼んでいました。これはエラーになりますが、エラーを握りつぶす包みで囲んでいたので、毎回 false が返ります。
その結果、プロジェクトの最初から、すべてのアイテムが「未鑑定・役に立つ・呪われていない」扱いでした。回復薬を飲む手は、薬を持っていた 4,697 回の判断で一度も出ていませんでした。
「一度も true にならないフラグ」は、「一度もそういう物を持たなかった」ようにしか見えません。
店に行く手が、11 回とも空振りしていた
「店に行く」手は 83 回の録画で 751 回出て、11 回選ばれ、11 回とも no_op でした。
原因は、体の作りの食い違いでした。体が自分で足した手は、元の状態(rc が書いたもの)には載っていません。それなのに、実行する時は元の状態からその手を探していたので、見つからず、キーを 1 つも送らずに終わっていました。
私は最初、キーの送り方を疑いました。手でキーを送ってみると、ちゃんと動きました。
黙って止まる
ゲームが Yes/No を聞いてきて、どのキーも受け付けなくなることがありました。記録の上では判断が続いているのに、ターンの数だけが止まっています。ある種は、522 ターン目で 199 回判断し続けていました。
確かめる順番
それからは、変更ごとにこう問うことにしました。
その手は 出ているか
選ばれているか
体は 実行しているか(キーを送ったか)
世界は進んだか(ターンや位置が変わったか)
それが入る記録や数字は 実際に変わったか
答えは記録から数えるか、ゲームのウィザードモードで実際に試して出します。一度も true にならないフラグ、一度も選ばれない手、progressed 以外で終わり続ける手。それが見つけるべき物です。
おまけ: 死に方を数えると、作戦が変わる
「どう死んでいるか」を数えたら、直す場所が変わった例です。
致命的だった 93 回を並べると、真ん中の典型はこうでした。体力 67% → 12% まで、5 回の判断のあいだ、ずっと「攻撃」を選び続けている。とどめは 3〜6 ダメージの、ふつうの敵のふつうの攻撃です。
危ない敵に負けていたのではなく、抜け出せない戦いに負けていたのです。それまでの「1 マス下がる」退却は、1,981 回試して一度も接触を切れていませんでした。1 マス下がると、相手も 1 マス付いてくるからです。
逃げた時の結果も数えました。
状況 戦い続ける 歩いて離れる
無傷、まだ接触していない 15% 17%
無傷、もう接触している 11% 28%
怪我、もう接触している 23% 58%
(数字はその戦いで死んだ割合)
逃げるなら、接触する前に決めないと逃げられない。接触していなければ戦いの致死率は 0.5%、接触すると 4.5% です。
逃げ道を増やした版(逃げる手が出る場面が 23% → 46%)は、かえって死亡が増えました(24 種中 14 → 23 種中 22)。効くのは逃げ道の数ではなく、接触する前に何をするか、そもそもその戦いに入るかどうかでした。
まとめ
ルールより、ゲームが言っている事実を渡す。 手で書いたルールは判断を変えても、結果を変えなかった
少ない事例から教訓を作らない。 最低件数を決めて数え、数字で渡す
基準も繰り返す。 1 回きりの基準との差は、平均への回帰かもしれない
結果より仕組み。 出ているか、選ばれるか、実行されるか、世界が進むか。本物の不具合はそこにしかなかった
この後、配信映えを考えて Minecraft に移りました。そちらの話は別の記事に書きました。
ローカルLLMにMinecraftを遊ばせる ― 指揮役をQwen3.6からNemotron 3.5 Lightningに替えて何が変わったか(リンクは公開後に追記)

3
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?