1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AI開発のトークンをローカルLLMで節約したい。コーディング担当は諦めて、前処理エージェントにした

1
Last updated at Posted at 2026-09-30

はじめに

UnityやUnreal Engine、Rojo(Roblox)等をMCP経由でAIに操作させることで、誰でも簡単にゲーム開発をすることができるようになってきました。

以前、Unityをほぼ触ったことがない状態から、Unity MCPでレースゲームを作って公開するまでを記録しました。今回は、その続きとして「便利になったぶん増えるAIの作業量を、どう抑えるか」を試してみた話です。

前回: [Unity を触ったことがない素人が、Unity MCP でレースゲームを作って公開するまで]

AI開発ではAIに渡す情報は膨大になります。Consoleのエラー、ビルドログ、diff、関連スクリプトなどを読むため、クラウドAIだとトークンも相当に使い、コストも膨らみます。

そこで考えました。

軽いタスクだけでもローカルLLMに任せられれば、クラウドAIのトークン消費を抑えられるのでは?

手元のPCは RTX 5060 Laptop GPU、VRAMは8GBしかありません。大きなモデルをそのまま動かすのは厳しいため、4B前後の量子化モデルで試してみました。

結論から先に書くと、ローカルLLMにコーディングを任せる案はいったん見送り、ログを整理する担当にしました。 この記事は、そこに至るまでの備忘録です。

まずはコーディング担当としてやらせてみた

最初は、クラウドAIが計画とレビュー、ローカルLLMが小さな修正を担当する形を考えました。

ただ、コーディングは「コードを書ける」だけでは足りません。

  • 指定されたファイルだけを直す
  • JSONなどの返答形式を守る
  • テストが失敗したら、原因に合わせて直す
  • 触ってはいけない場所を触らない

このあたりを毎回守れるかを確認するため、Pythonの小さなTODOアプリを使ってテストしました。課題は追加、完了、JSONでの永続化、名前変更、検索、並び替えの6種類です。公開テストだけでなく、AIに見せない追加テスト、指定外ファイルの変更検出も通ったものだけを成功にしています。

モデル 成功数 試行数
Qwen3.5 4B 9 12
Qwen2.5-Coder 7B 4 12
GLM4 9B 3 12

Qwen3.5 4Bは意外と健闘しました。ただし、これは小さく固定したPython課題での結果です。ゲーム開発などの案件では、コード以外にアセット、シーン、エディタ操作まで関わります。75%で「修正を任せて大丈夫」と言うには、まだ心配が残ります。

Qwen3.8も試してみた

次に、Hugging Faceに登録されていた Qwen3.8 Distill 4BのQ5量子化モデルを試しました。

モデル名について: ここでいうQwen3.8はAlibaba公式の「Qwen3.8」ではありません。ドイツを拠点に活動する独立系のAI研究ラボ EmperoがQwen3.5-4Bアーキテクチャをベースに蒸留したコミュニティモデル empero-ai/Qwen3.8-4B-Distill-GGUF です。モデルカードの説明と来歴を確認した上で使いました。

項目 結果
GPU RTX 5060 Laptop GPU
実VRAM 8,151MiB
モデル常駐時のGPU使用量 3,871MiB
コーディング評価 6 / 12 合格

モデルはVRAMには問題なく収まりました。小さな関数を一回で書ける場面もありました。

ただし、修正を繰り返すとこれが崩れてしまいました。JSONが途中で切れたり、括弧が合わなかったり、指定した制約を忘れたりします。このような失敗もあり、開発で使う実用的なレベルには達せていないことが分かりました。

限界が見えてきたので、方針を変えることにしました。

コーディング担当ではなく、クラウドAIが読む前に情報を整理する担当ならどうだろう?

ローカルLLMには「参照・整理だけ」を任せる

Qwen3.8には、ログ、diff、ファイルの抜粋を渡します。返してもらうのは、短い要約と「どのログを根拠にしたか」だけです。

Qwen3.8には、ファイル編集、コマンド実行、Unity/UE/Rojoの操作をさせません。最終判断はCodexやClaudeに残します。

また、Qwenの返答がおかしい場合や、自信がない場合は、要約を使わずに元のログを全部クラウドAIへ渡します。ここでは、これを fail_open と呼びます。

※セキュリティ分野の fail-open と同じ意味ではありません。この記事では、要約を採用せず、元データの全件確認へ戻すフォールバックを便宜上こう呼びます。

実際にQwenから受け取るのは、例えばこんな形です。

{
  "summary": "tasks.id のSQLite一意制約違反でPOST /tasksが500になっている",
  "source_refs": ["gateway-failure", "database-failure"],
  "candidate_files": [],
  "confidence": 0.92,
  "needs_full_context": false
}

コード側でも、以下なら fail_open にします。

  • JSONやスキーマが壊れている
  • 存在しない根拠IDが含まれる
  • confidence が0.8未満
  • 例外やタイムアウトが起きる

confidence はモデル自身の自己評価です。これだけを信用せず、JSON構造、根拠IDの存在、例外の有無と組み合わせて判断します。

このときは「要約を信じない」で終わりです。元データを捨てず、クラウドAIに全件を渡します。

「ローカルLLMが間違えない」ことを目指すのではなく、間違えても作業を危なくしない形にしました。

この役に向いている仕事には、共通点があります。

  • 副作用がない
  • 結果を捨てられる
  • 元データが残る
  • 上位モデルが再確認できる
  • 失敗時にそのままクラウドへ戻せる

ログの仕分けはこの条件に合います。逆に、コード編集やUnityのシーン変更は元に戻せても影響範囲が広いため、現時点では任せません。

長いログでは効果があった

42件のログから500エラーの原因を探す同じ問題で比べました。これは実案件のログではなく、評価用に作ったデータです。正常な GET /health 200 のログ40件に、POST /tasks の500ログと、それに対応するSQLite一意制約違反ログを1件ずつ混ぜました。

渡し方 クラウドAPIの合計トークン(入力+出力)
ローカルLLMを使わない 2,246
Qwen3.8でログを整理してから送る 264

このケースでは、クラウドAPIで消費した合計トークンを 88.2%減らせました。どちらの方法でも、原因をSQLiteの一意制約違反と判断できました。

トークン数は、同じクラウドAPI呼び出しが返した usage の入力・出力トークンを記録した値です。ローカルQwenのトークンはクラウド利用量には含めていません。

ただし、これは「コストが88.2%安くなる」という意味ではありません。クラウドAIにはキャッシュがあり、入力と出力で価格も違います。短いログでは、Qwenを呼ぶ分だけ余計になることもありました。

なので、今は「ログやdiffが多いときだけQwenを呼ぶ」運用にしています。

grepで十分では? ローカルLLMの優位性はどこにある?

その通りです。ERROR、Exception、Build failed のように定型で拾えるログなら、grepや正規表現で絞るほうが速く、安全で、LLMも不要です。

今回の42ログの実験は、Qwenによる前処理が成立するかを見るためのものです。grepとの同一条件のトークン・精度比較はまだしていません。数字を作らないため、ここでは未計測とします。

ローカルLLMを使いたいのは、次のようなgrepだけでは扱いにくい場面です。

  • 複数ツールのログをまたいで、同じリクエストIDやエラーの流れを結び付ける
  • 自然言語の警告、diff、ファイルの説明が混ざる
  • 「この症状に関係するものだけ」という曖昧な絞り込みをする

まず機械的に絞れるものはgrepで落とし、その後にまだ量が多いときだけQwenへ渡す、という順番がよさそうだったので、この3方式を同じログで比べることにしました。

前処理 速さ・安全性 今後測るもの
なし 基準 クラウド入力と正答
grep / 正規表現 最も速く、挙動が一定 重要行の取りこぼし
Qwen3.8 文脈をまたいだ整理ができる可能性 トークン、待ち時間、根拠の正確さ

