新規投稿のお知らせを受信されたい方は、サブスクリプションをご登録ください:

AI Search:エージェントのための検索の基本機能

2026-04-16

7分で読了
この投稿はEnglish繁體中文한국어简体中文でも表示されます。

このコンテンツは自動機械翻訳サービスによる翻訳版であり、皆さまの便宜のために提供しています。原本の英語版と異なる誤り、省略、解釈の微妙な違いが含まれる場合があります。ご不明な点がある場合は、英語版原本をご確認ください。

すべてのエージェントは検索が必要です。コーディングエージェントはリポジトリの数百万ファイルを検索し、サポートエージェントは顧客チケットや社内ドキュメントを検索します。ユースケースは異なりますが、根本的な問題は同じです。適切なタイミングでモデルに適切な情報を得ることです。

自分で検索を構築する場合は、ベクトルインデックス、ドキュメントを解析してチャンク化するインデックス作成パイプライン、そしてデータが変更されたときにインデックスを最新に保つためのものが必要になります。キーワード検索も必要な場合は、それは別のインデックスと融合ロジックです。また、各エージェントに独自の検索可能なコンテキストが必要な場合は、エージェントごとにすべて設定することができます。

AI Search(旧AutoRAG)は、必要なプラグアンドプレイの検索プリミティブです。Worker、Agents SDK、またはWrangler CLIを使用して、インスタンスを動的に作成し、データを提供し、検索することができます。内容は以下の通りです。

  • ハイブリッド検索。同じクエリで、セマンティックとキーワードの一致を有効にします。ベクトル検索とBM25は並行して実行され、結果は融合されます。(現在、当社ブログの検索はAI Searchによって行われています。右上の虫眼鏡アイコンをお試しください。)

  • 内蔵されたストレージとインデックス。新しいインスタンスには、独自のストレージとベクトルインデックスが付属しています。APIを介してインスタンスに直接ファイルをアップロードすると、インデックスが作成されます。R2バケットのセットアップも、最初に接続する必要のある外部データソースもありません。新しいai_search_namespacesバインディングでは、Workerから実行時にインスタンスを作成/削除できるため、再デプロイなしに、エージェントごと、顧客ごと、または言語ごとに1つをスピンアップできます。

ドキュメントにメタデータを添付してクエリ時にランキングを上げるために使用したり、1回の呼び出しで複数のインスタンスにクエリを実行したりすることもできるようになりました。

では、これが実際に何を意味するのかを見ていきましょう。

稼働中:カスタマーサポートエージェント

共有製品ドキュメントと過去の解決方法のような顧客ごとの履歴という2種類のナレッジを検索するサポートエージェントについて説明します。製品ドキュメントはコンテキストウィンドウに収まらない容量です。また、解決された問題ごとに顧客の履歴も更新されるため、エージェントは情報を検索して関連性のある情報を見つける必要があります。

AI SearchとAgents SDKを使用した場合の例を次に示します。まずはプロジェクトのスキャフォールディングから始めましょう:

npm create cloudflare@latest -- --template cloudflare/agents-starter

まず、AI SearchネームスペースをWorkerにバインドします。

// wrangler.jsonc 
{
  "ai_search_namespaces": [
    { "binding": "SUPPORT_KB", "namespace": "support" }
  ],
  "ai": { "binding": "AI" },
  "durable_objects": {
    "bindings": [
      { "name": "SupportAgent", "class_name": "SupportAgent" }
    ]
  }
}

共有製品ドキュメントがproduct-docというR2バケットにあるとします。Cloudflareダッシュボードのsupport名前空間内で、バケットにバックアップされた一度限りのAI Searchインスタンス( product-knowledgeという名前)を作成できます。

BLOG-3240 2

それが共有ナレッジベースであり、すべてのエージェントが参照できるドキュメントです。

お客様が新しい問題について再度報告してくれた場合、すでに試されたものを知ることができることで、誰もが時間を節約することができます。これは、顧客ごとにAI Searchインスタンスを作成することで追跡できます。各問題が解決されると、エージェントは何が問題だったのか、どのように修正したのかの概要を保存します。時間の経過とともに、これにより過去の解決状況を示す検索可能なログが蓄積されます。ネームスペースバインディングを使用して、動的にインスタンスを作成できます。

