Vectorize は Cloudflare のベクトルデータベースです。ベクトルデータベースを使うと、機械学習(ML)モデルでセマンティック検索、レコメンデーション、分類、異常検知ができます。大規模言語モデル(LLM)へのコンテキスト提供にも使えます。
このガイドでは、次の手順を説明します。
- 最初の Vectorize インデックスを作成する。
- Cloudflare Worker をインデックスに接続する。
- データを挿入し、インデックスをクエリして類似検索を実行する。
続けるには、次が必要です。
- まだの場合は Cloudflare アカウント ↗ に登録します。
npm↗ をインストールします。Node.js↗ をインストールします。権限の問題を避け、Node.js のバージョンを切り替えるには、Volta ↗ や nvm ↗ などの Node バージョンマネージャーを使います。Wrangler には Node バージョン16.17.0以降が必要です。
Vectorize インデックスのクライアントアプリケーションとなる Worker を含む、新しいプロジェクトを作成します。
次を実行して、vectorize-tutorial という名前の新しいプロジェクトを作成します。
npm create cloudflare@latest -- vectorize-tutorialyarn create cloudflare vectorize-tutorialpnpm create cloudflare@latest vectorize-tutorialセットアップでは、次のオプションを選びます。
- 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を選びます(デプロイ前にいくつか変更します)。
これで新しい vectorize-tutorial ディレクトリが作成されます。ディレクトリの内容は次のとおりです。
src/index.tsにある"Hello World"Worker。wrangler.jsonc設定ファイル。vectorize-tutorialWorker は、このwrangler.jsoncでインデックスにアクセスします。
ベクトルデータベースは、従来の SQL や NoSQL データベースとは異なります。ベクトルデータベースは、データの表現であるベクトル埋め込みを保存するように設計されており、元のデータそのものは保存しません。
最初の Vectorize インデックスを作成するには、先ほど作った Workers プロジェクトのディレクトリへ移動します。
cd vectorize-tutorialインデックスを作成するには、wrangler vectorize create コマンドを使い、インデックス名を指定します。よいインデックス名は次のとおりです。
- 小文字や数字の ASCII 文字の組み合わせで、32 文字未満、先頭は文字、スペースの代わりにダッシュ(-)を使う。
- 用途と環境が分かる。たとえば "production-doc-search" や "dev-recommendation-engine"。
- インデックスの説明にだけ使い、コードから直接参照しない。
加えて、インデックス作成時に、格納するベクトルの dimensions と、類似ベクトルの判定に使う距離 metric を定義する必要があります。metric は euclidean、cosine、または dot product です。この設定はあとから変更できません。ベクトルデータベースは固定のベクトル構成で設定されるためです。
次の wrangler vectorize コマンドを実行します。
npx wrangler vectorize create tutorial-index --dimensions=32 --metric=euclidean🚧 Creating index: 'tutorial-index'
✅ Successfully created a new Vectorize index: 'tutorial-index'
📋 To start querying from a Worker, add the following binding configuration into 'wrangler.toml':
[[vectorize]]
binding = "VECTORIZE" # available in your Worker on env.VECTORIZE
index_name = "tutorial-index"上記のコマンドは新しいベクトルデータベースを作成し、次の手順で必要な バインディング 設定を出力します。
Worker を Vectorize インデックスに接続するには、バインディングを作成する必要があります。バインディング により、Workers から Vectorize や R2 などのリソースにアクセスできます。バインディングは、Worker の Wrangler ファイルを更新して作成します。
インデックスを Worker にバインドするには、Wrangler ファイルの末尾に次を追加します。
{
"vectorize": [
{
"binding": "VECTORIZE", // available in your Worker on env.VECTORIZE
"index_name": "tutorial-index"
}
]
}[[vectorize]]
binding = "VECTORIZE"
index_name = "tutorial-index"具体的には次のとおりです。
<BINDING_NAME>に設定した値(文字列)で、Worker からこのデータベースを参照します。このチュートリアルでは、バインディング名をVECTORIZEにします。- バインディングは 有効な JavaScript 変数名 ↗ である必要があります。たとえば
binding = "MY_INDEX"やbinding = "PROD_SEARCH_INDEX"は、どちらも有効なバインディング名です。 - バインディングは Worker 内の
env.<BINDING_NAME>で利用でき、このバインディング上に Vectorize の クライアント API が公開されます。Workers アプリケーション内から使えます。
Vectorize では、ベクトルあたり最大 10KiB のメタデータをインデックスに追加でき、ベクトルのクエリ時にそのメタデータでフィルターできます。そのためには、メタデータフィールドを Vectorize インデックスの「メタデータインデックス」として指定します。
クエリ時にメタデータフィールドでベクトルをフィルターできるようにするには、次のようなコマンドを使います。
npx wrangler vectorize create-metadata-index tutorial-index --property-name=url --type=string📋 Creating metadata index...
✅ Successfully enqueued metadata index creation request. Mutation changeset identifier: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.ここで url は、フィルターを有効にするメタデータフィールドです。--type パラメーターはメタデータフィールドのデータ型を定義します。string、number、boolean 型に対応しています。
メタデータインデックスの作成には、通常数秒かかります。Vectorize インデックスのメタデータインデックス一覧は、次を実行して確認できます。
npx wrangler vectorize list-metadata-index tutorial-index📋 Fetching metadata indexes...
┌──────────────┬────────┐
│ propertyName │ type │
├──────────────┼────────┤
│ url │ String │
└──────────────┴────────┘Vectorize インデックスあたり、最大 10 個のメタデータインデックスを作成できます。
number 型のメタデータインデックスでは、インデックスされる数値の精度は float64 です。
string 型のメタデータインデックスでは、各ベクトルは文字列データの先頭 64B をインデックスします。UTF-8 文字境界で切り詰め、その上限内で最長の整形式 UTF-8 部分文字列にします。そのため、インデックスした各プロパティについて、値の先頭 64B でベクトルをフィルターできます。
制限の完全な一覧は Vectorize の制限 を参照してください。
ベクトルデータベースをクエリする前に、照合対象となるベクトルを挿入する必要があります。これらのベクトルは、テキストや画像などのデータを機械学習モデルに渡して生成します。ただしこのチュートリアルでは、ベクトル検索そのものの動きを示すために、静的なベクトルを定義します。
まず vectorize-tutorial Worker の src/index.ts ファイルを開きます。index.ts は、Worker と Vectorize インデックスのやり取りを設定するファイルです。
index.ts の内容を消し、次のコードスニペットを貼り付けます。env パラメーターでは、<BINDING_NAME> を VECTORIZE に置き換えます。
export interface Env {
// This makes your vector index methods available on env.VECTORIZE.*
// For example, env.VECTORIZE.insert() or query()
VECTORIZE: Vectorize;
}
// Sample vectors: 32 dimensions wide.
//
// Vectors from popular machine-learning models are typically ~100 to 1536 dimensions
// wide (or wider still).
const sampleVectors: Array<VectorizeVector> = [
{
id: "1",
values: [
0.12, 0.45, 0.67, 0.89, 0.23, 0.56, 0.34, 0.78, 0.12, 0.9, 0.24, 0.67,
0.89, 0.35, 0.48, 0.7, 0.22, 0.58, 0.74, 0.33, 0.88, 0.66, 0.45, 0.27,
0.81, 0.54, 0.39, 0.76, 0.41, 0.29, 0.83, 0.55,
],
metadata: { url: "/products/sku/13913913" },
},
{
id: "2",
values: [
0.14, 0.23, 0.36, 0.51, 0.62, 0.47, 0.59, 0.74, 0.33, 0.89, 0.41, 0.53,
0.68, 0.29, 0.77, 0.45, 0.24, 0.66, 0.71, 0.34, 0.86, 0.57, 0.62, 0.48,
0.78, 0.52, 0.37, 0.61, 0.69, 0.28, 0.8, 0.53,
],
metadata: { url: "/products/sku/10148191" },
},
{
id: "3",
values: [
0.21, 0.33, 0.55, 0.67, 0.8, 0.22, 0.47, 0.63, 0.31, 0.74, 0.35, 0.53,
0.68, 0.45, 0.55, 0.7, 0.28, 0.64, 0.71, 0.3, 0.77, 0.6, 0.43, 0.39, 0.85,
0.55, 0.31, 0.69, 0.52, 0.29, 0.72, 0.48,
],
metadata: { url: "/products/sku/97913813" },
},
{
id: "4",
values: [
0.17, 0.29, 0.42, 0.57, 0.64, 0.38, 0.51, 0.72, 0.22, 0.85, 0.39, 0.66,
0.74, 0.32, 0.53, 0.48, 0.21, 0.69, 0.77, 0.34, 0.8, 0.55, 0.41, 0.29,
0.7, 0.62, 0.35, 0.68, 0.53, 0.3, 0.79, 0.49,
],
metadata: { url: "/products/sku/418313" },
},
{
id: "5",
values: [
0.11, 0.46, 0.68, 0.82, 0.27, 0.57, 0.39, 0.75, 0.16, 0.92, 0.28, 0.61,
0.85, 0.4, 0.49, 0.67, 0.19, 0.58, 0.76, 0.37, 0.83, 0.64, 0.53, 0.3,
0.77, 0.54, 0.43, 0.71, 0.36, 0.26, 0.8, 0.53,
],
metadata: { url: "/products/sku/55519183" },
},
];
export default {
async fetch(request, env, ctx): Promise<Response> {
let path = new URL(request.url).pathname;
if (path.startsWith("/favicon")) {
return new Response("", { status: 404 });
}
// You only need to insert vectors into your index once
if (path.startsWith("/insert")) {
// Insert some sample vectors into your index
// In a real application, these vectors would be the output of a machine learning (ML) model,
// such as Workers AI, OpenAI, or Cohere.
const inserted = await env.VECTORIZE.insert(sampleVectors);
// Return the mutation identifier for this insert operation
return Response.json(inserted);
}
return Response.json({ text: "nothing to do... yet" }, { status: 404 });
},
} satisfies ExportedHandler<Env>;上記のコードでは、次を行っています。
- Workers のコードから Vectorize インデックスへのバインディングを定義します。このバインディングは、
wrangler.jsoncファイルの"vectorise"キーで設定したbinding値と一致します。 - 次の手順でクエリするサンプルベクトルの集合を指定します。
- それらのベクトルをインデックスに挿入し、成功を確認します。
次の手順では、挿入したベクトルをクエリできるように Worker を拡張します。
この手順では、受信クエリを表すベクトルを使い、インデックスを検索します。
まず vectorize-tutorial Worker の src/index.ts ファイルを開きます。index.ts は、Worker と Vectorize インデックスのやり取りを設定するファイルです。
index.ts の内容を消します。次のコードスニペットを index.ts に貼り付けます。env パラメーターでは、<BINDING_NAME> を VECTORIZE に置き換えます。
export interface Env {
// This makes your vector index methods available on env.VECTORIZE.*
// For example, env.VECTORIZE.insert() or query()
VECTORIZE: Vectorize;
}
// Sample vectors: 32 dimensions wide.
//
// Vectors from popular machine-learning models are typically ~100 to 1536 dimensions
// wide (or wider still).
const sampleVectors: Array<VectorizeVector> = [
{
id: "1",
values: [
0.12, 0.45, 0.67, 0.89, 0.23, 0.56, 0.34, 0.78, 0.12, 0.9, 0.24, 0.67,
0.89, 0.35, 0.48, 0.7, 0.22, 0.58, 0.74, 0.33, 0.88, 0.66, 0.45, 0.27,
0.81, 0.54, 0.39, 0.76, 0.41, 0.29, 0.83, 0.55,
],
metadata: { url: "/products/sku/13913913" },
},
{
id: "2",
values: [
0.14, 0.23, 0.36, 0.51, 0.62, 0.47, 0.59, 0.74, 0.33, 0.89, 0.41, 0.53,
0.68, 0.29, 0.77, 0.45, 0.24, 0.66, 0.71, 0.34, 0.86, 0.57, 0.62, 0.48,
0.78, 0.52, 0.37, 0.61, 0.69, 0.28, 0.8, 0.53,
],
metadata: { url: "/products/sku/10148191" },
},
{
id: "3",
values: [
0.21, 0.33, 0.55, 0.67, 0.8, 0.22, 0.47, 0.63, 0.31, 0.74, 0.35, 0.53,
0.68, 0.45, 0.55, 0.7, 0.28, 0.64, 0.71, 0.3, 0.77, 0.6, 0.43, 0.39, 0.85,
0.55, 0.31, 0.69, 0.52, 0.29, 0.72, 0.48,
],
metadata: { url: "/products/sku/97913813" },
},
{
id: "4",
values: [
0.17, 0.29, 0.42, 0.57, 0.64, 0.38, 0.51, 0.72, 0.22, 0.85, 0.39, 0.66,
0.74, 0.32, 0.53, 0.48, 0.21, 0.69, 0.77, 0.34, 0.8, 0.55, 0.41, 0.29,
0.7, 0.62, 0.35, 0.68, 0.53, 0.3, 0.79, 0.49,
],
metadata: { url: "/products/sku/418313" },
},
{
id: "5",
values: [
0.11, 0.46, 0.68, 0.82, 0.27, 0.57, 0.39, 0.75, 0.16, 0.92, 0.28, 0.61,
0.85, 0.4, 0.49, 0.67, 0.19, 0.58, 0.76, 0.37, 0.83, 0.64, 0.53, 0.3,
0.77, 0.54, 0.43, 0.71, 0.36, 0.26, 0.8, 0.53,
],
metadata: { url: "/products/sku/55519183" },
},
];
export default {
async fetch(request, env, ctx): Promise<Response> {
let path = new URL(request.url).pathname;
if (path.startsWith("/favicon")) {
return new Response("", { status: 404 });
}
// You only need to insert vectors into your index once
if (path.startsWith("/insert")) {
// Insert some sample vectors into your index
// In a real application, these vectors would be the output of a machine learning (ML) model,
// such as Workers AI, OpenAI, or Cohere.
let inserted = await env.VECTORIZE.insert(sampleVectors);
// Return the mutation identifier for this insert operation
return Response.json(inserted);
}
// return Response.json({text: "nothing to do... yet"}, { status: 404 })
// In a real application, you would take a user query. For example, "what is a
// vector database" - and transform it into a vector embedding first.
//
// In this example, you will construct a vector that should
// match vector id #4
const queryVector: Array<number> = [
0.13, 0.25, 0.44, 0.53, 0.62, 0.41, 0.59, 0.68, 0.29, 0.82, 0.37, 0.5,
0.74, 0.46, 0.57, 0.64, 0.28, 0.61, 0.73, 0.35, 0.78, 0.58, 0.42, 0.32,
0.77, 0.65, 0.49, 0.54, 0.31, 0.29, 0.71, 0.57,
]; // vector of dimensions 32
// Query your index and return the three (topK = 3) most similar vector
// IDs with their similarity score.
//
// By default, vector values are not returned, as in many cases the
// vector id and scores are sufficient to map the vector back to the
// original content it represents.
const matches = await env.VECTORIZE.query(queryVector, {
topK: 3,
returnValues: true,
returnMetadata: "all",
});
return Response.json({
// This will return the closest vectors: the vectors are arranged according
// to their scores. Vectors that are more similar would show up near the top.
// In this example, Vector id #4 would turn out to be the most similar to the queried vector.
// You return the full set of matches so you can check the possible scores.
matches: matches,
});
},
} satisfies ExportedHandler<Env>;インデックスにすでに存在するベクトルに近いベクトルを検索するには、Vectorize の queryById() 操作も使えます。
Worker をグローバルにデプロイする前に、次を実行して Cloudflare アカウントでログインします。
npx wrangler loginCloudflare ダッシュボードへのログインを求める Web ページが開きます。ログイン後、Wrangler が Cloudflare アカウントを変更していいか確認されます。下へスクロールし、Allow を選択して続けます。
ここから Worker をデプロイし、プロジェクトをインターネット上で利用できるようにできます。Worker をデプロイするには、次を実行します。
npx wrangler deployデプロイ後、https://vectorize-tutorial.<YOUR_SUBDOMAIN>.workers.dev で Worker をプレビューできます。
ベクトルを挿入してからクエリするには、デプロイした Worker の URL(例: https://vectorize-tutorial.<YOUR_SUBDOMAIN>.workers.dev/)を使います。ブラウザーを開き、次を行います。
- まず
/insertを開いてベクトルを挿入します。次の JSON が返るはずです。
// https://vectorize-tutorial.<YOUR_SUBDOMAIN>.workers.dev/insert
{
"mutationId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}ここでの mutationId は、この非同期挿入操作に対応する一意の識別子です。挿入したベクトルがクエリ可能になるまで、通常数秒かかります。
最後に処理された mutation は、インデックス情報の操作で確認できます。
npx wrangler vectorize info tutorial-index📋 Fetching index info...
┌────────────┬─────────────┬──────────────────────────────────────┬──────────────────────────┐
│ dimensions │ vectorCount │ processedUpToMutation │ processedUpToDatetime │
├────────────┼─────────────┼──────────────────────────────────────┼──────────────────────────┤
│ 32 │ 5 │ xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx │ YYYY-MM-DDThh:mm:ss.SSSZ │
└────────────┴─────────────┴──────────────────────────────────────┴──────────────────────────┘同じベクトル ID で続けて挿入すると mutation ID は返りますが、同じベクトル ID は 2 回挿入できないため、インデックスのベクトル数は変わりません。すでにインデックスに存在する ID のベクトル値を更新するには、代わりに upsert 操作を使います。
- インデックスをクエリします。ルートパス
/を開くと、クエリベクトル[0.13, 0.25, 0.44, ...]はベクトル ID4に最も近いはずです。このクエリは、最も近い 3 件(topK: 3)と、そのベクトル値およびメタデータを返します。
id: 4 の score は 0.46348256 です。距離メトリックに euclidean を使っているため、スコアが 0.0 に近いほど、ベクトルは近いことになります。
// https://vectorize-tutorial.<YOUR_SUBDOMAIN>.workers.dev/
{
"matches": {
"count": 3,
"matches": [
{
"id": "4",
"score": 0.46348256,
"values": [
0.17, 0.29, 0.42, 0.57, 0.64, 0.38, 0.51, 0.72, 0.22, 0.85, 0.39,
0.66, 0.74, 0.32, 0.53, 0.48, 0.21, 0.69, 0.77, 0.34, 0.8, 0.55, 0.41,
0.29, 0.7, 0.62, 0.35, 0.68, 0.53, 0.3, 0.79, 0.49
],
"metadata": {
"url": "/products/sku/418313"
}
},
{
"id": "3",
"score": 0.52920616,
"values": [
0.21, 0.33, 0.55, 0.67, 0.8, 0.22, 0.47, 0.63, 0.31, 0.74, 0.35, 0.53,
0.68, 0.45, 0.55, 0.7, 0.28, 0.64, 0.71, 0.3, 0.77, 0.6, 0.43, 0.39,
0.85, 0.55, 0.31, 0.69, 0.52, 0.29, 0.72, 0.48
],
"metadata": {
"url": "/products/sku/97913813"
}
},
{
"id": "2",
"score": 0.6337869,
"values": [
0.14, 0.23, 0.36, 0.51, 0.62, 0.47, 0.59, 0.74, 0.33, 0.89, 0.41,
0.53, 0.68, 0.29, 0.77, 0.45, 0.24, 0.66, 0.71, 0.34, 0.86, 0.57,
0.62, 0.48, 0.78, 0.52, 0.37, 0.61, 0.69, 0.28, 0.8, 0.53
],
"metadata": {
"url": "/products/sku/10148191"
}
}
]
}
}ここから、別の queryVector を渡して結果を観察してください。クエリベクトルとインデックス内のベクトルの距離が変わると、一致結果と score も変わるはずです。
実運用では、queryVector はユーザーやシステムからのクエリのベクトル埋め込み表現になり、sampleVectors は実際のコンテンツから生成されます。この例を発展させるには、Workers AI と Vectorize を組み合わせてエンドツーエンドのアプリケーションを作る ベクトル検索チュートリアル を参照してください。
このチュートリアルを終えると、最初の Vectorize インデックス、そのインデックスにアクセスする Worker、およびプロジェクトのグローバルデプロイまで完了しています。
- Workers AI と Vectorize で エンドツーエンドのベクトル検索アプリケーションを構築する。
- ベクトルデータベースの仕組み を詳しく学ぶ。
- Cloudflare Workers から Vectorize API を使う 例 を読む。
- Euclidean Distance vs Cosine Similarity ↗。
- Dot product ↗。