Cloudflare:Vectorize
2026年2月7日仕組み
VectorizeはWorkersとセットで使う
テキストをVector(= 数字の配列)として扱い、テキストの部分一致ではなく意味の近さで検索できるようにするのが目的
流れとしてはWorkersでテキストを受取りVectorに変換、Vectorにidやmetadataを付与してインデックスに保管
用語:
Vetorizeに保存しているデータは 「Vector」
DBのテーブル/コレクション相当のものは 「Index」
注意点や詰まった部分:
テーブルの中身を見ることが出来ないので作成・更新されたのかがわかりづらい。
npx wrangler vectorize listでcreatedとmodifiedに差が生じていれば、とりあえず操作自体は成功している事には気付ける
テキストを分割してVectorにするが、「渡した
idと同じものを全て削除・更新」ということが出来ないので削除・更新ロジックが複雑になる
始める
テンプレートから始めると良い(https://developers.cloudflare.com/vectorize/get-started/intro/)
npm create cloudflare@latest -- vectorize-tutorialCLIから始める場合:
npx wrangler vectorize create インデックス名 \
--dimensions 1024 \
--metric "cosine" \
--description "インデックスの説明" \
--update-config重要:日本語に対応したVector生成モデルを使う場合、dimensionsを1024にする。1536などにすると機能しなくなる
他のCLIコマンド:
// 削除
npx wrangler vectorize delete インデックス名
// 確認
npx wrangler vectorize listwrangler.jsonc :
"ai": {
"binding": "AI"
},
"vectorize": [
{
"binding": "VECTORIZE",
"index_name": "インデックス名",
"remote": true
}
]必要なbindingはaiとvectorize
Vectorの生成
まず受け入れたtextをVectorに変換
const embedding = await env.AI.run('@cf/baai/bge-m3', { text: texts });日本語を使う場合は多言語対応のモデルを使う
次にVectorをインデックスに保管
await env.VECTORIZE.upsert([{ id: 'example-id', values: [0.1, 0.2, 0.3], metadata: { info: 'example-metadata' } }]);insertではなくupsertを使う方が良いvaluesにテキストから生成されたVectorを入れるidは自分で付与する必要があるmetadataもあったほうが良い
chunk
受け取ったテキストが長い場合は数分割にしたほうが良い
const chunks = chunkText(content);
const embedding = await env.AI.run('@cf/baai/bge-m3', { text: chunks });上記はアプリ側から受け取った
contentをchunkText関数で800文字毎に分割してVectorを生成
chunkText.ts:
export function chunkText(text: string, chunkSize = 800): string[] {
const chunks: string[] = [];
let start = 0;
while (start < text.length) {
chunks.push(text.slice(start, start + chunkSize));
start += chunkSize;
}
return chunks;
}インデックスには分けて保管
await env.VECTORIZE.upsert(
embedding.data.map((vec: number[], i: number) => ({
id: `${memoId}-chunk-${i}`,
values: vec,
metadata: { index: i, memoId },
}))
);重要:Chunkに分けたVectorは同一のテキストから発生したことを
id・metadataに記しておく取得・削除・更新の為に必要
Vectorの検索
アプリ側から受け取ったqueryのテキストから合致するVectorを取得する
まずqueryをVectorにする
const embedding = await env.AI.run('@cf/baai/bge-m3', { text: [query] });次にインデックスから意味的に近いものを取得
const results = await env.VECTORIZE.query(embedding.data[0], { topK: 8, returnMetadata: true });returnMetadata: trueはchunk単位の検索結果を元の文章単位にまとめるために必要。つまりchunkとしてVecror化した元の文章を絞る。topKは意味的に近いものを上位から何個取得するか
filter
アプリ側からqueryなどでVector検索をするが、Vectorの数字配列データが欲しいわけではなく欲しいのはSearhInputなどに入力されたqueryとアプリ側のどのデータが近いのかという情報なので、返すべきはインデックス保管時にmetadataに設定したmemoIdやpostId
const memoIds = [
...new Set(
results.matches
.filter(
(m) => m.score !== undefined && m.score >= 0.6 && m.metadata?.memoId
)
.map((m) => m.metadata?.memoId)
),
];上記は意味的に近かった8件の中から、さらに
scoreが 0.6 以上だったものをインデックスから選ぶ...new Setで被り無しの配列にしているのはアプリ側に返すmemoIdは重複が必要無いため
*Vectorの削除
重要:
Vectorの削除には自分で作成した id を正確に指定しないといけない
metadataの識別子を使って削除することは出来ない
const { memoId, chunkCount }: any = await req.json();
const idsToDelete = Array.from(
{ length: chunkCount },
(_, i) => `${memoId}-chunk-${i}`
);
await env.VECTORIZE.deleteByIds(idsToDelete);問題になるのはVectorをchunkに分けて保存しているということ。
Prismaのように「渡したmemoIdやpostId がmetadata に存在するものを一括で削除」することが出来ないため、id に付与した${i}の数を削除時にも把握している必要がある。
${memoId}-chunkのようなidにすればIndexの把握は必要無くなるが、id は一意である必要があるのでそれは出来ない。
対策:
アプリ側から削除したいデータのChunkCountを計算して渡すことにより、一意の
memoIdが持つ Vector(${memoId}-chunk-${i})を全て削除できる。つまり、アプリ側のテキストサイズの変更をVectorに同期しないとchunk数が噛み合わないケースが出る。*chunkの長さが10を超えることは無いだろうという前提で
Array.form({ length:10 })のようにして削除を試行することは出来ない。存在しないidにアクセスすると機能しない為
*Vectorの更新
アプリから受け取った識別子( memoId, postId)を持つVectorを全て削除したのち新しいVectorを作成する
const idsToDelete = Array.from(
{ length: currentChunks },
(_, i) => `${memoId}-chunk-${i}`
);
await env.VECTORIZE.deleteByIds(idsToDelete);
const chunks = chunkText(content);
const embedding: any = await env.AI.run("@cf/baai/bge-m3", { text: chunks });
await env.VECTORIZE.upsert(
embedding.data.map((vec: number[], i: number) => ({
id: `${memoId}-chunk-${i}`,
values: vec,
metadata: { memoId, index: i },
}))
);エラーに詰まった場合
npx wrangler tail上記コマンドを実行すると、 index.tsに来たリクエストの成功や失敗、エラー原因等がリアルタイムでターミナルに表示される。
✘ [ERROR] Error: VECTOR_UPSERT_ERROR (code = 40012): invalid vector for id="155-chunk-0", expected 1536 dimensions, and got 1024 dimensions;