// create a per-customer instance when they first show up 
await env.SUPPORT_KB.create({
  id: `customer-${customerId}`,
  index_method:{ keyword: true, vector: true }
});

各インスタンスは、R2Vectorizeを活用した独自の内蔵ストレージとベクトルインデックスを取得します。インスタンスは空から始まり、時間の経過とともにコンテキストを蓄積します。次回のお客様の訪問時には、そのすべてが検索可能です。

一部のお客様のネームスペースは次のようになります:

namespace: "support"
├── product-knowledge     (R2 as source, shared across all agents)
├── customer-abc123       (managed storage, per-customer)
├── customer-def456       (managed storage, per-customer)
└── customer-ghi789       (managed storage, per-customer)

エージェント自体がそうです。Agents SDKのAIChatAgentを拡張し、2つのツールを定義しています。Workers AIを介し、LLMとしてKin K2.5を使用しています。モデルがツールを呼び出すタイミングを決定:

import { AIChatAgent, type OnChatMessageOptions } from "@cloudflare/ai-chat";
import { createWorkersAI } from "workers-ai-provider";
import { streamText, convertToModelMessages, tool, stepCountIs } from "ai";
import { routeAgentRequest } from "agents";
import { z } from "zod";

export class SupportAgent extends AIChatAgent<Env> {
  async onChatMessage(_onFinish: unknown, options?: OnChatMessageOptions) {
    // the client passes customerId in the request body
    // via the Agent SDK's sendMessage({ body: { customerId } })
    const customerId = options?.body?.customerId;

    // create a per-customer instance when they first show up.
    // each instance gets its own storage and vector index.
    if (customerId) {
      try {
        await this.env.SUPPORT_KB.create({
          id: `customer-${customerId}`,
          index_method: { keyword: true, vector: true }
        });
      } catch {
        // instance already exists
      }
    }

    const workersai = createWorkersAI({ binding: this.env.AI });

    const result = streamText({
      model: workersai("@cf/moonshotai/kimi-k2.5"),
      system: `You are a support agent. Use search_knowledge_base
        to find relevant docs before answering. Search results
        include both product docs and this customer's past
        resolutions — use them to avoid repeating failed fixes
        and to recognize recurring issues. When the issue is
        resolved, call save_resolution before responding.`,
      // this.messages is the full conversation history, automatically
      // persisted by AIChatAgent across reconnects
      messages: await convertToModelMessages(this.messages),
      tools: {
        // tool 1: search across shared product docs AND this
        // customer's past resolutions in a single call
        search_knowledge_base: tool({
          description: "Search product docs and customer history",
          inputSchema: z.object({
            query: z.string().describe("The search query"),
          }),
          execute: async ({ query }) => {
            // always search product docs;
            // include customer history if available
            const instances = ["product-knowledge"];
            if (customerId) {
              instances.push(`customer-${customerId}`);
            }
            return await this.env.SUPPORT_KB.search({
              query: query,
              ai_search_options: {
                // surface recent docs over older ones
                boost_by: [
                  { field: "timestamp", direction: "desc" }
                ],
                // search across both instances at once
                instance_ids: instances
              }
            });
          }
        }),

        // tool 2: after resolving an issue, the agent saves a
        // summary so future agents have full context
        save_resolution: tool({
          description:
            "Save a resolution summary after solving a customer's issue",
          inputSchema: z.object({
            filename: z.string().describe(
              "Short descriptive filename, e.g. 'billing-fix.md'"
            ),
            content: z.string().describe(
              "What the problem was, what caused it, and how it was resolved"
            ),
          }),
          execute: async ({ filename, content }) => {
            if (!customerId) return { error: "No customer ID" };
            const instance = this.env.SUPPORT_KB.get(
              `customer-${customerId}`
            );
            // uploadAndPoll waits until indexing is complete,
            // so the resolution is searchable before the next query
            const item = await instance.items.uploadAndPoll(
              filename, content
            );
            return { saved: true, filename, status: item.status };
          }
        }),
      },
      // cap agentic tool-use loops at 10 steps
      stopWhen: stepCountIs(10),
      abortSignal: options?.abortSignal,
    });

    return result.toUIMessageStreamResponse();
  }
}

