8人のエージェントが完璧なコンポーネントを書いた——しかし何も動かなかった

Dev.to / 2026/3/28

💬 オピニオンDeveloper Stack & InfrastructureTools & Practical Usage

要点

  • 並列AIエージェントは、個別には「正しい」コンポーネントを作れても、データベースの列名、APIのパス、パラメータ形式、識別子など明示的な契約(契約情報)を共有していないと統合に失敗することがある。
  • 8人の並列エージェントによるフルスタックAWS構築(CDKインフラ、Reactフロントエンド、Spring Bootバックエンド)では、コンパイルや型チェックは通ったのにもかかわらず、複数の境界でアプリが壊れた。
  • 最初の大きな失敗はスキーマの不一致だった。DBのシードは `passenger_id`/`full_name` を作成していたが、Javaのエンティティは `id`/`name` を期待しており、そのためSpring Bootアプリが起動できなかった。
  • 著者は、共有する契約を単一の参照ファイルに抽出し、生成後にエンドツーエンドのデータフローを追跡するレビューエージェントを追加したことで、1回のパスで17個のバグをすべて修正できたと報告している。
  • 中核となる教訓は、相互運用が必要な複数のエージェントを使う場合、明示的で集約されたインターフェース/契約アーティファクトと、統合レビューの手順を追加することだ。

TL;DR: 並列のAIエージェントは、列名やURLパス、パラメータ形式、識別子などの「共有契約」について調整しません。生成の前に、それらの契約を1つの参照ファイルに抽出し、並列エージェントが完了した後に、データのエンドツーエンドの流れを追跡するレビューエージェントを実行します。この1ステップで、17個すべてのバグを1回の実行で直せました。

AWS上でフルスタックアプリを作るために、8つのAIエージェントを並列で起動しました。インフラストラクチャのスタック、Reactフロントエンド、Javaバックエンドです。各エージェントは担当する1つの要素を持ち、すべてクリーンでコンパイルできるコードを出力しました。CDKは型チェックを通り、JavaバックエンドはSpring Bootの慣例に従い、ReactのUIも見栄えが良いものでした。

しかし、それらを組み合わせようとすると、境界のたびにバグにぶつかりました。

The architecture

AWS上のフルスタックアプリには、動く部品が非常に多いです。インフラストラクチャ用の複数のCDKスタック(IAM、VPC、シード関数付きのDB、Cognito、CodePipeline、CloudFront/WAF)、ECS Fargate上のSpring Bootバックエンド、S3上でホストするReactフロントエンド。

実装計画は徹底的で、すべてのコンポーネントをカバーしていました。しかし、共有契約に合意する必要があるエージェントにとっては、詳細さが足りませんでした。

The bugs

最初の2つはすべてを止めます。バグ3〜5は、前のものを直した後にだけ表面化します。

Bug 1: The Spring Boot app won't even start

シードデータ関数は passenger_idfull_name でスキーマを作成しますが、Spring Bootのエンティティは idname にマッピングしています:

-- Agent 1: seed data function creates the schema
CREATE TABLE passengers (
    passenger_id   VARCHAR(64) PRIMARY KEY,
    full_name      VARCHAR(255) NOT NULL,
    ...
);
// Agent 2: The Spring Boot entity maps the table
@Column(name = "id")       // Schema says "passenger_id"
@Column(name = "name")     // Schema says "full_name"

ddl-auto: validate を使うと、Hibernateは起動時にマッピングを検証します。しかし列が存在しないため、ECSのタスクは1回のリクエストもさばく前にクラッシュします。

Bug 2: Every call returns 404

CDKスタックは /approve と /generate のALBルートを登録しますが、Javaクライアントは /voucher/approve と /voucher/generate にリクエストを送っています:

CDK ALB routes:  /approve, /generate
Java client:     /voucher/approve, /voucher/generate

どちらのエージェントも、単体では正しく動くコードを書いていましたが、CDKスタックはクリーンなパスを使い、Javaクライアントはサービス接頭辞を追加していました。お互いに確認していません。

Bug 3: Missing request fields

下流サービスは4つの必須フィールドを検証します。Javaクライアントは3つだけ送っています:

Lambda expects:  escalationId, passengerId, amount, situation
Java sends:      escalationId, passengerId, amount

バグ2のURLを直しても、すべての承認が400になります。

Bug 4: User lookup doesn't work

これは一番面白かったです。3つのシステムがユーザーを扱い、それぞれが独自の識別子を作っていました:

Cognito custom attribute:  custom:passenger_id = "pax-a1b2c3d4-e5f6-..."
RDS seed data:             passenger_id = "PAX-a1b2c3d4-e5f6-..."
JWT subject claim:         sub = "a1b2c3d4-e5f6-..."  (Cognito UUID)