現在の担当分け

担当 使うもの
設計、実装、レビュー、テスト判断 Codex / Claude
Unity、UE、Rojoの操作 各ゲームエディタのMCP
長いログやdiffの整理 ローカルQwen3.8 + Ollama

Ollamaは、Qwenが必要になったときだけローカルで起動させます。普段から大きなモデルを常駐させないようにしています。

調べてみると、似た考え方はすでにあった

この記事を書いた後で少し調べると、ローカル小型モデルをクラウドモデルの前段に置く考え方は、すでに研究対象になっていました。Local-Splitterは、ローカルでの振り分け、プロンプト圧縮、意味的キャッシュなど複数の方法を測定しています。結果はワークロード依存で、万能の節約方法はないという結論です。

また、Anthropicもエージェントでは「何をコンテキストに入れるか」を設計する context engineering が重要だと説明しています。Effective context engineering for AI agents

今回の記事には特に新しい要素はありません。個人ノートPC、8GB VRAM、ゲーム開発MCPという条件で試してみたら、「小型モデルにコードを書かせる」より「ログを整理させる」ほうが、失敗時の影響を限定しやすかった、ということが改めてわかったという備忘録です。

これから確認したいこと

ここまでは開発に入る前のテストです。次は実際のUnity/UE/RojoのAI開発で、次のようなものを記録しようと考えています。

  • Qwenを呼んだ回数
  • fail_open になった回数
  • クラウドの入力・キャッシュ入力・出力トークン
  • 作業時間が本当に短くなるか
  • ログの重要な行を見落とさないか

呼び出す量の目安も、実測で決めます。例えば「クラウドに渡す候補が500トークン以下ならそのまま」「2,000トークンを超えたら前処理を検討」といった境界は考えられますが、まだ仮説です。クラウドのキャッシュ、Qwenの待ち時間、取りこぼし率も一緒に見て決めます。

小さなローカルLLMを、いきなり「ゲームを作るAI」にするのは難しそうでした。でも、ログ整理のような小さな役なら、8GBのVRAMでも役に立つかもしれません。

今回の一番大きな学びは、小型モデルを賢くしようとする前に、間違えても困らない仕事を渡すことでした。

実際の開発での結果は、次の記事で追記しようと思います。

検証メモ

  • 実行環境: Windows11、Ollama 0.34.4、RTX 5060 Laptop GPU(VRAM 8,151MiB)
  • 数値はこのPCと小さな評価課題での結果です。他のPCや開発で同じ結果になるとは限りません。
  • Qwen3.8のモデルは hf.co/empero-ai/Qwen3.8-4B-Distill-GGUF:Q5_K_M を使用しました。

追記: 2026年10月24日 — GPU/VRAMをレンタルしたら、コーディング担当の結論は少し変わった

この記事を公開したあと、「8GBのノートPCではなく、必要なときだけGPUを借りたらどうなるだろう」と思いました。

そこで RunPod でGPUを一時的に借り、Ollamaを入れて同じ評価をもう一度やってみました。使ったのは NVIDIA RTX PRO 4500 Blackwell、VRAM 32GB のPodです。

接続は、Ollamaの11434番ポートをインターネットへ公開する形にはしませんでした。Pod側のOllamaは 127.0.0.1 で待ち受けさせ、PCからSSHトンネルで http://127.0.0.1:11434 として使いました。これなら、CodexなどPC上のツールからはいつものOllama APIに見えます。

ポイントは、Podの11434番ポートをインターネットへ公開していないことです。PC側の localhost:11434 へ送ったリクエストだけがSSH接続の中を通ってPodへ届きます。Ollama APIキーを増やしたり、IP制限を追加したりせず、手元で使っていたOllamaの呼び出し先をほぼ変えずに済みました。

実際に使った最小構成

Pod側では、Ollamaを外向きに公開せず、ループバックアドレスだけで待ち受けさせました。OLLAMA_MODELS は、今回使った一時コンテナ内の保存先です。

bash
export OLLAMA_MODELS=/workspace/ollama/models
mkdir -p "$OLLAMA_MODELS"
export OLLAMA_HOST=127.0.0.1:11434
nohup ollama serve > "$HOME/ollama.log" 2>&1 &

ollama pull devstral-small-2:24b
ollama run devstral-small-2:24b 'Reply with exactly: Devstral GPU inference ready'
ollama ps

PC側では、RunPodが表示するSSH接続先を使ってローカルポートを転送します。<POD_SSH_USER> はPodごとにRunPod画面から確認する値です。鍵ファイルや接続先IPは載せていません。

powershell
ssh -i "$HOME\.ssh\id_edxxxxx_runpod" `
  -o IdentitiesOnly=yes `
  -o PasswordAuthentication=no `
  -N `
  -L 11434:127.0.0.1:11434 `
  -o ServerAliveInterval=30 `
  <POD_SSH_USER>@ssh.runpod.io

このPowerShellは開いたままにします。別のPowerShellで、PCの localhost からPod上のOllamaへ届くことを確認できます。

powershell
curl.exe http://127.0.0.1:11434/api/tags

HTTP 200とモデル一覧が返れば、CodexなどPC上のクライアントは http://127.0.0.1:11434 をOllamaの接続先として使えます。RunPodの公開HTTPポートを追加する必要はありません。
また、今回は一回限りの検証にしたかったので、永続ストレージは使いませんでした。50GBのコンテナ内で、Qwenを削除してからDevstralを入れ替える運用です。モデルのダウンロードは毎回必要になりますが、止め忘れたPodのストレージ料金を増やさずに済みます。

今回試したモデル

「27B前後のモデル」と考えていましたが、実際に試したのは次の2つです。

  • Qwen3-Coder 30B: 総パラメータは30Bですが、推論時に有効になるのは3.3BというMoEモデルです。
  • Devstral Small 2 24B: ツール利用、複数ファイル編集、ソフトウェアエージェント向けとして公開されている24Bモデルです。

Ollamaで取得したファイルサイズは、Qwen3-Coder 30Bが約19GB、Devstral Small 2 24Bが約15GBでした。実行中はいずれもGPUへ100%載り、Devstralの常駐表示は約19GBでした。

同じ小さな課題で比べ直した

以前と同じPython TODOアプリで、追加、完了、JSON永続化、名前変更、検索、並び替えの6課題を使いました。seedを2つ固定し、合計12試行です。

成功条件も変えていません。公開テスト、AIに見せない追加テスト、指定外の関数を変更していないことをすべて通り、承認済みのコミットになったものだけを成功にしました。

モデル 実行環境 合格 初回で合格 リトライ 生成速度
Qwen3.8 Distill 4B Q5 RTX 5060 Laptop / 8GB 6 / 12 - - -
Qwen3.8 Distill 4B Q8 RTX 5060 Laptop / 8GB 9 / 12 6 / 12 9 50.70 tok/s
Qwen3-Coder 30B RunPod RTX PRO 4500 / 32GB 9 / 12 9 / 12 6 49.07 tok/s
Devstral Small 2 24B RunPod RTX PRO 4500 / 32GB 11 / 12 11 / 12 2 19.45 tok/s

Devstral Small 2は12試行中11件を一発で通りました。今回の小さな課題では、4BモデルやQwen3-Coder 30Bより安定していました。

ただし、速さは別です。Devstralの生成速度はQwen3-Coderの半分以下で、12試行の総時間も約11分でした。Qwen3-Coderは約7分です。待ち時間を優先するならQwen、テスト可能な小さな実装をより確実に通したいならDevstral、という交換になりました。