// route requests to the SupportAgent durable object
export default {
  async fetch(request: Request, env: Env) {
    return (
      (await routeAgentRequest(request, env)) ||
      new Response("Not found", { status: 404 })
    );
  }
} satisfies ExportedHandler<Env>;

これによって、モデルは検索するタイミングと保存するタイミングを決定します。検索の際、product-knowledgeとこの顧客の過去の解決策を一緒に問い合わせます。問題が解決されると、今後の会話ですぐに検索できる概要を保存します。

AI Searchが探している情報を見つける方法

内部では、AI Searchは複数ステップの検索パイプラインを実行しており、その中ではすべてのステップが設定可能です。

ハイブリッド検索:意図を理解し、条件に一致する検索

これまでは、AI Searchはベクトル検索のみを提供していました。ベクトル検索は意図の理解に長けますが、具体性を失うことがあります。「ERR_CONNECTION_REFused timeout」クエリでは、エンベッディングは接続失敗の広範な概念を捉えています。しかし、ユーザーは一般的なネットワークドキュメントを探しているわけではありません。「ERR_CONNECTION_REFused」に記載された特定の文書を探しています。ベクトル検索は、その正確なエラー文字列を含むページを表示することなく、トラブルシューティングに関する結果を返す可能性があります。

キーワード検索がそのギャップを埋めるのです。AI Searchは、最も広く使用されている検索スコアリング機能の1つであるBM25をサポートするようになりました。BM25は、クエリー条件が表示される頻度、コーパス全体におけるその用語の出現頻度、文書に存在する期間によって文書を評価します。特定の条件で一致した場合に報酬を与え、一般的な単語を罰し、文書の長さを正規化します。「ERR_CONNECTION_REFused タイムアウト」を検索すると、BM25は実際に「ERR_CONNECTION_REFused」を用語として含むドキュメントを検索します。ただし、BM25では、同じ問題が記述されているにもかかわらず、「ネットワーク接続のトラブルシューティング」に関するページは見逃される可能性があります。そこではベクトル検索の出番であり、両方が必要なのです。

ハイブリッド検索を有効にすると、ベクトルとBM25を並行して実行し、結果を融合させ、必要に応じて再ランク付けします:

BLOG-3240 3

BM25の新しい設定とその組み合わせを見てみましょう。

  1. トークナイザーは、インデックス作成時に文書が一致する条件にどう分割されるかを制御します。Porter Stemmer(オプション:porter)は、「running」を「run」に一致させるように単語を語幹に変換します。Trigram (オプション:trigram) は、文字の部分文字列と一致するため、"conf" は "configuration" と一致します。ドキュメントのような自然言語コンテンツにはporterを使用し、部分一致が重要なコードにはtrigramを使用することができます。

  2. キーワード一致モードで、クエリ時にBM25のスコアリング対象となるドキュメントを管理します。ANDは、すべてのクエリー条件を文書内に表示する必要があるか、少なくとも1つの一致するものを含めます。

  3. Fusion は、クエリ時間中に、ベクトルとキーワードの結果をどのように最終的な結果リストに結合するかを制御します。再帰的ランクフュージョン(オプション:rrf)は、スコアではなくランク位置でマージします。これは、互換性のない2つのスコアリングスケールを比較することを回避しますが、最大融合(オプション:max)は、より高いスコアを取ります。

  4. (オプション)再ランク付けでは、クエリとドキュメントをペアとして評価することで結果を再スコアリングするクロスエンコーダーパスが追加されます。これは、結果が適切な条件を備えているにもかかわらず、質問に答えていないケースを見つけるのに役立ちます。

すべてのオプションは、省略された場合はきちんとしたデフォルトとなります。新しいインスタンスを作成するたびに、どのようなものを柔軟に構成することができます。

const instance = await env.AI_SEARCH.create({
  id: "my-instance",
  index_method: { keyword: true, vector: true },
  indexing_options: {
    keyword_tokenizer: "porter"
  },
  retrieval_options: {
    keyword_match_mode: "or"
  },
  fusion_method: "rrf",
  reranking: true,
  reranking_model: "@cf/baai/bge-reranker-base"
});

関連性の向上:重要なことを表面化

