Введение
В предыдущей лабораторной работе по эмбеддингам Workers AI текст преобразовывался в эмбеддинг — упорядоченный список чисел, который отражает полезные связи между значениями. Эмбеддинг не является исходной статьёй и не представляет собой сгенерированный ответ. Для поиска он становится полезным только тогда, когда приложение может сохранить его со стабильным идентификатором документа, а затем найти близкие векторы.
Cloudflare Vectorize — это векторная база данных. В отличие от таблицы, построенной вокруг строк и столбцов, векторный индекс предназначен для эффективного сравнения числовых векторов. При создании каждый индекс фиксирует два параметра совместимости:
- dimensions — количество чисел в каждом векторе;
- distance metric — способ, которым Vectorize определяет ближайшие векторы.
Вы создадите индекс размерностью 384 для эмбеддингов @cf/baai/bge-small-en-v1.5, размещённых в Cloudflare, и выберете косинусное расстояние — то же сравнение по направлению, которое было представлено в A04. Затем вы добавите индексы метаданных для category и published, вставите три небольших синтетических вектора справочных статей, дождётесь, пока асинхронная мутация станет доступна для чтения, и убедитесь, что трёхмерный вектор отклоняется.
Это первая лабораторная работа курса по Vectorize. Если вы открыли её напрямую, сначала выполните Connect LabEx to Your Cloudflare Account, чтобы научиться пользоваться терминалом виртуальной машины LabEx, авторизовать Wrangler, проверить свою учебную учётную запись и настроить её идентификатор. Сначала завершите Workers AI A04, если понятия векторов, размерностей или косинусного сходства вам незнакомы.
Vectorize доступен в Workers Free. Текущего включённого лимита значительно больше, чем требуется для трёх 384-мерных векторов и проверок только для чтения в этой лабораторной работе, поэтому Workers Paid не требуется. Эта лабораторная работа не вызывает Workers AI и не расходует Neurons.
В процессе настройки устанавливаются Node.js 22.22.0 и локальный Wrangler 4.132.0 в /home/labex/project/document-vector-index. Также предоставляются независимые проверки только для чтения. Настройка не авторизует Wrangler, не создаёт индекс, не записывает векторы и не изменяет вашу учётную запись Cloudflare.
Авторизация виртуальной машины и имя индекса
На этом этапе вы авторизуете новую виртуальную машину, выберете нужную учебную учётную запись и запишете уникальное временное имя индекса.
Вход в Cloudflare Dashboard относится к вашему браузеру. Wrangler в этой новой виртуальной машине — отдельный клиент, поэтому перед управлением ресурсами Vectorize ему требуется ограниченная авторизация.
Перейдите в подготовленный проект и проверьте закреплённую версию CLI:
cd /home/labex/project/document-vector-index
npx wrangler --version
Ожидаемый результат — 4.132.0. Запросите данные об учётной записи и разрешение на управление ресурсами Workers. В этой версии Wrangler область OAuth workers:write включает операции управления Vectorize, используемые в этой работе; область AI не запрашивается, поскольку вывод эмбеддингов не выполняется.
npx wrangler login --device --browser=false --scopes account:read user:read workers:write
Откройте показанную ссылку, введите текущий код, проверьте учётную запись и разрешения и авторизуйте свою учебную учётную запись. Затем просмотрите структурированные данные о текущей идентификации:
npx wrangler whoami --json
Убедитесь, что указано loggedIn: true, и определите нужную учебную учётную запись. Сгенерируйте уникальное временное имя индекса:
RUN="labex-c08-v01-$(openssl rand -hex 6)"
printf '%s\n' "$RUN"
Замените YOUR_ACCOUNT_ID фактическим идентификатором этой учётной записи:
cat > wrangler.jsonc <<JSON
{
"\$schema": "./node_modules/wrangler/config-schema.json",
"name": "$RUN-tools",
"account_id": "YOUR_ACCOUNT_ID",
"compatibility_date": "2026-09-16",
"vectorize": [
{ "binding": "DOCUMENTS", "index_name": "$RUN", "remote": true }
]
}
JSON
Привязка фиксирует связь, которую следующие лабораторные работы будут использовать из кода Worker: DOCUMENTS — это имя, видимое приложению, а index_name — принадлежащий вам облачный ресурс. Значение remote: true означает, что локальный Worker подключался бы к настоящему удалённому индексу, а не к изолированной локальной симуляции.
Создание индекса и фильтруемых полей
На этом этапе вы создадите фиксированный контракт векторов и подготовите два поля метаданных для последующей фильтрации.
Размерность и метрика расстояния индекса фиксированы, поскольку каждое сравнение должно выполняться по одному числовому контракту. BGE Small создаёт 384 числа. Косинусное расстояние сравнивает направление векторов, что подходит для смысловых эмбеддингов из A04.
Создайте индекс V2:
npx wrangler vectorize create "$RUN" --dimensions=384 --metric=cosine --update-config=false
Векторы также могут содержать небольшие метаданные, например категорию документа. Сохранение метаданных само по себе не делает их фильтруемыми. Индекс метаданных указывает Vectorize, какое поле нужно подготовить для фильтров. Создайте эти поля до вставки векторов:
npx wrangler vectorize create-metadata-index "$RUN" --propertyName=category --type=string | tee .labex/category-index-output.txt
npx wrangler vectorize create-metadata-index "$RUN" --propertyName=published --type=boolean | tee .labex/published-index-output.txt
Параметр --update-config=false не позволяет Wrangler предложить заменить уже записанную привязку. Создание индекса метаданных выполняется асинхронно. Каждая команда ставит мутацию в очередь, поэтому сообщение об успешном выполнении означает, что Cloudflare принял изменение, но ещё не гарантирует, что оно уже видно при каждом чтении.
Создайте небольшой многоразовый скрипт ожидания. Он выполняет только команду чтения vectorize info, сравнивает точный идентификатор мутации и требует три последовательных совпадающих результата чтения, прежде чем доверять состоянию. Дополнительное подтверждение не позволяет принять кратковременно устаревшую реплику чтения за окончательное состояние. Через четыре минуты скрипт завершится с ошибкой, а не будет ждать бесконечно:
cat > scripts/wait-for-vectorize.mjs <<'JS'
import { execFileSync } from "node:child_process";
const [indexName, mutationId, expectedCountText] = process.argv.slice(2);
const expectedCount = Number(expectedCountText);
const wrangler = "./node_modules/wrangler/bin/wrangler.js";
let consecutiveMatches = 0;
for (let attempt = 1; attempt <= 120; attempt += 1) {
const output = execFileSync(process.execPath, [wrangler, "vectorize", "info", indexName, "--json"], { encoding: "utf8" });
const info = JSON.parse(output);
if (info.processedUpToMutation === mutationId && info.vectorCount === expectedCount) {
consecutiveMatches += 1;
} else {
consecutiveMatches = 0;
}
if (consecutiveMatches === 3) {
console.log(`mutation ${mutationId} is consistently readable with ${expectedCount} vectors`);
console.log(JSON.stringify(info, null, 2));
process.exit(0);
}
await new Promise((resolve) => setTimeout(resolve, 2000));
}
throw new Error(`mutation ${mutationId} was not readable within four minutes`);
JS
METADATA_MUTATION_ID=$(sed -nE 's/.*Mutation changeset identifier: ([0-9a-f-]{36}).*/\1/p' .labex/published-index-output.txt | tail -n 1)
test -n "$METADATA_MUTATION_ID"
node scripts/wait-for-vectorize.mjs "$RUN" "$METADATA_MUTATION_ID" 0
npx wrangler vectorize get "$RUN"
npx wrangler vectorize list-metadata-index "$RUN"
В итоговых таблицах должны отображаться размерность 384, косинусное расстояние, category как String и published как Bool. Bool — это текущее название типа в API для поля, созданного с помощью --type=boolean. Ожидание второй мутации метаданных не позволяет следующей вставке векторов оказаться в очереди позади ещё не завершённой подготовки индекса.
Создание идентифицированных векторов документов
На этом этапе вы создадите небольшой наглядный набор векторов, идентификаторы и метаданные которого можно проверить независимо.
Векторная база данных не заменяет исходный документ. Для каждого вектора нужен стабильный идентификатор, по которому приложение сможет найти соответствующее реальное содержимое. В этой лабораторной работе используются три синтетических идентификатора справочных статей; их категория, состояние публикации, модель эмбеддингов и способ pooling записываются в метаданные.
Работа с реальными эмбеддингами появится в V03. Здесь детерминированные векторы делают поведение хранилища воспроизводимым и не требуют дополнительных затрат: каждый документ направлен вдоль отдельной оси, а затем дополнен нулями до 384 позиций.
Создайте наглядный генератор тестовых данных:
cat > scripts/create-vectors.mjs <<'JS'
import { writeFileSync } from "node:fs";
const DIMENSIONS = 384;
const MODEL = "@cf/baai/bge-small-en-v1.5";
const POOLING = "cls";
const documents = [
{ id: "password-reset", axis: 0, category: "account" },
{ id: "upload-pdf", axis: 1, category: "files" },
{ id: "billing-receipt", axis: 2, category: "billing" }
];
function unitVector(axis) {
const values = Array(DIMENSIONS).fill(0);
values[axis] = 1;
return values;
}
const rows = documents.map((document) => ({
id: document.id,
values: unitVector(document.axis),
metadata: {
category: document.category,
published: true,
model: MODEL,
pooling: POOLING
}
}));
writeFileSync("vectors/documents.ndjson", rows.map(JSON.stringify).join("\n") + "\n");
console.log(`wrote ${rows.length} vectors with ${DIMENSIONS} dimensions each`);
JS
node scripts/create-vectors.mjs
NDJSON означает JSON, разделённый переводами строк: одна полная запись вектора в каждой строке, а не один общий JSON-массив. Wrangler может передавать этот формат пакетами. Проверьте идентификаторы и структуру, не выводя в терминал все 1 152 числа:
node - <<'JS'
const rows = require("fs").readFileSync("vectors/documents.ndjson", "utf8").trim().split("\n").map(JSON.parse);
console.table(rows.map(({ id, values, metadata }) => ({ id, dimensions: values.length, category: metadata.category, published: metadata.published })));
JS
Во всех трёх строках должна отображаться размерность 384. Метаданные модели и pooling cls документируют совместимость; Vectorize не выводит и не проверяет этот смысл автоматически.
Вставка векторов и ожидание обработки мутации
На этом этапе вы вставите один пакет и дождётесь, пока именно его асинхронная мутация станет видимой для операций чтения.
Запись в Vectorize выполняется асинхронно. Сначала вставка попадает в надёжный журнал упреждающей записи и возвращает идентификатор мутации. Затем фоновая обработка делает эту мутацию доступной для чтения. Такой подход повышает эффективность записи, но означает, что «принято» и «доступно для чтения» — это разные моменты.
Вставьте пакет из трёх векторов и сохраните полный результат. pipefail не позволяет успешной команде tee скрыть ошибку Wrangler:
set -o pipefail
npx wrangler vectorize insert "$RUN" --file=vectors/documents.ndjson 2>&1 | tee .labex/insert-output.txt
Продолжайте только после того, как Wrangler сообщит о постановке в очередь трёх векторов и выведет идентификатор мутации. Если API вместо этого вернул ошибку авторизации или сети, результат нельзя считать однозначным: проверьте npx wrangler whoami --json, затем один раз повторно выполните этот же блок вставки. Не запускайте скрипт ожидания без настоящего идентификатора мутации.
Извлеките принятую мутацию и запускайте ожидание только при её наличии:
MUTATION_ID=$(sed -nE 's/.*Mutation changeset identifier: ([0-9a-f-]{36}).*/\1/p' .labex/insert-output.txt | tail -n 1)
if [ -z "$MUTATION_ID" ]; then
printf '%s\n' 'No mutation ID was returned; fix the insert error before waiting.' >&2
else
printf 'Waiting for mutation %s\n' "$MUTATION_ID"
node scripts/wait-for-vectorize.mjs "$RUN" "$MUTATION_ID" 3
fi
В итоговом JSON скрипта ожидания должны отображаться vectorCount со значением 3 и записанный идентификатор мутации. Требование трёх совпадающих результатов чтения защищает отображаемый учащемуся результат от кратковременной задержки реплики. Ограниченный по времени опрос безопаснее фиксированной паузы: быстрая мутация завершится сразу, а более медленная, но исправная мутация получит достаточно времени, не создавая повторных записей.
Чтение документов и проверка совместимости
На этом этапе вы прочитаете принятые записи, увидите отклонение несовместимой записи и сопоставите состояние CLI с Dashboard.
Прочитайте сохранённые записи по их идентификаторам приложения.
Сначала сохраните полные записи, затем выведите компактную таблицу вместо того, чтобы заполнять терминал 1 152 числами:
npx wrangler vectorize get-vectors "$RUN" --ids password-reset upload-pdf billing-receipt > .labex/stored-vectors.txt
node - <<'JS'
const text = require("fs").readFileSync(".labex/stored-vectors.txt", "utf8");
const rows = JSON.parse(text.slice(text.indexOf("[")));
console.table(rows.map(({ id, values, metadata }) => ({ id, dimensions: values.length, category: metadata.category, published: metadata.published })));
JS
В каждой строке сводной таблицы должны сохраняться идентификатор, форма вектора из 384 значений и метаданные. В необработанном файле находятся все значения для независимой проверки. get-vectors читает известные записи, а не выполняет поиск по сходству. Запросы по сходству появятся в V03.
Теперь создайте одну намеренно несовместимую запись только с тремя значениями:
cat > vectors/incompatible.ndjson <<'NDJSON'
{"id":"wrong-dimensions","values":[1,0,0],"metadata":{"category":"account","published":true}}
NDJSON
if npx wrangler vectorize insert "$RUN" --file=vectors/incompatible.ndjson > .labex/incompatible.log 2>&1; then
STATUS=0
else
STATUS=$?
fi
printf '%s\n' "$STATUS" > .labex/incompatible-exit.txt
sed -n '/invalid vector/p' .labex/incompatible.log
test "$STATUS" -ne 0
Отклонение защищает контракт индекса: вектор из трёх позиций нельзя осмысленно сравнивать с векторами из 384 позиций. Убедитесь, что принятые записи сохранились, а отклонённый идентификатор не появился:
npx wrangler vectorize info "$RUN"
npx wrangler vectorize list-vectors "$RUN" --count=10
npx wrangler vectorize get-vectors "$RUN" --ids wrong-dimensions
Откройте Cloudflare Dashboard для выбранной учётной записи и перейдите в AI → Vectorize. Список ресурсов связывает имя из CLI с настоящим индексом, показывает размерность 384 и косинусное расстояние, а также сообщает о трёх векторах и отсутствии тарифицируемого использования в этом небольшом примере.

