Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

AI SDK

最終更新 Markdown で表示Agent セットアップ

Vercel AI SDK は、大規模言語モデルでアプリケーションを構築するための TypeScript ツールキットです。ai-search-provider パッケージは AI Search を AI SDK につなぎ、同じ API からインデックス済みコンテンツに基づく応答の生成、チャンクの取得、ドキュメントの管理ができます。

このガイドでは、AI Search インスタンスを作成し、ドキュメントをアップロードしてインデックスし、AI SDK でクエリする Worker を作ります。

前提条件

  1. Cloudflare アカウント に登録します。
  2. Node.js をインストールします。

Node.js のバージョンマネージャー

権限の問題を避け、Node.js のバージョンを切り替えられるよう、Voltanvm などの Node バージョンマネージャーを使います。このガイドの後半で説明する Wrangler には、Node バージョン 16.17.0 以降が必要です。

1. Worker プロジェクトを作成する

create-cloudflare CLI(C3)で新しい Worker プロジェクトを作成します。C3 は、Cloudflare へのアプリケーションのセットアップとデプロイを支援するコマンドラインツールです。

次を実行し、ai-search-ai-sdk という名前のプロジェクトを作成します。

npm create cloudflare@latest -- ai-search-ai-sdk

セットアップでは、次のオプションを選びます。

  • What would you like to start with? では、Hello World example を選びます。
  • Which template would you like to use? では、Worker only を選びます。
  • Which language do you want to use? では、TypeScript を選びます。
  • Do you want to use git for version control? では、Yes を選びます。
  • Do you want to deploy your application? では、No を選びます(デプロイ前にいくつか変更します)。

アプリケーションのディレクトリへ移動します。

cd ai-search-ai-sdk

2. AI SDK とプロバイダーをインストールする

AI SDK と AI Search プロバイダーをインストールします。プロバイダーには AI SDK v6(ai@^6)が必要です。

npm i ai ai-search-provider

3. Worker を AI Search にバインドする

Worker と AI Search の間に binding を作成します。Bindings を使うと、Worker が Cloudflare Developer Platform 上のリソースとやり取りできます。

次を Wrangler 設定ファイル に追加します。

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "ai_search_namespaces": [
    {
      "binding": "AI_SEARCH",
      "namespace": "default",
      "remote": true
    }
  ]
}
[[ai_search_namespaces]]
binding = "AI_SEARCH"
namespace = "default"
remote = true

これで default namespaceenv.AI_SEARCH にバインドされます。remote オプションを付けると、wrangler dev がデプロイ済みインスタンスへリクエストをプロキシします。AI Search はローカルでは動きません。ai_search_namespaces binding には 2026-03-27 以降の compatibility_date が必要です。新しい C3 プロジェクトはすでに満たしています。

4. インスタンスを作成し、コンテンツをインデックスする

インスタンスを作成し、ドキュメントをアップロードする /setup ルートを追加します。作成時に index_method を設定し、ベクトルとキーワードの両方をインデックスして ハイブリッド検索 を有効にします。

create() メソッドは、プロバイダークライアントではなく namespace binding(env.AI_SEARCH)にあります。すでに存在するインスタンスを作成すると例外になるため、次のコードは初回に作成し、次回以降は更新します。

src/index.jsjs
import { createAISearchNamespace } from "ai-search-provider";
import { generateText, streamText } from "ai";

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) {
	// 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) {
		const url = new URL(request.url);
		// createAISearchNamespace adapts the binding to the AI SDK provider API.
		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);

			const instance = aiSearch.get(INSTANCE_NAME);

			// upload() queues the file and returns immediately. Indexing runs in
			// the background, so poll the item's status until it is searchable.
			const { id, key } = await instance.items.upload("caching.md", SAMPLE_DOC);

			let info = await instance.items.get(id).info();
			while (info.status === "queued" || info.status === "running") {
				await new Promise((resolve) => setTimeout(resolve, 2_000));
				info = await instance.items.get(id).info();
			}

			return Response.json({ key, status: info.status });
		}

		// Query the instance (see the next step).
		return new Response("Visit /setup first, then query with ?q=");
	},
};
src/index.tsts
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<Response> {
		const url = new URL(request.url);
		// createAISearchNamespace adapts the binding to the AI SDK provider API.
		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);

			const instance = aiSearch.get(INSTANCE_NAME);

			// upload() queues the file and returns immediately. Indexing runs in
			// the background, so poll the item's status until it is searchable.
			const { id, key } = await instance.items.upload("caching.md", SAMPLE_DOC);

			let info = await instance.items.get(id).info();
			while (info.status === "queued" || info.status === "running") {
				await new Promise((resolve) => setTimeout(resolve, 2_000));
				info = await instance.items.get(id).info();
			}

			return Response.json({ key, status: info.status });
		}

		// Query the instance (see the next step).
		return new Response("Visit /setup first, then query with ?q=");
	},
} satisfies ExportedHandler<Env>;

