Search is the agent primitive nobody wants to rebuild

Every agent shares one dependency: retrieval. A coding agent needs to pull the right file from millions of commits; a support agent needs the right ticket from years of history. The use cases differ, but the mechanics don't — get the correct context to the model before it answers.

Building that yourself means wiring a vector index, a parsing and chunking pipeline for documents, and a sync mechanism to keep everything current when data changes. Add keyword search and you're maintaining a second index plus fusion logic. Multiply that by every agent, customer, or tenant that needs isolated context, and the infrastructure sprawl gets out of hand fast.

Cloudflare's AI Search (formerly AutoRAG) is positioned as the plug-and-play answer to that problem. It's a managed search primitive you can instantiate, load with data, and query from a Worker, the Agents SDK, or Wrangler CLI. The current release adds hybrid retrieval, built-in storage, runtime instance management, metadata boosting, and cross-instance queries.

What's in the release

  • Hybrid search. Semantic vector search and BM25 keyword search run in parallel against the same query, with configurable fusion. Cloudflare's own blog search runs on this now.
  • Managed storage and indexing. New instances ship with their own storage and vector index. Upload a document via API and it's indexed immediately — no separate R2 bucket setup or external data source configuration.
  • Runtime instance management. The new ai_search_namespaces Workers binding allows create() and delete() calls from your Worker at runtime, making per-agent or per-tenant instances practical.
  • Metadata boosting and cross-instance search. Attach metadata to documents to influence ranking at query time, and query multiple instances in a single call.

A concrete example: customer support

Consider a support agent that needs two kinds of knowledge: shared product documentation and per-customer resolution history. The shared docs are too large for a context window; the per-customer history grows with every ticket. Both require retrieval.

With the Agents SDK, you'd start by scaffolding a project and binding an AI Search namespace to your Worker. Your shared documentation — say, in an R2 bucket called product-doc — becomes a one-off AI Search instance named product-knowledge within a support namespace, created from the Cloudflare Dashboard. That's the knowledge base every agent can reference.

Customer-specific history is handled by creating an instance per customer at runtime using the namespace binding:

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

Each instance gets its own built-in storage and vector index, backed by R2 and Vectorize. The instance starts empty and accumulates context as tickets are resolved. The next time that customer returns, the entire history is searchable.

The agent itself extends AIChatAgent from the Agents SDK, defining tools for searching and saving. With Kimi K2.5 as the LLM through Workers AI, the model decides when to invoke those tools based on the conversation. When it searches, it queries product-knowledge and the customer's resolution history together — the model doesn't need to know they live in different places.

Why hybrid search matters

Vector search alone has a blind spot. A query like ERR_CONNECTION_REFUSED timeout gets embedded into a semantic representation of "connection failures." The user, however, wants the document containing the literal string ERR_CONNECTION_REFUSED — not general networking troubleshooting guides. Vector search may never surface that exact page.

BM25 fills the gap. It scores documents by term frequency, term rarity across the corpus, and document length normalization. It excels at exact-term matching but misses semantically related content that lacks the literal terms — which is precisely where vector search is strong. Running both in parallel and fusing the results is the point of hybrid search.

AI Search exposes several configuration options for the BM25 and fusion pipeline, each with a sensible default:

  1. Tokenizer. Controls how documents break into matchable terms at index time. The porter option stems words so "running" matches "run," well-suited for natural language. The trigram option matches character substrings so "conf" matches "configuration," which works better for code.
  2. Keyword match mode. AND requires all query terms in a document; OR includes documents with any single match. This shapes the candidate pool for BM25 scoring.
  3. Fusion. Determines how vector and keyword scores combine. Reciprocal rank fusion (rrf) merges by rank position, avoiding the problem of comparing incompatible score scales; max fusion (max) takes the higher of the two scores.
  4. Reranking. An optional cross-encoder pass re-scores results by evaluating query-document pairs together, catching cases where a result has the right terms but misses the question's intent.

These options are set at instance creation time:

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

Metadata boosting and cross-instance queries

Relevance isn't always sufficient. An article from last week and one from three years ago might both be semantically relevant to "election results," but a user almost certainly wants the recent one. Metadata boosting layers that kind of business logic onto retrieval.

You can boost on the built-in timestamp every item carries, or on any custom metadata field you define:

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

Cross-instance search addresses the support agent scenario where context spans multiple stores. The namespace binding exposes a search() method that accepts an array of instance names, merges results across them, and returns a single ranked list:

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

The agent never handles the merge itself — that's the primitive's job.

Instance architecture and pricing

AI Search instances created before this release required manual assembly: an R2 bucket, a linked AI Search instance, a generated service API token, and a Vectorize index on your account. Uploading an object meant writing to R2 and waiting for a sync job to index it.

New instances work differently. Calling create() provisions an instance with built-in storage and a vector index — no external wiring. The uploadAndpoll() API uploads a file, indexes it, and checks indexing status in one call. Once complete, the instance is immediately searchable.

New instances can also connect to one external data source — an R2 bucket or a website — running on a sync schedule alongside the built-in storage. In the support example, product-knowledge syncs from R2 while customer instances rely on built-in storage for runtime uploads.

The ai_search_namespaces binding replaces the earlier env.AI.autorag() API. The old bindings continue to work under Workers compatibility dates; the new binding exposes create(), delete(), list(), and search() at the namespace level for dynamic instance management.

New instances are free while AI Search is in open beta, subject to listed limits. Website crawling via Browser Run is now built in, so it won't be billed separately either. After beta, Cloudflare plans unified AI Search pricing rather than per-component billing; Workers AI and AI Gateway usage remain separate line items. At least 30 days' notice will precede any billing change. Existing pre-release instances continue unchanged, with migration details to follow.