バックエンドはユーザー検索に jwt.getSubject() を使っています。これはCognitoのUUIDで、 pax- が付いても PAX- が付いてもいません。どのユーザー検索も結果を返しません。

3つのエージェント。3つの命名規約。ゼロの調整。

Bug 5: Every status lookup returns "not found"

下流サービスはJSONを返します。JavaクライアントはXMLとして解析します:

{"status": "FOUND_LOCAL", "location": "Warehouse-B-Shelf-47"}
String status = extractXmlElement(xml,"status");  // <status>...</status> を探します

JSON文字列にXMLタグはありません。extractXmlElement は、すべてのリクエストで空を返します。

下流サービスを書いたエージェントは、ある仕様(JSON)に従いました。Javaクライアントを書いたエージェントは、別の仕様(XML)に従いました。

Bugs 6 to 17: SSM parameter path mismatches

1つのCDKスタックがSSMパラメータを書き込みます。別のCDKスタックが読み取ります。しかし、パスについては一度も調整していません:

Producer stack writes:  /${AppName}/test/data/rds-secret-arn
Consumer stack reads:   /${AppName}/${Env}/data/rds-password-secret-arn

...

プロデューサースタックとコンシューマースタックの間で、12個のSSMパラメータが一致していません。アプリはそれらすべてで失敗します。

Why parallel agents can't catch this

各エージェントには、全体の計画と自分の担当コンポーネントに関する文脈がありました。しかし、他のエージェントが採用した実装詳細は誰も見ることができませんでした。

私がアプリを書くときは、契約(コントラクト)を作業メモリに保持します。「列は passenger_id だから、移行とエンティティの両方でそれを使う」。でも、マイグレーションを書いているAIエージェントは、エンティティ担当がどの列名を選んだのかを知りません。逆も同様です。

計画にはすべての高レベル情報が含まれていましたが、エージェントは別々のセクションを読んで、共有される詳細についてそれぞれ独自の判断をしていました。

各エージェントは、良い慣例に従って正しいコードを書きました。しかし、調整はされていませんでした。山を挟んでトンネルを両側から掘るようなものです。互いに一度も確認せずに。

How I found all of them at once

生成後、実際にデプロイする前に、私はアーキテクチャレビューのエージェントを実行しました。シンプルな指示は次の通りです:

返却形式: {"translated": "翻訳されたHTML"}
ユーザーのログインからフォーム送信、その後の下流サービス呼び出しまでの実際のデータフローを追跡し、あらゆるコンポーネント間の境界を越えて最後までたどってください。

それらのバグを1回のパスでことごとく見つけ出しました。

レビュー担当のエージェントは、ユーザー向けの入口から始め、すべての境界を通じてリクエストを追跡し、各地点で「一つのコンポーネントが送った内容」が「次のコンポーネントが期待する内容」と実際に一致しているかどうかを確認しました。これは、デプロイ後に行う統合テストと同じことをしますが、何もデプロイする前に見つけられます。

How to prevent seam bugs

並列エージェントを起動する前に、共有契約(コントラクト)をすべて計画(プラン)から取り出して単一の参照ファイルにまとめ、それを必須のコンテキストとしてすべてのエージェントに渡してください。

次に、並列エージェントがやるべきことを終えたら、すべての境界をまたいでいくつかの実際のユーザーフローを追跡するレビュー担当のエージェントを実行します。

シーム(境界)バグを1回のパスで修正してから、デプロイします。

FAQ

AI生成コードにおけるシームバグとは何ですか?

シームバグとは、異なるAIエージェントによって作られたコンポーネント同士の境界における統合上の欠陥のことです。各エージェントは、単独であれば正しく動作するコードを書けますが、コンポーネント同士がうまく噛み合いません。なぜなら、共有される細部について各エージェントがそれぞれ独自の判断をしてしまうからです。たとえば、列(カラム)の呼び名、APIが存在するパス、あるいは識別子(ID)がどのフォーマットを使うか、といった点です。

なぜ並列のAIコード生成は統合バグを生みやすいのですか?

各エージェントは自分のコンポーネントと、与えられた計画(プラン)しか見ていません。2つのエージェントが何かを一致させる必要がある場合、たとえばデータベースの列(カラム)が何と呼ばれるべきか、彼らはそれぞれが独立して合理的な名前を選びます。そうした名前はしばしば一致しません。計画(プラン)は、その列が何を表すべきかは示しますが、両者が使うべき「正確な文字列」が何かまでは必ずしも示しません。

並列のAIエージェントから生じる統合バグはどうやって見つけますか?

生成後に1人のレビュー担当エージェントを実行し、すべての境界をまたいで実際のユーザーフローを追跡します。「ユーザーのログインからフロントエンド、バックエンド、データベース、そして下流サービス呼び出しまでのデータフローを追跡し、すべての境界をチェックして」といったプロンプトを与えてください。そうすれば、1回のパスで不一致を見つけられます。

広告