Kaggle の無料 GPU で 30B モデルを動かし、手元のコーディングエージェントにつなぐ
まとめ
- Kaggle の T4 2 枚で qwen3-coder:30b(Q4_K_M)が 22.6GB で載り、44.5 tok/s 出た。cloudflared の一時 URL は Host ヘッダで振り分けられるため、Host を書き換えるプロキシを挟んで localhost に固定した
- つまずいたのは転送ではなく、モデルに渡る手前だった。opencode の system prompt が 137,727 文字になり、48K context を超えて切り捨てられ、ツール呼び出しが本文に漏れていた。専用の HOME で起動すると 8,637 文字に収まり、ファイル編集まで通った
- GPU の確保は
kaggle kernels pushのバッチだと 45 分待っても来なかった。ブラウザの対話セッションなら数分で来た
構成
手元の RTX 4080 SUPER は 16GB で、Q4_K_M の 30B は 18.6GB ある。
量子化をさらに落とす手もあるが、まずは無料枠の GPU にモデルだけ置く形を試した。 Google Colab の GPU で ComfyUI を動かしたときと同じで、手元にはエージェントとリポジトリを残す。
表1: 何をどこに置くか
| 場所 | 持つもの |
|---|---|
| 手元 | opencode、作業対象のリポジトリ、Host を書き換えるプロキシ |
| Kaggle | ollama、モデル、cloudflared |
| ntfy | 一時 URL の受け渡し |
Kaggle に推論サーバを立てる
notebook は kaggle kernels push で非対話に流せるが、GPU の確保で待たされた。
NvidiaL4 を指定すると GPU の無いマシンに割り当てられ、/dev/nvidia* が存在せず CPU で動いた。size_vram は 0 だった。 NvidiaTeslaT4 を指定したバッチは 45 分経っても QUEUED のままだった。 ブラウザで notebook を開き、Session options で GPU T4 x2 と Internet を On にして Run All すると、数分で確保できた。
インストールで 1 か所止まった。 ollama の install.sh は zstd を要求するが、Kaggle のイメージには入っていない。
apt-get update -qq && apt-get install -y zstd
curl -fsSL https://ollama.com/install.sh | shモデルの取得は 18.6GB で 201 秒だった。 セッションをまたぐキャッシュは無いので、毎回この時間がかかる。
手元から届かせる
Kaggle の notebook に外から入る口は無いので、cloudflared で内側から外へ出る。
cloudflared tunnel --url http://127.0.0.1:11434 --no-autoupdateURL の受け渡しで 1 つ、接続で 1 つ、それぞれ引っかかった。
実行中の kernel は kaggle kernels logs が空を返す。ログが読めるのは完了後だけなので、トンネルの URL をログから拾えない。 notebook から ntfy に URL を投げ、手元で拾う形にした。
cloudflared は Host ヘッダで振り分ける。 socat で 127.0.0.1:11435 に転送すると Host が 127.0.0.1 のままになり、Cloudflare が 403 を返す。 Host を書き換えるプロキシを挟み、手元からは常に http://127.0.0.1:11435/v1 を叩けばよい形にした。
ツール呼び出しが壊れた原因
最初はモデルとの相性を疑った。
ライブラリ版の qwen3-coder:30b は /api/show のテンプレートが `` だけで、ツール呼び出しが <function=read> という生テキストで本文に漏れた。 Hugging Face の GGUF に差し替えるとテンプレートは 1,482 文字になり、単発の呼び出しは finish_reason: tool_calls で返るようになった。
それでも opencode 経由だと 2 手目で崩れた。 プロキシに要求の骨格を記録させて気づいたのが、system prompt の長さだった。 opencode はスキル一覧と指示ファイルを読み込むため、system prompt が 137,727 文字になっていた。 48K context に対して大きすぎ、切り捨てられた状態でモデルに渡っていた。
専用の HOME で起動すると system prompt は 8,637 文字になった。 同じ依頼で、return a - b を return a + b に、a + b を a * b に直すところまで通った。
表2: 詰まった箇所
| 症状 | 原因 | 対処 |
|---|---|---|
ツール呼び出しが <function=read> として本文に出る | ライブラリ版のテンプレートが `` のみ | Hugging Face の GGUF に差し替える |
| 2 手目で JSON が本文に漏れる | system prompt 137,727 文字が 48K context を超える | 専用 HOME で 8,637 文字に減らす |
| プロキシ経由で 403 | Cloudflare が Host ヘッダで振り分ける | Host を書き換えるプロキシを挟む |
| 実行中の URL が取れない | kernels logs が完了後しか読めない | ntfy に publish する |
運用上の制約
表3: 実測値
| 項目 | 値 |
|---|---|
| 生成速度 | 44.5 tok/s |
| VRAM 使用 | 24.5 / 30 GB(context 49,152) |
| モデル取得 | 18.6GB、201 秒 |
| セッション | 最長 9 時間、タブを閉じると停止 |
| GPU 枠 | 30 時間/週 |
30B なので、複雑な指示は取りこぼす。まずは小さな修正から試している。
無料枠でサーバを立て続ける使い方が規約上どこまで許されるかは未確認で、個人の実験の範囲として使っている。