
# AI エージェントが実サイトで失敗する理由

あなたのエージェントは、テストに使ったページでは動きます。ところが開かれたウェブに向けたとたん、答えは曖昧になり、あるいは自信たっぷりに間違え、あるいはブラウザでは中身が見えているのに「ページは空でした」と言い出します。

とっさにプロンプトへ手が伸びます。たいていプロンプトに問題はありません。故障はモデルが何かを見るより前に起きています。URL を取得してテキストに変える層でのことです。

その層を実際のページで計測しました。壊れ方は五つ。それぞれの実際の姿とあわせて挙げます。

## 1. JavaScript が動くまでページに文字がない

`atlassian.com/software/jira/pricing` をふつうに取得すると、1.37 MB の HTML から読める内容は **1 トークン** です。1 段落ではありません。1 トークン、改行が一つです。

価格はその中にあります。`<script>` の中に、アプリケーションの状態として、ブラウザがそこからページを組み立てるのを待っています。

同じ URL をヘッドレスブラウザで取得すると、3,048 トークンになり、こう始まります。

```markdown
## **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% を下回ります](/ja/blog/what-ai-agents-see)。`shopify.com/pricing` は 410,515 トークンを送って 2,479 を生みます。

生の HTML をコンテキストウィンドウに詰め込むなら、ラッパーの `<div>` やインライン SVG に入力トークンの値段を払い、同時に読みたかった他のページを押しのけていることになります。送る前に抽出してください。さらに良いのは、そのサイトが Markdown をそのまま渡してくれるか尋ねることです。[すでに渡してくれるところもあります](/ja/blog/markdown-for-agents)。試した 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 なしで空、が群を抜いて多く、レンダリングはそれを直します。代償つきで、そして常にではなく。
- 信じる前に、抽出のたびに検証してください。
- 注目を集めるのは遮断です。損害を出すのは空白です。

[ページの読まれ方を調べる](/ja) · [エージェントが実際に見るもの](/ja/blog/what-ai-agents-see) · [エージェントに Markdown を出す](/ja/blog/markdown-for-agents)
