コンテンツにスキップ

ベクトル化の紹介

VectorizeはCloudflareのベクトルデータベースです。ベクトルデータベースを使用すると、機械学習(ML)モデルを利用して、セマンティック検索、推薦、分類、異常検出タスクを実行し、LLM(大規模言語モデル)にコンテキストを提供することができます。

このガイドでは、以下の内容を説明します:

  • 最初のVectorizeインデックスの作成。
  • インデックスにCloudflare Workerを接続する。
  • インデックスをクエリして類似検索を実行する。

前提条件

続行するには、以下が必要です:

  1. まだアカウントを作成していない場合は、Cloudflareアカウントにサインアップしてください。
  2. npmをインストールします。
  3. Node.jsをインストールします。権限の問題を避け、Node.jsのバージョンを変更するために、VoltanvmのようなNodeバージョンマネージャーを使用してください。Wranglerは、16.17.0以上のNodeバージョンを必要とします。

1. Workerを作成する

新しいプロジェクトを作成し、これがあなたのVectorizeインデックスのクライアントアプリケーションとして機能します。

次のコマンドを実行して、vectorize-tutorialという名前の新しいプロジェクトを作成します:

Terminal window
npm create cloudflare@latest -- vectorize-tutorial

For setup, select the following options:

  • For What would you like to start with?, choose Hello Worldの例.
  • For Which template would you like to use?, choose Hello World Worker.
  • For Which language do you want to use?, choose TypeScript.
  • For Do you want to use git for version control?, choose Yes.
  • For Do you want to deploy your application?, choose No (we will be making some changes before deploying).

これにより、新しいvectorize-tutorialディレクトリが作成されます。新しいvectorize-tutorialディレクトリには以下が含まれます:

  • src/index.tsにある「Hello World」Worker
  • wrangler.toml設定ファイル。wrangler.tomlは、vectorize-tutorial Workerがインデックスにアクセスする方法です。

2. インデックスを作成する

ベクトルデータベースは、従来のSQLまたはNoSQLデータベースとは異なります。ベクトルデータベースは、データの表現であるベクトル埋め込みを保存するように設計されていますが、元のデータ自体は保存しません。

最初のVectorizeインデックスを作成するには、先ほど作成したWorkersプロジェクトのディレクトリに移動します:

Terminal window
cd vectorize-tutorial

インデックスを作成するには、wrangler vectorize createコマンドを使用し、インデックスの名前を指定する必要があります。良いインデックス名は以下の条件を満たします:

  • 小文字および/または数字のASCII文字の組み合わせで、32文字未満で、文字で始まり、スペースの代わりにダッシュ(-)を使用します。
  • 使用ケースと環境を説明するもの。例えば、「production-doc-search」や「dev-recommendation-engine」など。
  • インデックスを説明するためだけに使用され、コード内で直接参照されることはありません。

さらに、インデックスに保存するベクトルのdimensionsと、インデックス作成時に類似ベクトルを決定するために使用される距離metricを定義する必要があります。metricはユークリッド、コサイン、またはドット積のいずれかです。この設定は後で変更できません。ベクトルデータベースは固定されたベクトル構成のために設定されています。

次のwrangler vectorizeコマンドを実行します:

Terminal window
npx wrangler vectorize create tutorial-index --dimensions=32 --metric=euclidean
🚧 インデックスを作成中: 'tutorial-index'
新しいVectorizeインデックスを正常に作成しました: 'tutorial-index'
📋 Workerからクエリを開始するには、次のバインディング設定を'wrangler.toml'に追加してください:
[[vectorize]]
binding = "VECTORIZE" # env.VECTORIZEでWorkerで利用可能
index_name = "tutorial-index"

上記のコマンドは新しいベクトルデータベースを作成し、次のステップで必要なバインディング設定を出力します。

3. Workerをインデックスにバインドする

WorkerがVectorizeインデックスに接続するためのバインディングを作成する必要があります。Bindingsを使用すると、Cloudflare WorkersからVectorizeやR2などのリソースにアクセスできます。Workerのwrangler.tomlファイルを更新することでバインディングを作成します。

インデックスをWorkerにバインドするには、wrangler.tomlファイルの末尾に次の内容を追加します:

[[vectorize]]
binding = "VECTORIZE" # env.VECTORIZEでWorkerで利用可能
index_name = "tutorial-index"

