OpenAICloudflareApr 16, 2026, 1:00 PM

AI Search: the search primitive for your agents

A condensed section focused on the key takeaways first.

Original Post

Quick Digest

Summary

A condensed section focused on the key takeaways first.

openaienmodel: gpt-5-mini-2025-08-07

AI Search: the search primitive for your agents

Key Points

  • Hybrid vector + BM25 search with fusion and optional reranking
  • Per-instance built-in storage and vector index (no external setup)
  • Runtime instance management via ai_search_namespaces binding

Summary

AI Search (formerly AutoRAG) is a plug-and-play search primitive for agents that combines vector and keyword retrieval, built-in per-instance storage and indexing, and runtime instance management via the ai_search_namespaces binding. It enables hybrid search (vector + BM25) with configurable tokenizers, fusion, and optional reranking, and supports multi-instance queries and metadata-based boosting so agents can retrieve the right context at the right time.

Key Points

  • Hybrid retrieval: vector search and BM25 run in parallel, results are fused (options: rrf, max) and can be reranked with a cross-encoder.
  • Built-in storage & index: each AI Search instance includes managed storage and a vector index (backed by R2 and Vectorize); files can be uploaded directly and indexed.
  • Dynamic instances: use the ai_search_namespaces binding to create/delete instances at runtime (one per agent, customer, or language) without redeploy.
  • Querying: search across multiple instances in one call via instance_ids; attach metadata to documents and use boost_by to influence ranking (e.g., recency).
  • Index & retrieval options: configure keyword_tokenizer (e.g., porter, trigram), keyword_match_mode (and/or), fusion_method, and reranking_model when creating instances.
  • Practical workflows: agent tools can call a shared KB plus per-customer instance together; use instance.items.uploadAndPoll so new docs are searchable before subsequent queries.

Quick implementation notes

  • Bind a namespace in wrangler.jsonc with ai_search_namespaces to manage instances at runtime.
  • Create an instance with both methods: 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' }).
  • For per-customer logs: create customer-{id} instances on first contact and save summaries via instance.items.uploadAndPoll(filename, content) so resolutions are immediately searchable.

Why it matters for engineers

AI Search reduces boilerplate (no separate R2 setup per instance), centralizes retrieval configuration, and simplifies agent design: models decide when to search or persist results; engineers configure tokenization, fusion, and ranking once per instance to match content types (docs vs. code) and business logic (recency, ownership).

Full Translation

Translations

A translation section that keeps the flow of the original article.

openaijamodel: gpt-5-mini-2025-08-07

AI Search:エージェント向け検索プリミティブ

AI Search:エージェント向け検索プリミティブ

2026-04-16 • Gabriel Massadas、Miguel Cardoso、Anni Wang • 読了約7分

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

独自に検索を構築する場合、ベクトルインデックス、ドキュメントを解析・チャンク化するインデクシングパイプライン、データが変化したときにインデックスを最新に保つ仕組みが必要です。キーワード検索も必要なら、別のインデックスとその上の融合ロジックが必要になります。さらに各エージェントごとに検索可能なコンテキストが必要なら、それらをエージェント単位でセットアップしなければなりません。

AI Search(旧 AutoRAG)は、そのためのプラグアンドプレイな検索プリミティブです。インスタンスを動的に作成し、データを与え、Worker、Agents SDK、または Wrangler CLI から検索できます。

主な提供内容:

  • ハイブリッド検索(Hybrid search)

    • セマンティック検索(vector)とキーワード検索(BM25)を同一クエリで有効にできます。ベクトル検索とBM25を並列で実行し、結果を融合します。
    • (このブログの検索は現在 AI Search によって動いています。右上の虫眼鏡アイコンを試してください。)
  • 組み込みのストレージとインデックス

    • 新しいインスタンスには専用のストレージとベクトルインデックスが付属します。ファイルをAPI経由でインスタンスに直接アップロードすればインデックス化されます。R2バケットを事前にセットアップしたり、外部データソースを先に接続したりする必要はありません。
  • ai_search_namespaces バインディング

    • このバインディングを使うと、Worker からランタイムにインスタンスを作成・削除できます。エージェントごと、顧客ごと、言語ごとに再デプロイなしでインスタンスを立ち上げられます。
  • ドキュメントにメタデータを付与してクエリ時にランクをブーストできる機能、複数インスタンスを単一の呼び出しで横断検索する機能。

以下では、実際の利用例を見ていきます。

実践:カスタマーサポートエージェント