唯一失敗したDevstralの試行では、並び替え実装で禁止しているprivate/reflection名を使おうとしました。ここはガードレールがコミット前に変更を止めました。失敗してもゲームプロジェクトのコードには書き込まれず、クラウドAIへ差し戻せます。

評価プログラムは何をしているか

評価プログラムは、モデルに「TODOアプリを作って」と頼んで目視で丸を付けるものではありません。モデル、seed、課題、生成パラメータをJSONで固定し、毎回まっさらな作業フォルダを作ります。

json
{
  "models": ["devstral-small-2:24b"],
  "seeds": [17, 43],
  "tasks": ["add", "complete", "persistence", "rename", "search", "sort"],
  "max_retries_per_task": 2,
  "options": {
    "num_ctx": 8192,
    "temperature": 0.2,
    "top_k": 40,
    "top_p": 0.9
  }
}

実装のあとには、モデルへ見せていない holdout.py を別プロセスで実行します。下は実際に使った採点処理の抜粋です。

python
# benchmark.py(抜粋)
def grade(root, task):
    result = subprocess.run(
        [sys.executable, "-I", "-B", str(root / "grade_entry.py"), str(root), task],
        capture_output=True,
        text=True,
        timeout=30,
        cwd=root,
    )
    return result.returncode == 0
python
# grade_entry.py(実際に隠しテストを呼ぶ部分)
root = Path(sys.argv[1]).resolve()
os.environ["LAB_TEST_WORKSPACE"] = str(root / "workspace")

raise SystemExit(pytest.main([
    str(root / "holdout.py"),
    "-q", "-p", "no:cacheprovider", "--noconftest",
    "--tb=short", "-k", "test_" + sys.argv[2] + "_edges",
]))

ここでいう seed は、文章を生成するときの乱数の出発点です。今回は17と43を固定し、同じ6課題を2回測りました。1回だけ偶然うまくいった結果ではないかを見やすくするためです。完全に同じ出力を保証するものではありませんが、条件をそろえて比べる助けになります。seed 17では通常順、43では逆順に課題を実行しました。順番によるモデルの温まり方や、前の課題の影響も少しでもならすためです。

python
for round_index, seed in enumerate([17, 43]):
    tasks = TASKS if round_index == 0 else list(reversed(TASKS))
    for task in tasks:
        results.append(trial(model, seed, task))
        write_results(results)  # 1試行ごとに途中結果も保存

さらに、モデルに渡すプロンプト、隠しテスト、評価プログラムのSHA-256も保存しました。評価の途中で条件が変わっていないかを後から確認するためです。全部のソースは長いため記事には載せませんが、少なくとも「何を固定し、何で合格としたか」は追えるようにしました。

ここで分かったこと

最初の記事の「8GBで4Bモデルにコーディングを任せるのは厳しい」という結論は変わりません。

一方で、GPU/VRAMを必要な時間だけ借りて、24B級以上のコーディング向けモデルを使うなら、役割を絞った実装担当としては再検討できそうです。

ただし、完全自動にはせず、今のところは次の分担がよさそうです。

担当 使うもの
設計、タスク分割、最終レビュー、権限判断 Codex / Claude
Unity、UE、Rojoの操作 各ゲームエディタのMCP
テスト可能な小さなコード変更 RunPod上のDevstral Small 2
長いログやdiffの整理 手元のQwen3.8、grep、正規表現

大きな実装を丸ごと渡すのではなく、「対象ファイル」「変更してよい関数」「成功条件」「テスト」を先に固定するのが大事でした。Devstralが11/12通ったのも、この狭い条件の評価によるものと思われます。Unityのシーン操作や複数アセットをまたぐ変更まで任せられることを示す数字ではありません。

次は実際のUnity/Rojo開発で、どの程度の大きさまでならこの分担が役立つかを測ります。成功率だけでなく、クラウドトークン、RunPodのGPU利用時間、待ち時間、差し戻し回数も一緒に記録する予定です。

1
1
0

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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?