AiSearchNamespace は、wrangler types を実行したあと使えるグローバル型です。

5. インスタンスをクエリする

AI SDK からインスタンスをクエリする方法は 3 つあります。アプリケーションに合うものを選んでください。

応答を生成する

instance.chat()generateText に渡すと、AI Search が関連コンテンツを取得し、1 回の呼び出しで応答を生成します。

fetch ハンドラーのクエリ用プレースホルダーを、次のコードに置き換えます。

src/index.jsjs
const query = url.searchParams.get("q") ?? "How does caching work?";

// chat() returns an AI SDK model that retrieves matching chunks and generates
// a grounded answer in one call, so there is no separate search step.
const { text, sources } = await generateText({
	model: aiSearch.get(INSTANCE_NAME).chat({
		ai_search_options: {
			// "hybrid" ranks results from both the vector and keyword indexes.
			retrieval: { retrieval_type: "hybrid", max_num_results: 5 },
		},
	}),
	messages: [{ role: "user", content: query }],
});

// `sources` holds the retrieved chunks, so you can cite them alongside `text`.
return Response.json({ text, sources });
src/index.tsts
const query = url.searchParams.get("q") ?? "How does caching work?";

// chat() returns an AI SDK model that retrieves matching chunks and generates
// a grounded answer in one call, so there is no separate search step.
const { text, sources } = await generateText({
	model: aiSearch.get(INSTANCE_NAME).chat({
		ai_search_options: {
			// "hybrid" ranks results from both the vector and keyword indexes.
			retrieval: { retrieval_type: "hybrid", max_num_results: 5 },
		},
	}),
	messages: [{ role: "user", content: query }],
});

// `sources` holds the retrieved chunks, so you can cite them alongside `text`.
return Response.json({ text, sources });

AI Search は取得したチャンクを AI SDK の source parts として sources に返します。生成テキストと並べて引用できます。インスタンスはベクトルとキーワードの両方をインデックスしているため、retrieval_type: "hybrid" は両方を使います。

応答をストリーミングする

長い応答では、generateText の代わりに streamText を使います。AI Search は、最初のテキストパートより前に、取得したチャンクを source parts として送ります。

// streamText returns right away; tokens stream in as they are generated.
const result = streamText({
	model: aiSearch.get(INSTANCE_NAME).chat(),
	messages: [{ role: "user", content: query }],
});

// toTextStreamResponse() streams the generated text only.
return result.toTextStreamResponse();
// streamText returns right away; tokens stream in as they are generated.
const result = streamText({
	model: aiSearch.get(INSTANCE_NAME).chat(),
	messages: [{ role: "user", content: query }],
});

// toTextStreamResponse() streams the generated text only.
return result.toTextStreamResponse();

toTextStreamResponse() は生成テキストだけを送り、sources は落とします。取得したチャンクもストリーミングするには、sendSources を有効にした UI メッセージストリームを返すか、result.fullStream を直接読みます。

// sendSources forwards each retrieved chunk as a source-url part. They arrive
// before the first text part, so you can render citations as the answer streams.
return result.toUIMessageStreamResponse({ sendSources: true });
// sendSources forwards each retrieved chunk as a source-url part. They arrive
// before the first text part, so you can render citations as the answer streams.
return result.toUIMessageStreamResponse({ sendSources: true });

ツールとして検索する

chat() では、AI Search は毎回インスタンスを検索します。検索するタイミングをモデルに任せるには、instance.search() を AI SDK の tool として公開し、function calling に対応するモデル(Workers AI モデルなど)に渡します。エージェントでは、検索とその他のツールをモデルが選ぶこのパターンを使います。