共通の製品ドキュメントと、顧客固有の履歴(過去の解決履歴)という2種類の知識を検索するサポートエージェントを例に説明します。製品ドキュメントはコンテキストウィンドウに収まりきらないことが多く、顧客ごとの履歴は解決ごとに増えていくため、関連する情報を取り出すために検索(retrieval)が必要です。

以下は AI Search と Agents SDK を使った構成例です。

まずプロジェクトをスキャフォールドします:

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

最初に、Worker に AI Search ネームスペースをバインドします(wrangler.jsonc の例):

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

たとえば、共通の製品ドキュメントが R2 バケット product-doc にあるとします。Cloudflare Dashboard からサポート用ネームスペース内に、そのバケットをソースにした一時的(one-off)AI Search インスタンス product-knowledge を作成できます。

それが共有ナレッジベースで、すべてのエージェントが参照できるドキュメント群になります。顧客が再度問題を報告した際に、既に試したことを把握しておくと時間が節約できます。

顧客ごとの履歴は、顧客毎に 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 } });

各インスタンスは専用の組み込みストレージとベクトルインデックス(R2 と Vectorize による)を持ちます。インスタンスは空の状態から始まり、時間とともにコンテキストが蓄積されます。次回顧客が戻ってきたとき、全てが検索可能です。

数名分の顧客が増えるとネームスペースは次のようになります:

  • namespace: "support"
    • product-knowledge (R2 をソースに、全エージェントで共有)
    • customer-abc123 (managed storage, per-customer)
    • customer-def456 (managed storage, per-customer)
    • customer-ghi789 (managed storage, per-customer)

エージェント本体

エージェントは Agents SDK の AIChatAgent を拡張し、2つのツールを定義します。LLM には Workers AI を通じて Kimi 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" を含む特定のドキュメントを探しています。ベクトル検索は一般的なトラブルシューティングの結果を返すかもしれませんが、エラー文字列そのものを含むページを表面化しない可能性があります。

キーワード検索(BM25)はこのギャップを埋めます。BM25 はクエリ用語の出現頻度、コーパス全体での希少性、ドキュメント長を元にスコアリングします。特定の用語一致を重視し、一般的な助詞などを減点し、文書長で正規化します。クエリ "ERR_CONNECTION_REFUSED timeout" なら、BM25 は実際に "ERR_CONNECTION_REFUSED" を含むドキュメントを見つけます。とはいえ BM25 は「ネットワーク接続のトラブルシューティング」的なページを見逃すかもしれません。そこをベクトル検索が補完するため、両方が必要になります。

ハイブリッド検索を有効にするとベクトルとBM25を並列で実行し、結果を融合して(必要に応じて再ランク付けも行い)返します。

以下は BM25 の新しい設定とそれらがどのように組み合わさるかの概要です。

  • tokenizer(トークナイザー)

    • ドキュメントをインデックス時にマッチ可能な用語へ分割する方法を制御します。
    • porter(Porter stemmer):"running" が "run" にステミングされるなど、自然言語コンテンツ向け。
    • trigram:文字列の部分一致を扱うので、コード内で部分一致が重要な場合に有効("conf" が "configuration" にマッチするなど)。
  • keyword match mode(キーワード一致モード)

    • クエリ時に BM25 の候補となるドキュメントの選び方を制御します。
    • AND は全てのクエリ用語が出現するドキュメントのみ、OR は少なくとも1つの用語があるものを含めます。
  • fusion(融合)

    • クエリ時にベクトルとキーワードの結果を最終的な結果リストにどのように統合するかを制御します。
    • rrf(reciprocal rank fusion):スコアではなく順位位置でマージするため、互換性のないスコア尺度を比較する問題を避けます。
    • max:スコアの高い方を採る方式。
  • (オプション)reranking(再ランク付け)

    • クエリとドキュメントをペアとして評価するクロスエンコーダーを追加で走らせ、結果を再スコアリングします。クエリの用語は含むが質問に答えていない結果を弾くのに有効です。

すべてのオプションは省略した場合に妥当なデフォルトが設定されています。新しいインスタンス作成時に必要な構成を指定できます:

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"
});

関連度のブースト:重要なものを表面化する

検索は関連性の高い結果を返しますが、関連性だけでは十分でないことがあります。たとえばニュース検索では「選挙結果」に関する記事が1週間前のものと3年前のものとで両方関連していても、ほとんどのユーザーはより最近の記事を欲しがるでしょう。ブースト(boosting)はドキュメントのメタデータに基づいてランキングを押し上げることで、ビジネスロジックを検索結果に重ねることを可能にします。