0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIエージェントにフォームを代理入力させる前に決めておきたい設計項目

0
Posted at

111-qiita-ai-agent-form-submit-checklist.png

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_enabledauto_reply_statenot_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 を実行したときだけ自動的に newin_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防御の問いを先に決めておくと、本番投入後の運用がぐっと楽になります。

人の送信者を排除する話ではないんです。

人の送信者を扱う設計はこれまで通り大事にしつつ、その横にもう一枚、エージェントが安全に代理入力できる経路を足しておく、というだけです。

このチェックリストを、いまの自社フォームに当てて読み返してみてもらえると、私はうれしいです。

関連リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?