ファランクスブログ

© 2026 all rights reserved.
  1. blog
  2. cloudflare-vectorize
  • 仕組み
  • 始める
  • Vectorの生成
  • chunk
  • Vectorの検索
  • filter
  • *Vectorの削除
  • *Vectorの更新
  • エラーに詰まった場合

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-tutorial

CLIから始める場合:

npx wrangler vectorize create インデックス名 \
  --dimensions 1024 \
  --metric "cosine" \
  --description "インデックスの説明" \
  --update-config
  • 重要:日本語に対応したVector生成モデルを使う場合、dimensionsを1024にする。1536などにすると機能しなくなる

他のCLIコマンド:

// 削除
npx wrangler vectorize delete インデックス名

// 確認
npx wrangler vectorize list

wrangler.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;

cloud
/
cloudflare
  • 仕組み
  • 始める
  • Vectorの生成
  • chunk
  • Vectorの検索
  • filter
  • *Vectorの削除
  • *Vectorの更新
  • エラーに詰まった場合