検索により関連性のある結果が得られますが、関連性だけでは必ずしも十分とは言えません。たとえば、ニュース検索では、先週の記事と3年前の記事の両方が、「選挙結果」に意味的に関連するかもしれませんが、ほとんどのユーザーはおそらく最近の記事を求めているでしょう。Boostingを使用すると、ドキュメントのメタデータに基づいてランキングを急上昇させることで、検索の上にビジネスロジックを重ねることができます。

タイムスタンプ(すべての項目に組み込まれている)または定義したカスタムメタデータフィールドに基づいてブーストできます。

// boost high priority docs
const results = await instance.search({
  query: "deployment guide",
  ai_search_options: {
    boost_by: [
      { field: "timestamp", direction: "desc" }
    ]
  }
});

クロスインスタンス検索:境界を越えたクエリー

サポートエージェントの例では、製品ドキュメントとお客様の解決履歴は設計上、別々のインスタンスに存在します。しかし、エージェントが質問に応答する場合、一度に両方の場所からのコンテキストが必要です。インスタンス間検索がなければ、2つの別々の呼び出しを行い、結果を自分で統合することになります。

ネームスペースのバインディングは、これを処理するsearch()メソッドを公開します。インスタンス名の配列を渡して、ランク付けされたリストを 1 つ取得する:

const results = await env.SUPPORT_KB.search({
  query: "billing error",
  ai_search_options: {
    instance_ids: ["product-knowledge", "customer-abc123"]
  }
});

結果はインスタンス間で統合され、ランク付けされます。エージェントは、共有ドキュメントと顧客解決の履歴が別々の場所に存在することを知る必要はなく、気にする必要はありません。

AI Searchインスタンスの仕組み

ここまで、AI Searchが適切な結果を見つける方法について説明してきました。次は、検索インスタンスの作成と管理方法です。

このリリース以前にAI Searchを使用していれば、R2バケットを作成し、AI Searchインスタンスにリンクし、AI SearchはサービスAPIトークンを生成し、アカウントにプロビジョニングされるVectorizeインデックスを管理します。オブジェクトをアップロードするには、R2に書き込み、同期ジョブが実行されるのを待ってオブジェクトにインデックスを付ける必要があります。

作成された新しいインスタンスは、他とは異なる動作をします。create()を呼び出すと、インスタンスには独自のストレージとベクトルインデックスが組み込まれます。ファイルをアップロードすると、ファイルはすぐにインデックスに送信され、インデックス作成ステータスを1つのuploadAndpoll() APIで調査できます。完了したら、すぐにインスタンスを検索でき、外部依存関係も連携させる必要はありません。

const instance = env.AI_SEARCH.get("my-instance");

// upload and wait for indexing to complete
const item = await instance.items.uploadAndPoll("faq.md", content, {
  metadata: { category: "onboarding" }
});
console.log(item.status); // "completed"

// immediately search after indexing is completed
const results = await instance.search({
  // alternative way to pass in users' query other than using parameter query 
  messages: [{ role: "user", content: "onboarding guide" }],
});

各インスタンスは、1つの外部データソース(R2バケットまたはWebサイト)に接続し、同期スケジュールで実行することもできます。提供されたビルトインストレージと並行して存在することができます。サポートエージェントの例では、product-knowledgeは共有ドキュメント用にR2バケットによって支えられ、各お客様のインスタンスは内蔵型ストレージを使用してその場でアップロードされるコンテキストを使用します。

ネームスペース:実行時に検索インスタンスを作成

ai_search_namespacesは、実行時に検索インスタンスを動的に作成するために活用できる新しいバインディングです。これは、以前のenv.AI.autorag()を置き換えるものです。AIバインディングを通じてAI SearchにアクセスしたAPI。古いバインディングは、Workers の互換性日付を使用して引き続き機能します。

// wrangler.jsonc 
{
  "ai_search_namespaces": [
    { "binding": "AI_SEARCH", "namespace": "example" },
  ]
}

ネームスペースバインディングは、ネームスペースレベルでcreate()delete()list()search()などのAPIを提供します。インスタンスを動的に作成する場合(例:エージェントごと、カスタマー、テナントごと)、これは使用するバインディングです。

// create an instance 
const instance = await env.AI_SEARCH.create({
  id: "my-instance"
});

