第2回:ローカルLLMが暴走する理由と、その止め方
サブタイトル:巨大プロンプトを解体せよ。TOMLカスタマイズによるコンテキスト制御
はじめに
第1回では、OllamaとRoo Codeを組み合わせたローカルLLM開発環境について紹介しました。
今回はその続きとして、実際に運用してみて見えてきた「AIの暴走」をどう防ぐかを扱います。
ローカルLLMを開発用途で使っていると、次のような挙動に遭遇することがあります。
- 実装済みのファイルを意図せず上書きする
- 関係ないREADMEを延々と書き始める
- ディレクトリ構成を勝手に変える
- 途中まで正しく動いていたのに、急に文脈を見失う
これは単なる“おかしな挙動”ではなく、コンテキストの扱い方やタスクの切り方が適切でないと起きやすい問題です。
そこで今回は、私たちが実際の検証を通じてたどり着いた、ローカルLLMを安定して使うための制御方法をまとめます。
1. 思考のフレームワーク「GitHub Spec Kit」
ローカルLLMで安定した開発を行うには、AIにいきなりコードを書かせるのではなく、先に仕様を固める必要があります。
そのために導入したのが、GitHub Spec Kitです。
Spec Kitは、AI開発を次の流れに整理するための仕組みです。
-
spec.md:何を作るのか、なぜ作るのか -
plan.md:どう実装するのか -
tasks.md:何をどの順番でやるのか
この流れにすることで、AIに対して「考える順番」を与えられます。
Spec Kitを採用した理由
ローカルLLMは、長い会話の中で少しずつ文脈がずれていきやすいです。
そのため、毎回の判断を会話だけに任せると、意図しない実装が混ざりやすくなります。
Spec Kitを使うことで、
仕様 → 設計 → タスク分割 → 実装
という順序を保ちやすくなり、AIの出力を安定させやすくなります。
特に、独立性の高い新規開発や、比較的小さな機能追加では相性が良いと感じました。
2. クラウドとローカルのハイブリッド分業
検証を進める中で、私たちは「全部をローカルでやる」よりも、役割を分けたほうが安定すると分かりました。
役割分担の考え方
| 工程 | 主な役割 |
|---|---|
| 仕様作成 | クラウドAI |
| 実装 | ローカルLLM |
この分業のポイントは、考える部分と手を動かす部分を分けることです。
ローカルLLMは、実装そのものには強みがあります。
一方で、複雑な仕様を長時間にわたって整理し続けるのは得意とは言えません。
そこで、仕様や設計はクラウド側で整理し、実装はローカル側で進める形にすると、全体の破綻が減りました。
3. 現場を凍りつかせた「AIの暴走」
問題は、GitHub Spec Kitの標準的な実装フローを、そのままローカルLLMに食わせたときに起きました。
クラウド向けに作られた大きなプロンプトは、ローカルLLMにとっては重すぎることがあります。
その結果、AIが現在の役割を見失い、想定外の操作を始めてしまうことがありました。
実際に起きた例としては、以下のようなものです。
- 実装済みのファイルを上書きしてしまう
- 指示していないREADMEの編集を始める
- フォルダ構成を勝手に作り替える
これは、AIが「何をするべきか」を見失った状態で、ツール実行の自由度だけが高くなってしまうと起きやすい問題です。
何が問題だったのか
原因は単純な性能不足だけではありません。
- 一度に読み込む情報が多すぎる
- タスクの境界が曖昧
- 役割が明確でない
- 実装前の確認が足りない
こうした要因が重なると、AIは“考えているつもり”でも、実際には文脈を取り違えやすくなります。
4. 解決策:コマンドを細かく分ける
この問題を防ぐために、私たちは一括実行の大きなコマンドをやめ、役割ごとに小さく分割したコマンドへ置き換えました。
ここで使っているのは、Spec Kitの標準フローをベースに、Roo Code側で実運用向けに分割したカスタムコマンドです。
公式の標準コマンドそのものというより、私たちの運用に合わせて整理したものだと考えてください。
考え方としては、AIに一度に多くのことをさせるのではなく、
- まず環境を見る
- 次に設定を見る
- 次に1タスクだけ実装する
- 最後に確認する
という形にします。
具体的な分割のイメージ
たとえば、次のように分けると扱いやすくなります。
- 実装前の準備を確認する
- 無視すべきファイルを整理する
- Task IDを指定して1件だけ実装する
- 変更内容を検証する
このようにすると、AIの視野を狭められるため、余計なファイルに手を出しにくくなります。
5. TOMLカスタマイズで「今やること」を固定する
Roo Codeのカスタムモードでは、TOML設定を使って動作を分けられます。
ここで重要なのは、AIに自由に動かせる範囲を広げることではなく、逆に制約を増やすことです。
どういう制約が効くのか
- 今回扱うタスクを1つに絞る
- 読み込むファイルを限定する
- 変更してよい範囲を狭める
- 実装前後に確認を入れる
こうした制御を入れると、ローカルLLMの挙動はかなり安定します。
なぜ細かく分けるのか
ローカルLLMは、長いプロンプトや複雑な指示を一度に処理すると、重要な前提を落としやすくなります。
そのため、1つのコマンドに詰め込みすぎず、小さく分けて順番に実行させるのが有効でした。
6. Before / After
Before
- 巨大プロンプトを一括投入
- AIが役割を見失う
- 意図しないファイル変更が起きる
After
- 仕様を先に固める
- タスクを分割する
- 1つずつ実装する
- 確認を挟む
この違いはかなり大きく、ローカルLLMを実務で使えるかどうかを分けるポイントになりました。
7. ローカルLLMに必要なのは「自由」より「レール」
AIに丸投げしたくなる瞬間はあります。
ただ、ローカルLLMを開発用途で使う場合は、自由度を上げるよりも、人間がレールを敷くほうが安定します。
そのレールが、今回の文脈では次の3つです。
- Spec Kitで仕様を先に固定する
- タスクを細かく分ける
- 実装の前後で確認を入れる
この3点を押さえるだけでも、AIの暴走はかなり減らせます。
おわりに
ローカルLLMは、正しく使えば非常に強力です。
ただし、クラウドAIと同じ感覚で雑に扱うと、簡単に文脈が崩れます。
今回の検証で見えてきたのは、ローカルLLMは「賢いかどうか」だけではなく、どう制御するかが何より重要だということでした。
次回は、長く開発を続けるための工夫として、Token Limit Summaryによる外部メモリ化と、コンテキスト・エンジニアリングについて紹介します。