AIエージェントをオフショアチームのように管理する:指示だけでは足りない

AIエージェントと本番サイトを運用した6か月間に起きた6件の障害と、それぞれを受けて追加したチェックを紹介します。

AIエージェントをオフショアチームのように管理する:指示だけでは足りない

AIエージェントの管理は、ある点でオフショアチームの管理に似ています。指示を出すだけでは足りない、という点です。このサイトをエージェントと作ってきた6か月間、私は不具合のうち検査条件にできるものを自動チェックに変えてきました。それでも変更内容を自分で読み、本番環境で確認する工程は残しています。

TL;DR(要約)

  • 課題: 3言語のサイトのコードの約90%をAIエージェントが書いたと私は見積もっています。文書化したルールも破られ、ローカルでは正常なのに本番環境で25日間続いた不具合がありました。
  • 対応: 文書のルール、ビルド時のゲート、Claude CodeのStop hook、本番環境での確認という4層で管理します。
  • 結果: 本番に出た6件の不具合を受け、対応するチェックを追加しました。不具合率の前後比較はまだできていません。

一人で運用する3言語サイトとAIエージェント

nguyenchau.devはCloudflare Pages上でベトナム語・英語・日本語の3言語に対応しています。2026年3月18日から9月25日までのコミットは1,279件で、そのうち694件にはClaudeの共同作成者行があります。Codexも含めると、自分で書いたコードは約10%だと見積もっています。ただし、共同作成者行の件数からAIが書いたコードの割合を算出することはできません。

理由は単純で、以前ほど自分でコードを書く時間がないからです。それでも、すべてを任せているわけではありません。次の三つは自分で持ちます。

  1. 目標を決める。 エージェントが読める形で書き残します。
  2. 判断に必要な情報を渡す。 データ、背景、過去の決定、その理由を共有します。
  3. 成果物を受け入れる。 変更内容を読み、本番環境でも確認します。

コードの作成、情報の整理、文章の修正はエージェントに任せることが多くなりました。この役割分担は、オフショアチームでデリバリーマネージャーを務めるときに近いものです。以下の教訓の多くは、人と仕事をする中でも経験してきました。

古い指示書どおりに動けば、成果物も古い方針に沿ってしまう

以前、私はサイトを顧客獲得の手段として位置づけていました。当時のエージェント向け文書には、コンテンツページに著者紹介と問い合わせへの案内を入れると書いてありました。エージェントは/learnに著者紹介を3回追加しました。2026年7月29日、8月4日、8月10日のことです。毎回、E-E-A-Tの補強や問い合わせ接点の追加という、一見もっともらしい理由がありました。

この3回については、エージェントは文書に残っていた古い目標に従っていました。その後、私はプライバシー上の理由から/learnの著者紹介を外し、サイトを問い合わせ獲得の導線にする方針もやめました。しかし、読ませる文書を更新しなければ、エージェントにはその変更が伝わりません。まず直すべきだったのはコードではなく文書でした。3回追加された経緯と、再び追加しない理由も記録しました。

オフショアチームでも同じです。古い仕様を丁寧に実装すれば、丁寧に間違った成果物ができます。私は「日本品質」をベトナムのオフショアチームで再現しにくい理由で、暗黙知を明示的なルールにする話を書きました。AIエージェントの場合、新しい決定はエージェントが実際に読める場所へ反映する必要があります。

本番環境に出た6件の不具合と、その後のチェック

文書を正しくしても十分ではありませんでした。以下は実際に本番環境に出た不具合です。発見が遅れた理由と、後から追加したチェックを並べます。

本番環境で起きたこと 発見が遅れた理由 後から追加したチェック
17ページの英語・日本語URL、計34件が25日間(2026年8月8日~9月2日)ベトナム語版へリダイレクトされた 別の不具合の修正が原因で、ローカル環境では再現しなかった 言語ルーティングの判断表を検査するバリデーターと、デプロイ後に本番を直接確認する手順
チャット画面には「この端末に一時保存」と表示していたのに、訪問者の回答が私宛てのメールに送られた チャットボットのテストがなく、私のレビューで発見した 5つの動作ルールを検査するバリデーターと、バリデーター自体のテスト
英語・日本語の構造化データで著者情報がベトナム語版と異なり、資格の欠落、肩書きやURLの誤りが3回起きた 静的HTMLは正しく、従来の検査は通った。言語切り替え時にスキーマを書き換えるJavaScriptに問題があった そのJavaScriptも読むバリデーター
構造化データにFAQがあるのに、ページ上には表示されていなかった 構造化データは画面に見えないため、目視では気づかなかった 表示中のFAQとスキーマ上のFAQを照合するバリデーター
ホームページでクローラーが読む文と、訪問者に見える文が異なった HTMLとJavaScriptの二つの版が並存し、READMEに書いた注意事項も守られていなかった 二つの版を文ごとに比較するバリデーター
同じ事業者の電話番号がJSON-LD内で二通りに書かれていた 情報を20個のファイルに手書きしていた 各項目を一つの基準データと照合するバリデーター