// delete an instance and all its indexed data
await env.AI_SEARCH.delete("old-instance");

新規インスタンスの価格設定

本日作成された新規インスタンスは、内蔵ストレージとベクトルインデックスを自動的に取得します。

これらのインスタンスは、AI Searchのオープンベータ版中は、以下に記載された制限付きで無料でご利用いただけます。Webサイトをデータソースとして使用する場合、Browser Run(旧ブラウザレンダリング)を使用したWebサイトのクローリングも、ビルトインのサービスとなり、個別に請求されることはありません。ベータ版提供後は、AI Searchを単一のサービスとして統一し、基本となるコンポーネントごとに個別に請求するのではなく、単一のサービスとして統一した価格設定を目指しています。Workers AIとAI Gatewayの使用量は引き続き個別に請求されます。

請求が開始される前に、少なくとも30日前に通知と価格の詳細をお伝えします。

制限

Workers 無料

Workers 有料

アカウントあたりのAI Searchインスタンス数

100

5,000

インスタンスあたりのファイル数

100,000

ハイブリッド検索は100万または50万

最大ファイルサイズ

4MB

4MB

月間のクエリー数

20,000

無制限

1日あたり最大クロールページ数

500

無制限

既存のインスタンスはどうですか? 

このリリース以前にインスタンスを作成した場合は、現在と同じように機能し続けます。R2バケット、Vectorizeインデックス、Browser Runの使用量はアカウントに引き続き使用され、課金も以前のままとなります。既存インスタンスの移行詳細は、近日中にお知らせいたします。

今すぐ始めましょう

検索は、エージェントができる最も基本的なことの1つです。AI Searchを使用すれば、インフラを構築する必要はありません。インスタンスを作成し、データを渡して、エージェントに検索させましょう。

今日から、このコマンドを実行して最初のインスタンスを作成しましょう。

npx wrangler ai-search create my-search

ドキュメントをご覧になり、Cloudflare Developer Discordで何を構築しているか、ぜひお聞かせください。

BLOG-3240 4

Cloudflareは企業ネットワーク全体を保護し、お客様がインターネット規模のアプリケーションを効率的に構築し、あらゆるWebサイトやインターネットアプリケーションを高速化し、DDoS攻撃を退けハッカーの侵入を防ぎゼロトラスト導入を推進できるようお手伝いしています。

ご使用のデバイスから1.1.1.1 にアクセスし、インターネットを高速化し安全性を高めるCloudflareの無料アプリをご利用ください。

より良いインターネットの構築支援という当社の使命について、詳しくはこちらをご覧ください。新たなキャリアの方向性を模索中の方は、当社の求人情報をご覧ください。
Agents WeekエージェントAI検索AI

Xでフォロー

Anni Wang|/aninibread
Cloudflare|@cloudflare

関連ブログ投稿

2026年4月30日

エージェントは、Cloudflare アカウントの作成、ドメインの購入、デプロイができるようになりました

本日より、エージェントはCloudflareのお客様になります。彼らはCloudflareアカウントを作成し、有料サブスクリプションを開始し、ドメインを登録し、APIトークンを返して、すぐにコードをデプロイできます。人間はループ内で許可を与えますが、ダッシュボードにアクセスしたり、APIトークンをコピー&ペーストしたり、クレジットカードの詳細を入力したりする必要はありません。 ...

2026年4月21日

過去のボット対人間

AIアシスタントやプライバシープロキシが従来のボット検出能力を難しくしているため、Webには責任を果たすための新しいモデルが必要です。私たちは、制御はクライアントに委ねられるべきであり、匿名認証情報のオープンなエコシステムが、ユーザーのプライバシーを守りながら、オリジンを悪用から保護するための鍵だと考えています。...

2026年4月20日

当社が提供するプラットフォーム上に構築した、社内向けAI技術スタック

当社は、出荷している製品と同じ製品を用いて、社内のAIエンジニアリングスタックを構築しました。これは、AI Gatewayを介してルーティングされた2,410億件のリクエスト、2,410億件のトークンを処理し、Workers AI上で推論を実行し、3,683人以上の社内ユーザーにサービスを提供することを意味します。その方法をご紹介します。 ...