Откройте индекс с именем из $RUN. В его сводке отображаются три сохранённых вектора. Число запросов остаётся равным нулю, поскольку в этой первой лабораторной работе используются чтения по идентификаторам; запросы по сходству начинаются в V03.

Прокрутите страницу до Stored Vectors. На графике наглядно показана асинхронная видимость: сначала количество остаётся равным нулю, а после обработки мутации вставки изменяется на три.

Текущая версия Dashboard не отображает отдельные идентификаторы векторов или определения индексов метаданных. Используйте предыдущие чтения Wrangler для password-reset, upload-pdf, billing-receipt, category и published; не делайте выводы об этих деталях по графику, который показывает только количество. Страницы Dashboard помогают сориентироваться, а независимые проверки используют авторитетные чтения API.
Показанные здесь скриншоты после принятия облаком результатов лабораторной работы являются примерами одного временного запуска. Имя вашего случайного индекса и временные метки будут отличаться; сопоставляйте конфигурацию и принадлежащие вам идентификаторы, а не копируйте значения из примеров.
Удаление временного индекса и выход из системы
На этом этапе вы удалите принадлежащий вам индекс с точным именем, подтвердите его отсутствие с действующей авторизацией, а затем удалите авторизацию виртуальной машины.
Индекс, его индексы метаданных и его векторы образуют один временный ресурс. Пока авторизация доступна, удалите точное имя, сохранённое в wrangler.jsonc:
npx wrangler vectorize delete "$RUN" --force
Проверьте отсутствие индекса с помощью авторизованного чтения списка ресурсов:
npx wrangler vectorize list --json > .labex/indexes-after-cleanup.json
node -e '
const rows = JSON.parse(require("fs").readFileSync(process.argv[1], "utf8"));
if (rows.some((row) => row.name === process.argv[2])) throw new Error("lab index still exists");
console.log("lab index is absent");
' .labex/indexes-after-cleanup.json "$RUN"
Успешное чтение списка важно: ошибка сети или авторизации не подтверждала бы удаление. Выполните проверку очистки до отзыва авторизации виртуальной машины:
bash verify6-1.sh
Наконец удалите вход Wrangler на виртуальной машине и просмотрите структурированный результат:
npx wrangler logout
npx wrangler whoami --json
Ожидайте loggedIn: false. Вход в Dashboard через браузер выполняется отдельно и остаётся доступным для вашей учебной учётной записи.
Итоги
Вы создали индекс Vectorize V2 с тем же контрактом размерности 384, что и у выбранной модели эмбеддингов, выбрали косинусное расстояние и подготовили два поля метаданных для последующих фильтров. Вы сгенерировали идентифицированные детерминированные векторы, вставили их в формате NDJSON, отличили принятую асинхронную мутацию от обработанной мутации и прочитали сохранённые записи по идентификаторам.
Вы также убедились, что Vectorize отклоняет вектор с неправильной размерностью, сохраняя совместимые записи. В конце вы проверили настоящий ресурс в Dashboard, удалили точный временный индекс, подтвердили его отсутствие с действующей авторизацией и удалили авторизацию Wrangler для новой виртуальной машины.
Следующая лабораторная работа продолжит этот жизненный цикл с помощью upsert и удаления, чтобы изменённые и снятые с публикации документы не оставляли индекс устаревшим.