現在、画面をバンドルする前に10個のバリデーターがビルドコマンド内で動きます。どれかが違反を検出すればビルドが止まり、その版はデプロイされません。ただし、検査できるのは定義済みの条件だけです。

なかでも、チャットボットの件は書きにくい失敗です。送信された内容に連絡先は含まれていませんでしたが、画面で伝えた動作と実際の動作が食い違っていました。私は2026年9月5日のレビューで発見し、同日中に修正しました。以後、訪問者が自分で連絡先を残さない限り、チャットボットは何も送信しません。

34件のURL障害:「ローカルでは動く」だけでは不十分だった

この件は、エージェントとテストの双方の限界がよく分かるため、少し詳しく書きます。

8月8日、エージェントによる監査で実際の問題が見つかりました。ベトナム語版しかないページの一部が英語URLでも内容を返し、重複URLが多数生まれていました。修正案はページ本文を読み、翻訳があるかを判定し、なければベトナム語版へリダイレクトするというものでした。

論理上は筋が通っています。しかしCloudflareでは、この場合の静的ファイルのレスポンス本文をミドルウェアがページのテキストとして読めません。その結果、翻訳済みの17ページを含むすべての静的ページが「翻訳なし」と判定されました。一方、ローカルの模擬環境では本文を読めたので、検査はすべて通りました。

直近のコミットを読み返し、言語URLの処理に違和感を覚えました。ローカルでは正常、本番では異常でした。その証拠をエージェントに渡し、実行時に本文を読む方法から、ビルド時に用意したデータで判断する方法へ修正しました。

ここから得た教訓は三つです。

  • 不具合Aの修正が、より大きな不具合Bを生むことがあります。 エージェントは目の前の問題を解きましたが、見せられていない環境での影響までは分かりませんでした。
  • ローカルのテストが通っても、証明できるのはローカルでの動作だけです。 以後、ルーティングを変えるデプロイでは本番を直接確認しています。オフショア案件でも、顧客の環境でUATを行う理由はここにあります。
  • 未知の不具合を最初に見つけるのは、人のレビューかもしれません。 この失敗条件はまだ定義されておらず、既存のゲートでは事前に検出できませんでした。

いつ、文書の注意事項を強制チェックに変えるか

リポジトリのREADMEには「Common AI Mistakes — DON'T DO THIS」という節があります。項目4bは、JSXを直したらHTMLのフォールバックも直すよう警告し、ランディングページ3件で過去に起きたミスだと記しています。それでも2026年8月6日、ホームページのフォームで再発しました。同じ日に、このルールをバリデーターにしました。

/learn用のStop hookを追加したコミットには、CLAUDE.mdのルールをrequestからguaranteeへ変えるという趣旨の言葉があります。Claude CodeのStop hookは、エージェントが作業を終えようとするときに動きます。/learnの内容を変更してバリデーターが失敗していれば、終了を止めてエラーを返し、修正を促します。人のチームなら、Definition of Doneを満たすまで「完了」と報告できない状態に相当します。設定方法はClaude Codeのpermissionsとhooksにまとめました。

同じ考え方を、顧客向けの案件にも適用しました。モデルの性質を十分に理解する前、私が担当した日本向けSaaSでは、10件中7件のAI機能をプロンプトだけに頼っていました。つまり、モデルが正しく推論し、指示にも従うことを前提にしていたのです。分かりやすい例は敬称です。日本のお客様向けの文には「様」が必要でしたが、プロンプトで指定しても抜けることがありました。

そこでリファクタリングを提案しました。顧客データをもとに、コード側で適用する敬称を決めます。日本のお客様に「様」を付けるルールもその一つです。プロンプトには、本当に判断が必要な部分だけを残します。ほかにも事例はありますが、NDAがあるため、ここでは最も単純な例だけを挙げます。私の判断基準は、既知の入力で決まる業務ルールをモデルの記憶に任せないことです。

ここでいう「ゲート」は、違反があれば先へ進めなくする自動チェックです。ビルドを失敗させる、マージを止める、エージェントの作業終了を止める、といった形を取ります。誰かが実行を忘れ得るチェックリストとは、その点が違います。

AIエージェントの仕事を管理する4層

