Claude Computer Use、ChatGPT Agent、ブラウザエージェント、社内の自動化、自前のエージェント。
なんらかの形でAIエージェントに「フォームを代わりに埋めて、送信する」役割を持たせる場面が、業務でも実装でも増えてきました。
このとき、設計を後付けでやると、本番でだいたい同じ場所で詰まります。
同じ意思の送信が二重になる
正規のエージェントがCAPTCHAではじかれる
フィールド名が見た目依存で、間違った欄に入力される
送信成功か失敗かが画面でしか判別できない
事後にトラブル調査するためのログがない
auto-reply enabled なのに届いていない
Slack に通知は飛んでいるが対応漏れになる
この記事では、私がFORMLOVAというフォームサービスを設計する中で「ここを先に決めておかないと後で必ず詰む」と感じた項目を、実装前のチェックリストとして整理します。
具体的な型サンプルは別記事に寄せて、ここでは判断と運用の粒度に絞ります。
0. 最初に決める:このフォームは代理入力されうるか
すべての設計判断は、この問いから始まります。
判定軸は3つあれば足ります。
1. このフォームは社外公開か
2. ユーザーが「AIに代わりに入れてほしい」と思う動機があるか
3. このフォームに繰り返し系の入力(毎月、毎週、定期申請)があるか
3つのうち1つでも当てはまるなら、そのフォームは代理入力されうると見たほうが安全です。
該当しないフォーム(社内限定、頻度が低い、毎回違う内容)は、いまから無理にエージェント対応する必要はありません。優先度を落として大丈夫です。
このトリアージを先にやっておくと、後の議論が「全フォームに対する大改修」にならずに済みます。
1. フィールド設計:見た目とは別に「意味」で識別できるか
エージェントが安定して値を入れるためには、ラベルの見た目ではなく、フィールドの意味が識別できる必要があります。
決めておく項目はこのあたりです。
- 各フィールドに「並び替えやリネームで変わらない安定ID」を割り当てる方針があるか
- 安定IDは、フォーム編集後も使い回さない(再利用しない)方針か
- 送信時点でのラベル(表示文言)を、回答側にスナップショットとして保存しているか
- フォームをまたいで意味が共通する項目には、semantic name(例:
email,company,consent_marketing)を持たせているか
「field_3 でも動いている」は、人の送信者だけだから動いているのであって、エージェントには通用しません。
ここは最初に正しい名前空間を引いておくと、あとの全部が楽になります。
FORMLOVA は29種類のフィールド型(text / textarea / number / radio / checkbox / dropdown / date / datetime / time / email / phone / url / file_upload / matrix / signature / address / rating_scale / NPS / linear_scale / slider / opinion_scale / ranking / picture_choice / yes_no / country / legal / statement / section_break / hidden_field)すべてに、安定 ID と semantic name を持たせています。レイアウト変更でもリネームでも、エージェントから見た契約は壊れません。
2. バリデーション:送信前に外から読めるか
エージェントの送信フローは、送って→エラーで返って→直して、をループするのが苦手です。
理由は単純で、ループのたびに、レート制限、重複防止、CAPTCHA再判定のリスクが増えるからです。
決めておく項目はこのくらいです。
- 必須項目、最大文字数、正規表現、enum、ドメイン制限などのルールが、サーバー外から取得できるか
- ルールが「画面のJS側にしか書かれていない」状態になっていないか
- バリデーションのバージョンが変わったとき、外から取得し直せばエージェントが追従できる構造か
- 重複防止の設定(メールアドレス単位 / クッキー単位 / 設定しない)も契約として公開しているか
「サーバー側にしか暗黙でルールがある」状態は、エージェントから見ると、契約のないフォームです。
公開しなくていいルール(不正対策の閾値など)は隠していいですが、正規の入力に関わるルールは公開する、という線で決めると運用しやすいです。
FORMLOVA では、条件分岐エンジンが使う演算子集合(eq, neq, gt, gte, lt, lte, contains, not_contains, is_empty, is_not_empty)と論理結合(all / any)が、フォームスキーマ側から固定で見えるようになっています。エージェントは送信前に「この値で評価される」を予測できます。
3. 重複防止:送信側発番の意思IDで判定するか
ここが、AIエージェント時代にもっとも壊れやすい場所です。
決めておく項目はこのあたりです。
- 送信側(エージェント)が発番する意思IDを受け取る仕組みがあるか
- 同じ意思IDの再送信が来たとき、二重に作成せずに同じレコードに紐づけるか
- 「同じユーザー」「同じフォーム」「同じ内容」だけを根拠にした重複判定で済ませていないか
- 既存レコードに紐づけた場合、その結果をきちんとレスポンスに返すか
- 定員ありのフォームでは、意思ID判定と定員チェックが同一トランザクションで動くか
「同じ内容っぽいから止めました」は、人にはやさしいですが、エージェントには「正規の更新だったのに止められた」になりがちです。
意思IDを基準にすれば、「同じ意思の再送信は1件」「違う意思の連続送信は別件」をきっちり分けられます。
ここは型レベルの話なので、最初に決めておく価値があります。
FORMLOVA では、定員ありフォームでの同時送信を insert_response_with_capacity_check という Postgres RPC で SELECT ... FOR UPDATE + INSERT をまとめて、定員と意思ID判定を同一トランザクションにしています。同時に2件の正規エージェントが定員ぎりぎりのフォームに送ってきても、超過は起きない構成です。
回答者の同一性も、フォームの項目ではなく respondent_identifier という独立カラムで持っています。メールアドレスがあればそれを正規化、なければ IP + UserAgent の salted hash。これで、エージェントが別ネットワークからリトライしてきても、同一回答者として追跡できます。
4. 送信レスポンス:機械可読な確認を返すか
送信が成功したかどうかを、画面のトーストやアニメーションでしか伝えない設計は、エージェント時代には不利です。
決めておく項目はこのくらいです。
- レスポンスにステータス(created / duplicate / rejected)が含まれるか
- 作成されたレコードのIDが返るか
- フィールドエラーが構造化されて返るか(メッセージだけでなく、どのフィールドのどのルールに違反したかが識別できる)
- 送信後の事後処理(自動返信メール、リダイレクト、レビュー待ち)が、レスポンスから推定できるか
-
auto_reply_enabledとauto_reply_state(not_required/pending/sent/failed)を別フィールドで返しているか
エージェントがこのレスポンスを読めれば、ユーザーへの報告がいきなり正確になります。
「登録は完了しました。確認メールが届きます。担当からの返信は2営業日以内です。」
この一文を、画面の表示を盗み読みすることなく、エージェントが自信を持って返せるようになるかどうかは、レスポンス設計しだいです。
ここで気をつけたいのが、「自動返信が有効」と「自動返信が届いた」を同じ意味で扱わないことです。Resend のような外部 ESP に投げた時点では、まだ送信されていません。バウンスや配信停止リスト(FORMLOVA だと email_suppressions テーブル)で抑止されるケースもあります。auto_reply_state を別フィールドで返して、エージェント側に「届いていない可能性」を伝えられるようにしておくと、過剰な約束を避けられます。
同じことが Slack 通知にも言えます。「Slack に通知された」と「対応されている」は同じ意味ではありません。FORMLOVA では、回答ステータス(new / in_progress / done / excluded)を Slack 通知とは別に持っていて、reply_to_respondent を実行したときだけ自動的に new → in_progress に遷移するようにしています。通知と所有は別物として扱う設計です。
5. BOT防御:問いの立て方を変えているか
「あなたは人間ですか?」のCAPTCHAは、人の送信者を確認する仕組みでした。
AIエージェント時代には、これは「正規の代理送信を拒否しますか?」と同じ意味になります。
決めておく項目はこのあたりです。
- 「人かどうか」だけを基準にBOTを判定していないか
- 「代理送信であることの宣言」と「ユーザーからの委任トークン」を受け取る経路があるか
- 委任トークンが正規であれば、CAPTCHAのフリクションを下げるレーンがあるか
- それでもブラウザ自動化に見える未宣言の経路には、別途の対策が走るか
- 「送信を壁で止める」のではなく「送信後に状態で扱う」選択肢も検討したか
ここを変えると、CAPTCHAそのものをなくす必要はありません。
「人を確認する」用途は残しつつ、「代理送信のための別レーン」を追加する、という二車線化のイメージです。
FORMLOVA では、未知の経路の前に Cloudflare Turnstile を置きつつ、内容に関する判定(営業メールっぽいか)は送信後の非同期分類に逃がしています。OpenRouter 経由の軽量LLMで legitimate / sales / suspicious のラベルを付け、spam_filter_enabled が立っているフォームでは分析やフィルタからの除外を可能にします(1回答あたり約 $0.0002)。「壁」ではなく「状態」でスパムを扱う形です。
6. 書き込み系操作には confirmation_token を要求しているか
代理入力されるフォームは、フォーム自体の運用もエージェントから呼ばれる場合があります。
そのとき、書き込み系操作には server-side の確認トークンを要求する仕組みが必要です。
決めておく項目はこのくらいです。
- ツールを「読み取り」「可逆書き込み」「回答者波及」「外部不可逆」の4段階に分類しているか
- 外部不可逆な操作(メール送信、削除、公開停止など)に confirmation_token を要求しているか
- token は HMAC 等で署名されていて、推論で偽造できない作りか
- token の有効期限(5分程度)が決まっているか
- 公開(publish)も状態機械として扱い、preview 確認 + 重複防止 + プライバシーポリシー + 最終承認が揃わないと token を発行しないか
FORMLOVA では、ツールを L0(読み取り) / L1(可逆書き込み) / L2(回答者波及) / L3(外部不可逆) の4段階に分類しています。L3 の11ツール(send_bulk_email, send_filtered_email, schedule_email, reply_to_respondent, set_email_sequence, delete_form, unpublish_form, remove_team_member, end_ab_test, delete_response, restore_form_version)と L2 の publish_form だけ、HMAC-SHA256 で署名された confirmation_token(有効期限5分)を要求します。
エージェントがどれだけ自信満々に「もう確認しました」と言ってきても、サーバー側に有効な token が来ない限り、これらは実行されません。
7. 公開フローを state machine にしているか
フォームの公開は、エージェントから「公開して」と一言来たときに即発火しては危険な操作です。
FORMLOVA では、publish_form をサーバー側の review state machine として実装していて、毎回次のものを返します。
- 現在の review state
- 次に必要なアクション(
next_required_action) - 不足している要件(
missing_requirements) - フォーム preview URL とサンクスページ preview URL
- preview を実際に開いたかどうか
- 重複防止の設定
- プライバシーポリシー URL の状態
- 最後の承認に必要な confirmation_token
実際のチャットだと、こういうやりとりになります。
> このフォームを公開してください。
公開前に確認が必要な項目があります。以下の2つのURLを確認してください。
- フォームのプレビュー
- サンクスページのプレビュー
確認できたら、以下も教えてください。
- 重複回答防止 - メールアドレス単位 / クッキー(同一ブラウザ)/ 設定しない
- プライバシーポリシー - URLはありますか?なければ「不要」で進めます。
ユーザー(あるいはエージェント)が preview を開き、設定を埋めると、最終確認テーブルが返ります。
設定がすべて揃いました。最終確認です。
- アクセス: URLを知っている人のみ
- 重複回答防止: メールアドレス単位
- プライバシーポリシー: https://formlova.com/ja/privacy
- 自動返信メール: 有効
「公開する」と返信いただければ即座に公開します。
「公開する」と返したときだけ、HMAC token と一緒に最終公開が走ります。
決めておく項目はこのくらいです。
- preview URL の「開封確認」を会話だけで進めず、サーバー側で記録しているか
- 重複防止とプライバシーポリシーを「公開前の明示確認項目」にしているか
- 最終承認のメッセージに confirmation_token を紐づけているか
- token が失効した場合に、ハードエラーではなく review state を返して再発行できるようになっているか
8. ログ:あとから何が起きたかを再現できるか
エージェント送信は、人の送信よりログの重要度が上がります。
理由は、エラーになった場合に「画面を見て」原因を確認できないからです。
決めておく項目はこのくらいです。
- 送信ごとに、actor(human / agent / automation)が記録されているか
- 意思IDが記録され、重複時の挙動が追えるか
- 利用された経路(ブラウザフォーム、MCPツール、サーバーAPI)が分かるか
- BOT防御で止めた場合、どの理由で止めたかが分かるか
- バリデーション失敗時、どのフィールドのどのルールで止めたかが残るか
- L3 ツールの実行ログには confirmation_token の検証結果が残っているか
- audit_logs テーブルにフォーム操作・メンバー管理・ワークフロー設定が記録されているか
ログがあれば、運用中に「エージェント送信が増えた」「正規のはずなのに止まっている」と気づける確率が上がります。
ログがなければ、「最近フォームのコンバージョンが下がった気がする」だけで気づきが終わります。
FORMLOVA では、audit_logs テーブルに L1 以上の操作を全部書いていて、カーソルベースのページネーション付きでチャットから get_audit_logs で読み出せるようにしています。
9. 経路:MCPツールとしても露出するか
ここは、やるかやらないかの判断項目です。
決めるポイントはこの2つです。
- ブラウザフォームだけを正規経路にするか
- MCPツールとしてもフォームを露出し、AIクライアントから直接呼べるようにするか
社内利用や、特定の業務フォームについては、後者を入れる価値が大きいです。
エージェントが画面を盗み読みするのではなく、ツールスキーマを通して構造化されたデータを返せるので、誤入力と二重送信のリスクが一段下がります。
FORMLOVA は、ここを最初から「フォーム自体がMCPツールとして呼べる」設計にしてあります。129ツール25カテゴリですが、エージェントが「フォームを埋める側」として最低限必要なのは list_forms / get_form_schema / submit_form / get_form_status の4つだけです。残りの125ツールは運営側の操作向けで、L3 のものは confirmation_token を要求します。
これは思想ではなくAPIの形でやっていることなので、ChatGPT、Claude、Cursor、Windsurfなど、カスタム MCP 対応クライアントから直接フォームを操作できます(「対応クライアント」と言いつつ、エンタープライズ契約や特定の実行モードを要求する場合があるので、ここは念のため確認してください)。
10. 段階的な導入順
全項目を一気にやるのは難しいので、私は次の順番で入れることをすすめています。
1. semantic name と stable id(フィールド設計)
2. バリデーションルールの公開
3. 送信側発番のintent idで重複判定
4. 送信レスポンスの構造化(auto_reply_state を別フィールドで返す)
5. ログの拡張(actor / 経路 / 失敗理由 / confirmation_token 検証結果)
6. BOT防御の二車線化
7. ツールの L0/L1/L2/L3 影響範囲分類
8. L3 ツールに confirmation_token を要求
9. publish_form を server-side state machine にする
10. MCPツールとしての露出
1〜3 だけでも、エージェント送信の事故率はかなり下がります。
4〜5 は、運用が回り始めてから入れても遅くないです。
6〜10 は、AIクライアントから本気で叩かれることが見えてきたら入れる、という温度感で大丈夫です。
最初から完璧を目指すよりは、「まず壊れない最小セット」を入れて、運用しながら追加していくのがおすすめです。
過去の失敗から拾った設計教訓
最後に、FORMLOVA を運用していて「これは事前に決めておいてよかった」と思ったことを3つだけ書きます。
ひとつめ。「Slack 通知 = 対応」と思っていると、必ず対応漏れが出ます。エージェント送信が増えると、通知量も増えるので、ここはもっと早く壊れます。Slack 通知は副作用、対応は別レイヤー(FORMLOVA だと回答ステータス + 担当者割当)で持つ。これは早めに決めておいてよかったです。
ふたつめ。「auto-reply enabled = delivered」と思っていると、本番でユーザーから「メールが届いていません」と言われる日が必ず来ます。Resend / SendGrid / SES、どの ESP を使っても、enabled から delivered までの間に5段階くらいの状態があります。auto_reply_state を別フィールドで返すようにしておくと、エージェント側の報告も正確になります。
みっつめ。「サンクスページに表示しただけ = 確認完了」と思っていると、エージェント送信ではほぼ確認が機能しません。サンクスページの内容を「受け取りました」「確認しました」「ルーティングしました」「対応しました」のどれが本当に保証されているかで分けて書く。これも、人とエージェントの両方に効きます。
これらは全部、「フォームを画面前提で設計していた頃には目立たなかった失敗」です。
エージェントが入ってくる時代には、目立つようになります。
まとめ
AIエージェントにフォームを代理入力させる、というのは、思いつきで始めると静かに事故ります。
逆に、フィールド設計、バリデーション公開、冪等送信、構造化レスポンス、ログ、BOT防御の問いを先に決めておくと、本番投入後の運用がぐっと楽になります。
人の送信者を排除する話ではないんです。
人の送信者を扱う設計はこれまで通り大事にしつつ、その横にもう一枚、エージェントが安全に代理入力できる経路を足しておく、というだけです。
このチェックリストを、いまの自社フォームに当てて読み返してみてもらえると、私はうれしいです。
