エージェント基盤構築の5層要件とは
この記事は、Web エージェントを本番環境で運用するための基盤構築において、単なるブラウザの起動を超えて、アイデンティティ管理、分離、観測性、モデルゲートウェイなどの複雑なインフラ層が必要不可欠であることを詳述し、自社開発の是非を判断する基準を提供している。
キーポイント
本番環境におけるエージェントの要件拡大
単なる HTTP クライアントではなく、JavaScript を実行し、セッションを維持し、ログイン壁を突破できる実際のブラウザ(Chromium/Playwright)が必要であり、これだけでは不十分である。
5 つの必須インフラ層
スケーラブルな運用には、サイトが認めるアイデンティティ(パスポート)、ウォームプール、環境分離、実行状況の観測性(Observability)、および意思決定ごとのモデルゲートウェイという 5 つの層が不可欠である。
自社開発の機会コストとリスク
これらの基盤はエージェントそのものではなく、製品独自性の高い部分にリソースを割くための「足場」であり、維持管理にはシニアエンジニアチームが必要で、四半期単位での投資が求められる。
パフォーマンスと遅延の課題
ブラウザの起動やセッション確立における遅延(コールドスタート)が直接エージェント全体のレイテンシとなり、タスク実行中に発生するクラッシュは復旧プロセスを要するため、システム設計上の重大なボトルネックとなる。
重要な引用
A web agent needs more than a browser. At scale it needs warm pools, isolation, an identity sites accept, observability, and a model gateway behind every decision.
The one browser you were watching becomes thousands you don't have more eyes for...
Every quarter spent building and maintaining it all is also a quarter not spent on the part of a product that's uniquely yours.
影響分析・編集コメントを表示
影響分析
この記事は、AI エージェントが研究段階から実社会での大規模運用へ移行する際の最大の障壁である「インフラの複雑さ」を明確に浮き彫りにしており、開発者が自社で基盤を構築すべきか、既存のマネージドサービスを利用すべきかを判断するための重要な指針となる。特に、単なる技術的な難易度だけでなく、組織リソースの配分(機会コスト)という経営視点からの分析は、CTO やエンジニアリングリーダーにとって極めて示唆に富む内容である。
編集コメント
本稿は、AI エージェントの実用化において「動くものを作る」ことと「安定して動かす基盤を維持する」ことの決定的な違いを鋭く指摘しており、技術選定やアーキテクチャ設計の文脈で非常に重要な示唆を与えます。
TL;DR: Web エージェントにはブラウザ以上のものが必要です。大規模運用では、ウォームプールの確保、隔離、サイトが受け入れるアイデンティティの管理、可観測性、そして各判断の背後にあるモデルゲートウェイが不可欠です。それぞれの層は個別に構築可能ですが、それらを統合したシステムを維持するには、シニアエンジニアによるチームの専有が必要です。この記事ではこれら 5 つの層を分解し、自社で構築する価値があるかどうかを見極める方法を解説します。
Web で実際に作業を行うエージェントには、それを遂行するための「手」が必要です。その手とは、マークアップを取得する HTTP クライアントではなく、ページの JavaScript を実行し、ナビゲーション間でセッションを保持し、モデルが推論する DOM をレンダリングし、ログインの壁を突破できる本物のブラウザのことです。モデルは「何をすべきか」を決定しますが、実際にページを描画するのはブラウザです。
しかし、「手」を用意することは始まりに過ぎません。見過ごされがちですが、私たちがラップトップを開くとき、想像以上に多くの機能を持ち寄っているものです。人間がオープンウェブ上で作業するようにエージェントが動くためには、人間の他の能力も必要になります。サイトが認識して通過させるパスポートのようなもの、実行中に何が起こったかを監視する「目」、そして各クリックの背後にある判断や推論です。それでも、エージェントに生物学はありません。そのすべてを支えるのは、コンピューター equivalent の基盤、つまり数千のエージェントを同時に稼働させながら互いに漏洩しないように支える配管のようなインフラストラクチャです。
ブラウザ環境を立ち上げるのは意外と簡単です。Chromium や Playwright はオープンソースですし、コンテナ運用の経験があれば、その日のうちにエージェントがフロー操作を開始できる状態にできます。
週末のプロジェクトとして始めるのも悪くないでしょう。ただし、本番環境での運用まで見据えると話は別です。
監視していたブラウザが一つ増えるだけで、管理すべき数は数千に膨れ上がります。各ブラウザは、サイトから信頼される「パスポート」を保持し、拒絶されやすいページへ到達し、タスク途中でセッションが切断された際に復旧し、失敗した 1 万分の 1 のケースでもデバッグ可能な十分なログを残す必要があります。もちろん、既存のツールでこれら全てを構築することは不可能ではありませんが、単に Chromium を起動するだけとは比べ物にならないほど複雑です。
重要なのは、これらはすべて「エージェントそのもの」ではないという点です。パスポート管理、プール、分離、プロキシ、観測性(オバザビリティ)、モデルのルーティングは、すべてエージェントとウェブの間を繋ぐ基盤(サブストレート)に過ぎません。つまり、これを構築・維持するために四半期を費やすことは、他社には真似できない独自の製品機能の開発時間を削ることを意味します。
では、人間のようにウェブを利用するエージェントのインフラを構築する際に考慮すべき点を整理しましょう。
エージェントはリクエストパスにブラウザを組み込むため、処理フローは以下のようになります。まずモデルがアクションを選択し、ブラウザがそれを実行、そしてページがレンダリングされて戻ってきます。このページこそが、ループが再度実行される前にモデルが確認できる唯一の視点です。したがって、ブラウザのレイテンシはそのままエージェントのレイテンシとなり、タスク中にコールドスタートが発生すれば、待機する下流プロセスのために実行中のエージェントが停止してしまいます。
ローカル環境ではこうした問題は感じないものです。Chromium を一度起動して常時稼働させておけば、セッションがクラッシュしても手動で再起動すれば済みます。しかしスケールすると「手」は存在しなくなります。小さな数値ももはや小さくありません。監視する担当者がいない数千台のブラウザ群において、かつては丸め誤差に過ぎなかった 0.1% のクラッシュ率が、数千セッションにわたる継続的なインシデントとして現れてしまいます。ローカルなら再起動で済んだメモリリークが、ファーム全体では慢性的な OOM(Out Of Memory)として顕在化します。
これに加えて、コールドスタートのコストも重くのしかかります。イメージの取得から Chromium の起動、そしてコマンド受け入れまでの数秒間は、エージェントがブラウザの準備完了を待ってブロックされている「死んだ時間」です。これを解決するには、事前にブラウザを準備しておく「ウォームプール」の活用です。すでに起動済みでアイドル状態にあるブラウザが、次のリクエストを待ち構えている仕組みです。
これをうまく運用するには、3 つのポイントに集約されます。
1 つ目はサイズ設定です。ウォームブラウザ数が少なすぎると、リクエストがコールドスタートの待ち行列に詰まり、本来排除しようとしていたレイテンシが再び発生します。逆に多すぎれば、何もしないブラウザのためにコストを払い続けることになります。適切な数はトラフィックに追従するものであり、プールは固定されたままではなく、1 日のうちで増減する必要があります。
2 つ目はアイドル比率です。ここが経済性の鍵となります。アクティブなブラウザ 1 台あたり、いくつの予備ブラウザを保有すべきかは、トラフィックのスパイク具合に依存します。バースト的で予測不能な負荷がかかるプロダクトの場合、ピーク時に耐えられるよう過剰に用意する必要があり、使用されているブラウザ 1 台に対して予備のブラウザも 1 台温存しておく必要があります。しかし、独立したトラフィックを同じプールに分散させれば、バーストは平滑化されます。その結果、予備のブラウザ 1 台で複数のアクティブなセッションをカバーできるようになり、セッションあたりのコストが低下します。
この比率は、処理量が増加するにつれて改善していきます。
3 つ目はドレイン(排水)です。セッションは状態を持ち、長期間継続するため、ステートレスなワーカーのように負荷が下がった瞬間に即座にキャパシティを解放することはできません。セッションの最中にブラウザを停止させれば、実行中の作業が中断されてしまいます。スケールダウンするには、セッションが完了するまで待って初めてキャパシティを回収できます。そのため、デプロイを行う際は、フラートを段階的にドレインしてリフィルするサイクルを組む必要があります。
これら 3 つの要素はすべてトラフィックに連動するため、一度設定して放置しておくことはできません。これは反復的な作業であり、今日調整したプールも、利用状況が少し変わっただけで最適化から外れてしまいます。
これが最初のレイヤーです。本質的には、状態管理によって複雑化した通常のオートスケーリングに過ぎません。応答性を保つために保有するアイドル容量のコストは、決してゼロになることはありません。
ブラウザは、サンドボックス内で信頼できないコードを実行するように設計された唯一の存在です。それ以外のコンポーネントは、すべて自社で記述またはレビューしたコードを実行しますが、ブラウザはエージェントがナビゲートした任意のページを読み込み、そのページが提供する JavaScript を、自社のプロセス内および自社のコンピュータ上で実行します。
これは普段のノートパソコンでは問題になりませんが、数千台のブラウザを同時に起動し、誰も検証していないページにアクセスさせ、さらにインフラを共有している状況では大きなリスクとなります。1 台でもブラウザが侵害されれば、そこは他のすべてのセッションと共有されているホストへの足掛かりになります。悪意のあるページが Chromium の脆弱性を突いてブラウザプロセスから脱出し、共有インフラ上ではその抜け道が並行して実行中のセッション全体に波及します。
これは想像以上に頻繁に起こる問題です。Chromium は常にセキュリティ修正をリリースしており、その多くは高レベルまたはクリティカルな評価を受けています。中には、実際に野外で悪用されていることが確認されてからようやくパッチが適用されるケースもあります。世界中で最も攻撃の標的となるソフトウェアを、制御できない入力に対して同時に数千コピーも稼働させるのは、非常に大きな課題です。
この問題は 2 つの側面から対処します。
まず重要なのは「隔離」そのものです。すべてのセッションには明確な境界線が必要であり、ブラウザからの脱出を試みたページがホストに到達するのではなく壁にぶつかるようにする必要があります。感染は防げます。つまり、1 つのサンドボックス内に 1 つのブラウザを配置し、できれば共有カーネルではなく軽量な仮想マシン(VM)ごとに分けることが理想です。これにより、Chromium のレンダラーで発生した脆弱性攻撃が脱出しても、ホストや隣接するセッションに到達できなくなります。多くのブラウザを 1 つのコンテナ内で共有するような単純な構成は、1 つの悪意あるページによってチーム全体が停止してしまうリスクを抱えています。
当社の計算層は Firecracker を基盤としており、これによりフルサイズの仮想マシンを個別に用意するコストをかけずに、ブラウザレベルでの完全な隔離を実現しています。
考慮すべきもう一つの要素はパッチ適用です。CVE(Common Vulnerabilities and Exposures)は絶えることなく発生し続けるため、パッチ適用もまた継続的な作業となります。すべてのリリースは迅速にテストされ、ファーム全体へ展開される必要があります。なぜなら、修正情報が公開されてから攻撃が武器化されるまでの時間は極めて短いからです。公開された CVE は、その時間窓を狙って侵入しようとする者にとって格好の標的となります。
やがて気づくことになるのは、パッチ適用とステルス性の追求は両立しにくいという事実です。もしステルス性を高めるために Chromium をフォークしたのであれば、本番リリース前にそのフォークに対してすべてのアップストリームからのセキュリティパッチを再適用する必要があります。つまり、カスタムによる回避策に依存すればするほど、最新状態を維持することがいかに困難になるかということになります。セキュリティ対策と回避策は、結局同じバイナリを巡って競合する同一のリリースパイプラインとして機能してしまうのです。
エージェントがアクセスするすべてのサイトは、その正体を評価し、2 つの問いを投げかけます。「これは誰か?」「どこから来たのか?」。人間であれば無意識にこの質問に応じますが、エージェントには「パスポート」のような証明を提示して信頼を得る必要があります。オープンウェブ上では、エージェントが認められれば通過でき、認められなければ拒絶されます。
ローカル環境ではこれらの問いは投げかけられず、誰かがあなたを検証することはありません。しかしスケールした運用においては、すべてのリクエストに対して両方の問いが確認されます。デフォルトの設定では、これらに直面すると CAPTCHA やレート制限、あるいは完全なブロックといった課題に遭遇し、通過できなくなってしまうのです。
サイト側から見た識別情報
ブラウザは、接続するたびに固有の「身元」を持っています。これはサイトが読み取れる一連のシグナルの集合体であり、ネットワーク層からアプリケーション層まで幅広く存在します。
ネットワークレベルでは、TLS ハンドシェイク の結果として指紋(JA3 またはより新しい JA4)が生成され、HTTP/2 フレームの順序付けもシグネチャとなります。これらは本物の Chrome ブラウザと、自動化ツールで使われるスタックの間では明確に異なります。
さらに上位の層では、ページ側が navigator.webdriver の値を読み取ったり、インストールされているフォントやプラグインを列挙したり、Canvas や WebGL の描画結果をハッシュ化して、グラフィックススタックがどのようにレンダリングされるかを解析できます。さらには、マウスの移動経路やクリック間のタイミングといった行動パターンまで監視可能です。
目立つ特徴を消し去ることは比較的容易です。ヘッドレス版の Chromium は、navigator.webdriver を true に設定し、「HeadlessChrome」というユーザーエージェントを送信し、プラグインを一切公開せず、Canvas の描画にもランダム性(エントロピー)を持たせません。これらはそれぞれ Chromium のフォーク を作成して修正を加えることで解消できます。
しかし、真に難しいのは「整合性」の確保です。検知システムはすべてのシグナルを総合的に読み取ります。そのため、「Windows 上で Chrome であるかのように振る舞いながら、Linux のレンダラー文字列を持ち、フォントの計測値が一致せず、IP アドレスの地理的位置とタイムゾーンが合わない」といったブラウザは、手つかずのヘッドレスブラウザよりも容易に特定されてしまいます。なぜなら、そのような組み合わせを呈する実機は存在しないからです。
GPU の識別情報から OS、フォント、ロケールに至るまで、すべての信号が一貫していることを維持する必要があります。また、トラフィックを分散させるためにこれらの要素をローテーションする際も、各要素の整合性を保ち続けることが求められます。
次に CAPTCHA の問題があります。ある程度の規模を超えると、ID がどれだけ清潔であっても、一部のセッションで検証が課されるようになります。そのため、解決パスを設け、直面している課題の種類を検知して、その回答をページ内に返す仕組みが必要です。
接続元について
完璧な ID を持っていたとしても、接続元が適切でなければ正体はバレてしまいます。2 つ目のポイントは IP アドレスに関するもので、クラウドから直接送信されるトラフィックにはデータセンターの住所が含まれるため、デフォルトの設定では即座に失敗します。主要プロバイダの IP 範囲は公開されているため、多くのサイトではページが読み込まれる段階で、それらのアドレスからのアクセスを自動化されたものとみなしてブロックしてしまいます。
これを突破するには、一般ユーザーが利用しているような住宅用 IP を経由する必要があります。これにより、ウェブ側からは通常のトラフィックとして認識されます。しかし、一歩進んで二歩下がる結果にもなりかねません。なぜなら、ここで新たな調達先と評判の問題が生じるからです。これらのアドレスはどこから入手する必要があるのか、そしてその供給元は自らの責任で保証できるものでなければなりません。また、評判も固定されたものではありません。アドレスがフラグを立てられたり、プール全体が使えなくなったり、同じプールを共有する他のユーザーのノイズによって評判が低下したりするリスクがあるためです。
IP レピュテーションは、一度購入して終わりではなく、継続的に維持する資産のようなものです。これは常に動き続ける要素の一つです。アドレスをローテーションし、フラグが立ったものは廃棄し、プールとしての容量が劣化すれば代替容量を補充し、さらにアドレスの由来を常に見直す必要があります。
これらの課題はそれぞれ独自のペースで進行します。検知システムは都合の良い時に更新され、プールは徐々に劣化していきます。今月まで機能していた ID とネットワーク構成でも、誰かがブロック率を監視して両方の要素を最新の状態に保たなければ、翌月にはブロックされるようになります。
最初の 3 つのレイヤーでエージェントがページへ到達し、行動を開始できるようになります。次の段階は、実際にその行動が行われたことを確認することです。監視できないエージェントを信頼することはできません。そのため、エージェントインフラストラクチャには「目」としてのログ機能が必要です。ブラックボックスからデバッグ可能なワークフローへと進化させるのです。
これはローカル環境ではすでに実現されています。ブラウザが目の前にあるため、フローを進める様子を直接観察でき、何か不審な点があれば開発者ツールを開いて確認できます。1 つのセッションで 1 つの画面に限定される限り、この可視性は無料で得られます。
しかしスケールすると状況は一変します。エージェントは自分が監視できないマシン上で動作し、67 ステップからなるタスクの 40 番目で失敗します。その際提示されるのは「クリックが着地しなかった」という *極めて詳細な* ログ行だけです。ボタンを処理したモーダルや、最初に発火したリダイレクトは見えません。デバッグするにはブラウザが見たものを確認する必要があり、つまりセッションの記録が必要です。しかし、これにはかなりのコストがかかります。
ビデオと DOM の比較
セッションを記録する方法には主に 2 つあり、それぞれが逆方向の弱点を抱えています。
ビデオ方式は、セッション実行中の実際のピクセルをエンコードします。レンダリングされた通りすべてを正確に捉えることができますが、多数の同時ストリームをエンコードすると、ブラウザを実行しているホスト上で CPU リソースを大量に消費してしまいます。つまり、観測機能(オバザビリティ)とファームウェアのリソース競合が生じるのです。
一方、DOM 再構築方式は、rrweb rrweb のように DOM とその変更点を記録し、後で新しいページ上で再生します。動画ではなく変更ストリームを保存するため、軽量です。しかし、実際のウェブが複雑になる領域では機能しなくなります。ネストされた iframe、シャドウ DOM、クロスオリジンのフレーム、そして Canvas はきれいに再構築されません。そのため、最もデバッグが必要なホスティングサイト上の不審なセッションほど、再生時に誤った挙動を示す可能性が高いのです。
ここでは、ユースケースに応じてどちらの失敗を受け入れるかを選ぶ必要があります。ビデオ方式は計算資源とストレージコストがかかりますが、真実を映し出します。再構築方式は安価ですが、信頼性が時折損なわれます。堅牢なシステムでは両方を併用するのが一般的で、忠実さが重要な場所にはビデオを、それ以外は再構築を採用します。つまり、1 つの記録パイプラインではなく、2 つのパイプラインを運用することになります。
なぜ起きたのか、何が起きたかを見る
ブラウザの再生機能は「ページ上で何が起こったか」を教えてくれます。しかし、エージェントにとってはそれが重要なのではありません。エージェントが行動した背景にはモデルの判断があります。そのため、実行に失敗した際にはその判断を問う必要があります。「モデルはページを読み間違えたのか」「ツールの結果が悪かったのか」、あるいは「存在しないボタンを思い込んでいたのではないか」。これらを検証するには、セッション全体に対してすべてのモデル呼び出しとツール呼び出しを追跡し、アクションを選択する瞬間のページ状態を確認できる仕組みが必要です。再生機能と追跡機能はそれぞれ物語の半分しか語らないため、両者は一つのタイムライン上に統合されなければなりません。具体的なアプローチとしては、セッションの再生記録、ネットワークログ、イベントログ、そしてモデルとツールのトレースを一つのビューに集約することです。
これらすべてのデータを意味のある期間保存・インデックス化する必要があります。フルレコーディングと、ファーム全体でのステップごとの追跡データはテラバイト規模になります。実際に必要になるのは、先週失敗した実行の古いログであることが多いでしょう。したがって、データの保持期間は極めて重要です。
ブラウザは「手」であり、モデルは「判断力」です。この判断力は、接続されたあらゆるラボへの API 呼び出しによって支えられています。ローカル環境では、SDK は一つ、キーは一つ、プロバイダーも一つで十分機能します。
しかし、スケールすると単一のプロバイダーでは不十分なケースが増えます。ステップごとに最適なモデルが異なるためです。安価で高速なモデルがルーティングを担い、高コストなモデルが複雑な推論を担当するといった使い分けが必要です。価格体系や利用制限、あるいは最先端技術そのものが毎月変化する中で、単一のモデルやラボに縛られるのは苦しいものです。
複数のエージェントを呼び出す瞬間、あなたは事実上、自社のエージェントとそれらすべてのサービス間をつなぐゲートウェイの運用を引き受けたことになります。どのフレームワークを採用していても、この事実は変わりません。
このゲートウェイを運用するには、主にいくつかの要素が重要になります。
まず挙げられるのがルーティングとキー管理です。各プロバイダーには独自の SDK、認証方式、リクエスト形式、レスポンスフォーマットが存在します。エージェントに直接各プロバイダーを接続することも可能ですが、通常は薄いラッパー(抽象化レイヤー)を設け、エージェントが単一のインターフェースを通じて通信するようにするのがベストプラクティスです。これにより、内部の 1 つの呼び出しが、そのステップで必要なラボ(モデル)へ自動的にルーティングされ、すべてのプロバイダーのキーや利用枠(クォータ)をコードベース全体に散在させるのではなく、一元管理できるようになります。
この構成は、特定のプロバイダーがダウンしている場合でも稼働し続けます。レート制限に引っかかったり、モデルの品質が低下したりしても、エージェントがタスクの最中に停止してはいけません。ゲートウェイには、指数バックオフを伴うリトライ機能や、プライマリモデルが応答しない場合に別のモデルへフォールバックする機能が備わっています。
誰もが知っている通り、コストはあっという間に膨らみます。エージェントの実行はチャット(会話)が多く、1 つのタスクを実行するために数十回のモデル呼び出しが必要になることもあります。大規模な運用(フリートボリューム)では、トークン使用量による請求額も莫大なものになります。ゲートウェイは、実行ごとのコストをメーターリングする場所であると同時に、キャッシュ戦略が効果を発揮する場所でもあります。例えば、あるステップで同じページを同じ方法で解析する処理が数千回の実行で繰り返される場合、ゲートウェイはモデルに再度呼び出すのではなく、すでに解決済みのアクションを即座に返却できます。これにより、呼び出し自体をスキップでき、レイテンシの短縮と請求額の削減という両方のメリットを得られます。素晴らしい機能ですが、その分、構築と維持の手間が増えることも事実です。
エージェントインフラストラクチャのビルダーが、モデルのパイプライン整備を行う「配管工」へと変貌する瞬間です。
では、これらを運用するには実際どれほどのコストがかかるのでしょうか。以下の数値は一例であり、中規模のファームを想定したものです。実際の数字に合わせて形状を変換すれば、そのまま適用できます。
例えば、24 時間体制で 8 つのブラウザコンテナを稼働させ、常にキャパシティを確保しているとしましょう。必要な計算リソースを考慮すると、1 コンテナあたり約 0.1 ドル/時間となります。これを 8 つ常時稼働させる場合、インフラの原価だけで年間約 5,800 ドルになります。これは請求書に明細として現れる金額であり、決して高すぎるわけではありません。
次に問題となるのがトークン数です。ファーム規模が大きくなるとモデル利用料が急増します。キャッシュ(ゲートウェイがモデルを呼び出さずに回答できる反復ステップ)の活用度合いにもよりますが、コストは計算リソースと同程度か、それ以上に膨らむ可能性があります。この規模では年間数千ドル、さらに規模が拡大すればその倍以上になるでしょう。
しかし、真に大きなコストは「これらが自動で動くわけではない」点にあります。前述した各レイヤーには、常に維持管理が必要なシステムが存在します。スケーリングが必要なウォームプール、各 CVE に対応して再パッチ適用が必要な Chromium のフォーク、 drifting するアイデンティティ、フラグが立つプール、リプレイパイプライン、そしてゲートウェイです。これらすべてを誰かが責任を持って運用する必要があります。おそらくシニアエンジニアからなるチームが必要となり、その人件費は一人あたり年間約 220,000 ドル程度になります。
もちろん、エンジニアがすべてをここで過ごすわけではありませんが、それでもかなりの割合を占めます。インフラ構築にエンジニアチームの年間作業時間の 10〜20% を割くとしても(これはかなり控えめな見積もりですが)、ブラウザの稼働維持のためにたった一人のエンジニアに年間で 22,000 ドルから 44,000 ドルの人件費がかかる計算になります。
コンプライアンスについても忘れてはいけません。ヘルスケアや金融分野で活動する場合、SOC 2 Type II や HIPAA の準拠、ペネトレーションテストの実施、データ完全消去の保証などが求められますが、これらはそれぞれ独立したプロジェクトとして立ち上げる必要があります(監査と維持管理も含まれます)。ただし、これらの対策を講じても、エージェント自体の性能が向上するわけではありません。あくまで実環境で運用するための「許可」を得るためのコストです。
ここまで説明してきたのは、自社構築時に負うべき負担の大きさを示す事例に過ぎません。だからといって、決して自社で構築すべきではないという話ではありません。スタックを自前で持つことが最適なケースも確かに存在します。
最も明確な例は、ブラウザインフラ自体が製品である場合です。ステルス機能やプロキシ、あるいは独自のブラウザプラットフォームを販売しているなら、前述の 5 つのレイヤーこそがチームが構築すべき核心となります。
それ以外の理由で自社開発を選ぶ場合は、技術的な必要性よりも戦略的な判断であることがほとんどです:
- エンジニアリングのリソースに余裕があり、自前で構築して制御権を握りたいと考えている場合。大規模なチームであれば、これを所有するコストを負担でき、明確な理由があるなら、自前構築は決して間違った選択ではありません。
- 外部プロバイダーに依存できない状況です。オンプレミスでの運用が必須だったり、外部ベンダーの導入を禁止するポリシーがあったり、データ境界の制約からすべてを自社内で完結させなければならない場合があります。こうした制約が現実的なものなら、効率性の議論はすべて無効化され、「やらざるを得ない」ため自前で構築することになります。
- 自社のハードウェア上で動かしたいという要望です。一部のチームでは、所有し完全に制御できるベアメタル上にブラウザを構築することを望んでいます。
ただし、これらの理由だけで多くのビルド判断がなされるわけではありません。実際には、「1 日目にはデモが成功した」ということがきっかけで、スケールする段階になって初めて真のコストが浮き彫りになるケースがほとんどです。「早く始めること」自体も、自前構築を正当化する十分な理由にはなりません。数回のセッションを実行して概念を検証しているプロトタイプ段階であれば、通常は購入して、プロバイダー上で Proof of Concept (PoC) を実行し、問題の規模を実際に把握した後に「ビルドか購入か」を見直すのが正解です。必要性すら不明な状態でインフラを構築して PoC を回すのは、非常に高価な作業を先送りすることになります。
ここからどう進むべきか
エージェントとオープンウェブの間に 5 つのレイヤーが存在し、それぞれが自前で構築可能であり、稼働すれば独立したシステムとして機能します。では、ビルドすべきか、それとも購入すべきか?
次に読むべきいくつかの記事をご紹介します:
⋆˚꩜。 harsehaj
原文を表示
TL;DR: A web agent needs more than a browser. At scale it needs warm pools, isolation, an identity sites accept, observability, and a model gateway behind every decision. Each layer can be built, but together they're a standing system a team of senior engineers must own. This post breaks down all five layers and how to know when building it yourself is worth it.
Every agent that does real work on the web needs hands to do it, and those hands are a browser. Not an HTTP client that fetches markup, but a real browser that runs the page's JavaScript, holds a session across navigations, renders the DOM the model reasons over, and gets through the login wall. The model decides what to do, but the browser is what actually renders the page.
Hands are only the start though. Often overlooked, but we actually bring a lot more to our desks when we open up our laptops than we think. To work the open web the way a human does, an agent needs the rest of a human’s faculties, too. It needs a passport that sites recognize and wave through, eyes on what happened when a run goes wrong, and the judgment or reasoning behind each click. Still, agents don’t have our biology, so underneath all of that is their own computer-equivalent, which is plumbing that keeps thousands of them running at once without leaking into each other.
Standing up the first browser is easy. Chromium and Playwright are open source and you already run containers, so an agent can be clicking through a flow by the end of the day.
Dare I say a weekend project, until you dream of production.
The one browser you were watching becomes thousands you don’t have more eyes for, and each one has to carry a passport that sites accept, reach pages built to turn it away, recover when a session dies mid-task, and record enough that you can debug the single run in ten thousand that failed. Don't get me wrong, you could build all of it with tools you already know, but it's a great deal more than launching Chromium.
The thing is that none of this is even your agent. The passport, pools, isolation, proxies, observability, and model routing are all a substrate between your agent and the web. So every quarter spent building and maintaining it all is also a quarter not spent on the part of a product that’s uniquely yours.
Let me unravel everything to consider when building infrastructure for agents to use the web like humans do.
An agent puts the browser in the request path, so: the model picks an action → the browser carries it out → page is rendered back. This page is the only view the model has before the loop runs again. The browser's latency is the agent's latency, and cold starts mid-task means a running agent is stalled while something downstream waits on it.
Locally you’d never feel this, because you start Chromium once and it stays up, and if the one session crashes you can restart it by hand. At scale that hand disappears, and small numbers aren’t small anymore. A fleet of thousands of browsers with nobody watching any singular session, so a 0.1% crash rate that used to be a rounding error unfortunately becomes a steady stream of incidents across thousands of sessions. The leak I could’ve restarted away on my laptop turns into slow OOM across the fleet.
The cold-start cost sits on top of that. Pulling the image, launching Chromium, and waiting for it to accept commands takes seconds, and these seconds are dead time when an agent is blocked waiting for a browser to exist. The fix for this is to keep browsers prepped in advance, which is what a warm pool is. The browsers are already booted and idling, waiting for the next request.
Running this well comes down to three things.
The first is sizing. Too few warm browsers, and requests will queue behind cold starts, which was the latency you were trying to remove in the first place. Too many warm browsers means paying for browsers that sit doing nothing. The right number tracks your traffic, so the pool has to grow and shrink through the day rather than sit fixed.
The second is the idle ratio where the economics live. How many spare browsers you carry per active one depends on how spiky your traffic is, so a product with bursty, unpredictable load has to over-provision for its peaks and might keep one warm browser for every one in use. Spread enough independent traffic across the same pool and then bursts smooth out, so one spare covers several active sessions and the cost per session drops.
The ratio improves as volume climbs high.
The third piece is draining. Sessions are stateful and long-lived, so you can't shed capacity the instant that load drops in the way you would with stateless workers. Killing one mid-session kills live work. Scaling down means waiting for sessions to finish before you can reclaim their capacity, and running a deploy that cycles the fleet to drain and refill in stages.
All three move with your traffic, so none of it can just be set and forgotten. It’s iterative, which means the pool you tuned for today is mistuned the moment usage shifts.
This is the first layer. It’s really just ordinary autoscaling made harder by state, and the idle capacity you carry to stay responsive is a cost that never reaches zero.
A browser is the one thing in your sandbox that’s designed to run untrusted code. Everything else executes code you wrote or reviewed, but a browser loads whatever page the agent navigates to and runs whatever JavaScript it serves, inside your own process and on your own computer.
Once again, this doesn’t hurt on your laptop, but it packs a punch once you’re spinning up thousands of browsers, when they’re on pages nobody has vetted and all sharing infrastructure. One compromised browser is a foothold on the host it shares with everyone else's. A malicious page exploits a Chromium bug to break out of the browser process, and on shared infrastructure, that escape reaches the sessions running in parallel.
This happens more often than you think. Chromium ships security fixes constantly, plenty rated high or critical, and some patched only after they were already being exploited in the wild. It’s quite the hurdle to be running the most attacked software on Earth, thousands of copies at a time against input you don't control.
You handle this in two parts.
The first is isolation itself. Every session needs a real boundary, so a page that breaks out of the browser hits a wall instead of the host. No infection! This means one browser per sandbox, ideally per lightweight VM rather than a shared kernel, so a renderer exploit that escapes Chromium still can't reach the machine or the sessions beside it. The naive version, many browsers sharing one container, is the version where one bad page takes down the whole crew.
Our compute layer runs on Firecracker, so our browsers stay isolated at the VM level without paying for a full VM each.
The second part to consider is patching. The CVEs never stop, so neither does the patching. Every release has to be tested and rolled across the fleet quickly, because the window between a fix going public and the exploit being weaponized is short. Public CVEs are a clear recipe for anyone who wants in during that window.
Soon you’ll notice that patching and stealth fight each other. If you've forked Chromium for stealth, every upstream security patch has to be reapplied on top of your fork before it ships, so the harder you lean on custom evasion, the more painful staying current becomes. Security and evasion end up being the same release pipeline fighting over the same binary.
Every site your agent visits evaluates it and asks two questions: who is this? Where are they coming from? A human clears both without noticing, while an agent has to earn both with a passport. The open web either waves an agent through or turns it away.
Locally, neither question gets asked and nobody's screening you. At scale, both are asked on every request, and a default setup fails them through challenges/CAPTCHAs, rate limits, and outright blocks.
Who the site sees
A browser carries an identity on every visit, which is just the set of signals a site can read about who's connecting, and they span the whole stack. At the network level, the TLS handshake produces a fingerprint (JA3, or the newer JA4) and the HTTP/2 frame ordering forms a signature, both of which differ between real Chrome and most automation stacks. Higher up, a page can read navigator.webdriver, enumerate installed fonts and plugins, and hash a canvas or WebGL draw to see how the graphics stack renders. It can even watch behavior, like mouse paths and the timing between clicks.
Cleaning up the obvious tells is the easy part. Headless Chromium sets navigator.webdriver to true, ships a HeadlessChrome user-agent, exposes no plugins, and renders canvas with no entropy. You can clear each of these directly by forking Chromium and making changes.
The challenging part is coherence. Detection systems read every signal together, so a browser that’s advertising Chrome on Windows while carrying a Linux renderer string, mismatched font metrics, and a timezone that doesn't match its IP geolocation is easier to spot than an untouched headless browser, because no real device produces that combination.
You have to maintain identities where every signal agrees with every other one, from the GPU string to the OS to the fonts to the locale, and when you rotate them to spread traffic, each one has to stay just as consistent.
Then there are CAPTCHAs. Past a certain volume, some sessions get challenged no matter how clean the identity is, so you wire in a solving path, detect which challenge type you're facing, and return the answer back into the page.
Where you connect from
A perfect identity still gives you away if it arrives from the wrong place. The second question points to the IP, and the default answer fails immediately since traffic straight out of your cloud carries a datacenter address. The ranges for every major provider are public, so a lot of sites treat anything from them as automated before the page even loads.
Getting through means routing through residential IPs, which are the kind ordinary people browse from, so the web reads it as normal traffic. One step forward and two steps back, because this now opens a sourcing and reputation problem. Those addresses have to come from somewhere, and the somewhere has to be one you can stand behind. Reputation isn't fixed either as addresses get flagged, whole pools get burned, and a noisy neighbour sharing your pool can degrade it for everyone else.
IP reputation behaves like an asset you maintain rather than one you buy once. It’s another moving piece. You rotate addresses, retire the flagged ones, replace capacity as a pool wears down, and keep checking where the addresses came from in the first place.
Both questions drift on their own schedule. Detection systems update whenever they like, pools decay, and an identity + network setup that worked this month will start collecting blocks next month, unless someone's watching the block rate and keeping both halves current.
The first three layers get an agent onto the page and acting. The next part is seeing that they actually did. You can’t trust an agent that you can’t watch, which is why agent infrastructure needs eyes and logs. We go from black box → a debuggable workflow.
This is already the case locally. The browser is right there, so you can watch it click through the flow, and open devtools when something looks off. It’s free visibility, as long as it’s one session and one screen.
It goes dark at scale. The agents run on a machine you don’t have eyes on, fails at step #40 out of a 67-step task, and you’re presented with an *incredibly* detailed log line of *“click didn’t land.”* You can't see the modal that took care of the button or the redirect that fired first. To debug it you need to see what the browser saw, which means recording the session. That gets pretty pricey.
Video versus DOM
There are two ways to record a session, and they fail in opposite directions.
Video encodes the actual pixels as the session runs. It captures everything exactly as it rendered, but encoding thousands of concurrent streams burns CPU on the same hosts running the browsers. So observability competes with the fleet for compute.
DOM reconstruction, on the other hand, records the DOM and its mutations instead, then replays them into a fresh page later, the way rrweb does. It’s much lighter, since you're storing a stream of changes rather than video, but it breaks precisely where the real web gets complicated. Nested iframes, shadow DOM, cross-origin frames, and canvas don't reconstruct cleanly, so the weird sessions on hostile pages (which need the most debugging) are most likely to replay wrong.
You have to pick your failure for your use-case here. Video costs compute and storage but shows the truth, while reconstruction is cheap but sometimes unreliable. Robust setups run both, so video where fidelity is important and then reconstruction everywhere else, which means two recording pipelines instead of one.
Seeing why, not just what
Replaying the browser tells you what happened on the page. For an agent, this isn’t the important part. The agent acted because a model decided to, so when a run goes wrong, the decision should be questioned. *Did the model misread the page, get a bad tool result, or perhaps hallucinate a button that wasn't there?*
Answering this means tracing every model call and tool call lined up against the session, so you can see the page state that the model was reasoning over when it chose an action. Replay and trace have to be placed in one timeline because they each tell half the story separately. One way to approach this is to put session replay, network and event logs, as well as the model and tool traces in one view.
This all has to also be stored and indexed long enough to matter. Full recordings + per-step traces across a fleet are terabytes of data. The log you'll probably want is an old one from the run that broke last week, so that retention is key.
The browser is the hands, but the model is the judgment, and judgment runs on API calls to whatever labs you've wired in. Locally, this is one SDK, one key, one provider, and it works.
Then at scale, one provider isn’t necessarily enough. Different steps call for different models, since the cheap/fast one handles routing and the expensive one handles the hard reasoning. It’s rough being locked to a single model or lab when pricing, limits, or the frontier itself shifts every month.
And the moment you're calling more than one, you've signed up to run a gateway between your agent and all of them, no matter whose framework you're using.
Running this comes down to a few things.
The first is routing and keys. Every provider has its own SDK, auth, request shape, and response format. You *can* wire each provider directly into the agent, but it’s usually better to put a thin wrapper in front so the agent talks to one interface. This way, one internal call routes to whichever lab a given step needs, with the keys and quotas for all of them managed in one place instead of dispersed through the codebase.
This setup stays live even when a provider isn't. Once you hit a rate-limit or a lab degrades, the agents still shouldn’t stall mid-task. Gateway enables retries with backoff, and fallback to another model when the primary won't answer.
We all know costs creep up quickly. Agent runs are chatty, since a single task can be dozens of model calls, and at fleet volume, the token bill gets hefty. The gateway is also where you meter spend per run, and where caching becomes relevant. When a step repeats, the same page parsed the same way across thousands of runs, the gateway can return the action it already resolved instead of calling the model again. That skips the call entirely, cutting both latency and bill. It's great, and it's one more thing to build and maintain.
Agent infrastructure builder turned model plumber!
So what does all of this cost to run? The figures below are illustrative - one plausible mid-size fleet, but the shape maps to whatever your real numbers are.
Let’s say you run eight browser containers around the clock to keep capacity ready. At the compute they need, that lands around $0.10 per container-hour, so eight of them running continuously is roughly $5,800 a year in raw infrastructure. This will actually show up on an invoice, and it’s pretty reasonable.
The next part gets muddier with tokens. At fleet volume the model bill racks up fast, and depending on how much you wind back with caching (the repeat steps the gateway can answer without calling a model), it runs into the same range as the compute or well past it. I'll roughly put a few thousand dollars a year at this scale, and more as you grow.
The real cost is that none of this runs itself. For each layer I walked through, the warm pool that has to be resized, the Chromium fork that has to be re-patched against each CVE, the identities that drift, the pools that get flagged, the replay pipeline, and the gateway, is a standing system that someone has to own. Probably a team of senior engineers, which costs somewhere around $220,000 a year each.
Of course, they won't spend all their time here, but it’s still a decent chunk. Even if infrastructure takes 10-20% of an engineering team’s year (which is pretty conservative), that's $22,000 to $44,000 a year in salary for just one engineer spent keeping browsers alive.
Don’t forget about compliance either. If you’re working with healthcare or finance, SOC 2 type II, maybe HIPAA, a pentest, and zero-retention guarantees must also be stood up with each as its own project (audits and upkeep included). BTW, none of that actually makes the agent better. It’s the cost of being allowed to run it for real use cases.
Everything so far is a case for how much you would take on when you build this, which doesn’t mean you should never build it. There are real situations where owning the stack is right.
The clearest is when browser infrastructure is literally your product. If you sell stealth, proxies, or a browser platform of your own, the five layers are in fact the thing your team is building.
Past that, the honest reasons to build in-house tend to be strategic rather than technical:
- You have the engineering bandwidth and you want the control. A large team that can afford to own this, and has a deliberate reason to, isn't wrong to.
- You can't depend on outside providers. There are on-prem requirements, a policy against procuring external vendors, or a data boundary that forces everything to run inside your own walls. When the constraint is real, it outweighs every efficiency argument, and you build because you have to.
- You want it on your own hardware. Some teams want browsers on bare metal they own and control end to end.
Most build decisions aren't made for these reasons, though. It’s often because a demo worked on day one, and the real costs appeared later with scale. Even “being early” isn’t a compelling enough reason to build. If you're prototyping by running a few sessions to prove something out, the right move is usually to buy, ship the proof of concept on a provider, and revisit build-versus-buy once you actually know the scope of the problem. Building infrastructure to run a POC means doing some really expensive work before knowing if you even need it.
Where to go from here
That's the whole map with five layers between your agent and the open web, each one buildable, and each one as a standing system once it's live. So build? Or buy?
A few things worth reading next:
- What is a headless browser → the foundation the whole stack sits on, if you want the primitive before the fleet.
- Forking Chromium for AI automation → why suppressing the identity tells means going all the way to the binary.
- The CAPTCHA arms race → a deeper look at the detection side of identity, from distorted text to full browser identity.
- TLS fingerprinting with JA3 and JA4 → how the network layer reads you before a page even loads.
- rrweb → the open-source DOM-reconstruction approach behind the lighter half of session replay.
You can read the full blog post with interactive components here.
⋆˚꩜。 harsehaj
関連記事
今日のまとめ
AI日報で今日の重要ニュースをまとめ読み