OpenHands × Ollama ローカルLLM実践記 — Mac Studio M3 Ultra で動かすまでの全記録
TL;DR: OpenHands(旧OpenDevin)をMac Studio M3 Ultra(96GB)+ Ollama + Qwen3-Coder 30B で動かそうとした。Docker-in-Docker のビルド問題、Playwright依存、ランタイムイメージ手動構築を経てUI起動まで到達したが、30Bモデルのtool calling精度不足で実用には至らなかった。
1. OpenHands とは
OpenHands(旧 OpenDevin)は、オープンソースのAIコーディングエージェントプラットフォーム。75以上のLLMプロバイダーに対応し、SWE-bench で Qwen3-Coder 使用時に 69.6% のスコアを記録している。
公式リポジトリ: https://github.com/All-Hands-AI/OpenHands
特徴:
- Web UI でブラウザから操作
- Docker サンドボックスで安全にコード実行
- CodeActAgent による自律的なタスク遂行
- Playwright 統合によるブラウザ操作
2. 動機 — なぜ OpenHands を試したか
前回の実験で Qwen Code(CLI エージェント)を Ollama + Qwen3-Coder 30B で動かしたが、複雑な multi-step タスク(GitHub PR レビューなど)で tool calling が破綻する問題に直面した。
OpenHands は SWE-bench で高スコアを出しており、エージェントスキャフォールディングの力で同じ 30B モデルでも改善されるのでは?という仮説を検証するために試した。
3. 環境
| 項目 | スペック |
|---|---|
| マシン | Mac Studio M3 Ultra |
| メモリ | 96GB 統合メモリ |
| OS | macOS (Darwin 25.3.0) |
| Docker | Docker Desktop 28.5.2 |
| Ollama | ローカル稼働中 |
| モデル | qwen3-coder:30b (18GB) |
4. セットアップ手順と遭遇した問題
4.1 Docker イメージの選定
| |
問題1: runtime:main と openhands:main のバージョン不一致
openhands:main は /openhands/micromamba/bin/micromamba を期待するが、runtime:main は miniforge3 に移行済みだった。
exec: "/openhands/micromamba/bin/micromamba": stat: no such file or directory
解決: openhands:0.40(安定版タグ)に切り替え。runtime は自前でビルドする方針に変更。
4.2 Docker-in-Docker ビルド失敗
OpenHands は初回起動時にサンドボックス用のランタイムイメージを コンテナ内から Docker buildx で自動ビルドする設計。しかし macOS Docker Desktop 環境ではこのビルドがゾンビプロセスになり停止。
[docker] <defunct>
--privileged フラグを付けても解決しなかった。
解決: ホスト側で手動ビルドし、環境変数 SANDBOX_RUNTIME_CONTAINER_IMAGE で指定する方式に変更。
4.3 ランタイムイメージの手動ビルド
OpenHands 0.40 のソースからテンプレート(Dockerfile.j2)を抽出し、手動で Dockerfile を作成。
| |
問題2: Debian Trixie パッケージ名変更
nikolaik/python-nodejs ベースイメージが Debian Trixie に更新されており、パッケージ名が変わっていた。
E: Package 'libgl1-mesa-glx' has no installation candidate
E: Package 'ttf-unifont' has no installation candidate
解決: libgl1-mesa-glx → libgl1、Playwright の --with-deps を手動インストールに変更。
| |
4.4 ランタイムサーバーが起動しない
最初のビルドでは Playwright の依存ライブラリが不足しており、ブラウザ環境の初期化でクラッシュ → メインの Action Execution Server が起動しなかった。
Host system is missing dependencies to run browsers.
Playwright 依存ライブラリを追加して再ビルドし解決。
4.5 最終的な起動コマンド
| |
4.6 LLM 設定
UIのドロップダウンにローカルモデルが表示されないため、API 経由で設定を注入。
| |
ポイント:
- モデル名は
ollama/qwen3-coder:30b(litellm のプレフィックス規則) - Base URL は
host.docker.internal(Docker → ホストのOllama接続) - API Key は空文字不可のため
EMPTYを設定
5. 結果 — UI起動成功、しかし…
Web UI(http://localhost:3000)の起動、ランタイムサンドボックスの起動まで到達。チャット入力も可能になった。
しかし、実際にタスクを実行すると:
Agent encountered an error.
Parameter 'is_input' is expected to be one of ['true', 'false'].
原因: OpenHands の execute_bash ツールは is_input パラメータに文字列の "true" / "false" を期待する。30B モデルが以下のいずれかの問題を起こしている:
- パラメータを省略する
- boolean 型(
true/false)で返す(文字列の"true"ではなく) - 不正な値を渡す
これは前回 Qwen Code で確認した「30B モデルは API レベルの tool calling はできるが、複雑なエージェントワークフローでのツールスキーマ遵守が不安定」という制限と同根の問題。
6. 教訓とまとめ
セットアップで学んだこと
| 問題 | 原因 | 解決策 |
|---|---|---|
| runtime:main 互換性 | micromamba → miniforge3 移行 | バージョンタグを揃える |
| Docker-in-Docker ビルド失敗 | macOS Docker Desktop の制限 | ホスト側で手動ビルド |
| Debian パッケージ名変更 | Trixie 移行 | パッケージ名を修正 |
| Playwright クラッシュ | 依存ライブラリ不足 | 手動インストール |
| UI でモデル選択不可 | litellm リスト固定 | API 経由で設定注入 |
30B ローカルモデルの限界(再確認)
| ツール | 結果 | 失敗パターン |
|---|---|---|
| Qwen Code | 複雑タスクで失敗 | XML テキスト出力(tool_calls API 未使用) |
| OpenHands | エージェントエラー | is_input パラメータのスキーマ違反 |
結論: OpenHands のエージェントスキャフォールディングは優秀だが、モデルの tool calling 精度が十分でなければスキャフォールディングでは救えない。30B ローカルモデルでは:
- 簡単な単発タスク → Qwen Code で実用可能
- 複雑なマルチステップタスク → Claude Code(クラウド API)が必要
- OpenHands → クラウド API(Claude / GPT-4 / Qwen OAuth)と組み合わせれば真価を発揮
推奨構成
┌──────────────────────┐
│ タスクの複雑さ │
├──────────────────────┤
│ 簡単(ファイル編集) │ → Qwen Code + Ollama ローカル
│ 中程度(実装) │ → Claude Code
│ 複雑(PR レビュー) │ → Claude Code / OpenHands + クラウドAPI
└──────────────────────┘