レガシー移行を「読み取り範囲を絞ったAIエージェント編成」で回す設計

概要

10年もののレガシーモノリス(旧世代の PHP / Laravel)を、複数のマイクロサービス(Laravel 12)+ Vue 3 フロントエンドへ移行する案件を担当している。移行対象は数百エンドポイント、共有モデルは600超。人間ひとりで「どこまで移ったか」「旧ロジックが正しく写っているか」を追い切るのは現実的ではない。

そこで、Claude Code のサブエージェント機能を使い、役割ごとに読み取り範囲を絞った複数のAIスタッフを編成して移行を回す仕組みを組んだ。本稿はその設計——特に「情報隔離」と「ファイル経由の受け渡し」——を再現可能な粒度でまとめたものだ。

環境

  • モノレポ(親リポジトリ)+ Git サブモジュール構成
  • 認証 / メイン業務 / ワークフロー / 外部連携(各 Laravel 12)
  • 共有モデルパッケージ / 共有設定パッケージ
  • フロントエンド(Vue 3 + Vite)は独立リポジトリ
  • ローカルは Docker Compose、DB は MySQL 8 + Memcached
  • 移行元の旧モノリスは参照専用(書き換え禁止)
  • Claude Code のネイティブサブエージェント(.claude/agents/)

発生した問題

AIに移行を丸投げすると、次の失敗が起きる。

  1. ハルシネーション:旧実装を読まずにレスポンス構造を捏造する
  2. 文脈の汚染:フロント・旧実装・新実装の都合を1コンテキストに載せると判断がブレる
  3. 見切り発車:仕様未確定のまま実装を始め、やり直しになる
  4. 依存順序の破壊:共有パッケージより先に個別サービスをいじって整合性を壊す

これらは「AIが賢くない」から起きるのではなく、役割と情報範囲を設計していないから起きる。人間のチームでも、全員に全情報を渡せば同じ事故が起きる。

原因

根本原因は、1つのコンテキストに責務と情報を詰め込みすぎていること。

  • 旧実装の抽出役に新実装を見せると、新実装に引きずられる
  • 契約設計役にコードを触らせると、事実照合ではなく「良かれと思った仕様変更」を始める
  • 実装役に計画抜きで着手させると、既存パターンとの矛盾を裏取りせず突っ走る

つまり、「何を読めるか」「何を出せるか」を制約することが、そのままハルシネーション抑止になる。

対応方法

1. 役割ごとにエージェントを分け、読み取り範囲を物理的に絞る

各エージェントの定義で、使えるツールと読み取り許可範囲を宣言する。抽出役は編集ツールを持たない。契約設計役はコードを一切読まない。

役割 読める範囲 書けるもの
FE分析役 フロントのコードだけ FE契約ファイル
レガシー分析役 旧モノリスのコードだけ 旧ロジック抽出ファイル
契約設計役 抽出済みファイルだけ(コード禁止) 確定契約ファイル
配置判定役 確定契約 + 各サービスのルート定義 配置判定ファイル
実装役 担当サービス + 共有パッケージ 実装 + 実装ログ
検証役 確定契約 + 実装コード 差分検出レポート

ポイントは、分析役はお互いのソースを見ないこと。両者の出力を突き合わせるのは、コードを読まない契約設計役の仕事にする。こうすると「片方に引きずられた解釈」が構造的に生まれない。

2. 受け渡しは会話ではなくファイルで行う

エージェント間の情報は、会話ではなく docs/ 配下のファイルで受け渡す。次工程は前工程の「発言」ではなく「ファイル」を読む。

FE分析     → docs/triage-inputs/{domain}/fe/_index.md
レガシー分析 → docs/triage-inputs/{domain}/storage-plus/_index.md
                  │ この2ファイルだけを入力に
契約設計    → docs/triage-inputs/{domain}/contract.md
                  │
実装        → docs/pipelines/{domain}/30-impl-*.md
                  │
検証        → docs/pipelines/{domain}/40-review-*.md

ファイルに落とせば、次工程はそのファイルだけで文脈を再現でき、サブエージェントを使い捨てにできる。未確認情報には ⚠️UNVERIFIED: を付け、事実と推測を機械的に区別させる。

3. 実装は「計画 → 承認 → 実装」の二段階でゲートする

コード変更は、必ず読み取り専用の planner が計画(20-plan.md)を書き、人間が承認してから executor が実装する。

planner(読み取り専用)
  → 20-plan.md を status: draft で出力
  → 人間が中身を確認して承認
  → status: approved に更新
  → executor が実装(approved でなければ停止)

planner の仕事は、既存コードを深掘りして仕様と実装の矛盾・影響範囲を裏取りすること。疑義は ❓要ユーザー判断: として列挙し、勝手に埋めない。

4. 出力先をフックで機械的に強制する

docs/ 直下や未定義ディレクトリへの書き込みを PreToolUse フックでブロックする(サブエージェント内も対象)。規約は文章だけだとドリフトするので、機械で弾く。

実装例

レガシー分析役の定義の骨格。

---
name: legacy-analyst
description: 旧モノリスのロジック抽出専任。旧コードのみを読む
tools: Read, Grep, Glob, Write
model: opus
---

## 絶対ルール
- 旧モノリスのコードだけを読む。フロントや新サービスは一切見ない
- ロジックを簡略化しない。条件分岐・エッジケースを全て記録する
- 「多分こうだろう」で補完しない。事実だけを出力する

## 読み取り許可範囲
- 旧モノリスのリポジトリのみ

実装役にはゲートを明記する。

## 実装開始の前提条件(ゲート)
- 承認済み計画(20-plan.md, status: approved)が必須
- 無い / approved でなければ実装せず停止する
- 軽微な修正でも例外にしない

確認方法

  • トリアージ入力が「完備」になるまで移行を始めない(manifest でゲート)
  • 実装後は契約検証役(確定仕様 vs 実装)と結合検証役(フロントの実呼び出し vs 実装)の2系統で差分を出す
  • 進捗はスケジュールファイルのチェックボックスを唯一の正本にする

注意点

  • 1回で処理するEPは5〜10件に絞る(コンテキスト保全)
  • 複数ドメインを1コンテキストで連続処理しない
  • サブエージェントは入れ子起動不可。委譲はディスパッチ元を経由する
  • 移行元は書き換え禁止。「無い」と結論する前に必ず fetch

まとめ

AIエージェントを実務で回す肝は、賢いモデルを選ぶことではなく、役割・読み取り範囲・受け渡し方法・着手ゲートを設計することだった。

  • 情報を絞ることがハルシネーション抑止になる
  • 受け渡しは会話でなくファイルで(文脈を再現可能にする)
  • コード変更は必ず計画→承認→実装でゲートする
  • 規約はフックで機械的に強制する

人間のチーム運営で当たり前にやっている「担当分け」「引き継ぎ書」「レビューゲート」を、そのままエージェント編成に写しただけとも言える。組織設計の知見が、そのままAIオーケストレーションに効く——という体験だった。

\ 最新情報をチェック /

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください。