技術ノート | 作ったものを、運用に耐えるまで作り切る
AIエージェントを世に出すとき、機能を作り切るのと同じくらい重要なのが、公開した瞬間に始まるbotとスパムへの備えだ。しかもLLMを組み込んだプロダクトには、従来のWebフォームには無かった攻撃面が一つ増える——リクエストを1回処理するたびに、LLMの利用料金が発生する。つまり「壊される」前に「請求が爆発する」。
電脳技巧集団(AI職人ギルド)が開発する顧客対応AI「御用聞きAI」は、問い合わせ受信 → AIの一次対応 → 顧客専用ダッシュボード → 継続発注までを自動で回すSaaSだ。これを正式ローンチする前に、被害が出てから慌てるのではなく、予防として多層の防御を入れた。本記事はその実装記録——「設定を一つ入れて終わり」ではなく、どの層で・何を・なぜそう判断したかを、実際に書いたコードと設定の単位で残す。
結論を先に言う。防御は4層で重ねた。①アプリ層のレート制限(AIコスト防衛)/②Turnstile(人間性の確認)/③エッジWAF(流量の遮断)/④LLMによるスパム判定(受信メール)。そして4層を貫く設計原則が2つある——「多層で重ねる」と「安全側の向きを層によって逆にする」。

関連記事 | 本記事で守っているプロダクトの中身このサイトのAIは、自分たちで作った ──HITL・顧客別メモリ・双方向を実装した顧客対応AIの中身
→
01まず攻撃面を"1回いくらか"で棚卸しする
防御を足す前に、「どの入口が、誰でも叩けるか」を一覧にした。守るべきは公開された口であって、社内向けや署名付きの口ではない。
- 公開問い合わせフォーム(誰でもPOSTできる)
- 埋め込みウィジェットAPI(外部サイトからのサーバー間連携・キー+Origin照合あり)
- AIチャットの応答エンドポイント(ログイン済みクライアント)=ここがLLMを呼ぶ=1回ごとにコストが出る
- 受信メール(専用ドメイン宛・botや営業メールが"問い合わせ"として流入し得る)
- ログインリンク送信(乱用でメール爆撃になり得る)
この棚卸しで分かったのは、最もコストリスクが高いのに無防備だったのがAIチャットの応答エンドポイントだったこと。フォームやログインには既にレート制限とハニーポットがあったが、LLMを呼ぶ口だけが素通しだった。攻撃面は「壊れるか」だけでなく、"1回いくらか"で評価しないといけない。
02第1層:レート制限 — AIコスト爆発を止める
AIチャットの応答は、1リクエストでLLMの推論が走る=1回ごとに課金される。ここにレート制限が無いと、トークンが漏れたり一人のクライアントが連打したりするだけで、請求が青天井になる。普通のWebの「DoSで落ちる」とは別種の、財務的なDoSだ。
そこでAI応答エンドポイントに、クライアント単位の二段のレート制限を入れた。短期(直近の一定時間に数十回まで)と日次上限。短期は連打を止め、日次上限は「じわじわ叩き続ける」攻撃のコスト天井を作る。重要なのは、メッセージを保存しLLMを呼ぶ"前"でガードすること——超過したリクエストはDBにも書かず、推論にも入らず、即座に拒否する。守るのは「処理結果」ではなく処理が始まる手前だ。
記録はサーバーレスでも効くようDBベースで持つ(インメモリはインスタンスをまたぐと効かない)。公開フォームやログイン再送にも、IP単位・メール単位で同じ仕組みを掛けている。「1分に数十回も問い合わせる人間はいない」前提で、通常利用には当たらず機械的な連打だけを止める閾値にする。
03第2層:Turnstile — 人間性をフォームで確かめる
レート制限は"量"を止めるが、"そもそもbotか人間か"は判定しない。そこで公開フォームには Cloudflare Turnstile(CAPTCHAの後継・多くの訪問者には何も見せず裏で検証する方式)を入れた。送信時にクライアントがトークンを取得し、サーバー側でそのトークンを検証してから受け付ける。
実装で効かせた設計判断が一つある。「サイトキーとシークレットの両方が設定されているときだけ有効化する」ことだ。地味だが重要で、サーバーのシークレットだけが設定された状態だと、クライアントはトークンを発行できないのにサーバーは全送信を弾く——設定ミスでフォームが全滅する。両方揃ったときだけ検証する設計にしておけば、鍵が無い環境(プレビューや開発)では自動でスキップされフォームは普通に動く。"安全機構が原因で本番が壊れる"事故を、コードの構造で防いでおく。

このTurnstileは既存の自社サイトと同じウィジェット・同じ鍵を共用した。共用にすると許可ドメインを一箇所で管理できる。なおこの作業中に、自社サイトの本番ドメインがTurnstileの許可ホストに入っていなかった穴も同時に塞いだ(詳細は後述の落とし穴で)。
04第3層:WAF — エッジで流量を止める
アプリ層のレート制限は強力だが、関数が起動してから効く。攻撃が大量に来ると、弾く処理自体にサーバーレス関数の起動コストがかかる。そこでもう一段、関数が走る手前=エッジ(Vercel WAF)でレート制限を掛けた。
公開APIのパス(問い合わせ・ウィジェット・AIチャット・受注フォーム)にIP単位の流量ルールを設定し、超過は即座に遮断する。これで攻撃トラフィックは関数に到達する前に落ち、コストと負荷の両方を削れる。プラットフォームの自動DDoS緩和は元々常時オンなので[1]、その上に「自分たちのAPI形状に合わせた」ルールを重ねる形だ。
ここでの設計判断は「webhookとバックグラウンド連携のパスは除外する」こと。受信メールのwebhookやジョブ基盤は、外部サービスが正当に短時間で大量送信する。ここに素朴なIPレート制限を掛けると、連携そのものを壊す。"公開フォーム/チャットの口だけ"を対象にし、機械が正当に叩く口は外す——何を守り、何を守らないかを明示的に決めるのがWAF設計の肝だ。
05第4層:LLMスパム判定 — 受信メールをLLM自身で仕分ける
御用聞きAIは、専用ドメイン宛のメールも"問い合わせ"として取り込む。ここはCAPTCHAを置けない入口だ——botや営業メール、フィッシングが、そのまま問い合わせ一覧を埋める。
この層にはLLM(軽量モデル)を使った。受信メールを種別判定・要約するついでに、「これはスパムか」を同じ一回の推論で判定させる。スパムと判定されたメールは、消さずに"隔離"し、スタッフ通知だけを抑制する。完全に捨てない理由は2つ。監査できることと、誤判定からの復旧ができることだ。サイレントに削除すると、本物の問い合わせを1通でも誤って捨てたとき誰も気づけない。隔離なら一覧から消えても後から拾える。

判定プロンプトには明確な"向き"を持たせた——迷ったらスパムではない(通す)側に倒す。理由は次章の設計原則につながる。
064層を貫く2つの設計原則
多層で重ねる(defense in depth)。4層は役割が違う。Turnstile=人間性/WAF=エッジの流量/アプリ層=精緻なキー単位/LLM=メールの内容判定。どれか一つで全部を止めようとしない。1層が抜けても次が拾う、重なりで守る。攻撃面ごとに最適な層が違うから、束で掛ける。
安全側の"向き"を、層によって逆にする。これが最大の設計判断だ。「迷ったときにどちらへ倒すか」は、層によって正反対になる。
- AIコスト系(レート制限・WAF)=迷ったら止める。誤って数リクエストを弾いても、ユーザーは少し待てば再送できる。失うのは数秒。守るのは青天井の請求。
- スパム判定=迷ったら通す。正当な問い合わせを1通でも誤って捨てたら、失うのは見込み客そのもの。だから安全側は"通す"で、しかも隔離(復旧可能)にする。
コストは取り返せるが、機会は取り返せない。この非対称を意識して各層のデフォルトを決めた。あわせて徹底したのが「サイレントな失敗を作らない」こと。弾いたとき・隔離したときは必ず記録に残し、後から「なぜ弾いたか」を追えるようにする。静かに失敗する防御は、効いているのか壊れているのか分からない——それは防御していないのと同じだ。
07実装の落とし穴(実際に踏んだもの)
机上で「CAPTCHAを入れる」と決めるのと、実装してエラーコードを実際に見るのは別の作業だ。今回踏んだ罠を残しておく。
- Turnstileは登録ドメイン(apex)を自動カバーしない。
www.example.comは手入力で追加できるのに、example.com(apex)は手入力では追加されない。「Cloudflare上の既存サイト(ゾーン)から選択」で明示的に許可する必要があり、許可外のドメインでは検証が110200(ホスト名不許可)で失敗する[2]。反映には数分かかるので直後のエラーで焦らない。 - WAFはwebhook/連携パスを除外する。ここを忘れると正当な連携が壊れる。
- 書き込み専用のシークレットは読み戻せない。"sensitive"で保存した鍵は後からAPIでも値を取得できない(それが正しい設計)。別環境へ展開するときは発行元から取り直す前提で運用する。
- ビルド時に焼き込まれる公開設定は、変更後に必ず再デプロイ。クライアントに渡る設定値はビルド時に埋め込まれるため、設定だけ変えても再ビルドしないと反映されない。
どれも「ドキュメントに小さく書いてあるが、踏むまで気づかない」種類の罠だ。実装してエラーコードを実際に見て、初めて設計が固まる——これは魔法ではなく、地に足のついた検証の積み重ねだ。
AIエージェントの防御は、「壊れるか」だけでなく「1回いくらか」で攻撃面を捉え直すところから始まる。
| 観点 | 無防備なAIエージェント | CAG の4層防御 |
|---|---|---|
| AIチャット | 叩かれ放題=LLM請求が青天井 | レート制限(短期+日次上限・推論の手前で遮断) |
| 公開フォーム | botが自由に送信 | Turnstile+ハニーポット+レート制限 |
| 流量・DoS | 関数が起動してから対処 | エッジWAFで関数の手前に遮断 |
| 受信メール | スパムが問い合わせ一覧を埋める | LLMが判定し隔離(消さず復旧可) |
| 誤判定時 | サイレントに失敗・気づけない | 記録に残し追跡可・安全側の向きを層で設計 |
| 設計思想 | 一つの対策で済ます | 多層で重ね、何を守り何を守らないかを明示 |
※ 本記事は御用聞きAI(CAGの自社プロダクト)の実装に基づく一次情報。レート制限の具体閾値・鍵・内部IDは安全のため非掲載。各プラットフォームの仕様(自動DDoS・Turnstile挙動等)は公式ドキュメントに基づく(v0・2026-06-16時点。仕様は更新されうる)。
脚注・出典
- Vercel は全プロジェクトで自動DDoS緩和を常時提供。その上に WAF のカスタムルール(レート制限等)を重ねられる。Vercel公式ドキュメント(Vercel Firewall / WAF)。vercel.com/docs/vercel-firewall
- Cloudflare Turnstile はウィジェットの許可ホスト名でリクエスト元を検証し、許可外ホストではエラーコード
110200を返す。登録ドメインの扱いは Cloudflare 公式ドキュメント参照。developers.cloudflare.com/turnstile - 本記事のレート制限・Turnstile・WAF・LLMスパム判定の設計と実装は、電脳技巧集団が御用聞きAI向けに行った自社実装に基づく(2026-06-16時点・v0)。閾値や鍵等の具体値はセキュリティ上非掲載。
「作る」だけでなく「公開して運用に耐える」まで作り切る。
AIエージェントの設計・実装から、コスト防衛・bot/スパム対策・運用までを一気通貫で。最新AIを、現場で本当に回る仕組みとして設計します。まずは相談から──問い合わせは、AIがその場でお応えします。
無料で相談する →








