この記事の対象読者
LLM の API を叩いてアプリを1つは作ったことがあるが、Web検索をエージェントに組み込んだ経験はまだない開発者を想定しています。具体的には、こういう方です。
- OSS のエージェントを動かそうとしたら
TAVILY_API_KEYを要求されて、そこで手が止まった - LangChain のサンプルコードに出てくる
TavilySearchが何をしているのか、なんとなくしか分かっていない - Exa と Tavily は名前を見たことがあるが、両者が同じ棚に並ぶ商品なのかどうか自信がない
この記事で分かること
- Exa と Tavily を包括する親概念には、粒度の異なる3つの呼び名が存在すること
- エージェントとWebの間に挟まる層が、6つの型に整理できること
- その6つのどれを選んでも壊れないコードの書き方
この記事で扱わないこと
- 各サービスの価格比較や、ベンチマーク順位の紹介
- ベクトルDBを使った社内文書向けRAGの構築手順
- 生成AI向けのコンテンツ最適化、いわゆるGEOの話
0. 先に結論 — 名前は3つある
このセクションで分かること: 質問への答えそのものです。
Exa と Tavily をまとめて何と呼ぶか。調べていくと、これは「答えが1つに決まらない」タイプの質問だと分かります。粒度によって、業界が使っている名前が違うからです。
| 粒度 | 呼び名 | 何を指しているか |
|---|---|---|
| 製品カテゴリ | AIネイティブ検索API | Exa と Tavily が並ぶ「商品の棚」の名前 |
| アーキテクチャ上の層 | 検索レイヤ / Webアクセス層 | エージェントとWebの間に挟まる「構造上の位置」の名前 |
| 振る舞いのパターン | Agentic Search | 推論しながら都度検索する「動き方」の名前 |
一番よく使われるのは1行目です。より広い括りとして単に「Web検索API」と呼ぶこともありますが、それだと後述する SerpApi のようなスクレイパー型まで同じ袋に入ってしまい、区別がつかなくなります。
面白いのは、当の2社が自分自身をどう名乗っているかです。
Tavily は自社ブログの Tavily 101 という記事で、自らを "the web access layer for AI agents" と定義しています。和訳すると「AIエージェントのためのWebアクセス層」。検索エンジンではなく「層」と名乗っているのがポイントです。
Exa は公式ドキュメントの冒頭で "a custom search engine built for AIs" と名乗っています。和訳すると「AIのために作られた独自の検索エンジン」。こちらは「検索エンジン」を名乗っています。
つまり、当事者同士でも自己認識がずれている。だから外側から1つの名前で括りにくいわけです。この記事では、その「括りにくさ」の正体を6分類の地図として描いていきます。
1. 図書館に閉じ込められた研究者
このセクションで分かること: なぜ検索レイヤという概念が必要になったのか。
この記事では最後まで、図書館の比喩で話を進めます。
LLM は、巨大な図書館に閉じ込められた研究者のようなものです。蔵書は膨大ですが、その内容は学習が終わった時点で止まっています。昨日出た論文も、今朝更新されたAPIリファレンスも棚には並んでいません。
そこで、外の世界から資料を運んでくる人が必要になります。この運搬係の総称が検索レイヤです。
重要なのは、運搬係が1職種ではないことです。他館の目録をコピーしてくるだけの人もいれば、本文を読みやすい形に書き写す人も、読んだ上で口頭で要約する人もいます。
Exa と Tavily は、この6職種のうちの1つ、「用件を聞いて必要な箇所を抜き書きして渡してくれる司書」に相当します。この記事の3-3で詳しく扱います。
2. なぜ職種が急に増えたのか
このセクションで分かること: 2025年以降にこのカテゴリが一気に膨らんだ、2つの構造的な理由。
「図書館の運搬係」という職種自体は昔からありました。Google や Bing の検索APIを叩けばよかったからです。それが数年で職種が細分化したのには、はっきりした原因があります。
2-1. 窓口が1つ閉まった — Bing Search API の終了
Microsoft は 2025年8月11日に Bing Search API を完全に停止しました。公式のライフサイクル告知にはこう書かれています。
Bing Search APIs will be retired on August 11, 2025.
和訳: Bing Search API は2025年8月11日に提供終了となります。
同じページで、移行先として Azure AI Agents の Grounding with Bing Search が案内されています。注意すべきは、これが「同等のAPIへの乗り換え」ではないことです。移行先はエージェント製品であり、素の検索結果を返すエンドポイントではありません。
長年 Bing の検索APIを土台にしていた製品群は、これで足場を失いました。図書館の比喩で言えば、みんなが使っていた資料請求の窓口が予告して閉まったわけです。その空白に、新しい職種が一気に流れ込みました。
2-2. 代行業者の足場が揺れた — スクレイパー型の法的リスク
もう1つが法的リスクです。Google の検索結果を代理取得して売る、いわゆるスクレイパー型のサービスに対して、2025年12月19日に Google が米カリフォルニア州北部地区連邦地裁で提訴しました。争点は著作権侵害そのものではなく、DMCA 第1201条、技術的保護手段の回避です。
この訴訟は2026年7月20日に、中心的な主張が棄却されています。裁判所は、検索結果に含まれるURLやスニペットは著作物ではないため、それを守る仕組みを回避しても1201条の対象にならないと判断しました。
この訴訟の帰結は執筆時点で確定していません。一部の主張については修正した訴状を提出する余地が残されています。実務上の教訓は勝敗そのものではなく、他社の検索結果を代理取得するモデルは、技術以外の要因で突然使えなくなる可能性を構造的に抱えているという点です。
2-3. 時系列で見る
窓口が閉まり、代行業者の足元が揺れた。この2つが重なった結果、「自前でインデックスを持ち、AI向けに整形して返す」職種に大きな需要が生まれました。Exa と Tavily はその流れの真ん中にいます。
ここまでが背景です。次から、実際にどんな職種がいるのかを見ていきます。
3. 検索レイヤの6分類
このセクションで分かること: エージェントとWebの間に立てる選択肢の全体像と、それぞれが何を肩代わりしてくれるか。
まず全体像です。
この6つは「どこまで下ごしらえして渡してくれるか」の連続体として並んでいます。1に近いほど生のまま、6に近いほど調理済みです。生に近いほど自由度が高く、調理済みに近いほど楽ですが融通が利きません。
3-1. SERPプロキシ型
代表例: SerpApi、Serper、SearchApi
既存の検索エンジンの結果ページを代理取得し、構造化データとして返す型です。図書館で言えば他館の目録カードを写してきてくれる代行業者。返ってくるのはタイトル、URL、数百字程度のスニペットが中心で、本文は入っていません。
強みは Google の検索結果ページの構造をそのまま扱えることです。ショッピング枠やローカルパックといった特殊な結果まで取れるので、順位計測やSEOツールでは今でも第一選択です。
弱みは2つ。本文を得るには自分で各URLを取りに行く必要があることと、2-2で触れた法的・運用的な不安定さです。
3-2. 独自インデックス型
代表例: Brave Search API
自前のクローラで自前の索引を持ち、そこから結果を返す型です。自分の書庫と目録を持っている図書館にあたります。
Brave は自社の独立したWebインデックスを土台にしていると説明しています。他社の結果を代理取得しているわけではないので、3-1の構造的リスクからは自由です。一方でインデックス規模は大手検索エンジンより小さいのが通例で、ニッチな日本語ページの網羅性は自分のクエリで確かめる必要があります。
3-3. AIネイティブ検索型 — Exa と Tavily はここ
代表例: Exa、Tavily、You.com、Linkup
この記事の主役です。自前のインデックスを持ちつつ、返り値を最初から LLM が消費する前提で整形する型。
図書館の比喩で言えば、用件を伝えると、必要な箇所だけを抜き書きしたメモにして渡してくれる司書です。目録カードでもなく、本そのものでもなく、その中間。
3-1との決定的な違いは返り値の粒度にあります。SERPプロキシ型が「URLと短いスニペット」を返すのに対し、この型は「クエリとの関連度が高い本文の抜粋」を返します。
この差がどれだけ実装量に効くかを、シーケンス図で見てみます。
パターンAで自分が書くことになる「本文抽出、Markdown化、トークン削減」の3工程を、パターンBはAPI側で肩代わりします。この工程の引き受けこそがAIネイティブ検索型の定義だと理解すると、カテゴリの輪郭がはっきりします。
3-4. 取得・整形型
代表例: Jina Reader、Firecrawl
URLを渡すと、そのページを取得して LLM が読める形に変換して返す型です。指定した本を開いて、読める形に書き写してくれる写本係。
Jina Reader は "Convert any URL to an LLM-friendly input" を掲げています。和訳すると「任意のURLをLLMにとって扱いやすい入力へ変換する」。使い方も徹底的に単純で、URLの前に https://r.jina.ai/ を付けるだけです。
この型は検索機能とは本来別の関心事である点に注意してください。「探す」ではなく「読む」を担当します。ただし実際の製品は境界をまたいでおり、Firecrawl は検索とクロールも持ち、Tavily も /extract エンドポイントを持っています。分類は製品ではなく機能に対して当てるのが正しい読み方です。
3-5. 回答生成型
代表例: Perplexity Sonar
検索して、読んで、要約して、出典付きの文章として返す型です。読んだ上で口頭で答えてくれるレファレンス係。
返ってくるのは資料ではなく回答文です。楽ですが、要約の過程がブラックボックスになるので、エージェントが自分で証拠を突き合わせて判断したい場面には向きません。
3-6. モデル内蔵型
代表例: OpenAI の web_search ツール、Anthropic のWeb検索ツール、Gemini の Grounding with Google Search
モデル提供者が最初から用意している検索ツールです。図書館に最初から併設されている案内カウンター。
Gemini の公式ドキュメントによれば、google_search を有効にするとモデルが検索の要否を自分で判断し、必要なら複数クエリを生成して実行し、統合した回答を出典の注釈付きで返します。
外部APIキーが不要で導入が最も速いのが強みです。弱みはモデルを乗り換えると検索の挙動ごと変わること。ドメイン絞り込みや期間指定の細かさもモデル次第です。
4. ここまでのまとめ
このセクションで分かること: 6分類の要点を1枚の表に圧縮します。
| # | 型 | 返ってくるもの | 自分で書く必要が残る処理 | 主な用途 |
|---|---|---|---|---|
| 1 | SERPプロキシ型 | URL + スニペット | 本文取得、抽出、整形 | 順位計測、SEO、特殊な検索結果 |
| 2 | 独自インデックス型 | URL + スニペット | 本文取得、抽出、整形 | 依存先を分散したいとき |
| 3 | AIネイティブ検索型 | 関連度付きの本文抜粋 | ほぼなし | エージェントの汎用検索 |
| 4 | 取得・整形型 | 整形済み本文 | 検索そのもの | URLが既に分かっているとき |
| 5 | 回答生成型 | 出典付きの回答文 | なし | 単発の質問応答 |
| 6 | モデル内蔵型 | 出典付きの回答文 | なし | 最速で動かしたいとき |
選び方を図にすると、こうなります。
冒頭の問いに戻ります。Exa と Tavily を包括する親概念とは、この表の3行目のことです。そして表全体を指す言葉が「検索レイヤ」、その中で推論しながら反復的に検索する動き方を指す言葉が「Agentic Search」になります。
ここから先は、3行目の中身をもう少し細かく見ていきます。
5. 同じ司書でも、得意分野が違う
このセクションで分かること: Exa と Tavily が同じ分類の中でどこに力を入れているか。優劣ではなく設計思想の軸として見ます。
同じ「抜き書きしてくれる司書」でも、力を入れている方向が違います。どちらが優れているかという話ではなく、自分の用途がどちらの軸に乗るかという話です。
Exa — 意味で引く方向に振っている
Exa の公式ドキュメントを読むと、力点が2つに置かれているのが分かります。
1つはレイテンシとのトレードオフを呼び出し側が選べること。instant から deep-reasoning まで複数の検索タイプがあり、リアルタイムのチャット用途と、時間をかけて多段推論させる調査用途を同じAPIで撃ち分けられます。
もう1つはカテゴリ別インデックス。企業、人物、学術論文、ニュース、SEC提出書類といった領域ごとに専用の索引を持ち、category パラメータで検索対象を切り替えられます。対象の種類が最初から決まっている検索に効きます。
highlights オプションは、ページ全文ではなくクエリに関連する箇所だけを返す機能で、コンテキストに載せるトークン量を直接削ります。
Tavily — 工程を合成する方向に振っている
Tavily はエンドポイントの組み合わせを前面に出しています。公式ブログで整理されている使い分けは次のとおりです。
| エンドポイント | 役割 |
|---|---|
/search |
探して、関連度順に並べる |
/extract |
URLから本文を取り出す。1回の呼び出しで複数URLをまとめて処理できる |
/crawl |
サイトを辿って配下ページを収集する |
/map |
クロールの軽量版。サイト構造だけを返す |
つまり Tavily は3-3だけでなく3-4の機能も内包しています。1社で「探す」と「読む」を両方引き受ける設計です。
公式ブログは /search に本文取得を同居させるオプションにも触れつつ、精度を求める場面では2段に分けるほうがよいと案内しています。「1発で済ませるか、2段に分けるか」を選べること自体が設計思想だと読めます。
もう1点、Tavily は取得したコンテンツをスキャンしてプロンプトインジェクションを遮断する機能を明示的に打ち出しています。これは後述する落とし穴と直結する話です。
どちらを選ぶかで迷った場合、決め手になりやすいのは「検索対象の種類が事前に決まっているか」です。企業や論文など対象カテゴリが固定されているなら Exa のカテゴリ指定が効きます。対象が定まらず、探す・読む・辿るを行き来するなら Tavily のエンドポイント合成が効きます。
6. 近接概念の整理
このセクションで分かること: 検索レイヤの周辺でよく混ざる用語を、図書館の設備として位置づけ直します。
図書館には運搬係以外の設備もあります。混同しやすい概念を並べておきます。
RAG と Agentic RAG
RAG は、事前に索引を作っておいて、そこから引いた内容をモデルに渡す仕組みです。図書館で言えば自館の書庫。索引を作った時点の情報しか入らない代わりに、自分でコントロールできます。Agentic RAG は、エージェント自身が「いつ、どのツールで引くか」を判断する構成で、この記事の検索レイヤはその外向きのツールにあたります。
社内文書はRAG、外部の生きた情報は検索レイヤ。この住み分けが基本形です。
Retriever
より形式的な名前です。Java向けの LangChain4j のドキュメントは、Web検索エンジンについて "Web search engines can be used as ContentRetrievers" と書いています。和訳すると「Web検索エンジンは ContentRetriever として利用できる」。
つまりフレームワークの世界では、ベクトルDBもWeb検索APIも同じ Retriever インターフェースの実装として扱われます。この視点は7章のコード設計に直結します。
MCP
MCP は検索そのものではなく、エージェントと外部ツールをつなぐ配線規格です。図書館で言えば館内電話の規格。
Exa も Tavily も MCP サーバを公式に提供しています。MCPは6分類のどれかではなく、6分類のどれとでも組み合わさる直交した概念である点を押さえてください。
Grounding と Deep Research
Grounding は、モデルの出力を外部の情報源に紐づけて出典を示せるようにすることです。手段ではなく目的を指す言葉なので、6分類のどれを使っても達成できます。
Deep Research は、1回のクエリから複数の検索を反復し、結果を突き合わせてレポートを生成する仕組みです。Exa の deep-reasoning、Tavily の /research のように、検索レイヤ側にこの機能を持たせる動きが進んでいます。エージェント側で組んでいたループを、API側へ押し込む流れと理解すると位置づけがはっきりします。
7. どれを選んでも壊れないコードを書く
このセクションで分かること: 6分類のどれに乗り換えてもエージェント側を書き換えずに済む構造の作り方。
ここからPythonのコードが出てきます。難しい構文は使っていません。要点は1つだけです。
エージェントに渡すのは「司書の名刺」ではなく「受付票の書式」にする。
つまり、エージェントのコードが TavilyClient や Exa を直接知っている状態を避けます。乗り換えのたびに本体を書き換える羽目になるからです。依存性逆転の原則そのものですが、実務上のメリットもはっきりしています。
- サービス障害時に別の型へフォールバックできる
- テストでは実APIを叩かないダミー実装に差し替えられる
- 開発環境と本番環境で違う型を使える
7-1. 受付票の書式を決める
まず、検索レイヤが返す唯一の型を定義します。この型より先には、ベンダ名は一切出てきません。
クリックでソースコードを展開 — retriever/core.py
"""検索レイヤの抽象定義。ここにベンダ固有の知識を置かない。"""
from __future__ import annotations
from abc import ABC, abstractmethod
from dataclasses import dataclass
@dataclass(frozen=True)
class Document:
"""検索レイヤが返す唯一の型。
エージェント側はこの型だけを知っていればよい。
どのベンダから来たかは source フィールドで観測できるが、
処理の分岐には使わない。
"""
url: str
title: str
text: str
score: float | None = None
source: str = ""
def to_context(self, max_chars: int = 1200) -> str:
"""LLM のコンテキストに載せる形へ整形する。"""
body = self.text[:max_chars]
return f"# {self.title}\n{self.url}\n\n{body}"
class WebRetriever(ABC):
"""検索レイヤの抽象。
単一責任: 「クエリを受け取って Document のリストを返す」だけ。
整形も要約も再ランキングもここではやらない。
"""
@abstractmethod
def retrieve(self, query: str, k: int = 5) -> list[Document]:
"""クエリに関連する文書を最大 k 件返す。"""
raise NotImplementedError
7-2. 各ベンダをアダプタで吸収する
次に、実際のSDKをこの書式に変換する層を書きます。SDKの返り値の形はバージョンによって変わることがあるので、変換処理はこの1ファイルに閉じ込めておくのが要点です。壊れたときに直す場所が1箇所で済みます。
クリックでソースコードを展開 — retriever/adapters.py
"""ベンダ固有のSDKを Document へ変換するアダプタ群。
このファイルだけが各SDKの返り値の形を知っている。
SDKのバージョンアップで壊れるとしたら、必ずここが壊れる。
"""
from __future__ import annotations
import logging
from typing import Any
from .core import Document, WebRetriever
logger = logging.getLogger(__name__)
class TavilyRetriever(WebRetriever):
"""Tavily の /search を Document へ変換する。"""
def __init__(self, client: Any, *, topic: str = "general") -> None:
self._client = client
self._topic = topic
def retrieve(self, query: str, k: int = 5) -> list[Document]:
payload = self._client.search(
query=query,
max_results=k,
topic=self._topic,
)
return [
Document(
url=item.get("url", ""),
title=item.get("title", ""),
text=item.get("content", ""),
score=item.get("score"),
source="tavily",
)
for item in payload.get("results", [])
]
class ExaRetriever(WebRetriever):
"""Exa の search を Document へ変換する。
highlights を有効にすると、ページ全文ではなく
クエリに関連する箇所だけが返る。トークン量が直接減る。
"""
def __init__(self, client: Any, *, search_type: str = "auto") -> None:
self._client = client
self._search_type = search_type
def retrieve(self, query: str, k: int = 5) -> list[Document]:
response = self._client.search(
query,
type=self._search_type,
num_results=k,
contents={"highlights": True},
)
return [self._to_document(item) for item in response.results]
@staticmethod
def _to_document(item: Any) -> Document:
highlights = getattr(item, "highlights", None) or []
text = " ".join(highlights) if highlights else getattr(item, "text", "") or ""
return Document(
url=getattr(item, "url", "") or "",
title=getattr(item, "title", "") or "",
text=text,
score=getattr(item, "score", None),
source="exa",
)
class StubRetriever(WebRetriever):
"""テスト用。実APIを叩かず、固定の結果を返す。
ライブ検索は毎回結果が変わるため、
CI でこれを使わないとテストが不安定になる。
"""
def __init__(self, documents: list[Document]) -> None:
self._documents = documents
def retrieve(self, query: str, k: int = 5) -> list[Document]:
logger.debug("stub retrieve: query=%s", query)
return self._documents[:k]
7-3. 切り替えだけを担当する層を1枚足す
障害時のフォールバックは、アダプタの中に書いてはいけません。アダプタの責務は変換だけだからです。切り替え専用のクラスを別に作ります。同じインターフェースを実装しているので、エージェント側からは区別がつきません。
クリックでソースコードを展開 — retriever/fallback.py
"""フォールバック専用の合成リトリーバ。"""
from __future__ import annotations
import logging
from .core import Document, WebRetriever
logger = logging.getLogger(__name__)
class FallbackRetriever(WebRetriever):
"""優先順に試し、最初に結果が得られたものを返す。
単一責任: 「どれを使うか決める」だけ。
変換も整形もしない。
WebRetriever を実装しているので、
これ自体をさらに別の FallbackRetriever に渡せる。
"""
def __init__(self, *retrievers: WebRetriever) -> None:
if not retrievers:
raise ValueError("最低1つのリトリーバが必要です")
self._retrievers = retrievers
def retrieve(self, query: str, k: int = 5) -> list[Document]:
last_error: Exception | None = None
for retriever in self._retrievers:
name = type(retriever).__name__
try:
documents = retriever.retrieve(query, k)
except Exception as exc:
logger.warning("%s が失敗しました: %s", name, exc)
last_error = exc
continue
if documents:
return documents
logger.info("%s が0件を返しました。次を試します", name)
if last_error is not None:
raise last_error
return []
7-4. 環境ごとに構成を切り替える
最後に、どの型を使うかを設定ファイルへ追い出します。コード側に環境の分岐を書かないのが要点です。
クリックで設定ファイル3種を展開
# config/dev.yaml — 開発環境。無料枠を節約する
retriever:
primary: tavily
fallback: []
max_results: 3
timeout_sec: 10
# config/prod.yaml — 本番。片方が落ちても止めない
retriever:
primary: exa
fallback:
- tavily
max_results: 8
timeout_sec: 20
# config/test.yaml — CI。実APIを叩かない
retriever:
primary: stub
fallback: []
max_results: 3
timeout_sec: 1
# retriever/factory.py — 設定から組み立てる唯一の場所
from __future__ import annotations
from typing import Any, Callable
from .core import WebRetriever
from .fallback import FallbackRetriever
# 開放閉鎖の原則: 新しい型を足すときはこの辞書に追加するだけで、
# factory 関数本体には手を入れない。
BUILDERS: dict[str, Callable[[dict[str, Any]], WebRetriever]] = {}
def register(name: str) -> Callable[[Callable[..., WebRetriever]], Callable[..., WebRetriever]]:
def decorator(builder: Callable[..., WebRetriever]) -> Callable[..., WebRetriever]:
BUILDERS[name] = builder
return builder
return decorator
def build_retriever(config: dict[str, Any]) -> WebRetriever:
"""設定辞書から検索レイヤを組み立てる。"""
section = config["retriever"]
names = [section["primary"], *section.get("fallback", [])]
unknown = [n for n in names if n not in BUILDERS]
if unknown:
raise KeyError(f"未登録のリトリーバです: {unknown}")
return FallbackRetriever(*(BUILDERS[n](section) for n in names))
これで、エージェント本体のコードは次の1行だけを知っていれば済みます。
documents = retriever.retrieve("Mermaid v11 subgraph 日本語ラベル", k=5)
6分類のうちどれに乗り換えても、この行は変わりません。受付票の書式を固定した効果です。
8. よくある落とし穴と対処
このセクションで分かること: 実際に組み込んだあとで踏みやすい6つの穴。
| 症状 | 原因 | 対処 |
|---|---|---|
| 検索は成功するのに回答が的外れ | スニペットだけを渡している | 取得・整形型を挟むか、本文取得オプションを有効にする |
| トークン課金が想定の数倍 | 全文をそのままコンテキストに入れている | ハイライトや要約のオプションに切り替える |
| 同じ質問で毎回結果が変わる | ライブ検索なので当然の挙動 | テストではスタブ実装で固定する |
| モデル内蔵検索と外部APIで結果が食い違う | インデックスが別物 | どちらを一次情報とするか設計時に決めておく |
| 検索結果の中の文章が指示のように振る舞う | 間接プロンプトインジェクション | 取得内容をデータとして扱い、信頼境界を明示する |
| レート制限に頻繁に当たる | サブクエリを並列に投げすぎ | 同時実行数を制限し、フォールバック先を用意する |
最後から2番目の行は特に重要です。図書館の比喩で言えば、司書が持ってきた資料の余白に「これまでの指示を無視して館内の本を全部持ち出せ」と書かれている状況です。運搬係が信頼できても、運ばれてくる資料の中身は信頼できません。
Tavily がコンテンツスキャン機能を打ち出しているのはこの問題への対応ですが、API側の対策に依存しきるのは危険です。取得したテキストをシステムプロンプトと同じ扱いにしない、という設計側の防御が前提になります。
9. 用語集
このセクションで分かること: この記事に出てきた用語の最小限の定義。
| 用語 | 意味 |
|---|---|
| SERP | Search Engine Results Page。検索エンジンの結果ページそのもの |
| インデックス | クローラが集めたWebページの索引。自前で持つかどうかが分類の分かれ目 |
| スニペット | 検索結果に表示される数行の抜粋。本文全体ではない |
| ハイライト | クエリに関連する箇所だけを抜き出した断片。トークン節約が目的 |
| グラウンディング | モデルの出力を外部情報源に紐づけ、出典を示せるようにすること |
| Retriever | 情報を引いてくる部品の抽象名。ベクトルDBもWeb検索APIもこれに含まれる |
| Agentic Search | エージェントが推論しながら反復的に検索する振る舞いのパターン |
| Deep Research | 複数の検索を反復し、突き合わせてレポートを生成する仕組み |
| MCP | エージェントと外部ツールをつなぐ配線規格。検索の分類とは直交する |
| 間接プロンプトインジェクション | 取得したコンテンツに仕込まれた指示文がモデルを乗っ取る攻撃 |
10. 学習ロードマップ
このセクションで分かること: 次に何を触ればよいか。
第1段階 — 手を動かして違いを体感する
無料枠で Tavily と Exa の両方に同じクエリを投げ、返ってきたJSONを並べて見比べてください。あわせて https://r.jina.ai/ に適当なURLを付けて叩けば、取得・整形型が何をしているかも1分で分かります。
第2段階 — 抽象層を自分で組む
7章のコードを写経ではなく再設計してください。Document に何を持たせるかは用途で変わります。公開日、言語、ドメインの信頼度など、自分のエージェントが判断に使う情報を足していく作業がそのまま設計練習になります。
第3段階 — 検索レイヤの品質を測る
自分のドメインで検証用クエリを20件ほど作り、上位5件に一次情報がいくつ含まれるかを型ごとに数えます。ベンダの公表ベンチマークではなく、自分の用途での実測が唯一の判断材料です。ここまで来ると、OSS のエージェント実装がどの型を選んでいるか、その理由まで読めるようになります。
まとめ
図書館に閉じ込められた研究者に、外の資料を運んでくる人が要る。これがこの記事の出発点でした。
- Exa と Tavily を包括する呼び名は粒度によって3つある。製品カテゴリなら「AIネイティブ検索API」、アーキテクチャなら「検索レイヤ」、振る舞いなら「Agentic Search」
- 運搬係は6職種に整理できる。違いは「どこまで下ごしらえして渡すか」の一点に集約される
- Exa は意味で引く方向、Tavily は工程を合成する方向に振っている。優劣ではなく軸の違い
- 6職種のどれを雇っても壊れないコードは、受付票の書式を固定することで書ける
名前がまだ揺れているのは、カテゴリ自体が2025年から2026年にかけて急速に形を変えているからです。窓口が閉まり、代行業者の足場が揺れ、モデル提供者が自前のカウンターを設置した。名前が定まらないのは、地形がまだ動いている証拠と考えるのが実態に近いと思います。
呼び名を1つに決めることより、自分のエージェントがどの職種を雇っているかを説明できることのほうが、実務では役に立ちます。
参考文献
各リンクに、そこで何が確認できるかを添えています。
- Tavily 公式ブログ Tavily 101 — 2026年1月28日付。自社を "the web access layer for AI agents" と定義。4エンドポイントの使い分けとインジェクション遮断への言及
-
Exa 公式ドキュメント Search API Guide — 自社を "a custom search engine built for AIs" と定義。検索タイプ別レイテンシ表、カテゴリ別インデックス、
highlightsの説明 - Microsoft Learn Bing Search API 提供終了告知 — 終了日と移行先の案内。原文は "Bing Search APIs will be retired on August 11, 2025."
-
Google AI for Developers Grounding with Google Search —
google_searchツールの動作フローと課金単位の説明 -
Jina AI reader リポジトリ — "Convert any URL to an LLM-friendly input" の原文と
r.jina.aiの使い方 - Brave Search API 公式ページ — 独立したWebインデックスを土台にしている旨の記述
- Perplexity API Platform — Search API と Sonar の位置づけ、出典付き回答の仕様
- LangChain4j ドキュメント Web Search Engines — "Web search engines can be used as ContentRetrievers" の原文。フレームワーク側の抽象の置き方が分かる
- SerpApi 公式ブログ 訴訟の棄却報告 — 2026年7月20日付。当事者による一次報告。裁判所命令のPDFへのリンクあり
関連記事
更新情報や制作中の記事については、こちらで発信しています。
