diff --git a/src/content/docs/ai-search/agent-sdks/agents-sdk.mdx b/src/content/docs/ai-search/agent-sdks/agents-sdk.mdx new file mode 100644 index 00000000000..cc4370b3d83 --- /dev/null +++ b/src/content/docs/ai-search/agent-sdks/agents-sdk.mdx @@ -0,0 +1,256 @@ +--- +title: Agents SDK +pcx_content_type: how-to +sidebar: + order: 1 +description: Build a Cloudflare Agent that provisions an AI Search instance, indexes content, and searches it with a tool. +products: + - ai-search +--- + +import { + Render, + PackageManagers, + WranglerConfig, + TypeScriptExample, + LinkCard, +} from "~/components"; + +The [Cloudflare Agents SDK](/agents/) lets you build stateful AI agents that run on Workers. This guide builds a chat agent that provisions its own AI Search instance, indexes a document, and then searches that content with a tool before it answers. + +For the agent-side reference, refer to [AI Search as an agent tool](/agents/tools/ai-search/). + +## Prerequisites + + + +## 1. Create a Worker project + +Create a new Worker project using the `create-cloudflare` CLI (C3). [C3](https://github.com/cloudflare/workers-sdk/tree/main/packages/create-cloudflare) is a command-line tool designed to help you set up and deploy new applications to Cloudflare. + +Create a new project named `ai-search-agent` by running: + + + + + +Go to your application directory: + +```sh +cd ai-search-agent +``` + +## 2. Install the Agents SDK packages + +Install the Agents SDK, the AI SDK, and the Workers AI provider: + + + +## 3. Bind your Worker to AI Search + +Replace your [Wrangler configuration file](/workers/wrangler/configuration/) with the following. This adds a [namespace binding](/ai-search/concepts/namespaces/) for AI Search, a Workers AI binding for response generation, and the Durable Object that stores chat history for the agent. + + + +```toml +name = "ai-search-agent" +main = "src/server.ts" +compatibility_date = "$today" +compatibility_flags = ["nodejs_compat"] + +[ai] +binding = "AI" + +[[ai_search_namespaces]] +binding = "AI_SEARCH" +namespace = "default" +remote = true + +[[durable_objects.bindings]] +name = "SearchAgent" +class_name = "SearchAgent" + +[[migrations]] +tag = "v1" +new_sqlite_classes = ["SearchAgent"] +``` + + + +The namespace binding (`ai_search_namespaces`), not the single-instance `ai_search` binding, is required because the agent calls `create()` at runtime. The `remote` option lets `wrangler dev` proxy requests to your deployed instance, since AI Search does not run locally. `AIChatAgent` persists messages to SQLite, so its class must be listed in `new_sqlite_classes`. + +## 4. Write the agent + +Create `src/server.ts`. The agent provisions an AI Search instance with [hybrid search](/ai-search/configuration/indexing/hybrid-search/) enabled the first time it runs, seeds it with a document, and exposes two tools: `search_knowledge_base` retrieves content, and `save_resolution` writes new content back. + + + +```ts +import { AIChatAgent } from "@cloudflare/ai-chat"; +import { routeAgentRequest } from "agents"; +import { createWorkersAI } from "workers-ai-provider"; +import { streamText, convertToModelMessages, tool, stepCountIs } from "ai"; +import { z } from "zod"; + +const INSTANCE_ID = "knowledge-base"; + +const SEED_DOC = `# Getting started +AI Search indexes your content so an agent can retrieve it at query time.`; + +export class SearchAgent extends AIChatAgent { + private ready = false; + + // Create the agent's instance with hybrid search enabled, then seed it so + // the first query has content. create() throws if the instance already + // exists, so the try/catch makes this idempotent. + private async ensureInstance() { + if (this.ready) return; + try { + // index_method with both vector and keyword enables hybrid search. + await this.env.AI_SEARCH.create({ + id: INSTANCE_ID, + index_method: { vector: true, keyword: true }, + }); + // uploadAndPoll waits until the file is indexed and searchable. + await this.env.AI_SEARCH.get(INSTANCE_ID).items.uploadAndPoll( + "getting-started.md", + SEED_DOC, + { timeoutMs: 120_000 }, + ); + } catch { + // Instance already exists. + } + this.ready = true; + } + + async onChatMessage() { + await this.ensureInstance(); + + const workersai = createWorkersAI({ binding: this.env.AI }); + + const result = streamText({ + model: workersai("@cf/meta/llama-3.3-70b-instruct-fp8-fast"), + system: + "You are a support assistant. Use search_knowledge_base to find " + + "relevant content before answering, and cite what you use.", + messages: await convertToModelMessages(this.messages), + tools: { + search_knowledge_base: tool({ + description: "Search the knowledge base for relevant content.", + inputSchema: z.object({ + query: z.string().describe("The user's question or search terms"), + }), + execute: async ({ query }) => { + const instance = this.env.AI_SEARCH.get(INSTANCE_ID); + return await instance.search({ + query, + ai_search_options: { retrieval: { max_num_results: 5 } }, + }); + }, + }), + save_resolution: tool({ + description: + "Save a resolved answer to the knowledge base for reuse.", + inputSchema: z.object({ + title: z.string().describe("Short descriptive title"), + content: z.string().describe("The resolution to save"), + }), + execute: async ({ title, content }) => { + const instance = this.env.AI_SEARCH.get(INSTANCE_ID); + const item = await instance.items.uploadAndPoll( + `${title}.md`, + content, + ); + return { key: item.key, status: item.status }; + }, + }), + }, + stopWhen: stepCountIs(5), + }); + + return result.toUIMessageStreamResponse(); + } +} + +export default { + async fetch(request: Request, env: Env) { + return ( + (await routeAgentRequest(request, env)) || + new Response("Not found", { status: 404 }) + ); + }, +} satisfies ExportedHandler; +``` + + + +`this.env.AI_SEARCH.get(INSTANCE_ID)` is synchronous and resolves lazily. It does not create the instance, so `ensureInstance` creates it first. To search several instances in one call, use a namespace-level search with `ai_search_options.instance_ids`. Refer to [Namespaces](/ai-search/concepts/namespaces/). + +## 5. How the tools work + +The `search_knowledge_base` tool calls `search()` on the instance. Because the instance indexes both vectors and keywords, retrieval uses hybrid search by default. + +The `save_resolution` tool calls `items.uploadAndPoll()`, which uploads a document to built-in storage and waits until indexing completes, so the content is searchable on the next query. Uploading a file with the same name overwrites and re-indexes it. + +## 6. Run locally + +Generate types and start the development server: + +```sh +npx wrangler types +npm run dev +``` + +On the first message, the agent creates and seeds the instance, so the first response may take a few extra seconds while `uploadAndPoll` indexes the document. + +## 7. Deploy + +Log in with your Cloudflare account: + +```sh +npx wrangler login +``` + +Deploy your Worker to make it accessible on the Internet: + +```sh +npx wrangler deploy +``` + +## Next steps + + + + + + + + diff --git a/src/content/docs/ai-search/agent-sdks/ai-sdk.mdx b/src/content/docs/ai-search/agent-sdks/ai-sdk.mdx new file mode 100644 index 00000000000..5a740dc062d --- /dev/null +++ b/src/content/docs/ai-search/agent-sdks/ai-sdk.mdx @@ -0,0 +1,289 @@ +--- +title: AI SDK +pcx_content_type: how-to +sidebar: + order: 2 +description: Use AI Search from the Vercel AI SDK to create an instance, index content, and generate grounded responses in a TypeScript project. +products: + - ai-search +--- + +import { + Render, + PackageManagers, + WranglerConfig, + TypeScriptExample, + LinkCard, +} from "~/components"; + +The [Vercel AI SDK](https://sdk.vercel.ai/) is a TypeScript toolkit for building applications with large language models. The [`ai-search-provider`](https://www.npmjs.com/package/ai-search-provider) package connects AI Search to the AI SDK, so you can generate responses grounded in your indexed content, retrieve chunks, and manage documents from the same API. + +This guide builds a Worker that creates an AI Search instance, uploads and indexes a document, and then queries it with the AI SDK. + +## Prerequisites + + + +## 1. Create a Worker project + +Create a new Worker project using the `create-cloudflare` CLI (C3). [C3](https://github.com/cloudflare/workers-sdk/tree/main/packages/create-cloudflare) is a command-line tool designed to help you set up and deploy new applications to Cloudflare. + +Create a new project named `ai-search-ai-sdk` by running: + + + + + +Go to your application directory: + +```sh +cd ai-search-ai-sdk +``` + +## 2. Install the AI SDK and provider + +Install the AI SDK and the AI Search provider. The provider requires AI SDK v6 (`ai@^6`): + + + +## 3. Bind your Worker to AI Search + +Create a binding between your Worker and AI Search. [Bindings](/workers/runtime-apis/bindings/) allow your Worker to interact with resources on the Cloudflare Developer Platform. + +Add the following to your [Wrangler configuration file](/workers/wrangler/configuration/): + + + +```toml +[[ai_search_namespaces]] +binding = "AI_SEARCH" +namespace = "default" +remote = true +``` + + + +This binds the `default` [namespace](/ai-search/concepts/namespaces/) to `env.AI_SEARCH`. The `remote` option lets `wrangler dev` proxy requests to your deployed instance, since AI Search does not run locally. The `ai_search_namespaces` binding requires a `compatibility_date` of `2026-03-27` or later, which new C3 projects already satisfy. + +## 4. Create an instance and index content + +Add a `/setup` route that creates an instance and uploads a document. Enable [hybrid search](/ai-search/configuration/indexing/hybrid-search/) at creation by setting `index_method` to index both vectors and keywords. + +The `create()` method is on the namespace binding (`env.AI_SEARCH`), not on the provider client. Creating an instance that already exists throws, so the following code creates it and, on the next run, updates it instead. + + + +```ts +import { createAISearchNamespace } from "ai-search-provider"; +import { generateText, streamText } from "ai"; + +interface Env { + AI_SEARCH: AiSearchNamespace; +} + +const INSTANCE_NAME = "knowledge-base"; + +const SAMPLE_DOC = `# Caching on Cloudflare +Cloudflare caches static assets at the edge. Use Cache Rules to control what is +cached, set an Edge Cache TTL to control how long objects stay in cache, and +purge the cache after a deploy.`; + +// Create the instance with hybrid search, or update it if it already exists. +async function ensureInstance(env: Env) { + // index_method with both vector and keyword enables hybrid search. + const hybrid = { index_method: { vector: true, keyword: true } }; + try { + await env.AI_SEARCH.create({ id: INSTANCE_NAME, ...hybrid }); + } catch { + await env.AI_SEARCH.get(INSTANCE_NAME).update(hybrid); + } +} + +export default { + async fetch(request, env): Promise { + const url = new URL(request.url); + const aiSearch = createAISearchNamespace({ binding: env.AI_SEARCH }); + + // Visit /setup once to create the instance and index a document. + if (url.pathname === "/setup") { + await ensureInstance(env); + + // uploadAndPoll uploads to built-in storage and waits until the file + // is indexed. A new instance's first index can take longer than the + // 30-second default, so raise the timeout. + const item = await aiSearch + .get(INSTANCE_NAME) + .items.uploadAndPoll("caching.md", SAMPLE_DOC, { + timeoutMs: 120_000, + pollIntervalMs: 2_000, + }); + + return Response.json({ key: item.key, status: item.status }); + } + + // Query the instance (see the next step). + return new Response("Visit /setup first, then query with ?q="); + }, +} satisfies ExportedHandler; +``` + + + +`AiSearchNamespace` is an ambient type available after you run `wrangler types`. + +## 5. Generate a grounded response + +Pass `instance.chat()` to `generateText`, and AI Search retrieves relevant content and generates a response in one call. This is the recommended way to query an instance. + +Replace the query placeholder in your `fetch` handler with the following: + + + +```ts +const query = url.searchParams.get("q") ?? "How does caching work?"; + +const { text, sources } = await generateText({ + model: aiSearch.get(INSTANCE_NAME).chat({ + ai_search_options: { + retrieval: { retrieval_type: "hybrid", max_num_results: 5 }, + }, + }), + messages: [{ role: "user", content: query }], +}); + +return Response.json({ text, sources }); +``` + + + +AI Search returns the retrieved chunks as AI SDK source parts in `sources`, so you can cite them alongside the generated text. Because the instance indexes both vectors and keywords, `retrieval_type: "hybrid"` uses both. + +## 6. Stream a response + +For longer responses, use `streamText` instead of `generateText`. The stream sends the retrieved chunks before the generated text. + + + +```ts +const result = streamText({ + model: aiSearch.get(INSTANCE_NAME).chat(), + messages: [{ role: "user", content: query }], +}); + +return result.toTextStreamResponse(); +``` + + + +## 7. Search as a tool + +To let a model decide when to search — for example, in an agent loop — expose `instance.search()` as an AI SDK [tool](https://sdk.vercel.ai/docs/foundations/tools). The AI Search chat model does not call tools itself, so use a tool-capable model such as a [Workers AI](/workers-ai/) model for this pattern. + +Install the Workers AI provider and Zod: + + + +Add a Workers AI binding to your Wrangler configuration: + + + +```toml +[ai] +binding = "AI" +``` + + + +Then define a search tool. The model calls it when it needs to retrieve content: + + + +```ts +import { createWorkersAI } from "workers-ai-provider"; +import { generateText, tool, stepCountIs } from "ai"; +import { z } from "zod"; + +const instance = aiSearch.get(INSTANCE_NAME); +const workersai = createWorkersAI({ binding: env.AI }); + +const { text } = await generateText({ + model: workersai("@cf/meta/llama-3.3-70b-instruct-fp8-fast"), + messages: [{ role: "user", content: query }], + tools: { + search_knowledge_base: tool({ + description: "Search the indexed knowledge base for relevant content.", + inputSchema: z.object({ + query: z.string().describe("The search query"), + }), + execute: async ({ query }) => + instance.search({ + query, + ai_search_options: { retrieval: { max_num_results: 5 } }, + }), + }), + }, + stopWhen: stepCountIs(5), +}); +``` + + + +## 8. Develop locally and deploy + +Start a local development server: + +```sh +npx wrangler dev +``` + +Visit `/setup` once (usually at `localhost:8787/setup`) to create the instance and index the document, then query it at `/?q=your+search+terms`. + +Log in with your Cloudflare account: + +```sh +npx wrangler login +``` + +Deploy your Worker to make it accessible on the Internet: + +```sh +npx wrangler deploy +``` + +## Considerations + +- The chat model is text-only. File and image message parts are not supported. +- AI Search does not apply generation options such as `temperature`, `tools`, and `maxOutputTokens` on the chat model. They surface as warnings. +- AI Search uses the generation model configured on the instance by default. Pass `instance.chat({ model: "..." })` to override it per request. + +## Next steps + + + + + + diff --git a/src/content/docs/ai-search/agent-sdks/index.mdx b/src/content/docs/ai-search/agent-sdks/index.mdx new file mode 100644 index 00000000000..f841168a65e --- /dev/null +++ b/src/content/docs/ai-search/agent-sdks/index.mdx @@ -0,0 +1,19 @@ +--- +pcx_content_type: navigation +title: Agent SDKs +description: Use AI Search from agent and application frameworks, including the Vercel AI SDK, the Cloudflare Agents SDK, and LangChain. +sidebar: + order: 6 + group: + hideIndex: true +products: + - ai-search +--- + +import { DirectoryListing } from "~/components"; + +These guides show how to use AI Search from agent and application frameworks. Each guide starts from an empty project and ends with a working integration that queries an AI Search instance. + +These pages cover how to _consume_ an AI Search instance from a framework. To create and manage instances themselves, refer to the [REST API](/ai-search/api/), the [Workers binding](/ai-search/api/search/workers-binding/), or [Wrangler commands](/ai-search/wrangler-commands/). + + diff --git a/src/content/docs/ai-search/agent-sdks/langchain.mdx b/src/content/docs/ai-search/agent-sdks/langchain.mdx new file mode 100644 index 00000000000..d479ffc8d7a --- /dev/null +++ b/src/content/docs/ai-search/agent-sdks/langchain.mdx @@ -0,0 +1,223 @@ +--- +title: LangChain +pcx_content_type: how-to +sidebar: + order: 3 +description: Use AI Search from LangChain — create an instance, index content, and search it with the CloudflareAISearchRetriever. +products: + - ai-search +--- + +import { LinkCard } from "~/components"; + +[LangChain](https://python.langchain.com/) is a framework for building applications with large language models. The [`langchain-cloudflare`](https://pypi.org/project/langchain-cloudflare/) package provides `CloudflareAISearchRetriever`, a standard LangChain retriever backed by AI Search. + +The retriever only searches. To create an instance and upload content, pair it with the [Cloudflare Python SDK](https://github.com/cloudflare/cloudflare-python). This guide uses the Python SDK to create an AI Search instance with hybrid search enabled and index a file, then uses the LangChain retriever to search it as a tool. + +## Prerequisites + +- [Python](https://www.python.org/downloads/) 3.10 or later +- Your [account ID](/fundamentals/account/find-account-and-zone-ids/) +- An API token with both the **AI Search:Edit** and **AI Search:Run** permissions + +To create the token, follow [Create an API token](/ai-search/get-started/api/#1-create-an-api-token) and add both permissions. **Edit** provisions the instance and uploads files; **Run** performs the search. + +## 1. Install the packages + +Create a project directory and a virtual environment to isolate your dependencies. + +```sh +mkdir ai-search-langchain && cd ai-search-langchain +python3 -m venv .venv +source .venv/bin/activate +``` + +On Windows, activate the virtual environment with `.venv\Scripts\activate` instead. + +Install both packages: + +```sh +pip install -U langchain-cloudflare cloudflare +``` + +The `cloudflare` SDK creates the instance and uploads files. The `langchain-cloudflare` package provides the retriever and the RAG and agent-tool helpers. Installing `langchain-cloudflare` also installs `langchain-core`, so you do not need to install `langchain` separately. + +## 2. Set your credentials + +Export your account ID and API token. The Cloudflare SDK reads these automatically. + +```sh +export CLOUDFLARE_ACCOUNT_ID="" +export CLOUDFLARE_API_TOKEN="" +``` + +## 3. Create an instance with hybrid search + +Create a file named `main.py`. The following code creates an instance with [hybrid search](/ai-search/configuration/indexing/hybrid-search/) enabled by setting `index_method` to index both vectors and keywords. Because no data source is connected, the instance uses [built-in storage](/ai-search/configuration/data-source/built-in-storage/). + +Creating an instance that already exists fails, so the code checks for it first and creates it only if it is missing. + +```python title="main.py" +import os + +from cloudflare import Cloudflare, NotFoundError + +ACCOUNT_ID = os.environ["CLOUDFLARE_ACCOUNT_ID"] +API_TOKEN = os.environ["CLOUDFLARE_API_TOKEN"] +NAMESPACE = "default" +INSTANCE_ID = "knowledge-base" + +client = Cloudflare(api_token=API_TOKEN) + +try: + client.aisearch.namespaces.instances.read( + INSTANCE_ID, account_id=ACCOUNT_ID, name=NAMESPACE + ) + print(f"Instance '{INSTANCE_ID}' already exists.") +except NotFoundError: + client.aisearch.namespaces.instances.create( + name=NAMESPACE, + account_id=ACCOUNT_ID, + id=INSTANCE_ID, + index_method={"vector": True, "keyword": True}, + ) + print(f"Created instance '{INSTANCE_ID}'.") +``` + +The first positional argument to `create()` is the namespace name. If you created a vector-only instance earlier, enable hybrid search on it with `client.aisearch.namespaces.instances.update(...)` instead. + +## 4. Upload and index a file + +Add the following to `main.py` to upload a document to built-in storage. Setting `wait_for_completion` to `True` inside the `file` argument waits until the file is indexed before returning. + +```python title="main.py" +item = client.aisearch.namespaces.instances.items.upload( + id=INSTANCE_ID, + account_id=ACCOUNT_ID, + name=NAMESPACE, + file={ + "file": ( + "workers-ai.md", + b"To configure Workers AI, add an [ai] binding and call env.AI.run().", + "text/markdown", + ), + "wait_for_completion": True, + }, +) + +print(f"Uploaded '{item.key}' (status: {item.status}).") +``` + +If indexing is still finishing, `item.status` may be `running`; the file continues indexing in the background and becomes searchable shortly after. + +## 5. Search the indexed content + +Point a `CloudflareAISearchRetriever` at the instance. Set `retrieval_type` to `hybrid` to use the vector and keyword indexes you enabled. + +```python title="main.py" +from langchain_cloudflare import CloudflareAISearchRetriever + +retriever = CloudflareAISearchRetriever( + account_id=ACCOUNT_ID, + api_token=API_TOKEN, + instance_name=INSTANCE_ID, + namespace=NAMESPACE, + retrieval_type="hybrid", + k=5, +) + +docs = retriever.invoke("How do I configure Workers AI?") + +for doc in docs: + print(doc.metadata["score"], doc.metadata["filename"]) + print(doc.page_content) +``` + +The `k` parameter sets the maximum number of results, mapped to `max_num_results` and capped at 50. + +## 6. Use AI Search as an agent tool + +Wrap the retriever with `create_retriever_tool` to give an agent the ability to search your content. This is the recommended way to use AI Search from a LangChain agent. + +```python title="main.py" +from langchain_core.tools import create_retriever_tool + +search_tool = create_retriever_tool( + retriever, + name="cloudflare_ai_search", + description="Search the knowledge base for relevant passages.", +) + +print(search_tool.invoke({"query": "How do I configure Workers AI?"})) +``` + +`search_tool` is a standard LangChain tool. Pass it to any LangChain or LangGraph agent alongside your other tools. + +## 7. Build a RAG chain + +To answer questions from the retrieved content, combine the retriever with a model. This example uses `ChatCloudflareWorkersAI`, which is included in the same package and reads a Workers AI token from `CF_AI_API_TOKEN`. + +```python title="main.py" +from langchain_cloudflare import ChatCloudflareWorkersAI +from langchain_core.output_parsers import StrOutputParser +from langchain_core.prompts import ChatPromptTemplate +from langchain_core.runnables import RunnablePassthrough + +llm = ChatCloudflareWorkersAI(model="@cf/meta/llama-3.3-70b-instruct-fp8-fast") + +prompt = ChatPromptTemplate.from_template( + "Answer the question using only the context below.\n\n" + "Context:\n{context}\n\n" + "Question: {question}" +) + + +def format_docs(docs): + return "\n\n".join(doc.page_content for doc in docs) + + +chain = ( + {"context": retriever | format_docs, "question": RunnablePassthrough()} + | prompt + | llm + | StrOutputParser() +) + +print(chain.invoke("How do I configure Workers AI?")) +``` + +## Use inside a Python Worker + +Inside a [Python Worker](/workers/languages/python/), pass a Worker binding instead of REST credentials. The binding path is asynchronous, so use `ainvoke`. + +```python +from langchain_cloudflare import CloudflareAISearchRetriever + + +async def on_fetch(request, env): + retriever = CloudflareAISearchRetriever(binding=env.MY_SEARCH) + docs = await retriever.ainvoke("How do I configure Workers AI?") + return Response.json({"matches": [doc.page_content for doc in docs]}) +``` + +`env.MY_SEARCH` is a dedicated `ai_search` binding, not the Workers AI binding (`env.AI`). For a [namespace binding](/ai-search/concepts/namespaces/), pass `env..get("my-instance")`. + +## Next steps + + + + + + diff --git a/src/content/docs/ai-search/configuration/index.mdx b/src/content/docs/ai-search/configuration/index.mdx index a67bd862bca..a13e2044e38 100644 --- a/src/content/docs/ai-search/configuration/index.mdx +++ b/src/content/docs/ai-search/configuration/index.mdx @@ -3,7 +3,7 @@ pcx_content_type: navigation title: Configuration description: Customize how your AI Search instance indexes data, retrieves results, and generates responses. sidebar: - order: 6 + order: 7 products: - ai-search ---