具体的には:

  • <BINDING_NAME>に設定する値(文字列)は、Worker内でこのデータベースを参照するために使用されます。このチュートリアルでは、バインディングの名前をVECTORIZEとします。
  • バインディングは有効なJavaScript変数名でなければなりません。例えば、binding = "MY_INDEX"binding = "PROD_SEARCH_INDEX"は、バインディングの有効な名前です。
  • バインディングは、Worker内でenv.<BINDING_NAME>で利用可能で、VectorizeのクライアントAPIがこのバインディングで使用できるように公開されています。

4. [オプション] メタデータインデックスを作成する

Vectorizeでは、ベクトルとともにメタデータをインデックスに追加することができ、ベクトルをクエリする際にベクトルメタデータでフィルタリングする機能も提供します。これを行うには、Vectorizeインデックスの「メタデータインデックス」としてメタデータフィールドを指定する必要があります。

クエリ中にメタデータフィールドでベクトルフィルタリングを有効にするには、次のようなコマンドを使用します:

Terminal window
npx wrangler vectorize create-metadata-index tutorial-index --property-name=url --type=string
📋 メタデータインデックスを作成中...
メタデータインデックス作成リクエストを正常にキューに追加しました。変更セット識別子: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx。

ここでurlはフィルタリングが有効になるメタデータフィールドです。--typeパラメータはメタデータフィールドのデータ型を定義します。stringnumberbooleanタイプがサポートされています。

メタデータインデックスの作成には通常数秒かかります。次のコマンドを実行して、Vectorizeインデックスのメタデータインデックスのリストを確認できます:

Terminal window
npx wrangler vectorize list-metadata-index tutorial-index
📋 メタデータインデックスを取得中...
┌──────────────┬────────┐
propertyName type
├──────────────┼────────┤
url String
└──────────────┴────────┘

5. ベクトルを挿入する

ベクトルデータベースをクエリする前に、クエリ対象のベクトルを挿入する必要があります。これらのベクトルは、機械学習モデルに渡すデータ(テキストや画像など)から生成されます。ただし、このチュートリアルでは、ベクトル検索がどのように機能するかを示すために静的なベクトルを定義します。

まず、vectorize-tutorial Workerに移動し、src/index.tsファイルを開きます。index.tsファイルは、WorkerがVectorizeインデックスとどのように相互作用するかを設定する場所です。

index.tsの内容をクリアし、次のコードスニペットをindex.tsファイルに貼り付けます。envパラメータの<BINDING_NAME>VECTORIZEに置き換えます:

export interface Env {
// これにより、ベクトルインデックスメソッドがenv.VECTORIZE.*で利用可能になります。
// 例えば、env.VECTORIZE.insert()やquery()など。
VECTORIZE: Vectorize;
}
// サンプルベクトル: 32次元の幅。
//
// 人気のある機械学習モデルからのベクトルは通常、~100から1536次元の幅があります。
// (またはそれ以上)。
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 });
}
// インデックスにベクトルを挿入するのは一度だけです
if (path.startsWith("/insert")) {
// サンプルベクトルをインデックスに挿入します
// 実際のアプリケーションでは、これらのベクトルは機械学習(ML)モデルの出力になります。
// 例えば、Workers AI、OpenAI、またはCohereなど。
const inserted = await env.VECTORIZE.insert(sampleVectors);
// この挿入操作の変更識別子を返します
return Response.json(inserted);
}
return Response.json({ text: "まだ何もすることはありません..." }, { status: 404 });
},
} satisfies ExportedHandler<Env>;

上記のコードでは、以下のことを行います:

  1. WorkersコードからVectorizeインデックスへのバインディングを定義します。このバインディングは、wrangler.toml[[vectorize]]で設定したbinding値と一致します。
  2. 次のステップでクエリ対象となるサンプルベクトルのセットを指定します。
  3. それらのベクトルをインデックスに挿入し、成功したことを確認します。

次のステップでは、Workerを拡張してインデックスと挿入したベクトルをクエリします。

6. ベクトルをクエリする

このステップでは、受信クエリを表すベクトルを取得し、それを使用してインデックスを検索します。

まず、vectorize-tutorial Workerに移動し、src/index.tsファイルを開きます。index.tsファイルは、WorkerがVectorizeインデックスとどのように相互作用するかを設定する場所です。

index.tsの内容をクリアし、次のコードスニペットをindex.tsファイルに貼り付けます。envパラメータの<BINDING_NAME>VECTORIZEに置き換えます:

export interface Env {
// これにより、ベクトルインデックスメソッドがenv.VECTORIZE.*で利用可能になります。
// 例えば、env.VECTORIZE.insert()やquery()など。
VECTORIZE: Vectorize;
}
// サンプルベクトル: 32次元の幅を持つ.
//
// 人気のある機械学習モデルからのベクトルは通常、約100から1536次元の幅を持ちます
// (またはそれ以上の幅を持つこともあります)。
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 });
}
// インデックスにベクトルを挿入するのは一度だけで済みます
if (path.startsWith("/insert")) {
// サンプルベクトルをインデックスに挿入します
// 実際のアプリケーションでは、これらのベクトルは機械学習 (ML) モデルの出力になります、
// 例えば、Workers AI、OpenAI、またはCohereなどです。
let inserted = await env.VECTORIZE.insert(sampleVectors);
// この挿入操作の変更識別子を返します
return Response.json(inserted);
}
// return Response.json({text: "nothing to do... yet"}, { status: 404 })
// 実際のアプリケーションでは、ユーザーのクエリを取得します。例えば、「ベクトルデータベースとは何か」
// というクエリを最初にベクトル埋め込みに変換します。
//
// この例では、ベクトル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,
]; // 32次元のベクトル
// インデックスをクエリし、最も類似した3つのベクトル (topK = 3) のIDとその類似度スコアを返します。
//
// デフォルトでは、ベクトルの値は返されません。多くの場合、ベクトルIDとスコアだけで
// 元のコンテンツにマッピングするのに十分です。
const matches = await env.VECTORIZE.query(queryVector, {
topK: 3,
returnValues: true,
returnMetadata: "all",
});
return Response.json({
// これは最も近いベクトルを返します: ベクトルはスコアに従って配置されます。
// より類似したベクトルは上位に表示されます。
// この例では、ベクトルID #4がクエリされたベクトルに最も類似していることがわかります。
// 可能なスコアを確認できるように、完全な一致セットを返します。
matches: matches,
});
},
} satisfies ExportedHandler<Env>;

7. ワーカーをデプロイする

ワーカーをグローバルにデプロイする前に、次のコマンドを実行してCloudflareアカウントにログインします:

Terminal window
npx wrangler login

Cloudflareダッシュボードにログインするように求められるウェブページにリダイレクトされます。ログインした後、WranglerがCloudflareアカウントに変更を加えることができるかどうかを尋ねられます。下にスクロールして許可を選択して続行します。

ここから、プロジェクトをインターネット上でアクセス可能にするためにワーカーをデプロイできます。ワーカーをデプロイするには、次のコマンドを実行します:

Terminal window
npx wrangler deploy

デプロイが完了したら、https://vectorize-tutorial.<YOUR_SUBDOMAIN>.workers.devでワーカーをプレビューします。

8. インデックスをクエリする

ベクトルを挿入し、その後クエリするには、デプロイしたワーカーのURLを使用します。例えば、https://vectorize-tutorial.<YOUR_SUBDOMAIN>.workers.dev/です。ブラウザを開いて:

  1. 最初に/insertにアクセスしてベクトルを挿入します。これにより、以下のJSONが返されるはずです:
// https://vectorize-tutorial.<YOUR_SUBDOMAIN>.workers.dev/insert
{
"mutationId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

ここでのmutationIdは、この非同期挿入操作に対応する一意の識別子を指します。挿入されたベクトルがクエリ可能になるまで通常数秒かかります。

インデックス情報操作を使用して、最後に処理された変更を確認できます:

Terminal window
npx wrangler vectorize info tutorial-index
📋 インデックス情報を取得中...
┌────────────┬─────────────┬──────────────────────────────────────┬──────────────────────────┐
dimensions vectorCount processedUpToMutation processedUpToDatetime
├────────────┼─────────────┼──────────────────────────────────────┼──────────────────────────┤
32 5 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx YYYY-MM-DDThh:mm:ss.SSSZ
└────────────┴─────────────┴──────────────────────────────────────┴──────────────────────────┘

同じベクトルIDを使用した後続の挿入は変更IDを返しますが、同じベクトルIDを二度挿入することはできないため、インデックスのベクトルカウントは変更されません。既にインデックスに存在するIDのベクトル値を更新するには、upsert操作を使用する必要があります。

  1. インデックスをクエリします - クエリベクトル[0.13, 0.25, 0.44, ...]がベクトルID 4に最も近いことを期待して、ルートパス/にアクセスします。このクエリは、3つの(topK: 3)最も近い一致と、そのベクトル値およびメタデータを返します。

id: 4scoreの値0.46348256を持っていることに気付くでしょう。ユークリッド距離を距離メトリックとして使用しているため、スコアが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インデックスを作成し、クエリを実行し、そのインデックスにアクセスするワーカーを作成し、プロジェクトをグローバルにデプロイすることに成功しました。

関連リソース