AI エージェントが実サイトで失敗する理由
あなたのエージェントは、テストに使ったページでは動きます。ところが開かれたウェブに向けたとたん、答えは曖昧になり、あるいは自信たっぷりに間違え、あるいはブラウザでは中身が見えているのに「ページは空でした」と言い出します。
とっさにプロンプトへ手が伸びます。たいていプロンプトに問題はありません。故障はモデルが何かを見るより前に起きています。URL を取得してテキストに変える層でのことです。
その層を実際のページで計測しました。壊れ方は五つ。それぞれの実際の姿とあわせて挙げます。
1. JavaScript が動くまでページに文字がない
atlassian.com/software/jira/pricing をふつうに取得すると、1.37 MB の HTML から読める内容は 1 トークン です。1 段落ではありません。1 トークン、改行が一つです。
価格はその中にあります。<script> の中に、アプリケーションの状態として、ブラウザがそこからページを組み立てるのを待っています。
同じ URL をヘッドレスブラウザで取得すると、3,048 トークンになり、こう始まります。
## **Transparent pricing for every team.**
同じ URL。同じ日。JavaScript を実行したかどうかだけで、ゼロか、ページ全体かが決まります。
これはモデルの問題に見えて、そうではない故障です。エージェントが「このページに価格の記載はありません」と言ったのは、渡されたバイトの中ではそれが事実だったからです。
2. しかしレンダリングは、期待するような解決策ではない
当然の反応は、全部レンダリングすることです。stripe.com/docs で試してみてください。
FetchError: Rendered fetch failed: Navigation timeout of 25000 ms exceeded
単純な取得なら 1 秒未満で返るページが、ヘッドレスブラウザでは 25 秒かけても読み込みを終えませんでした。レンダリングしない版は、欠点はあれど、少なくとも返ってきました。
レンダリングの代償は、ページごとのブラウザプロセス、数百ミリ秒から数秒の遅延、そして新しい種類の故障です。タイムアウト、ブロックされたリソース、load を最後まで発火しないページ。既定でレンダリングするエージェントは、ウェブの一部において遅く、そして信頼性が下がります。上がりません。
使える形はこうです。まず安価な取得、次に何か得られたかの確認、そして空で返ってきたページにだけレンダリングへ進む。この確認こそ、皆が省くところです。
3. 使いもしないマークアップに課金されている
計測した 12 の有名ページでは、サーバーからモデルまで生き残る割合の中央値は 1% を下回ります。shopify.com/pricing は 410,515 トークンを送って 2,479 を生みます。
生の HTML をコンテキストウィンドウに詰め込むなら、ラッパーの <div> やインライン SVG に入力トークンの値段を払い、同時に読みたかった他のページを押しのけていることになります。送る前に抽出してください。さらに良いのは、そのサイトが Markdown をそのまま渡してくれるか尋ねることです。すでに渡してくれるところもあります。試した 12 ページのうち五つが何らかの Markdown 表現を提供しており、うち一つは私たち自身のものです。
4. 抽出が違う部分を残す
抽出はヒューリスティックです。記事を探して家具を捨てますが、記事の形をしていないページでは判断を誤ります。
stripe.com/docs は 1,057 文字に縮み、こう始まります。
$ stripe payment_intents create --amount 1099 --currency "usd"
これはページの実在する断片です。同時に、Stripe が何をする会社かを述べた文ではなく、ターミナルの例です。それだけを渡されたエージェントは、コードの断片から製品を推し量るほかありません。
つまり、抽出が成功したことを、抽出が有用であることの証拠にしてはいけません。安上がりな見張りとして、抽出結果に文の形をしたテキストがない場合や、半メガバイトを送ってきたページで数百文字にも満たない場合は、読み取り失敗として扱って次の手段に進んでください。事実としてモデルに渡してはいけません。
5. 同じ URL が同じページを返すとは限らない
shopify.com/pricing を単純に取得すると英語が返りました。同一のマシンから同一の URL をレンダリングするとスペイン語が返り、単純な取得が Pay monthly€32 EUR/mo と言っていたところが Pago mensual32 € EUR/mes になりました。
壊れているものは何もありません。ヘッドレスブラウザは単純な取得とは異なる Accept-Language と位置情報のシグナルを送り、異なる、そして正しい表現を受け取ったのです。しかし URL だけでキャッシュしたり、今日の読み取りを昨日のものと比べたりすれば、この差はサイトが値段を変えたように見えます。
Accept-Language は明示的に送ってください。キャッシュのキーは、実際に要求した内容で作ってください。
起きなかった故障
遮断を予想していました。12 ページすべてを 2 回ずつ調べ、ブラウザとして 1 回、OAI-SearchBot として 1 回、Cloudflare・Vercel・CloudFront をまたいでステータスコードを比べました。
ボットを別扱いしたページは一つもありませんでした。 一つもです。
エージェントを遮断する人がいないという意味ではありません。遮断は多くあり、robots.txt は今後も読んで尊重すべきです。意味しているのは、ウェブの広い標本でエージェントが失敗しているなら、その理由が遮断である見込みは低い、ということです。閉ざされた扉より、空の部屋のほうがずっと多いのです。
ウェブとの接触に耐える読み取り層
1. まず Markdown を求める → Accept: text/markdown、または /page.md
2. 単純な取得 → 安価、JS なし
3. 何が来たか確認する → 文の形か。送られたバイト量に見合う長さか
4. だめならエスカレート → レンダリング。タイムアウトと予算つきで
5. 正直にあきらめる → 「このページは読めませんでした」
5 番目は見た目より重要です。読めないページを報告するエージェントはデバッグできます。モデルに改行を一つ渡して即興させるエージェントこそ、あなたの価格を作り話にする張本人です。
短い版
- エージェントの失敗のほとんどは、推論の失敗ではなく読み取りの失敗です。
- JavaScript なしで空、が群を抜いて多く、レンダリングはそれを直します。代償つきで、そして常にではなく。
- 信じる前に、抽出のたびに検証してください。
- 注目を集めるのは遮断です。損害を出すのは空白です。