ローカルLLMに 20 ファイルのコードベースを任せてみた ― OpenCode でバグ発見から修正・テスト・再レビューまで
第0章 最初にお断りを
以下の文章は、ほぼ ChatGPT がまとめたもの。AI の特徴なのか、文章が「〇〇ということ。つまり、〇〇・・・。」「〇〇。結局、・・・」みたいな冗長な書き方が多々出てくる。それから、やたらと事例を並べたがる傾向も見られた。
それから、今回の検証は全くの個人的な興味の話で、同じような環境だからといって他人が検証しても同じような結果になるとは限らないのでご注意を。参考程度にはなるかもしれないけれど、間違っても鵜呑みにしないようによろしく。
記事に出てくる検証用のコードはhttps://github.com/NBE03xxx/local-ai-coding-agent-labに公開している。時間があればいろんな LLM で試してみるのもいいかも。
ただし、検証用コードは ChatGPT が作ったもの。バグの仕込み方についても AI が考えたものだから、検証する LLM も当然「ここはあやしい」と既に学習しているハズなので話半分で丁度いいと思う。
それから、検証に使用したプロンプトを以下に記述する。
ただし、Level 1 および Level 4 については、検証当時に使用したプロンプトの完全な原文が現在残っていない。うまく進んでいるのが嬉しくて保存するのを失念してた。そのため、当時の検証内容と Level ごとの目的をもとに、使用したと考えられるプロンプトを再構成してみた。Level 2・3・5・6 については、当時使用したプロンプトそのままです。
Level 1(再構成)
このプロジェクトの既存コードを調査してください。
目的は、既存の実装に問題がないか確認し、問題があれば修正することです。
以下を守ってください。
まずREADMEとプロジェクト全体の構成を確認してください。
主要なソースコードを読み、各ファイルの役割を確認してください。
既存のテストを確認してください。
現在のテストを実行し、結果を確認してください。
コードを調査して、明らかなバグや実装上の問題がないか確認してください。
問題を発見した場合は、原因を説明してください。
必要なコードを修正してください。
必要であれば、問題を再現するテストを追加してください。
既存テストを不必要に変更したり削除したりしないでください。
最後にすべてのテストを実行し、結果を確認してください。
問題の場所や原因、修正方法はこちらからは提示しません。
自分でコード、README、テストを調査して判断してください。
Level 2
このプロジェクトの既存コードを調査してください。
目的は、既存の実装に潜んでいるバグを見つけて修正することです。
以下を守ってください。
1. まずプロジェクト全体を調査してください。
2. 各モジュールの役割と依存関係を確認してください。
3. READMEに記載された仕様と実装を比較してください。
4. 既存のテストをすべて確認してください。
5. テストが現在PASSしている場合でも、仕様違反や潜在的なバグがないか調査してください。
6. バグの原因を特定してから修正してください。
7. 必要であれば、バグを再現するテストを追加してください。
8. 既存テストを不必要に変更しないでください。
9. 関係のないファイルは変更しないでください。
10. 最後にテストをすべて実行し、結果を確認してください。
11. 最終的に、発見したバグ、原因、修正内容、追加・変更したテストを説明してください。
問題の場所や原因、修正方法はこちらからは提示しません。
自分でコード、仕様、テスト、モジュール間の依存関係を調査して判断してください。
Level 3
このプロジェクトの既存コードを調査してください。
目的は、既存の実装に潜んでいるバグや仕様上の問題を見つけて修正することです。
以下を守ってください。
1. まずプロジェクト全体を調査してください。
2. READMEに記載された仕様と実装を照合してください。
3. 複数ファイル間の依存関係やデータの流れも確認してください。
4. 既存のテストをすべて確認してください。
5. まず現在のテストを実行してください。
6. テストで検出されない問題についても、仕様とコードから検討してください。
7. バグを発見した場合は、原因を説明してください。
8. 必要なコードを修正してください。
9. 必要であれば、バグを再現するテストを追加してください。
10. 既存のテストを不正に弱めたり、削除したりしないでください。
11. 最後にすべてのテストを実行し、結果を確認してください。
12. 修正したファイルと修正理由を最後にまとめてください。
問題の原因や修正方法はこちらからは提示しません。
自分でコード、README、テストを調査して判断してください。
Level 4(再構成)
このプロジェクトの既存コードを横断的に調査してください。
目的は、複数のモジュールや処理が連携することで発生する、潜在的なバグや仕様違反を見つけて修正することです。
以下を守ってください。
READMEに記載された仕様を確認してください。
プロジェクト全体の構造と各モジュールの役割を確認してください。
複数モジュール間の依存関係とデータの流れを追跡してください。
主要な処理について、入力から最終的な状態までの流れを確認してください。
既存のテストをすべて確認し、テストで検証されている範囲を把握してください。
既存テストがPASSしている場合でも、仕様と実装を比較して潜在的な問題がないか調査してください。
特に、複数の処理が連続して実行される場合や、一部の処理だけが失敗した場合に、データや状態の不整合が発生しないか確認してください。
複数のモジュールが同じデータや状態を扱う場合、その整合性が維持されるか確認してください。
問題を発見した場合は、具体的な再現条件を考えて検証してください。
実際のバグや仕様違反であることを確認してから修正してください。
必要であれば、バグを再現する回帰テストを追加してください。
既存テストを不必要に変更したり、削除したりしないでください。
関係のないファイルは変更しないでください。
最後にすべてのテストを実行し、結果を確認してください。
最終的に、発見した問題、原因、再現条件、修正内容、追加したテストをまとめてください。
問題の場所や原因、修正方法はこちらからは提示しません。
自分でREADME、コード、テスト、モジュール間の依存関係を調査して判断してください。
Level 5
このプロジェクトの既存コードを徹底的に調査してください。
目的は、既存テストを通過するだけでは検出できない潜在的なバグや仕様違反を発見することです。
README.md の全Business Rulesを基準として、実装・状態遷移・例外処理・並行処理・リトライ・冪等性・
在庫・決済・永続化・キャッシュの整合性を横断的に調査してください。
特に以下を重点的に確認してください。
- 同じ注文を複数回処理した場合
- 決済とキャンセルが同時に実行された場合
- 在庫確保後に決済が失敗した場合
- 決済成功後に注文保存が失敗した場合
- 一時的な決済障害をリトライした場合
- リトライによって二重課金が発生しないか
- アプリケーション再実行時に二重処理されないか
- キャッシュされた価格と実際の価格が一致するか
- 複数スレッドから同一注文を処理した場合
- 部分的な成功・失敗によって在庫と注文状態が不整合にならないか
まずコードとテストを調査し、
発見した問題を具体的な再現ケースで確認してください。
その後、問題が確認できたものだけを修正し、
回帰テストを追加してください。
最後に全テストを実行し、
「既存テストでは検出できなかったバグ」が何件あったか、
それぞれについて
1. 原因
2. 再現条件
3. 影響
4. 修正内容
5. 追加したテスト
をまとめてください。
既存テストがPASSしていることだけを理由に
問題なしと判断しないでください。
Level 6
このプロジェクトの既存コードを徹底的に調査してください。
目的は、既存の実装に潜んでいるバグを見つけ、README.md に記載された仕様と実装を照合し、仕様違反となるバグを修正することです。
単に既存テストを実行してPASSするだけでは不十分です。
以下を実施してください。
1. README.mdを読む
2. プロジェクト全体の構造を調査する
3. 全ソースコードを読む
4. 全テストコードを読む
5. 注文作成から完了までの状態遷移を追跡する
6. 在庫・決済・永続化・イベント・キャッシュの依存関係を調査する
7. 並行実行時の競合やTOCTOUを検討する
8. リトライ・冪等性・部分的失敗を検討する
9. 仕様から具体的な再現ケースを作って検証する
10. 発見したバグを修正する
11. バグごとに回帰テストを追加する
12. 全テストを実行する
13. 最後にもう一度コード全体をレビューする
重要:
既存テストがPASSしていることを「バグがない」という根拠にしないでください。
また、単なる改善提案ではなく、READMEの仕様に対する実際の仕様違反を優先してください。
最後に、
- 発見したバグ
- バグの原因
- 再現条件
- 修正内容
- 追加したテスト
- 最終テスト結果
をまとめて報告してください。
第1章 ローカル AI コーディング環境を作る
1. はじめに 〜 なぜローカルAIコーディング環境を作ったのか〜
世の中は AI によるコーディング支援が急速に進化する中で、Claude Code や Codex のような AI コーディングエージェントを実際の開発にどれくらい使えるのか気になっていた。
ただし、今回やりたかったことは、既に一般化されている「最新の AI コーディングツールを使ってみる」ことではなく、自分で用意した GPU とローカル LLM を使って、AI コーディングエージェントを実際の開発環境として運用できるのかを確かめるという全くの興味本位な話。
クラウド AI の代わりを作りたかったわけではない。
今回の目的は、当然ながら OpenAI や Anthropic などのクラウド AI を否定したり、ローカル LLM がクラウドの最新モデルより優れていることを証明したりすることではなく、「自分の手元にあるハードウェアで、どこまで実用的な AI 開発環境を作れるのか?」という、かなり現実的な疑問から始まった。
特に、GeForce RTX 5090 32GB のような高価な GPU ではなく、比較的安価な GPU でどこまで実用に耐えられるか検証してみた。
ちなみに、ローカル環境には、次のようなクラウドサービスとは違ったメリットがあるのは周知かと思う。
- 自分でモデルを選べる
- API や構成を自由に変更できる
- 外部サービスへの依存を減らせる
- 手元のコードをローカル環境で扱える
- 利用量(料金)を気にせず継続的に試せる
- モデルや推論環境そのものを自分で検証できる
一方で、当然ながら問題もある。モデルの性能だけでなく、GPU の VRAM 容量、推論速度、ROCm などの GPU ソフトウェア、推論サーバー、API 互換性、そして AI コーディングエージェントとの相性まで考える必要がある。
「ローカルLLMが動く」ことと「ローカルLLMを使ったコーディングエージェントが実用になる」ことは、同じではない。
ここを実際に確認してみたかった。
2. きっかけになったハードウェア
今回の検証では、AI サーバーに 5 万円台で買える AMD Radeon RX 9060 XT 16GB を 2 枚搭載した環境を用意した。合計 32 GB の VRAM を利用できるため、7Bや14Bクラスだけでなく、より大きな 27B クラスのモデルも現実的な検証対象になる。詳細はローカル LLM 基盤を Radeon RX9060 XT x2で構築したのとおり。
そこで、ローカル LLM として Qwen 系のモデルなどを試しながら、
「27B クラスのモデルを、単なるチャットではなくコーディングエージェントとして使ったら、実際にはどこまで仕事ができるのか?」
という問いを立ててみた。もちろん、この時点では、まだ答えは知らない。
27B というモデルサイズだけを見れば、最先端のクラウドモデルと比較すれば小規模。それでも、コードベースを理解し、ファイルを編集し、テストを実行し、問題を調査するという一連の作業をエージェントとしてこなせるのであれば、個人開発の環境としては十分に価値があると思っている。規模が大きくなればオンプレミスの高性能 AI サーバーやクラウド AI に移行すればいい。
そこで、単純なベンチマークではなく、実際の開発作業に近い形で段階的に検証することにしてみた。
3. 「賢いモデル」ではなく「仕事ができるエージェント」を評価する
今回の検証で特に意識したのは、モデル単体の能力と、AI コーディングエージェントとしての能力を分けて考えることだった。
例えば、LLM にコードを生成させて、そのコードが正しそうに見えたとしても、それだけでは実際の開発作業を任せられるとは限らない。
実際の開発では、
- プロジェクトの構造を理解する
- 必要なファイルを探す
- 既存コードを読む
- テストを確認する
- 仮説を立てる
- コマンドを実行する
- 結果を分析する
- 必要な箇所だけ修正する
- テストを追加する
- 回帰テストを実行する
- 修正による副作用を確認する
といった作業が必要になる。
結局のところ、「このモデルはコードを書けるか?」ではなく、「このモデルをエージェントとして使ったとき、開発者の仕事の一部を実際に任せられるか?」ということが大事。個人の開発環境であっても基本的に変わらない。
4. 最初から一つの構成に決めなかった
Ollama を中心にコーディングエージェントを動かすところから始め、Claude Code や Codex との組み合わせも試した。仮想マシンを作って OpenClaw も試してみた。
さらに、OpenAI 互換 API を扱いやすくするために LiteLLM を試し、推論基盤そのものを変更する可能性も考えて vLLM にも挑戦した。
しかし、検証を進める中で分かったのは、構成を複雑にしても、それだけで実用性が高くなるわけではないということだった。
LiteLLM では中継層を追加することで得られるメリットがある一方、認証やモデルマッピングなど、別の問題も発生した。
vLLM についても、ROCm、Docker、NCCL、AITER、Triton、RDNA 4 など、今回のハードウェア環境では推論基盤そのものに多くの検証事項が生じた。
最新のバージョンを使いつつも安定した動作を確保したかったので、最終的には、
OpenCode → Ollama → Qwen3.6 27B
という、よりシンプルな構成に戻ることにした。随分遠回りだったけど、これは自分にとって重要な経験だった。
今更ながらだけど、「高機能な構成を作ること」と「実用的な環境を作ること」は別です。
自分が実際に使う環境である以上、構成そのものを維持するために時間を使うのではなく、AI に開発作業をさせることに価値がなければ意味がない。必要な機能は必要なときに追加すればいい。もちろん検証したうえで。
5. 最終的に知りたかったこと
検証を進めていくうちに、今回の目的はかなり明確になっていった。知りたかったのは、27B モデルのベンチマークスコアでも、クラウド AI との性能ランキングでもない。
自分の開発環境として、ローカル AI コーディングエージェントを本当に使えるのか?
という素朴な疑問。そのため、最終的には単純なコード生成テストではなく、複数ファイルからなるコードベースを AI に調査させ、バグ候補を探させるところまで検証を進めた。さらに、
バグの発見 → 原因の分析 → コード修正 → 回帰テスト → 全テスト実行 → 修正内容の再レビュー
という、実際の開発作業に近い一連の流れまで試してみた。ここで初めて、開発環境として「使える」から「任せられる」という最初の問いに、具体的な答えを出せるところまでたどり着いた。
本記事では、そこに至るまでに試した構成、途中で発生した問題、採用しなかった技術、そして最終的に OpenCode + Ollama + Qwen3.6 27B でどこまで開発作業を任せられたのかを、順を追って記録してみる。
もちろん、この記事を書いている時点の話なので、秒針分歩で進化を続ける AI の世界では既に古くなっているかもしれないことにご注意を。
第2章 検証環境 〜手元の GPU でローカル AI コーディング環境を作る〜
今回の検証では、AI コーディングエージェントを操作する開発 PC と、LLM を実行する AI サーバーを分けた。開発 PC で OpenCode を動かし、AI サーバーにある LLM を利用する構成。
1. 最終的な構成
最終的には、次のような構成になった。
| 層 | 使用したもの | 役割 |
|---|---|---|
| 開発PC | ThinkPad T16 Gen 4 AMD / Ubuntu 26.04 | OpenCode を実行 |
| AI コーディングエージェント | OpenCode 1.18.12 | コードの調査・編集・テストなどを実行 |
| API プロキシ | Qwen Proxy | OpenCode と Ollama の API 互換性の問題を吸収 |
| 推論サーバー | Ollama | LLM を実行 |
| LLM | Qwen3.6 27B Q6 | コーディング作業を担当 |
| GPU | Radeon RX 9060 XT 16GB × 2 | LLM の推論処理 |
| GPU ソフトウェア | ROCm | AMD GPU を利用するための実行環境 |
通信経路は OpenCode → Qwen Proxy → Ollama → Qwen3.6 27B Q6 → RX 9060 XT × 2 となる。開発 PC と AI サーバーは同一 LAN にあり、ネットワーク経由で接続している。
2. 開発PC
AI コーディングエージェントを動かす PC には、ThinkPad T16 Gen 4 AMD を使用した。OS は Ubuntu 26.04。 この PC で OpenCode を実行し、プロジェクトのファイルを読み書きしたり、コマンドやテストを実行したりする。LLM の推論処理は AI サーバーの仕事。そのため、普段使っている開発環境と AI の推論環境を分けて検証できる。
3. AI サーバー
LLM を実行する AI サーバーには、AMD Radeon RX 9060 XT 16GB を 2 枚搭載した。1 枚あたり 16GB なので、合計 32GB の VRAM を利用できる。今回の検証では最終的に、この GPU 環境で 27B クラスのローカル LLM を実際のコーディングエージェントから利用した。
ただし、VRAM が 32GB あるからといって、32GB までのモデルなら単純に快適に動くわけではない。モデルの量子化方式やコンテキスト長、推論ソフトウェア、GPU ドライバなどによって、実際の動作は変わる。そのため、今回は単に「モデルが動くか」だけではなく、コーディングエージェントとして使えるかを確認した。
4. ROCm
GPU の実行環境には AMD ROCm を使用した。今回の GPUはRadeon RX 9060 XT という RDNA 4 世代の GPU で、AI ソフトウェアがこの GPU をどこまでサポートしているかが重要になる。
実際、この点は後の vLLM 検証で問題になった。vLLM では、ROCm だけでなく、RDNA 4、gfx1200、NCCL、AITER、Triton、Dockerなど、いくつかの要素が関係する問題に遭遇した。
一方、Ollama では今回の環境で Qwen 系モデルを動かし、OpenCode から利用できるところまで構築できた。
5. Ollama
LLM の推論サーバーには CUI で完結する Ollama を使用した。今回の主な検証モデルは、 Qwen3.6 27B Q6。
最初に考えた構成は、とても単純な OpenCode → Ollama だった。Ollama は OpenAI 互換 API を提供しているため、そのままOpenCode から利用できると考えた。
ところが、実際に試してみると次のような問題が発生した。
6. OpenAI 互換 API とtools の問題
今回問題になったのは、tools(ツール呼び出し)だった。普通のチャットであれば、ユーザーが質問し、LLM が文章で答えるだけでもよい。しかし、コーディングエージェントではそうはいかない。
OpenCode は LLM とやり取りしながら、
- ファイルを読む
- ファイルを編集する
- ファイルを検索する
- コマンドを実行する
- テストを実行する
といった作業を行う。そのため、LLM との通信では tools が重要になる。
Ollamaが「OpenAI 互換 API」に対応していても、OpenCode が必要とする tools の処理まで完全に同じとは限らない。実際にOpenCode と Ollama を組み合わせて検証したところ、tools を含む API の互換性に問題があることが分かった。
ただし、もう既に改良されているかもしれないので、あくまでもこの記事を書いている時点の、私の環境に限定した話であることにご注意を。
ここで改めて、「OpenAI 互換 API に対応している」ことと、「OpenCode から問題なく利用できる」ことは同じではないことを理解した。これは自分にとって、今回の検証で得られた重要な知見の一つだった。
7. Qwen Proxy を作る
この問題を解決するため、OpenCode と Ollama の間にある API、特に tools の互換性問題を解決するために、OpenCode と Ollamaの間に専用のプロキシ proxy.py を作った。ただし、私自身がコードを書いた訳じゃなく、ChatGPT との会話の中で生成してもらった python コード。
このプロキシは基本的に OpenAI 互換 API で OpenCode からリクエストを受け取り、Ollama へ転送するだけ。今回の自分の環境では、Ollama 側の thinking を無効化する処理や、OpenCode が必要とする通信形式への調整も行った。
結果、通信経路は OpenCode → Qwen Proxy → Ollama として、プロキシで必要な変換や調整を行うことで、OpenCode から Ollama 上のモデルをコーディングエージェントとして利用できるようになった。
もちろん、最初から計画していた訳じゃなく、実際に動かしてみて問題が発生したので、その問題を解決するために追加したもの。
コードは https://github.com/NBE03xxx/local-ai-coding-agent-lab/tree/main/proxy で公開している。
8. プロキシを systemd 化
最初はプロキシを手動で起動して動作を確認したが、実際に使い続けることを考え、systemd サービスとして動かす構成にした。これにより、AI サーバーを再起動した場合にプロキシを自動起動できる。結果、実際に使い続けられる環境にすることができた。
最終的には ThreadingHTTPServer を使用して複数リクエストに対応し、systemd サービスとして常時起動する構成にした。詳細は上記の Github リンクのとおり。
9. 使用したモデル
今回の検証では、Qwen 系を中心に複数のモデルを試してみた。Qwen3.6 では 27B だけでなく 35B も試し、そのほかのモデルも比較した。その中で、OpenCode の Level 1 〜 6 の検証に主に使用したのが、エージェント型の高度なコーディングが得意と言われている Qwen3.6 27B Q6 である。
Ollama 公式サイトからはサイズ 17GB の Qwen3.6:27B Q4_K_M がダウンロードできる。でも 32GB の VLAM をフル活用するため、今回は hf.co/unsloth/Qwen3.6-27B-MTP-GGUF:UD-Q6_K_XL を使用した。ダウンロードは、
ollama pull hf.co/unsloth/Qwen3.6-27B-MTP-GGUF:UD-Q6_K_XL
で OK。
モデルの評価では、ベンチマークの数字だけを見るのではなく、実際の開発作業をどこまで任せられるかを確認するため、例えば、
- プロジェクトの構造を理解できるか
- 複数のファイルを読めるか
- テストを実行できるか
- バグを見つけられるか
- 間違った指摘を減らせるか
- コードを修正できるか
- 回帰テストを追加できるか
- 自分の修正を再確認できるか
といったことを検証してみた。
10. 途中では別の構成も試した
最終的な構成は、OpenCode → Qwen Proxy → Ollama → Qwen3.6 27B Q6 となったけど、最初からこの構成だったわけではない。検証の途中では、Claude Code や Codex と Ollama を組み合わせたり、この後で書くけれど LiteLLM を使ったり、vLLM + ROCm も試した。
それぞれを試した理由は異なるが、目的は「実際に使えるローカルAIコーディング環境を作る」ことなので、機能が多いことよりも、実際の開発作業を安定して行えることを重視した結果、最終的には Ollama を推論サーバーとして使い、OpenCode と Ollama の間に必要なプロキシだけを置く構成に落ち着いた。
Claude + Ollama の問題点はさっくりいうと「認証・プロキシ」「大コンテキスト」「遅延」「ROCm」「エージェントとしての安定性」の 5 本柱だった。頑張れば解決できたかもしれないけれど。
Codex + Ollama の組み合わせでは次のような問題があった。こちらも頑張れば何とかなったかもしれないけど。
- こんにちは、のような単純な質問でも回答が返ってこないことがあった。
- Codex 側の /model によるモデル一覧・切り替えが正常に機能しないケースがあった。
Generic OpenAI / Ollamaをローカルモデルとして扱う部分に問題があり、Codex がモデルのメタデータを取得できず、
Model metadata not found → フォールバック、という挙動になるケースがあった。
つまり、単純に OpenAI 互換 API として接続すればすべての Codex 機能が使える、というわけではなかった。 - Tool / Function Calling の互換性問題。Ollama の OpenAI 互換 API を通した場合、ツール呼び出しの形式や復元で互換性問題が発生していた。MCPが特に問題だった。「Codex 側で MCP サーバーを認識できる→resource 取得もできる→しかし実際の MCP tool invocation が実行されない」という回帰問題があった
さらに、flattenされたMCP tool名「mcp__xxxx__search」のような namespace を Codex 側が正しく解決できず、unsupported / unknown server と扱う問題もあった。 - Ollama 側にも Responses API / namespace の問題があり、Responses API の tool call について、namespace field の扱いが Codex 側の期待と一致しない問題があった。その結果、「Ollama → tool call → Codex」が正しく復元できないという構造的な互換性問題につながっていた。
第3章 構成を改善しようとして、逆に複雑になった
Ollama とコーディングエージェントを実際に動かしてみると、単純に
コーディングエージェント → Ollama → ローカルLLM
という構成だけで終わるわけではないことが分かってきた。例えば、
- モデルを切り替えたい。
- 複数のクライアントから同じ LLM を使いたい。
- OpenAI 互換 API として統一したい。
- 認証やモデル名を吸収したい。
そう考えていくと、Ollama の前にもう一つ中間層を置けば、もっと柔軟にできるのではないかと浅はかな考えが浮かんだ。そこで試したのが LiteLLM だった。
1. LiteLLM を中継層として使う
LiteLLM は、複数の LLM プロバイダーを OpenAI 互換 API として扱うためのプロキシとして利用できる。
今回の環境では、例えば、
Claude Code→LiteLLM→Ollama→ローカルLLM
という構成を考えた。これなら、クライアント側から見ると接続先を LiteLLM に統一でき、将来的に Ollama 以外の推論サーバーを試す場合でも、クライアント側の設定を大きく変更せずに済む可能性がある。
一見すると、かなり合理的な構成に思えた。実際、LiteLLM にはそのための機能が揃っている。
しかし、実際に組んでみると、別の問題が見えてきた。
2. 中間層を入れると、問題も一つ増える
LiteLLM を導入すると、当然ながら通信経路が一つ増える。
すると、何らかの問題が発生したときには、
- クライアントの問題なのか
- LiteLLM の設定なのか
- 認証の問題なのか
- モデル名のマッピングなのか
- Ollama の問題なのか
- モデルそのものの問題なのか
を切り分けなければならない。例えば、LiteLLM 経由で API を利用するときには、認証設定も関係してくる。
実際の検証では、/v1/models へのアクセスで 401 が返るケースもあり、Ollama へ直接アクセスしていたときには存在しなかった「LiteLLM 側の認証」という問題を考える必要が出てきた。
また、クライアントから指定するモデル名と、実際に Ollama で動作しているモデル名との対応関係についても考慮する必要がある。
つまり、LiteLLM によって「複数の LLM を統一的に扱える」というメリットが得られる一方で、そのための設定や状態も増える。
3. 自分にとって「できる」と「必要」は違う
LiteLLM そのものが悪いわけではない。むしろ、複数のモデルや複数のプロバイダーを一元管理したい環境では、とても有用な仕組みだと思う。
ただ、自分の目的は少し違う。やりたいことは、
ローカルの27Bクラスのモデルを、コーディングエージェントから実用的に使えるか検証する。
ことである。
その目的だけを考えるなら OpenCode→Ollama→Qwen3.6 27B という構成でも十分に成立する。この時点では OpenCode を使ってみよう、と決断した段階だけど。
ここに LiteLLM を追加しても、今回検証したい「ローカル LLM をコーディングエージェントとして使えるか」という本質的な問題が解決するわけではない。むしろ、問題が発生したときの切り分け対象が増える。なので、構成を最初のシンプルな方向へ戻して考えることにした。
4. 「便利そうだから入れる」から「必要だから入れる」へ
この検証を通じて、環境構築に対する考え方も少し変わった。最初は、
自鯖だし誰にも迷惑かけないから、便利そうな機能は、とりあえず追加してみる。
という発想だった。しかし、実際に複数のソフトウェアを組み合わせていくと、当たり前だけどそれぞれが独立して動いているわけではない。一つ中間層を追加すれば、その分だけ、
- 設定
- 認証
- ログ
- エラー処理
- バージョン
- 通信経路
- 障害切り分け
を考える必要が出てくる。もちろん、それでも得られるメリットが大きければしっかり検証して導入する価値はある。ただ、重要なのは、
「便利そうだから入れる」のではなく、「必要だから入れる」
という判断だった。今回の LiteLLM の検証は、改めてそのことを実感するきっかけになった。
5. そして、もう一つの選択肢へ
ただし、この時点ではまだ「Ollamaで十分」と結論を出したわけではない。
もし推論サーバーそのものを変更すれば、もっと性能や安定性を改善できるのではないか、と考えた。
既に、Ollama とは別に、より本格的な推論基盤として使われているものもある。
そこで次に試すことにしたのが、vLLM だった。
そもそも推論サーバーをOllamaから変更したらどうなるのか?
を確かめることにした。
なので、次章では、Hugging Face のモデルを使い、ROCm 環境で vLLM を動かすところから検証していく。ここでは、今回のハードウェアである Radeon RX 9060 XT を2枚使った環境で、vLLM がどこまで実用になるのかを試してみることにした。
第4章 vLLMならもっと良くなるのか?
LiteLLM を試した結果、今回の用途では中間層を増やすことによるメリットよりも、構成が複雑になるデメリットのほうが気になってきた。そこで、別の方向から改善できないかを考えてみた。Ollamaの前段に何かを追加するのではなく、そもそも推論サーバーをOllamaから変更したらどうなるのか?という疑問である。
そこで次に試したのが vLLM だった。自鯖環境だから自由に試すことができて楽しいところ。
1. vLLM を試してみる
vLLM は、LLM の推論・サービングを目的とした比較的本格的な推論基盤で、OpenAI 互換 API も提供している。今回の用途にはかなり相性が良さそうだと思った。
構成としては、コーディングエージェント→vLLM→ローカルLLM というシンプルな形にできる。しかも、OpenAI 互換 API を利用できるので、Claude Code や Codex、OpenCode などから利用できる可能性がある。
もし Ollama よりも高速で、安定して、複数 GPU を効率的に利用できるのであれば、今後のローカル AI 環境の基盤としてかなり魅力的になる。
そこで、実際の AI サーバー環境で検証することにした。
2. 今回のハードウェアは少し特殊だった
今回使用している GPU は、Radeon RX 9060 XT 16GB を 2 枚搭載した構成である。NVIDIA GPU を前提とした一般的な LLM 環境とは少し事情が違う。
AMD GPU では ROCm を利用する必要があり、さらに今回の RX 9060 XT は RDNA 4 世代の GPU である。
そのため、単純に、
vLLMはAMDにも対応しているから動くはず。
とは考えられない。
GPU アーキテクチャ、ROCm、PyTorch、vLLM、各種カーネル実装など、複数のソフトウェア層が正しく噛み合う必要がある。ここが今回の検証の大きなポイントになった。
3. Hugging Face のモデルを用意する
Ollama では、モデルの取得や実行環境を比較的簡単に管理できる。一方、vLLM では Hugging Face 上のモデルを直接扱うケースが多い。そこで、今回利用していた Qwen3.6 系のモデルを Hugging Face から取得し、vLLM で読み込ませる構成を試した。
モデルによっては数十GBのファイルを扱うことになる。実際、今回試した Qwen3.6-35B-A3B-FP8 では、ダウンロード対象が多数のファイルに分かれており、合計容量もかなり大きかった。
ここで、
モデルを取得する
↓
コンテナからモデルを参照する
↓
vLLM を起動する
↓
GPU へロードする
↓
API として公開する
という、Ollama とは少し異なるセットアップが必要になった。
4. Docker + ROCmというもう一つの壁
vLLM は Docker コンテナとして動かすことができる。そのため、今回も ROCm 対応の vLLM コンテナを利用して検証した。
一見すると、
ホスト
├─ AMD GPU
├─ amdgpu
└─ ROCm 関連環境
↓
Docker
↓
vLLM
↓
PyTorch
という構成になる。しかし、ここではホスト側の GPU ドライバとコンテナ内の ROCm 環境との組み合わせが重要になる。さらに、vLLM 内部では GPU を利用するために PyTorch や各種 GPU カーネル、通信ライブラリなどが関係してくる。
つまり、vLLM をインストールできたからといって、目的のモデルを安定して推論できるとは限らない。
5. Tensor Parallel も試す
GPU が2枚あるので、当然、2 枚の GPU を使えば大きなモデルを効率よく動かせるのではないか?とも考えた。そこで Tensor Parallel を利用して、モデルを 2 枚の GPU へ分散する構成も試した。
しかし、ここでも単純にはいかなかった。GPU 間の通信や各種カーネルの動作が必要になるため、単に GPU を 2 枚搭載すれば、それだけで Tensor Parallel が効率よく動くわけではない。検証中には NCCL 関連のエラーにも遭遇した。
また、vLLM のログには AITER や Triton に関する警告も出ていた。
これらは、今回の環境における GPU 最適化が十分に整っていないことを示す材料になった。
6. gfx1200という現実
今回の RX 9060 XT は gfx1200 として認識される。ここが重要だった。
AMD GPU を利用するソフトウェアでは、単純に「ROCm 対応」と書かれているだけでは十分ではない。GPU アーキテクチャごとの対応状況や最適化状況によって、実際の動作や性能が大きく変わる可能性がある。
今回の環境では、
RDNA 4、gfx1200、ROCm、vLLM、PyTorch、Triton、AITER、NCCL
といった複数の要素が絡み合った。
結果として、Ollama で比較的素直に動作していた環境を、そのまま vLLM へ置き換えれば同じように使える、という状況ではなかった。
7. 当たり前だけど「vLLMが悪い」のではない
ここは今回の検証で最も重要な部分だと思う。vLLM そのものが使えないという話ではない。むしろ、vLLM は大規模な LLM 推論基盤として非常に有力な選択肢である。問題は、今回のハードウェア・ROCm・モデル・用途の組み合わせだった。
NVIDIA GPU を中心とした環境や、vLLM が十分に最適化されている GPU・モデルの組み合わせであれば、今回とは違う結果になった可能性は十分にある。しかし、今回の目的は「vLLMを動かすこと」ではない。
ローカル LLM をコーディングエージェントのバックエンドとして実用的に使うこと。
である。その観点から見ると、vLLM を採用するために必要になる追加の調整やトラブルシューティングに対して、それに見合うだけのメリットを確認できなかった。
8. 今回は Ollama を残す
ここまで検証した結果、vLLM への移行はいったん見送ることにした。
判断としては、
今回のハードウェア・ソフトウェア構成では、vLLM へ移行するメリットが十分に確認できなかったため、Ollama を継続して使う。
ということ。前向きに考えれば、これは「失敗」というより、実際の環境で検証したからこそできた選択だと思う。もちろん、日々改良が重ねられている世界だから、明日には普通に利用できるようになっているかもしれない。
9. そして、構成をもう一度シンプルに戻す
LiteLLM を試し、vLLM も試した。その結果、むしろ最初に使っていた Ollama のシンプルさが目立つようになった。最終的に今回の検証環境では、OpenCode→Ollama→Qwen3.6 27B という構成を中心に検証を続けることにした。
ここまで来ると、ようやく環境そのものを追いかける段階から、実際に AI へ仕事を任せる段階へ進むことができる。これまで試してきたのは、いわば「どの基盤を使うか」という検証だった。しかし、本当に知りたいのはそこではない。
この環境で、27B クラスのローカル LLM にどこまでコーディング作業を任せられるのか?
次章からは、いよいよ OpenCode を使って、AI そのものの能力を検証していく。
第5章 OpenCode に戻る
ここまで、ローカル AI コーディング環境を作るために、いくつかの構成を試してきた。最初は Ollama。そこから Claude Code や Codex を試し、さらに LiteLLM を中継層として導入し、最後には vLLM まで検証した。
しかし、最終的に残ったのは、意外にも最初に使っていたシンプルな構成の OpenCode→Ollama→Qwen3.6 27B。
ここでようやく、環境構築そのものを検証する段階から、AIに実際の仕事を任せる段階へ進むことにした。
1. なぜ OpenCode なのか
今回の目的は、単純なチャット性能を評価することではない。例えば、「Pythonでフィボナッチ数列を書いてください」と質問して、正しいコードが返ってくるかどうかだけを見ても、コーディングエージェントとしての実用性は分からない。
実際の開発では、
- プロジェクトのファイルを調べる
- 既存コードを読む
- 関連するファイルを探す
- コードを修正する
- テストを実行する
- エラーを確認する
- 必要ならもう一度修正する
といった作業が発生する。つまり、評価したいのは「コードを書く能力」だけではない。
既存のコードベースを理解し、ツールを使いながら、自分で作業を進められるか。
である。そのため、今回は OpenCode を使って検証することにした。
2. コーディングエージェントとして評価する
OpenCode を使うことで、AI は単に回答を生成するだけではなく、実際のプロジェクトに対して操作を行える。
例えば、
プロジェクト
├── README.md
├── src/
│ ├── ...
│ └── ...
└── tests/
├── ...
└── ...
というプロジェクトを与えた場合、AI 自身にファイルを調査させ、必要なファイルを読み、テストを実行し、その結果をもとに判断させることができる。
これは、チャット形式のコード生成とはかなり違う。
AI が本当に開発作業を行うのであれば、「正しいコードを生成できるか」だけではなく、
- 「何を調べるべきか判断できるか」
- 「調べた結果から次の行動をきめられるか」
- 「ツールの結果を正しく解釈できるか」
という能力も必要になる。そこで、今回の検証ではこの部分を重視することにした。
3. 評価対象を「モデル」だけにしない
ここまでの検証で分かったことの一つは、コーディングエージェントの性能をモデルのベンチマークだけで判断するのは難しいということだった。
実際の動作には、
┌─ モデル
│
├─ 推論サーバー
│
├─ API
│
├─ コンテキスト
│
├─ ツール呼び出し
│
└─ エージェント側の制御
↓
最終的な作業結果
という複数の要素が関係している。例えば、モデルがコードを理解できたとしても、必要なファイルを調べられなければ実際の作業にはつながらない。
逆に、ツールを正しく使えても、読み取ったコードの意味を理解できなければ問題を解決できない。
そのため、今回の検証では「Qwen3.6 27B のベンチマークスコア」を測ることを目的にはせず、あくまで、
この環境でコーディングエージェントとして実際に仕事を任せられるか
を確認することにした。
4. いきなり難しい仕事はさせない
とはいえ、いきなり大規模なプロジェクトを渡して、「全部調査して問題を直してください」とやっても、何が原因で成功したのか、あるいは失敗したのか分からない。そこで、段階的に難易度を上げていくことにした。
最初は簡単なコード操作から始める。そこから、
- ファイル操作
- コード修正
- テスト
- デバッグ
- 複数ファイルの理解
- より実際の開発に近い作業
へと徐々に難しくしていく。で、最終的には、複数ファイルから構成されたコードベースを与え、
「このプロジェクトに潜んでいる問題を調査してください」
というところまで持っていき、段階的にAIへ仕事を任せていってみる。これを今回の検証では Level 1 〜 Level 6 として整理した。
5. ここからが本当の検証
ここまでの章では、主に「どの環境を使うか」を検証してきた。
Ollamaを試した。
Claude Code や Codex を試した。
LiteLLMも試した。
vLLMも試した。
・・・LM Studio は試してない。
そして最終的に、OpenCode→Ollama→Qwen3.6 27B というシンプルな構成に戻ってきた。
ここから先は、環境そのものを変更するのではなく、同じ構成でAIにどこまで仕事を任せられるかを見る。そして、この検証では「一度うまく動いた」だけでは成功とはしない。
- AIが何を調べたのか。
- どのように判断したのか。
- 間違った可能性をどう扱ったのか。
- テストを実行できたのか。
- 修正したコードが本当に正しいのか。
そういったところまで含めて確認していく。
最終的には、AI にコードを書かせるところから、AI に開発作業そのものを任せるところまで到達できるのかを見てみる。
次章から、その検証を Level 1 から順番に進めていく。
第6章 AIに仕事を任せる
第5章までで、今回の検証に使用する環境が決まった。
OpenCode + Ollama + Qwen3.6 27B
ここから、いよいよ本題の、
この環境で、ローカル LLM にどこまで開発作業を任せられるのか?
これを確認するため、AI に与える仕事を少しずつ難しくしていくことにした。
1. 「コードを書ける」だけでは足りない
コーディングエージェントの能力を考えるとき、まず思い浮かぶのはコード生成能力だと思う。しかし、実際の開発ではコードを書くことだけが仕事ではない。既存のコードを読み、関連するファイルを探し、テストを確認し、実際にプログラムを動かす。エラーが出れば原因を調べ、コードを修正し、もう一度テストする。さらに、修正によって別の問題が発生していないか確認する必要もある。
つまり、実際の開発作業では、
- コードを理解する
- ファイルを操作する
- ツールを使う
- テストを実行する
- 結果を解釈する
- 問題を切り分ける
- 修正する
- 修正結果を確認する
といった複数の能力が必要になるハズ。というのも自分自身が普段から開発作業に携わっている訳じゃないから本当のところは知らない。
まあそのことはさておき、今回の検証では、単純なコード生成から始め、徐々に実際の開発に近い作業へと難易度を上げていくことにした。
2. Level 1〜6 で段階的に検証する
検証全体は 6 段階に分けた。
Level | 主な検証内容 | 確認したかったこと |
|---|---|---|
| Level 1 | 基本的なコード理解・操作 | 指示を理解し、基本的な作業ができるか |
| Level 2 | ファイル操作・編集 | 既存ファイルを確認し、適切に変更できるか |
| Level 3 | テスト・デバッグ | テスト結果を理解し、問題を修正できるか |
| Level 4 | 複数ファイル・コードベース理解 | ファイル間の関係を追えるか |
| Level 5 | 実際の開発に近い作業 | 調査・判断・実装を組み合わせられるか |
| Level 6 | 調査・バグ発見・修正・テスト・再レビュー | 一連の開発作業をどこまで任せられるか |
Level が上がるにつれて、評価の対象を「コードを正しく生成できるか」から、「与えられた仕事を自分で進められるか」へと変えていった。
Level 1 まずは基本的な操作から
最初から難しい課題を与えるのではなく、まずは基本的なコード理解や操作から始めた。
ここで確認したかったのは、OpenCode から Qwen3.6 27B を利用したときに、AI がコーディングエージェントとして最低限の作業を安定して行えるかどうかだった。これは以降の検証の土台になる。
AI が基本的な指示を理解し、プロジェクト内のファイルを確認し、必要な操作を行えることを確認したうえで、次の段階へ進んだ。
Level 2 ファイルを操作する
次は、実際にファイルを扱わせる。
コードを読むだけではなく、既存のファイルを編集し、変更内容を確認する。ここで重要なのは、単純に「変更されたコードが正しいか」だけではない。
AI が、
- どのファイルを変更すべきか
- 既存の実装を理解しているか
- 不要な部分まで変更していないか
- 変更によって既存コードを壊していないか
を確認する必要がある。ここから、AI は「コードを回答する存在」ではなく、実際にプロジェクトへ変更を加える存在になっていく。
Level 3 テストとデバッグ
次の段階では、テストを組み合わせた。
コードを変更して終わりではない。実際にテストを実行し、その結果を読み、問題があれば修正する。この段階で重要になるのが、AI による結果のフィードバックである。
例えばテストが失敗した場合、AI には単純に「もう一度コードを書いてください」と指示するのではなく、なぜテストが失敗したのか?を考えさせる必要がある。
なので、Level 3では「実装する」だけではなく、実装 → テスト → 結果確認 → 修正という開発に近いサイクルを経験させた。
Level 4 複数ファイルを理解する
Level 4では、対象を一つのファイルから複数ファイルへ広げた。
実際のソフトウェアでは、一つの機能が一つのファイルだけで完結することは少ない。ある関数の動作を理解するためには、別のモジュールを見る必要がある。さらに、その関数を呼び出している側や、関連するテストコードまで確認しなければならないこともある。
そのため、ここでは、AI が一つのファイルではなく、複数のファイルの関係を追いながらコードを理解できるかを確認した。
これは、単純なコード生成とコードベース調査の間にある、大きな段差だった。
Level 5 実際の開発に近づける
Level 5では、さらに実際の開発作業に近い課題へ進めた。
ここでは、「このコードを書いてください」という明確な指示だけを与えるのではなく、既存コードを調査し、状況を理解したうえで必要な変更を行わせる。つまり、AI には単純な実装能力だけでなく、何を調べ、何を変更する必要があるのかを判断する能力が求められる。この段階になると、コーディングエージェントとしての性格がかなり強くなってくる。
そして、いよいよ最後の Level 6 へ進む。
Level 6 コードベースを調査する
Level 6 では、複数のファイルから構成されたコードベースをそのまま与えた。
そして、AIへの指示はあえてシンプルに「このプロジェクトの既存コードを調査してください。目的は、既存の実装に潜んでいるバグを見つけることです。」としてた。ここでは、
バグの場所を教えない。
バグの個数も教えない。
どのファイルを調べるべきかも指定しない。
として、AI 自身にプロジェクトを調査させてみる。
想定していた調査内容は、例えば次のようなものだ。
調査段階 | AI に求められること |
| プロジェクト理解 | README やディレクトリ構成を確認する |
| コード調査 | 関連する実装を読み解く |
| テスト確認 | 既存テストの内容と不足を確認する |
| 実行 | 必要に応じてpytest などを実行する |
| 仮説 | 問題になりそうな箇所を洗い出す |
| 検証 | 各候補が本当に問題なのか確認する |
| 判定 | バグと正常な実装を区別する |
ここで重要なのは、「怪しいコードを見つけること」と「それが本当にバグだと判断すること」は違うという点である。
3. False Positiveも評価する
今回の Level 6 では、「バグをたくさん見つければ成功」とは考えなかった。コードを調査すれば、改善できそうな箇所はいくらでも見つかる。しかし、改善できることと、バグであることは別の話。
例えば、あるコードが一見すると危険に見えても、呼び出し側や仕様、テストまで確認すると問題がない場合がある。
そこで今回の検証では、
AIが見つけたバグ候補を、自分自身で検証し、False Positive を排除できるか
も評価対象にした。これは実際に AI を開発現場で使う場合、非常に重要なポイントだと思う、たぶん。AI が「怪しい箇所」をすべてバグとして報告するなら、その報告を人間が一つずつ確認しなければならない。
一方で、「一見すると問題に見えるが、コード全体を見ると仕様上正しい」と判断できるのであれば、AI によるコードレビューや調査の価値は大きくなる。
4. Level 6 は何が違うのか
Level 1 から Level 5 までは、それぞれ個別の能力を確認する意味合いが強かった。Level 6 では、それらを一つの仕事として組み合わせている。
これまで確認した能力 | Level 6での利用 |
| コード理解 | 既存実装を読み解く |
| ファイル操作 | 必要なファイルを調査・編集する |
| ツール利用 | ファイル検索やテスト実行を行う |
| テスト | 既存テストを実行する |
| デバッグ | 問題の原因を調べる |
| 複数ファイル理解 | コードベース全体の関係を追う |
| 判断 | バグ候補を絞り込む |
| 修正 | 実際にコードを変更する |
| 再確認 | テストとレビューで修正を検証する |
つまり Level 6 で見ようとしているのは、個々の能力の有無ではなく、それらを組み合わせて、一つの開発作業を最後まで進められるかである。
5. ここからが本当の検証
ここまでの検証は、いわば Level 6 へ向けた準備だった。基本的な操作から始め、ファイル編集、テスト、デバッグ、複数ファイルの理解へと徐々に難易度を上げてきた。そして最後に残ったのが、「このコードベースに潜んでいるバグを探してください」という、かなり自由度の高い課題だった。
これは単純なベンチマークではない。AI に正解を教えて、その答えを当てさせるテストでもない。
AI 自身にコードベースを調査させ、何が問題なのかを考えさせる。さらに、見つけた問題が本当にバグなのかまで確認させる。そして必要であれば、修正してテストする。ここまでできれば、少なくとも、
「ローカルの27B モデルでは、簡単なコード生成しかできない」
という私の固定観念、先入観が正しいのか、検証ではあるが実際の開発作業を通じて確かめることができる。
次章では、この Level 6 を実際に実行する。AI には約 20 ファイルからなるコードベースを渡し、事前にバグの場所を教えずに調査させた。そこで AI は、何を見つけ、何を「バグではない」と判断したのか。
いよいよ、今回の検証の最終試験である。
第7章 Level 6という最終試験
Level 1 から Level 5 まで、AI に任せる仕事を少しずつ難しくしてきた。そして最後に、Level 6 を実行する。ここで用意したのは、約 20 個の Python ファイルとテストコードからなる、小さなコードベースだった。
規模としては、本格的な商用システムほど大きくはない。しかし、一つのファイルを読んで答えれば済むような単純な課題ではない。
複数のモジュールがあり、状態を持つ処理があり、データベースやキャッシュを扱うコードがあり、イベント処理や決済に関係するロジックも含まれている。
テストも用意した。
今回の目的は、このコードベースにあらかじめ仕込まれている問題を AI 自身に見つけさせることだった。
1. AIには答えを教えない
Level 6 で重要なのは、AI にヒントを与えすぎないことだった。
例えば、「checkout_keyの処理を確認してください」と指示してしまえば、ほとんど答えを教えていることになる。
そこで、指示はできるだけシンプルにした。
「このプロジェクトの既存コードを調査してください。目的は、既存の実装に潜んでいるバグを見つけることです。」
どこに問題があるのかは伝えない。
バグが何個あるのかも伝えない。
どのファイルを調べるべきかも指定しない。
AI自身にプロジェクト全体を調査させる。
ここで見たいのは、あらかじめ用意された問題の正解率ではない。
「何を調べればよいのか」をAI自身が判断できるかどうかである。
2. 最初に README を読む
OpenCode にこの指示を与えると、AI はいきなりコードを修正し始めるのではなく、まずプロジェクトの情報を確認していった。
最初に README を読み、プロジェクトが何をするものなのかを把握する。これは人間が初めて知らないコードベースを調査するときと同じだ。いきなり一つの関数を読み始めても、その関数が何のために存在するのか分からなければ、正しい判断は難しい。そのため、まずプロジェクトの目的と構造を理解するというスタートになった。
続いてディレクトリ構造を確認し、どのようなモジュールが存在するのかを調べていった。この時点で、AI は単に「コードを生成する」のではなく、調査対象を自分で把握するという行動を取っている。
3. 次に主要な実装を読む
プロジェクト全体の構造を把握した後は、主要な Python モジュールを順番に調べていった。ここでは、一つのファイルだけを見て結論を出すのではなく、関連するコードを追いながら確認していく。
例えば、
- データモデル
- リポジトリ
- キャッシュ
- 決済関連処理
- クーポン計算
- 注文処理
- イベント処理
- 並行処理に関係するコード
などを調査していった。
この段階で重要なのは、AI が単に「怪しそうなコード」を拾っているわけではないことだった。コードの動作を追い、「この条件ではどうなるのか」という形で問題を考えている。
つまり、静的なコードレビューだけではなく、実際の実行時の挙動を想像しながら調査している。
4. テストコードも確認する
実装を調べるだけではなく、既存のテストも確認した。
今回のプロジェクトには、基本的な動作を確認するテストのほか、イベント処理や並行処理に関係するテストも含まれていた。テストを見ることで、「このプロジェクトでは、何を正常な動作として期待しているのか」を確認できる。
これはバグを探すうえで非常に重要だ。
例えば、実装だけを見ると「これはおかしいのではないか」と思える処理でも、テストや他のコードを見ると、それが意図された仕様である可能性がある。
逆に、実装とテストの間に矛盾があれば、それ自体が問題を発見する手がかりになる。
今回の検証では、この実装とテストの両方を見ることが、後の False Positive の切り分けにもつながった。
5. そして pytest を実行する
コードを読むだけで終わらなかった。
AI は実際に pytest を実行し、既存のテストが通ることも確認した。ここは今回の検証で非常に重要なポイントだった。
もし最初からテストが失敗していたなら、その原因が、
- もともとのバグなのか
- テスト環境の問題なのか
- 今回の調査によるものなのか
を切り分ける必要がある。そのため、まず現在のコードベースがどのような状態なのかを実行によって確認する。読むだけではなく、動かして確認する。これはコーディングエージェントを評価するうえで、かなり重要な違いだと思う。
6. 「怪しいところ」を列挙する
ここまで調査したところで、AI は問題の可能性がある箇所を洗い出していった。
その数は、なんと 22 項目に及んだ。最初にこの数字を見たとき、」「22個もバグを見つけたのか?と思うかもしれない。しかし、今回の検証ではそれを成功とは判定しなかった。むしろ、ここからが重要だった。
22 項目すべてをバグとして扱うなら、コードレビューとしては簡単だからだ。
「ここも怪しい」「ここも危険そう」「これもバグかもしれない」
と列挙するだけなら、いくらでも候補を増やせる。
本当に見たいのは、その22項目を、どこまで正しく判定できるかだった。
7. 22 項目を一つずつ検証する
AI は挙げた候補について、それぞれコードやテスト、実際の処理内容を確認しながら検証していった。
例えば、テストの期待値に関する問題や、並行処理に関する問題。イベントや Outbox に関する問題。決済状態に関する問題。キャッシュやRepositoryに関する問題。金額計算に関する問題。一見すると、どれも「バグかもしれない」。しかし、コードベース全体を確認すると、必ずしもそうとは限らない。
そこで、それぞれを次のように分類していった。
判定 | 意味 |
|---|---|
| Confirmed Bug | コードを確認した結果、実際にバグである |
| Likely Bug | 問題の可能性が高いが、仕様などの追加確認が必要 |
| Specification-dependent | 仕様次第で正誤が変わる |
| False Positive | 問題に見えるが、実際にはバグではない |
この分類を行ったことで、単なる「バグ候補の大量検出」から、候補を検証して正しいものだけを残す作業へ進んだ。
8. 22個すべてを「バグ」としなかった
ここが Level 6 の中でも特に興味深かった部分だった。
AI は 22 個の問題候補を挙げた。しかし、そのすべてを「バグ」と断定したわけではない。コード全体を調べることで、「これは一見すると問題に見えるが、実際には仕様上成立している」というものも切り分けていった。
また、「実装だけでは判断できず、仕様が分からなければバグとは断定できない」というケースもあった。これは、今回の検証で非常に重要な結果だと思う。なぜなら、実際の開発で AI を使う場合、バグを見つける能力だけではなく、「バグではないものをバグと報告しない能力」も重要だからだ。
False Positive が大量に含まれるレビュー結果では、人間がそれを一つずつ確認しなければならない。
一方で、候補を絞り込み、
- 「これは問題」
- 「これは仕様次第」
- 「これは問題ではない」
と分類できれば、AI による調査結果を人間が利用しやすくなる。
9. そして、確定した問題
最終的に、今回の調査で特に重要だったのは 2 つのバグだった。
一つは、注文処理における checkout_keyの競合問題。
もう一つは、送料無料判定の計算ロジックだった。
この二つは性質がまったく違う。
checkout_key の問題は、複数の処理が同時に実行されたときの状態整合性に関係する、並行処理と Race Condition の問題である。
一方、送料無料判定は、割引や商品の金額をどの順番で計算するかという、ビジネスロジックの問題だった。
この二つを AI が同じ調査の中から見つけたことは、今回の検証においてかなり興味深い結果だった。ただし、この段階ではまだ「バグを見つけた」で終わりではない。
本当に重要なのは、その問題を正しく修正できるのか?、そして、修正した結果、別の問題を作らないのか?ということだ。
10. Level 6は「発見」で終わらなかった
今回の検証では、バグ候補を報告させるだけで終わらせなかった。
確定した問題について、AI自身に修正まで行わせた。さらに、修正内容を検証するためのテストも追加させる。その後、既存テストと追加テストをまとめて実行する。ここで初めて、調査 → バグ発見 → 修正 → テストという一連の作業になる。
そして、この後さらにもう一段階進めた。「では、今行った自分の修正を、もう一度レビューしてください」と AI に指示した。
これは単なるコード生成ではない。一つの開発作業を、調査・判断・実装・検証・レビューまで一通り経験させるテストだった。
11. 最終的に何が分かったのか
Level 6 を実行して分かったことは、「Qwen3.6 27B が 22 個のバグを見つけた」という単純な話ではない。
むしろ重要なのは、約 20 ファイルのコードベースを自分で調査し、問題候補を複数挙げ、その中から False Positive を切り分け、実際のバグを特定できたという点だった。
しかも、その後には修正とテストが続く。ここまで来ると、検証対象は明らかに「コード生成能力」だけではなくなっている。
AI が、
- コードベースを読む
- 必要な情報を探す
- 仮説を立てる
- 実際に確認する
- バグかどうか判断する
- コードを修正する
- テストで確認する
- 自分の修正を再レビューする
という、一連の開発作業をどこまで進められるのか。今回の Level 6 は、それを確認するための最終試験になった。そして次章では、22 項目の候補の中から、実際に**「これは本当にバグだった」**と判断された 2 つを詳しく見ていく。
一つは並行処理の Race Condition。
もう一つは、送料無料判定というビジネスロジックの問題だ。
性質のまったく異なる 2 つのバグを、AI がどのように見つけ、どのように原因を特定したのか。ここからは、実際のコードを見ながら確認していく。
第8章 そして、本当にバグを見つけた
第7章では、Level 6 で AI が約 20 ファイルのコードベースを調査し、22 項目の問題候補を挙げたことを紹介した。しかし、今回の検証で重要だったのは、22 個という数字ではない。
その中には、仕様上問題がないものや、追加の情報がなければバグとは断定できないものも含まれていた。AI はそれらを一つずつ検証し、最終的に本当に問題だと判断できるものを絞り込んだ。
その中でも特に興味深かったのが、次の 2 つだった。
バグ | 種類 | 問題の本質 |
|---|---|---|
| checkout_key の競合 | 並行処理 | Race Condition による状態の不整合 |
| 送料無料判定 | ビジネスロジック | 割引後金額と送料判定の計算順序 |
この2つは、まったく性質が違う。
一つはコンピューターサイエンス的な並行処理の問題。もう一つは、業務ルールをコードに落とし込む際のロジックの問題である。
それを同じコードベースの調査から見つけた。
ここでは、この2つを順番に見ていく。
Bug #1 checkout_key の競合
最初の問題は、チェックアウト処理で利用される checkout_key に関するものだった。
一見すると、コード自体はそれほど複雑ではない。あるキーがすでに存在するかを確認し、存在しなければ新しい状態を作成する。単一の処理だけを考えれば、自然な実装に見える。
しかし、問題は同じ処理が同時に実行された場合。
概念的には、次のような処理になっている。
- checkout_key が存在するか確認
- 存在しなければ新しく作成
- その状態を保存
一つのリクエストだけが実行されるなら、これで問題は起きない。
ところが、同じ checkout_key に対して複数のリクエストがほぼ同時に到着すると状況が変わる。例えば、リクエスト A とリクエスト B が同時に処理されたとする。
時間 | リクエスト A | リクエスト B |
|---|---|---|
| T1 | checkout_key を確認 | |
| T2 | 「存在しない」と判断 | checkout_key を確認 |
| T3 | 「存在しない」と判断 | |
| T4 | 新しい状態を作成 | |
| T5 | 新しい状態を作成 |
A も B も「まだ存在しない」と判断してしまう。その結果、本来一つだけ存在するべき状態が、複数作られてしまう可能性がある。
これが Race Condition である。
なぜ通常のテストでは見つけにくいのか
この問題が難しいのは、普通にテストしているだけでは再現しにくいことだ。
例えば、
リクエストA
↓
処理完了
↓
リクエストB
という順番で実行される限り、問題は起きない。問題が発生するのは、
リクエストA ─────┐
├─ 同時実行
リクエストB ─────┘
のような状況だからだ。つまり、
コードを一行ずつ読んでいるだけでは気付きにくく、実行タイミングまで考える必要がある。
今回 AI がこの問題を指摘したことは、単純な構文チェックやコードスタイルのレビューとは明らかに違う。AI は処理の順序と共有状態に着目し、複数のリクエストが同時に実行された場合の挙動を考えていた。
問題の本質
この問題の本質は、
「存在確認」と「作成」が一つの不可分な操作になっていない
ことだった。
確認した直後に別の処理が割り込む可能性がある。
そのため、
存在確認
↓
← ここで別の処理が入れる
↓
作成
という隙間が生まれる。
このような処理は、並行実行される環境では注意が必要になる。必要なのは、単純にコードを修正することではない。共有状態をどのように保護するかという設計上の問題になる。
今回の検証では、最終的に Lock を利用してこの競合状態を防ぐ方向で修正することになった。
Bug #2 送料無料判定
二つ目の問題は、まったく違う種類だった。
こちらは並行処理ではない。金額計算に関するビジネスロジックの問題である。対象となったのは送料無料になる条件の判定だった。
例えば、「一定金額以上の購入で送料無料」というルールがあるとする。一見すると単純な条件に見える。しかし、ECサイトのようなシステムでは、商品金額、割引、クーポン、送料、税金など、複数の金額が存在する。そして、送料無料判定にどの金額を使うのかによって結果が変わる。
割引前か、割引後か
今回問題になったのは、商品の金額と割引額をどのような順番で扱うかだった。
概念的には、
商品金額
↓
割引
↓
割引後の商品金額
↓
送料無料判定
となるべきところが、実装では判定に使用する金額の扱いが適切ではなかった。
例えば、
- 商品金額:6,000円
- 割引:1,500円
- 割引後:4,500円
- 送料無料条件:5,000円以上
というケースを考える。
割引後の金額を基準にする仕様なら、4,500円なので送料無料ではないという判定になる。ところが、割引前の商品金額を基準にすると、6,000円なので送料無料となる。同じ注文なのに、計算の順番によって結果が変わってしまう。
これは単なる計算ミスではない
この問題が興味深いのは、コードだけを見ると、それほど不自然に見えないことだ。変数の名前も自然。計算式も成立している。型も間違っていない。プログラムとして実行すれば、当然エラーも発生しない。
それでも、業務ルールに照らすと結果が間違っている。
つまり、プログラムとして動くこととシステムとして正しいことは別だということが分かる。これは実際のソフトウェア開発では非常に重要な問題である。
AIはコードの外側まで考える必要がある
送料無料判定の問題を見つけるには、単純に、「この計算式は文法的に正しいか?」を見るだけでは足りない。必要なのは、「この金額は、業務上何を意味しているのか?」という理解である。変数 → 計算 → 業務ルール → 最終的なユーザーへの結果までつなげて考えなければならない。
今回 AI がこの問題をバグとして特定したことは、単なるコードの静的チェックとは違う意味を持つ。
二つのバグを並べてみる
この二つのバグは、原因も性質も大きく異なる。
項目 |
| 送料無料判定 |
|---|---|---|
| 分野 | 並行処理 | ビジネスロジック |
| 問題 | Race Condition | 計算・判定ロジック |
| 発生条件 | 同時実行 | 特定の金額・割引条件 |
| 通常の実行 | 問題が見えにくい | 正常に動作する |
| 発見に必要な視点 | 実行タイミング・共有状態 | 業務ルール・計算順序 |
| テストの難しさ | 同時実行の再現が必要 | 境界条件のテストが必要 |
この二つを見比べると、今回の検証で確認したかったことがよく分かる。もし AI が単純なコード生成しかできないのであれば、
- 文法的に正しいコード
- 一般的な実装パターン
- 既存テストが通るコード
を作るところまではできても、こうした問題を見つけるのは難しい。しかし実際には、コードベース全体を読み、処理の流れを追い、実行条件を考え、業務ロジックまで確認することで、異なる種類の問題を見つけることができた。
もちろん、これだけで「27B モデルなら人間のコードレビューを完全に代替できる」とは言えない。今回のコードベースは、あくまで検証用に用意したものだ。
それでも、ローカルの27B クラスのモデルでも、単純なコード生成を超えて、既存コードを調査し、異なる種類のバグを切り分けられる可能性がある
ことは確認できた。
そして、ここで終わらせると、まだ半分しか検証していない。バグを見つけることと、正しく直すことは別だからだ。
見つけただけでは終わらない
今回の Level 6 では、ここからさらに AI に仕事を任せた。
見つけたバグについて、
- 原因を確認する
- 修正方法を考える
- コードを修正する
- 再発防止のテストを追加する
- 既存テストも含めて実行する
- 最後に自分の修正内容をもう一度レビューする
ところまで進めた。ここまでやって初めて、「AI がバグを見つけた」ではなく、「AI が開発作業の一連の流れをどこまで任せられるか」という今回の本当のテーマに到達する。次章では、この二つのバグを実際に修正し、テストを追加する。
そして最後に、「今行った自分の修正を、もう一度レビューしてください」と AI 自身に問い直す。バグ発見の次に待っているのは、修正能力と、その修正を自分で疑う能力の検証である。
第9章 見つけただけでは終わらない
第8章では、Level 6 の調査によって、二つの重要なバグが見つかった。一つは checkout_key の競合による Race Condition。もう一つは、送料無料判定におけるビジネスロジックの問題だった。
しかし、今回の検証で本当に確認したかったのは、AI はバグを見つけられるのか?だけではない。実際の開発では、バグを報告しただけでは仕事は終わらない。原因を調べ、修正し、テストを書き、既存機能が壊れていないことを確認する必要がある。そこで、ここからは AI 自身に見つけた問題を修正させることにした。
まず原因を確認する
最初に行ったのは、単純なコード変更ではない。見つかった問題について、もう一度原因を確認することだった。
これは意外と重要だ。例えば Race Condition を見つけたからといって、単純に Lock を一つ追加すればよいとは限らない。
どこをロックするのか。どの処理と同じロックを共有するのか。複数のLockがある場合、取得順序はどうするのか。ロックによって別の問題が発生しないか。
こうしたことまで考える必要がある。今回の checkout_key の問題でも、単に「Lock を入れる」という修正ではなく、既存の状態管理との関係を確認しながら修正方法を検討していった。
一方、送料無料判定については、金額計算のどの段階を基準にすべきなのかを確認する必要がある。
つまり、二つのバグに対して、それぞれ異なる観点から原因を確認することになった。
Bug #1を修正する
まず checkout_key の Race Condition を修正する。
問題の本質は、複数の処理が同時に、「このキーはまだ存在しない」と判断できてしまうことだった。そのため、状態を確認してから作成するまでの処理が、並行実行によって分断されないようにする必要がある。
今回の修正では Lock を利用して、この共有状態へのアクセスを保護する方向で実装を変更した。
ただし、ここで重要なのは Lock を追加することそのものではない。Lock を使うコードでは、別の問題が発生する可能性がある。例えば複数の Lock を異なる順番で取得すると、次のような状態になる。
処理A | 処理B |
|---|---|
| Lock Aを取得 | Lock Bを取得 |
| Lock Bを待つ | Lock Aを待つ |
この状態になると、お互いに相手が Lock を解放するのを待ち続ける。いわゆる deadlock である。そのため、Race Condition を修正するだけでは十分ではない。修正によって新しい並行処理の問題を作っていないかまで確認する必要がある。
Bug #2を修正する
次に送料無料判定を修正する。こちらは Lock とはまったく関係がない。問題となっていたのは、送料無料の判定に使用する金額だった。
商品の金額、割引、最終的な商品金額など、複数の金額が処理される中で、送料無料判定が正しい段階の金額を参照するように修正した。
ここで重要なのは、単純に計算式を変更するだけではない。境界値も確認する必要がある。例えば、
- 送料無料条件をちょうど満たす場合
- 条件を1円下回る場合
- 割引によって条件を下回る場合
- 割引がない場合
などだ。こうしたケースをテストに含めなければ、今回のようなバグが再び入り込む可能性がある。
テストを追加する
修正しただけでは、まだ終わらない。そこで、今回見つかった二つの問題について、再発防止のためのテストを追加した。ここがコーディングエージェントとしての評価では重要だった。
AI が、「ここを修正しました」と言うだけなら、通常のコード生成と大きな違いはない。しかし、「この問題が再発しないことを確認するには、どんなテストが必要なのか」まで考えられるなら、話が変わってくる。
今回追加したテストでは、それぞれのバグが再発した場合に検出できる条件を確認する。特に Race Condition については、単純な一回の実行だけでは問題を捉えられないため、並行実行時の状態を意識する必要がある。
送料無料判定については、割引と送料無料条件の境界を確認する。
単にテストの数を増やすのではなく、今回見つかったバグを、将来のテストで再び検出できるようにすることが目的だった。
12 テストから 16 テストへ
修正前には、既存のテストが 12 件あった。そこに今回のバグを検証するためのテストを追加した。結果は、12 existing tests → 16 tests となった。
そして、16 tests PASS となった。
これは今回の検証の中でも、かなり分かりやすい結果だった。単に、「AI がバグを直しました」という自己申告ではない。修正前から存在していたテストを含めて、追加したテストもすべて実行し、全件が PASS した。
もちろん、テストがすべて PASS したからといって、ソフトウェアに絶対にバグがないとは言えない。
しかし少なくとも、今回修正したコードが、既存のテストを壊さず、新しく追加した問題検出テストも通過したことは確認できた。
しかし、ここで終わらせなかった
普通なら、ここで検証を終了してもよい。
バグを発見した。
修正した。
テストを追加した。
16 個のテストがすべて PASS した。
十分に見える。しかし、今回はもう一つ試してみることにした。AI 自身に、自分が行った修正をもう一度レビューさせる。指示はシンプルだった。
「では、今行った自分の修正をもう一度レビューしてください。」
ここが今回の検証で特に面白かった部分だった。AI に自分の仕事をもう一度疑わせる。
自分の修正を疑わせる
再レビューでは、単に、「テストは PASS しています」で終わらせなかった。修正そのものに問題がないかを、もう一度コードベース全体から確認させた。
特に確認したのは、次のような点だった。
確認項目 | 確認したこと |
|---|---|
| Lock ordering | 複数 Lock の取得順序に問題がないか |
| Deadlock | 修正によってデッドロックが発生しないか |
| 状態遷移 | checkout の状態管理を壊していないか |
| 冪等性 | 同じ処理を繰り返しても不整合が起きないか |
| Shipping calculation | 送料無料判定の修正が他の金額計算に影響しないか |
| 副作用 | 今回の修正によって別の機能が壊れていないか |
ここで重要なのは、修正した箇所だけを見直すのではなく、修正による影響範囲まで確認していることだった。特に並行処理の修正では、Race Condition を防ぐために Lock を入れた結果、Deadlock を作ってしまうということもあり得る。
また、送料無料判定についても、判定を正しくした結果、別の金額計算や注文処理に副作用が発生する可能性がある。そのため、再レビューでは「修正そのものが正しいか」だけではなく、修正によって別の問題が生まれていないかという視点も必要になる。
「バグを見つける」から「開発作業を完了する」へ
ここまでを振り返ると、今回AIに任せた作業はかなり長い。
段階 | AIが行った作業 |
|---|---|
| 1 | README・プロジェクト構造を調査 |
| 2 | 複数ファイルの実装を確認 |
| 3 | 既存テストを確認 |
| 4 | pytest を実行 |
| 5 | 問題候補を抽出 |
| 6 | 22 項目を検証 |
| 7 | False Positive を切り分け |
| 8 | 2 つのバグを特定 |
| 9 | 原因を分析 |
| 10 | コードを修正 |
| 11 | 回帰テストを追加 |
| 12 | 16 テストを実行 |
| 13 | 全テスト PASS を確認 |
| 14 | 自分の修正を再レビュー |
これは、最初に想定していた単純な「コード生成」とはかなり違う。一つの質問にコードを返して終わるのではなく、一つの開発作業を最初から最後まで進めるところまで来ている。もちろん、すべてを人間の確認なしで任せられるという意味ではない。
しかし、27B クラスのローカル LLM でも、適切なコーディングエージェントと組み合わせれば、ある程度まとまった開発作業を実際に遂行できるということは、今回の検証から確認できた。
ここで一つの疑問が残る
では、これは実用レベルなのだろうか。
今回、AIはバグを見つけた。
しかも、性質の違う二つのバグだった。
それを修正した。
テストも追加した。
16個のテストをすべてPASSさせた。
さらに、自分の修正をもう一度レビューした。
ここまでできるなら、「27BのローカルLLMでも、コーディングエージェントとして十分使えるのではないか?」と錯覚するかもしれない。
だから、ここで慎重になる必要がある。今回のコードベースは、あくまで検証用に用意したものだ。実際の巨大なプロジェクトとは規模が違う。数十万行、数百万行のコードベースでも同じように動くとは限らない。
また、今回確認できたのは、あくまで今回設定した条件の範囲である。そのため当然ながら、「27Bモデルなら何でもできる」という結論にはならない。
むしろ、今回の検証から言えるのはもっと限定的だ。「少なくとも今回の環境と今回の規模のコードベースでは、27B クラスのローカル LLM に、かなり実際の開発に近い作業を任せることができた」ということである。
この違いは重要だ。
そして、検証の目的に戻る
そもそも今回の検証を始めたときの疑問は、「27B クラスのローカルモデルを、単なるチャットではなくコーディングエージェントとして使ったらどうなるのか?」だった。
ここまでの結果を見ると、その答えはかなり見えてきた。少なくとも、
- コードを読む
- 複数ファイルを調査する
- テストを実行する
- バグ候補を探す
- False Positiveを排除する
- バグを特定する
- コードを修正する
- テストを追加する
- 全テストを実行する
- 修正内容を再レビューする
ところまでは実際にできた。もちろん、これだけで「人間の開発者と同等」とは言えない。しかし、「ローカルLLMだから、コーディングエージェントとしては実用にならない」とも言えなくなった。ここまで来ると、次に考えるべきなのは、では、どこまでが実用範囲なのか?ということになる。
次章では、今回の検証全体を振り返り、27B クラスのローカル LLMで「できたこと」と「まだ分からないこと」を整理する。そして最後に、今回の検証をどこで終えるべきなのかを考える。
第10章 結局、27B ローカルLLMは使えるのか?
ここまで、かなり遠回りをしてきた。Ollama を試し、Claude Code や Codex を試した。LiteLLM を導入してみた。さらに vLLM も試した。
その結果、最終的に残った構成は、
OpenCode + Ollama + Qwen3.6 27B
という、比較的シンプルなものだった。そこから Level 1 〜 6 まで、AI に任せる仕事を少しずつ難しくしていった。最後の Level 6 では、約 20 ファイルのコードベースを調査させ、22 個の問題候補を挙げさせた。その中から False Positive を切り分け、実際のバグを特定した。さらに、そのバグを修正し、テストを追加し、16個のテストをすべてPASSさせ、最後には自分の修正内容まで再レビューさせた。
では、結局どうだったのか。
まず、「できたこと」
今回の検証で、実際に確認できたことを整理してみる。
項目 | 結果 |
|---|---|
| 複数ファイルのコード理解 | できた |
| コードベースの調査 | できた |
| ファイル操作・編集 | できた |
| テストの実行 | できた |
| デバッグ | できた |
| バグ候補の抽出 | できた |
| False Positive の切り分け | できた |
| 並行処理の問題分析 | できた |
| ビジネスロジックの問題分析 | できた |
| コード修正 | できた |
| 回帰テストの追加 | できた |
| 全テストの実行 | できた |
| 修正内容の再レビュー | できた |
ここで特に重要なのは、単にコードを書けたという話ではないことだ。今回確認できたのは、既存のコードベースを調査し、問題を発見し、その問題を修正し、テストによって確認するところまで一連の作業を進められたということである。これは、最初に想定していた「ローカル LLM でコード生成ができるか」という話とは、かなり違う。
では、人間の開発者はいらないのか?
もちろん、そんな話ではない。今回の検証結果を見て、「27Bモデルに開発を全部任せられる」と結論づける人はいないだろう。今回できたことと、まだ確認できていないことには大きな差がある。
例えば、次のような課題については、今回の検証だけでは判断できない。
まだ分からないこと | 理由 |
|---|---|
| 巨大なコードベース | 今回は約 20 ファイル規模だった |
| 非常に複雑なアーキテクチャ | 大規模システムまでは検証していない |
| 大規模リファクタリング | 長期間にわたる変更は試していない |
| 曖昧な要件からの設計 | 今回は検証対象が比較的明確だった |
| 数時間〜数日単位の自律作業 | 長時間の完全自律運用は確認していない |
| 長期的な安定性 | 一つの検証プロジェクトだけでは判断できない |
| 大規模プロジェクトでのコンテキスト管理 | 今回の規模では限界が表面化しなかった |
つまり、「できることが分かった」一方で、「どこまでできるかは、まだ分からない」のである。
27Bというサイズについて考える
今回使用したのは 27B クラスのモデルだった。現在のローカルLLMの世界では、もっと大きなモデルも存在する。そのため、「27Bは小さいモデルだから、簡単なことしかできない」と思われるかもしれない。
しかし、今回の結果を見る限り、それは少し違う。もちろん、より大きなモデルのほうが複雑な推論や長いコンテキストで有利になることは間違いない。しかし、今回の環境では 27B クラスでも、適切なコーディングエージェントと組み合わせることで、かなり実用的な開発作業を行うことができた。つまり、モデルのパラメータ数だけを見て、「27Bだから実用にならない」と判断するのは適切ではない。重要なのは、モデル × エージェント × ツール × コンテキスト × 推論基盤の組み合わせだった。
「ローカルだから遅い」という問題もある
一方で、今回の環境には当然ながら制約もある。ローカル LLM では、クラウド API のように巨大な計算資源を簡単に利用できるわけではない。
モデルのロードには時間がかかる。
推論速度にも限界がある。
長い処理では待ち時間も発生する。
今回の検証でも、最初のモデルロードや推論にはそれなりの時間が必要だった。それでも、今回重要だったのは、多少時間がかかっても、実際に仕事を完了できるのかという点だった。
人間が一からコードベースを調査することを考えれば、数分、場合によってはそれ以上の処理時間が必ずしも致命的とは限らない。もちろん、リアルタイム性が必要な用途では話が変わる。
ここでも、「速いか遅いか」だけではなく、「その待ち時間に対して、得られる作業量が見合っているか」という視点が必要になる。
ローカルLLMの最大のメリット
今回、検証を続ける中で改めて感じたのは、ローカル LLM には性能以外の価値もあるということだった。
自分の環境でモデルを動かせる。
外部 API に依存しない。
モデルや推論基盤を自分で選べる。
必要に応じて環境を変更できる。
そして、今回のように、失敗した構成をやめて別の構成へ戻すこともできる。
今回も、Ollama から始まり、LiteLLM を試し、vLLM を試し、最終的に Ollama へ戻った。これは、クラウド API を利用するうえでは必要のないことだから、その意味では得にくい経験だったと思う。
もちろん、その代わりに、ソフトウェア、ハードウェアとも自分で管理する必要がある。ローカルLLMは「無料で簡単」というものではない。自由度と引き換えに、自分で環境を管理する必要がある。今回の検証は、それも含めたものだった。
「実用になる」の意味を考える
ここで、「実用になる」という言葉についても整理しておきたい。
もし、「人間のシニアエンジニアと同じ仕事を完全自律でできるか?」という意味なら、今回の答えは明確に No である。そこまでの検証はしていない。
しかし、「人間がすべてのコードを書かなければならないのか?」という意味なら、答えは変わってくる。今回の検証では、AI にかなりの部分を任せることができた。人間が必要なのは、AI の作業を一行ずつ指示することではなく、結果を確認し、重要な判断を行うことだった。
既に言われていることを再確認しただけじゃないか、とも言えるけれど、いずれにしてもこの違いは大きい。
今回の結論
ここまでの結果から、今回の環境については次のように考えている。
ーできたー
- 複数ファイルのコード理解
- コードベース調査
- テスト実行
- デバッグ
- バグ候補の抽出
- False Positive の排除
- 並行処理の問題分析
- ビジネスロジックの問題分析
- コード修正
- 回帰テスト追加
- 全テスト実行
- 修正内容の再レビュー
ーまだ分からないー
- 巨大なコードベース
- 非常に複雑なアーキテクチャ
- 大規模リファクタリング
- 曖昧な要件からの設計
- 数時間〜数日単位の完全自律作業
- 大規模プロジェクトでの長期的な安定性
そして、この二つを合わせると、今回の答えは次のようになる。
「何でも任せられる」とは言えない。
しかし、
「ローカルLLMだから実用にならない」とも言えない。
むしろ、「適切な範囲を見極めれば、27B クラスのローカル LLM でも、コーディングエージェントとして十分に実用的な仕事を任せられる」というのが、今回の検証から得られた結論だった。
では、どこまで検証すれば十分なのか?
ここで最後に、一つの問題が残る。
「では、もっと大きなコードベースなら?」
「もっと難しいバグなら?」
「もっと長いコンテキストなら?」
「100 ファイルなら?」
「1,000 ファイルなら?」
「数日間、自律的に動かしたら?」
こうした疑問はいくらでも出てくる。そして、これらを人工的な検証環境で一つずつ試していくこともできる。しかし、どこまでやれば十分なのだろうか。
今回の検証を始めた当初は、「27B モデルがどこまでできるのか知りたい」と思っていた。しかし、Level 6 まで到達してみると、少し考え方が変わってきた。
今度は、「これ以上、人工的なテストを続けることにどれだけ意味があるのか?」という疑問が出てきた。
今回の環境で、すでにかなり実際の開発に近い作業を任せられることは確認できた。だったら、次にやるべきことは、さらに難しいテストを人工的に作ることではないのかもしれない。
実際のプロジェクトで使ってみること。
そこで初めて、本当の実用限界が見えてくる。そして、そのときに問題が発生したら、その問題を改めて検証すればいい。ここまで来たところで、今回の人工的な能力検証には、一つの区切りをつけることにした。
最終章 だから、検証を終える
ここまで、かなり長い道のりだった。
最初は、
27B クラスのローカルLLMを、コーディングエージェントとして実際の開発に使えるのか?
という、単純な疑問から始まった。しかし、実際に環境を作ってみると、モデルを動かすだけでは終わらなかった。
APIの互換性。
ツール呼び出し。
コンテキスト。
推論速度。
認証。
プロキシ。
推論サーバー。
ROCm。
GPU。
モデルフォーマット。
そして、コーディングエージェントそのもの。
一つを解決すると、別の問題が出てきた。
最初は Ollama だった
最初に使ったのは定番の Ollama だった。ローカル LLM を動かすという目的では、比較的シンプルに環境を作ることができた。そこから Claude Code や Codex と組み合わせて、実際にコーディングエージェントとして使ってみた。すると、単純なチャットとは違う問題が見えてきた。
モデルが文章を生成できるだけでは足りない。エージェントには、
- ファイルを読む
- ファイルを編集する
- コマンドを実行する
- テストを実行する
- 結果を解釈する
- 次の処理を判断する
という能力が必要になる。LLM の性能だけではなく、LLM を取り巻く仕組み全体が重要だということが分かってきた。
LiteLLMを入れてみた
次に考えたのが、もっと柔軟な構成だった。複数のモデルや API を扱うのであれば、中間に LiteLLM を置けば便利なのではないか。そう考えて導入してみた。
確かに便利な部分はある。しかし、その一方で、
- 認証
- APIキー
- モデル名のマッピング
- OpenAI互換API
- クライアントごとの挙動
など、別の問題も増えていった。そこで一つのことを学んだ。
便利そうだから中間層を増やすのではなく、必要だから増やす。
これは今回の検証全体を通じて、今更ながら、かなり大きな教訓になった。
vLLM にも挑戦した
それでも、「もっと本格的な推論基盤を使えば、さらに良くなるのでは?」という考えは残った。そこで vLLM にも挑戦した。Hugging Face からモデルを取得し、Docker で環境を構築し、ROCm 上で動かす。Tensor Parallel も試した。AITER や Triton、NCCL など、さまざまな要素を確認した。
しかし、ここでは今回のハードウェア Radeon RX 9060 XT 16GB × 2 組み合わせが大きな壁になった。RDNA 4、gfx1200、ROCm、vLLM、モデル、Tensor Parallel。それぞれ単体では成立していても、組み合わせると簡単にはいかなかった。
最終的には、今回のハードウェア・ROCm・モデル・用途の組み合わせでは、vLLM から Ollama から乗り換えるだけのメリットを得られないと判断した。
ここでも、「失敗した」で終わらせなかった。今回は採用しない。それだけである。負け惜しみに聞こえるけれど。
そして、シンプルな構成に戻った
いろいろ試した結果、最終的に残ったのは非常にシンプルな構成だった。
OpenCode→Ollama→Qwen3.6 27B
最初に使っていた Ollama に戻ってきた。随分遠回りだった。しかし、この遠回りには意味があったと思いたい。最初からこの構成だけを使っていたら、「本当にこれが最適なのか?」という疑問が残っていたかもしれない。
LiteLLMを試した。vLLMも試した。
その結果として、今の自分の環境では、シンプルな構成が一番合理的だと判断できた。これは単なる「最初に戻った」という話ではない。一周して、理由を持って戻ってきた。
そこから Level 1 が始まった
環境が決まったところで、今度はモデルそのものの能力を確認することにした。いきなり難しい課題を与えるのではなく、Level 1 から始めた。少しずつ、AI に任せる仕事を増やしながら。
最初は単純なコード理解や操作だった。そこからファイル操作、テスト、デバッグへ進み、複数ファイルのコードベースを扱うようになった。そして最後には、「このプロジェクトの既存コードを調査して、潜んでいるバグを見つけてください」というところまで進んだ。
Level 6 で見えたもの
Level 6 では、約 20 ファイルのコードベースを AI に調査させた。README を読み、ファイル構造を確認し、実装を読み、テストを確認し、pytest を実行する。そして問題候補を挙げる。その数は 22 項目だった。
しかし、22 個すべてをバグとはしなかった。コードをさらに調査し、これは本当にバグなのか?を一つずつ確認していった。その結果、実際の問題として重要だったものが見つかった。
一つは、
checkout_key の競合による Race Condition。
もう一つは、
送料無料判定のビジネスロジック。
性質の違う二つの問題だった。そして、それだけでは終わらなかった。
修正して、テストした
AI にバグを修正させた。修正に合わせてテストも追加した。既存の 12 テストに新しいテストが加わり、16 tests PASS となった。さらに、「今行った自分の修正を、もう一度レビューしてください」と指示した。
Lock ordering。
Deadlock。
状態遷移。
冪等性。
Shipping calculation。
修正による副作用。
そうした観点から、もう一度自分の変更を確認させた。ここまで来たとき、最初の疑問に対する答えは、かなり明確になっていた。
27BローカルLLMは使えるのか?
今回の答えは、使える。ただし、何でも任せられるわけではない。というものだった。
- 複数ファイルのコードを理解できる。
- コードベースを調査できる。
- テストを実行できる。
- バグ候補を挙げられる。
- False Positive を切り分けられる。
- 異なる種類のバグを分析できる。
- コードを修正できる。
- テストを追加できる。
- 修正内容を再レビューできる。
少なくとも今回の条件では、ここまで実際にできた。一方で、
- 巨大なコードベース
- 非常に複雑なアーキテクチャ
- 大規模リファクタリング
- 曖昧な要件からの設計
- 数時間〜数日単位の完全自律作業
- 大規模プロジェクトでの長期的な安定性
については、まだ分からない。
だから、「27B なら何でもできる」とは言えない。しかし、「ローカル LLM だから実用にならない」とも言えない。今回の検証で確認できたのは、その中間だった。
では、さらに検証するべきなのか?
ここで、今回の検証を続けることもできる。
- もっと大きなコードベースを用意する。
- もっと難しいバグを仕込む。
- もっと長いコンテキストを要求する。
- もっと複雑なタスクを与える。
- Level 7、Level 8……と続けていくこともできる。
しかし、ここで一度立ち止まった。
どこまでやれば十分なのだろうか。
- 100ファイルならできるのか。
- 1,000ファイルならどうなのか。
- 10時間動かしたらどうなるのか。
- もっと複雑な設計ならどうなのか。
こうした疑問には、きっと終わりがない。そして、人工的に作ったテスト環境でどこまでも難易度を上げていくより、もっと有意義な方法がある。
実際のプロジェクトで使ってみることだ。
本当の実用限界は、実プロジェクトで分かる
今回の検証環境には、明確な限界がある。どれだけ現実に近づけても、所詮は「検証のために作ったプロジェクト」だ。実際の開発では、
- 要件が途中で変わる
- 仕様書が不完全
- 過去の設計判断が残っている
- ドキュメントが古い
- 他のコードとの依存関係がある
- 人間のレビューが入る
- Git の履歴がある
- 本番環境がある
- 納期がある
など、さまざまな要素が絡んでくる。そして、そこで初めて、「この環境なら、どこまでAIに仕事を任せられるのか」という本当の答えが見えてくる。
だから、ここから先は人工的な Level を増やすのではなく、実際のプロジェクトに投入してみる。そこで問題が起きたら、その問題を改めて調べる。もし、それが今回の環境の限界だったなら、そこで初めて次の対策を考えればいい。
検証を終えるということ
今回の検証を終えることは、「もう問題はない」という意味ではない。むしろ逆で、これから実際に使って、問題を見つける段階に進む。今回の検証は、「少なくとも、この環境なら実際の開発作業を任せてみる価値がある」というところまでだった。それなら、ここから先は実際に使ってみるしかない。
- AI に仕事を任せる。
- 結果を見る。
- 問題があれば、人間が確認する。
- 必要なら環境を改善する。
- そこで得られた経験をまた次の判断につなげる。
そのほうが、人工的なベンチマークを延々と続けるより、ずっとマシだと思う。
最後に
今回の検証を始めたとき、私は「27B モデルでどこまでできるのか」を知りたかった。しかし、終わってみると、少し違うことが分かった。重要なのは、モデルのサイズだけではない。モデル、エージェント、ツール、コンテキスト、推論基盤、そして人間の使い方。それらが組み合わさって、初めて「使える AI コーディング環境」になる。
そして、最も大きかった発見は、
ローカルLLMでも、コーディングエージェントとして実際の開発作業を任せられるところまで来ている。
ということだった。もちろん、まだ万能ではない。人間の確認も必要だ。できないこともある。性能の限界もある。それでも、ローカルLLMだから実用にならない」と最初から決めつける必要はない。少なくとも今回の環境では、「実際のプロジェクトで使ってみよう」と思えるところまで来た。だから、今回の検証はここで一旦終了する。
これ以上、人工的に難しい問題を作って、「27B モデルはあとどこまでできるのか?」を追い続けることはしない。これからは、実際のプロジェクトで使ってみる。そこで問題が出たら、そのとき改めて考える。もしかすると、すぐに限界が見つかるかもしれない。あるいは、思っていた以上に使えるかもしれない。
その答えは、もう検証用のプロジェクトではなく、実際の開発の中で確かめることにする。そして、そのとき何が起きたのか。
それは、また別の記事で書くことにしたい。