OpenCode経由でローカルOllamaモデルにTDDタスクを委任する
Claude CodeのBashツールからopencode runを叩き、qwen3.8:27b・nemotron-3-ultra:cloud・ornith-1.5:35bの3モデルに同一のTDDタスクを委任。実ファイルとpytest実行結果で成功を確認した実測記録。
目的
「ローカルLLMへの委任がうまくいくかどうかは、モデルの実力よりハーネス(委任経路)の選択で決まるのではないか」という疑問を、推測ではなく実際に手を動かして検証した記録。Claude CodeのBashツールからopencode runを叩き、同一のTDDタスク(add(a, b)関数をpytestで検証しながら実装する)を複数のOllamaローカルモデルに与え、実際に生成されたファイルとpytestの実行結果で成否を判定した。
エージェントの標準出力の「完了しました」という自己申告は信用しない。必ず作業ディレクトリの実ファイルを
ls・catで確認し、python -m pytestを独立に実行して初めて成功と認定する。
前提:なぜOpenCodeか
Claude Code純正のAgent/Taskツールは、modelパラメータがAnthropic専用のenum(sonnet/opus/haiku/fable)に固定されており、構造的にOllamaのローカルモデルを指定できない。一方、OpenCode CLIは@ai-sdk/openai-compatible経由でOllamaのOpenAI互換エンドポイント(http://127.0.0.1:11434/v1)を素直に喋れるため、Claude CodeのBashツールからopencode run --model ollama/<model>を実行すれば委任経路として機能する。
検証タスク
1. calc.py に add(a, b) を実装する
2. test_calc.py に pytest で add(2, 3) == 5 を検証するテストを書く
3. まずテストを書いて失敗を確認し、その後実装してテストを通す(TDD)
4. 変更したファイル一覧を報告する
全モデル・全試行で同一のプロンプトを使用し、専用の空ディレクトリで実行した。
事前準備でハマった点
1. opencode.jsoncに未登録のモデル名は即クラッシュする
~/.config/opencode/opencode.jsoncのprovider.ollama.modelsに明示登録されていないモデル名を--modelに渡すと、プロンプトの中身に関係なくUnknownError: Unexpected server errorで即死する。エラーメッセージは「モデル未登録」とは一切言わないため、原因究明にログの突き合わせが必要だった。
{
"provider": {
"ollama": {
"npm": "@ai-sdk/openai-compatible",
"options": { "baseURL": "http://127.0.0.1:11434/v1", "timeout": 600000 },
"models": {
"qwen3.8:27b-opencode-32k": {
"name": "Qwen3.8 27B (opencode 32k)",
"limit": { "context": 32768, "output": 4096 }
},
"nemotron-3-ultra:cloud": {
"name": "Nemotron 3 Ultra (Ollama Cloud, free)",
"limit": { "context": 32768, "output": 4096 }
}
}
}
}
}
Ollama側に同名モデルが存在していても、この設定ファイルへの登録が別途必須。ollama pullしただけでは使えない。
2. [killed]表示のプロセスが実は生きていたことがある
タイムアウトで[killed]と表示された前のテストのopencode.exeプロセスが、実際にはバックグラウンドで無限ループを続けていたことがあった(compaction/subagentループをOllamaに投げ続け、22GBのVRAMを占有し続けていた)。これが後続テストの謎のサーバーエラーの一因になった。tasklistやGet-CimInstance Win32_Processで実プロセスの生死を確認し、ollama psでロード中モデルを確認してから次のテストに進む運用が必要。
検証結果(成功モデルのみ)
| モデル | 結果 |
|---|---|
qwen3.8:27b |
✅ 完全成功 |
nemotron-3-ultra:cloud(Ollama Cloud、無料枠) |
✅ 完全成功 |
ornith-1.5:35b |
✅ 完全成功(事前にPython/pytestのバージョン確認までする丁寧さあり) |
いずれも以下を満たした。
- 指定したpytest形式を維持(勝手にunittestへ書き換える等の逸脱なし)
calc.py・test_calc.py以外の余計なファイルを作らない- 実際に「失敗するテストを書く→失敗を確認→実装→合格を確認」の順で進行
python -m pytestで独立に実行しても1 passed
nemotron-3-ultra:cloudについて
Ollama Cloudの無料枠モデル。ollama pull nemotron-3-ultra:cloudで取得できる(ollama pull nemotron-3-ultraだけだと存在せず、:cloudタグが必要と親切にエラーメッセージで教えてくれる)。ローカルVRAMを消費しない代わりにクラウド推論になる点は留意。
実務上のチェックリスト
- [ ] 使いたいOllamaモデルが
opencode.jsoncのprovider.ollama.modelsに登録済みか確認する - [ ] 未登録なら
ollama cp <元タグ> <opencode.jsonc記載のタグ>でエイリアスを作るか、modelsに追記する - [ ] テスト前に
ollama psでロード中モデルがないか、tasklist系で幽霊プロセスがいないか確認する - [ ] エージェントの「完了報告」テキストを信用せず、必ず実ファイルとテスト実行で検証する
- [ ] 1モデルにつき専用の空ディレクトリで実行し、他の試行の残骸と混ざらないようにする
まとめ
同一タスク・同一ハーネス(OpenCode)で複数のOllamaローカルモデルを比較したところ、qwen3.8:27b・nemotron-3-ultra:cloud・ornith-1.5:35bの3モデルが実際にTDD手順を忠実にこなし、pytestが本当に通ることを確認できた。ハーネス側の落とし穴(モデル未登録時の不透明なクラッシュ、幽霊プロセスによるリソース競合)を踏まえた運用手順を整えれば、Claude CodeからBash経由でローカルモデルへ小さな実装タスクを委任する経路として実用に足ることが分かった。
関連ノート
- 2026-08-31_サブエージェントは別物_CodexとClaudeでローカルLLM回収率が違う構想記事
- 00-project-concept