Workers AI プロバイダーと Zod をインストールします。workers-ai-provider はバージョン 3 を使います。最新のバージョン 4 は AI SDK v7 が必要ですが、ai-search-provider は v6 が必要なため、一緒にインストールすると npm が失敗します。

npm i workers-ai-provider@^3 zod

Wrangler 設定に Workers AI binding を追加します。

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "ai": {
    "binding": "AI"
  }
}
[ai]
binding = "AI"

次に検索ツールを定義します。コンテンツが必要なとき、モデルがこのツールを呼びます。

import { createWorkersAI } from "workers-ai-provider";
import { generateText, tool, stepCountIs } from "ai";
import { z } from "zod";

const instance = aiSearch.get(INSTANCE_NAME);
// The tool-caller must support function calling. Use a dedicated model here
// rather than the AI Search chat model, which retrieves on every request.
const workersai = createWorkersAI({ binding: env.AI });

const { text } = await generateText({
	model: workersai("@cf/zai-org/glm-5.2"),
	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"),
			}),
			// The model decides when to call this; it searches the instance.
			execute: async ({ query }) =>
				instance.search({
					query,
					ai_search_options: { retrieval: { max_num_results: 5 } },
				}),
		}),
	},
	// Cap the tool-call loop so the model cannot invoke tools indefinitely.
	stopWhen: stepCountIs(5),
});
import { createWorkersAI } from "workers-ai-provider";
import { generateText, tool, stepCountIs } from "ai";
import { z } from "zod";

const instance = aiSearch.get(INSTANCE_NAME);
// The tool-caller must support function calling. Use a dedicated model here
// rather than the AI Search chat model, which retrieves on every request.
const workersai = createWorkersAI({ binding: env.AI });

const { text } = await generateText({
	model: workersai("@cf/zai-org/glm-5.2"),
	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"),
			}),
			// The model decides when to call this; it searches the instance.
			execute: async ({ query }) =>
				instance.search({
					query,
					ai_search_options: { retrieval: { max_num_results: 5 } },
				}),
		}),
	},
	// Cap the tool-call loop so the model cannot invoke tools indefinitely.
	stopWhen: stepCountIs(5),
});

6. ローカルでテストする

デプロイ前に、wrangler dev でリモートの AI Search binding をプロキシし、一連の流れがインスタンスに対して動くことを確認します。次の出力は 応答を生成する の場合です。

ローカルの開発サーバーを起動します。

npx wrangler dev

まず /setup にアクセスし、サンプルドキュメントをインデックスします。

curl http://localhost:8787/setup

初回の /setup は、ドキュメントのインデックス中に 1〜2 分かかることがあります。完了すると、アイテムのキーと completed ステータスが返ります。

{ "key": "caching.md", "status": "completed" }

次にインスタンスをクエリします。

curl "http://localhost:8787/?q=How+does+caching+work%3F"

連携が正しく動くと、ドキュメントに基づく生成 text と、取得したファイルを参照する sources 配列が返ります(フィールドは省略しています)。

{
	"text": "Cloudflare caches static assets at the edge...",
	"sources": [{ "sourceType": "url", "url": "caching.md" }]
}

sources が空の場合、ドキュメントのインデックスがまだ終わっていません。もう一度 /setup を実行してから、クエリを再試行してください。

7. デプロイする

Cloudflare アカウントでログインします。

npx wrangler login

Worker をデプロイし、インターネットからアクセスできるようにします。

npx wrangler deploy

注意事項

  • チャットモデルはテキストのみです。ファイルや画像のメッセージパートには対応していません。
  • temperaturemaxOutputTokens などの生成オプションは、インスタンスの生成モデルにそのまま渡されます。maxOutputTokens は応答を切り詰め、finishReason"length" にします。モデルがオプションを無視しても、結果の warnings 配列は空のままです。未対応のオプションは静かに失敗します。
  • AI Search は、デフォルトでインスタンスに設定した生成モデルを使います。リクエスト単位で上書きするには、instance.chat({ model: "..." }) を渡します。

次のステップ

ハイブリッド検索

設定可能な融合で、ベクトル検索とキーワード検索を組み合わせます。

Agents SDK

インスタンスを検索する、状態を持つチャットエージェントを構築します。

役に立ちましたか?