層 このサイトでの実装 オフショアチームでの対応
1. 文書のルール CLAUDE.md、README、SEO関連文書 仕様書、コーディング規約
2. ビルド時のゲート 10個のバリデーター。違反すればデプロイしない CIゲートでマージを止める
3. 作業中のチェック /learnのバリデーターが失敗している間、Claude CodeのStop hookが作業終了を止める DoDを満たすまで「完了」と報告しない
4. 実環境での確認 デプロイ後に本番環境を確認する 顧客環境でUATを行う

各層には役割と限界があります。

  • 文書のルールは目標、例外、過去の判断理由を残せますが、エージェントが見落としたり、別の解釈をしたりします。
  • ビルド時のゲートは定義した条件への違反を止めますが、不具合の起こり方をすべて網羅できません。
  • 作業中のチェックは人がレビューする前にエラーを返して修正を促します。このサイトでのClaude CodeのStop hookは、現時点では/learnだけが対象です。
  • 実環境での確認は本番環境だけで起きる問題を見つけられますが、ここでは自動化しておらず、人の時間がかかります。

本番障害が起きたら、どの部分をビルド時の検査条件にできるかを考えます。文書のルールも残します。目標や判断理由は、自動チェックだけでは表せないからです。

良いゲートは文言ではなく動作を検査する

ゲートの作り方を誤ると、保守の負担になります。私が守っている原則は三つです。

  • 文言ではなく動作を検査する。 チャットボットのバリデーターは実際のエンジンに入力を与え、「訪問者が連絡先を残す前には何も送信しない」などの条件を検査します。ボタンの文字列は照合しません。文言に依存するテストでは、コピーを直すたびに失敗し、動作を見ずにテストだけ直す癖がつきます。
  • 全項目の記載より整合性を検査する。 事業者情報のバリデーターは20ページすべてに全項目を要求しません。書かれている項目が基準データと一致するかを検査します。
  • ゲート自体もテストする。 チャットボットのバリデーターには、過去の不具合を意図的に再発させ、正しく失敗することを確かめるテストがあります。一度も赤にならないゲートは、壊れている可能性があります。

限界と保守コスト

きれいな手順だけを示して負担を隠せば、成果を大きく見せすぎます。実際には次の限界があります。

  • ゲートが捕まえられるのは、設計時に検査条件にしたものだけです。 34件のURL障害は、私が変更を読み、本番を確認して初めて分かりました。人のレビューは残ります。
  • ゲートごとに保守コストがかかります。 ポートフォリオページのFAQの一文は、今も4か所にあります。バリデーターは一致を保ちますが、文を直すときは4か所を直す必要があります。
  • ソースコードを静的に調べるだけのゲートもあります。 スキーマのバリデーターは、間接的な代入をすべて捕まえられるわけではありません。経験済みの不具合を対象にしており、あらゆる問題の検出を保証しません。
  • 不具合率の前後比較はありません。 ゲートを作る前の月間不具合件数を数えていないため、「不具合がX%減った」とは言えません。上の6件にはそれぞれ対応するチェックを追加しましたが、あらゆる変種を止められる証明にはなりません。
  • 一人で一つのサイトを運用した経験です。 6か月間、誤検知が原因でゲートを外す事態にはなっていません。ただし、30人のチームでは別の負担が出るでしょう。オフショアチーム向けの設計案はAI品質ゲートの記事に書きました。そちらは未測定の設計案で、この記事は小規模ながら実際に運用した内容です。

AIエージェントを使うようになっても、私のデリバリーマネージャーとしての役割は消えませんでした。時間を使う場所が変わりました。一行ずつ確認する代わりに、目標を定め、どの不具合をゲートにするか判断し、変更内容を読みます。AIの成果物を読み取って受け入れる力については、BrSEからAI Bridge SEへでも書きました。判断が必要な部分と、ルールで処理する部分の分け方は、中小企業向けAI自動化でも扱っています。

よくある質問

本番システムのコードの大半をAIエージェントに書かせてもよいですか?

目標を決め、必要な情報を渡し、成果物を受け入れる責任者がいるなら可能です。nguyenchau.devではコードの約90%をAIエージェントが書いたと私は見積もっています。変更内容のレビュー、ビルド時のチェック、本番環境での動作確認は自分で行います。

CLAUDE.mdやプロンプトにルールを書くだけでは、なぜ足りないのですか?

文書のルールは要求であって、強制される条件ではありません。エージェントは見落としたり、例外だと判断したりします。このリポジトリにはJSXを修正したらHTML側も更新するよう明記していましたが、同じミスが再発しました。現在は不一致を検出するとビルドを失敗させます。

何をプロンプトに任せ、何をコードにすべきですか?

顧客データに応じた敬称の選択など、入力から決まる業務ルールはコードにします。判断が必要な部分をモデルに任せます。固定ルールをモデルの記憶に頼ると、遵守の確認が難しくなります。