
Article
Qwen3.8-Flash-NextとJevで、Ultima Onlineの自律エージェントを作ってみた
Ultima Onlineのキャラクターに、**「忍術を60まで上げて」**と自然言語で目標を渡す。あとは現在地やマナ、技能値を読みながら、自分で移動し、練習し、必要なら方針を考え直してもらう。
そんなエージェントを、Qwen3.8-Flash-NextとJev、そしてTazUOへ独自実装したMCPを使って作っている。
今回いちばん大事にしたのは、すべてを一つのAIへ任せないことだ。
Qwenが方針と行動候補を作り、Jevが現在の状況から次の一手を選び、Pythonが条件を再確認して実行する。
実ゲームでは、同じ方針のまま5区間を移動してDojoの掲載位置付近へ到着し、Mirror Imageの詠唱を3回送信した。Journalで詠唱開始を読み取り、忍術スキルが52.7から52.9へ上がったところまで確認できた。後続の保存記録には53.2も残っている。
ただし、目標の60へ到達したわけではない。補給・購入・蘇生まで含めた長時間の完全自律運用も、まだ確認できていない。
この記事では「どこまで動いたか」と「まだ動いていない部分」を分けながら、二つの速度を持つエージェントの構成を紹介する。
この記事は2026年9月19日時点の実装と保存済み検証記録をもとにした。執筆時にゲーム操作、モデル推論、テストを再実行した結果ではない。
一手ごとにQwenを待たない構成
全体像はこうなっている。
| 担当 | 役割 |
|---|---|
| Qwen3.8-Flash-Next | 目標を段階へ分け、行動候補・完了条件・期限を含む方針を作る |
| Jev | 最新の観測と方針を見て、候補、待機、再検討から次の一手を選ぶ |
| Python Runtime | 状態、時間、前提条件、停止、重複送信を確認し、単一のExecutorから操作する |
| 独自実装のTazUO MCP | Ultima Onlineの状態を読み、移動や詠唱などの操作をゲームへ届ける |
| SQLite | 会話、受理した目標、計画、進捗イベントを保存する |
補足: ここでいう「TazUO MCP」は、公開されているTazUO本体の標準機能ではない。私が手元のTazUOへ独自実装したMCP拡張を指している。
MacではTazUOをベースにしたクライアント、そのクライアントへ追加したMCP、Python Agent、Web UI、SQLiteが動く。QwenはGX10からOpenAI互換APIで提供し、JevはCloudflare経由で利用している。
Qwen側は、以前紹介したGX10 1台のQwen3.8-Flash-Next環境だ。記事ではモデル名までを扱い、内部IPや認証情報は載せない。
Qwenは「何をするか」をまとめて考える
Qwenへ渡すのは、ユーザーの目標、現在の観測、地点やゲームの参照資料、直近の結果だけではない。
接続中のMCPからtools/listで取得したツール一覧とJSON Schemaも見せる。利用できる操作と実際の引数を確認したうえで、次のような方針を作らせる。
- 移動、育成、補給、帰還などの段階
- 各段階の完了条件
- MCPのツール名と引数を持つ行動候補
- 実行前提、反復できるか、実行間隔、実行後の確認方法
- 方針そのものの有効期限
「Dojoで忍術を60まで」という目標なら、まず目的地へ移動し、その後に練習へ進む。目的地へ着く前から詠唱を始めないよう、位置とfacetも完了条件に含める。
現在の設定では、計画の有効期限は既定300秒、最大600秒。定期レビューは180秒ごとだ。初回の戦略生成には74.4秒かかった例もあるので、これを毎操作の前に待っていてはゲームを継続して動かしにくい。
そこで、Qwenは長めの時間軸を担当する。
Jevは「いま何をするか」を選ぶ
Jevへ渡すのは、最新の観測、現在の段階、実行可能な行動候補だ。
Cloudflareの公式仕様では、Jevは状態に対して型付きのNoul、Choice、Scoreを評価する構造化モデルとして説明されている。今回の主経路ではchoiceを使い、候補ID、wait、escalateのどれかを選ばせている。
ここで重要なのは、JevにMCPの任意のツール名や引数を毎回作らせていないことだ。Qwenが作った候補の範囲で、現在のマナ、詠唱中か、フォロワー数に空きがあるかといった状態を見て選ぶ。
方針がまだ有効なら、Qwenの定期レビュー中も行動ループを続けられる。状態と方針が合わなくなったときは、escalateでQwenへ再検討を戻す。
保存された検証では、Jevはおおむね0.5〜1秒で応答していた。フォロワー枠が回復した状態を使った読み取り試験では809.26msで再詠唱候補を選んでいる。ただし、この値は単発の確認であり、p95の性能測定ではない。
Pythonが最後の安全確認をする
QwenもJevも、ゲーム操作を直接送らない。
現在のWorld Modelの中心は、Pydanticで定義したObservationだ。HP、マナ、座標、技能値、周囲のオブジェクト、包帯、詠唱状態などをMCPから組み立てる。取得できなかった値はNoneにし、実際の0と混同しない。
Jevが候補を選んでも、Pythonは送信直前にもう一度確認する。
- 人間が停止していないか
- 目標、制御権、接続セッションが変わっていないか
- 観測、判断、方針が古くなっていないか
- 前提条件と実行間隔を満たすか
- ツール名と引数がMCP Schemaに合っているか
- 一回限りの操作や、結果不明の操作を重ねて送ろうとしていないか
操作経路は単一のExecutorへ集めた。停止や目標変更の後からQwenやJevの遅い回答が届いても、そのまま実行しないためだ。
また、MCPの応答がdispatchedでも、それだけでゲーム内成功とは扱わない。移動後の座標、Journal、技能値などを再び読み、計画の進捗へ反映する。
戦略ループと行動ループを分ける
この構成には二つのループがある。
戦略ループ
初回、新しい目標、例外、停滞、期限切れ、定期レビューでQwenを呼ぶ。Qwenは複数段階の方針と候補をまとめて更新する。
行動ループ
観測 → 候補の絞り込み → Jevの選択 → Pythonの再検証 → MCP操作 → 結果の再観測を繰り返す。
旧方針が有効な範囲なら、定期レビューをQwenが考えている間もこちらを続ける。期限切れや緊急の再検討が必要な状態まで、無条件に古い方針で走り続けるわけではない。
この分離によって、長く考える時間と、ゲーム内でテンポよく動く時間を両立させた。
長距離移動は16タイルずつ作り直す
実ゲームで早めにぶつかったのが経路探索だった。
MCPの移動上限は64タイルだが、長距離を一度に指定するとNo path was generated.になる場所があった。読み取り検証では32タイルと64タイルで経路が出ず、16タイル先なら79ノードの経路が生成された。
そこで、現在地から目的地へ向かう16タイルの区間を毎回作り直すことにした。Jevがその区間の実行を選び、移動後に位置を再観測する。
これにより、保存された実ゲーム検証では、Qwenの再計画を挟まずに5区間を移動し、Dojoの掲載位置付近へ到着できた。
万能なナビゲーションが完成したわけではない。掲載座標から実際に歩けるか、入口を通れるかは別の問題だ。それでも、長い経路を一度に解かせるより、現在地から短く刻む方が継続制御に向いていることは確認できた。
実ゲームで確認できたこと
2026年9月19日の保存済み検証記録をまとめると、次のようになる。
| 確認項目 | 保存された結果 |
|---|---|
| Qwenを呼び直さずに移動 | 5区間 |
| Qwenを呼び直さずに詠唱送信 | 3回 |
| Journalで確認した詠唱開始 | 4回 |
| 最初の詠唱送信間隔 | 6.806秒、6.091秒 |
| 忍術スキル | 52.7 → 52.9 |
| 後続の観測 | 53.2、HP 124/124、停止解除 |
詠唱送信間隔は、その場での行動間隔だ。Jev単体の推論時間ではない。
また、途中のJournalにはフォロワー数上限による詠唱拒否も残っていた。過去の失敗だけを見て待ち続けないよう、短期判断では現在のフォロワー数を優先するようにした。実MCPの現在値が0/5まで戻った状態では、Jevが再詠唱候補を選べた。
「過去に失敗した」と「いまも実行できない」を分けることは、ゲームに限らず継続型Agentで重要だと思う。
繰り返してよい操作、いけない操作
詠唱、短い区間移動、技能使用、攻撃、自己包帯には反復条件を持たせられる。
一方、購入、物品移動、Gumpへの返信、銀行引き出しを同じように自動反復すると危ない。現在の実装では、一回限りの操作を識別し、送信結果が不明な操作を別IDでやり直さないようにしている。
銀行MCPについては、別クライアントが行った100 Gold引き出しの結果をAgent側から読み戻し、口座と手持ちの差分が一致するところまで確認した。これはAgent自身が新しく引き出して補給を完遂した実績ではない。
計画を作れたこと、MCPへ送れたこと、ゲーム内で成功したことは、それぞれ別の証拠として扱う必要がある。
まだ「完全自律」とは言わない
今回確認できたのは、限定した時間の中で、同じ方針を使って移動と忍術練習を継続できたことだ。
まだ確認できていないものも多い。
- 忍術60までの育成完了
- 補給、購入、装備をつないだ一連の運用
- 死亡後の蘇生と復帰
- 長時間運用での停止・再開・通信障害からの回復
- 多数試行によるレイテンシや成功率の評価
現在の主な実装はPython、Pydantic、HTTPX、FastAPI、SQLite、ブラウザのHTML/CSS/JavaScriptだ。原案にあったPydanticAIやpydantic-graph、網羅的なMonster DB、戦闘履歴からの危険度学習まで完成したわけではない。
だから今の説明は、「UOを全部任せられるAgentが完成した」ではなく、方針生成と継続判断を分けた構成で、実ゲームの移動と練習が続くところまで動いたになる。
次は補給と復帰をつなぐ
次の課題は、移動と育成だけでなく、消耗品の不足、銀行、購入、装備、死亡後の復帰を一つの流れにすることだ。
ただし、自動化する範囲が広がるほど、AIの自由度を上げるだけでは危なくなる。Qwenには方針を考えてもらい、Jevには候補から選んでもらい、Pythonには止める責任を持たせる。
この役割分担は、Ultima Onlineに限らず、長く考えるAIと素早く反応するAIを組み合わせたいAgentにも使えるはずだ。
今のところ、Qwenは「どこへ向かうか」を考え、Jevは「次の一歩」を選び、Pythonは「本当に踏み出してよいか」を確認する。
その三つを分けたことで、ようやくゲームの時間で動き続けるAgentらしくなってきた。
