Маршрутизация инференса через шлюз

CloudflareBeginner
Практиковаться сейчас

Введение

В курсе Workers AI приложение отправляло prompt напрямую в модель, размещённую в Cloudflare. Это работает, но растущему приложению также требуется единое место для наблюдения за трафиком к моделям и его контроля. Cloudflare AI Gateway выполняет роль такого контрольного узла: вызывающая сторона отправляет запрос в именованный шлюз, а шлюз пересылает его вышестоящему провайдеру модели, например Workers AI.

В этой лабораторной работе явно видны три роли:

  • вызывающая сторонаcurl в вашей виртуальной машине LabEx;
  • шлюз — проверяет, может ли вызывающая сторона войти, и записывает запрос;
  • вышестоящий провайдер — Workers AI, который проверяет, разрешено ли выполнить запрос к модели.

Для этих двух проверок используются разные учётные данные. cf-aig-authorization аутентифицирует вызывающую сторону в AI Gateway. Обычный заголовок Authorization аутентифицирует запрос шлюза в Workers AI. Действительный токен шлюза не является автоматически учётными данными Workers AI, а учётные данные Workers AI не позволяют обойти аутентификацию шлюза.

Вы создадите один временный аутентифицированный шлюз в Cloudflare Dashboard, создадите токен AI Gateway с минимально необходимыми правами, отправите короткий запрос к размещённой в Cloudflare модели Llama 3.3 и изучите созданную запись журнала. Затем вы замените только учётные данные шлюза недействительным значением, чтобы определить, какая граница отклоняет запрос. В конце вы удалите шлюз, удалите его токен, чтобы тот больше не мог авторизовывать запросы, и выйдете из Wrangler.

Если вы открыли этот курс напрямую, сначала пройдите лабораторную работу Connect LabEx to Your Cloudflare Account. В ней рассказывается о терминале виртуальной машины LabEx, авторизации устройства Wrangler, подтверждении аккаунта и явном указании идентификаторов аккаунта. Лабораторная работа по инференсу Workers AI также будет полезной предварительной подготовкой.

AI Gateway доступен в тарифном плане Free, а базовое журналирование бесплатно в пределах ограничений аккаунта. Выбранная модель @cf/meta/llama-3.3-70b-instruct-fp8-fast может использовать общую бесплатную квоту Workers AI при стандартном биллинге. Workers Paid и Unified Billing не требуются. Если дневная квота Workers AI для аккаунта недоступна, остановитесь и не повторяйте запросы многократно.

Настройка устанавливает Node.js 22.22.0 и локальный Wrangler 4.132.0 в /home/labex/project/ai-gateway-route. Она предоставляет независимые проверки только для чтения, но не авторизует Wrangler, не создаёт токен или шлюз, не отправляет запрос к модели и не изменяет ваш аккаунт Cloudflare. После завершения лабораторной работы LabEx не сохраняет эту временную виртуальную машину. Тем не менее вы должны явно удалить облачный токен и стереть его копию с виртуальной машины, чтобы очистка была завершена до уничтожения виртуальной машины.

Авторизация виртуальной машины и сохранение имён ресурсов

На этом шаге вы подключите новую виртуальную машину к своему учебному аккаунту и сохраните имена, чтобы однозначно определить ресурсы этой лабораторной работы.

Вход с устройства Wrangler авторизует Workers AI, но не создаёт отдельные учётные данные вызывающей стороны AI Gateway, которые понадобятся позже. Раздельное хранение этих учётных данных помогает лучше увидеть границу доверия.

Перейдите в подготовленный проект и проверьте закреплённую версию CLI:

cd /home/labex/project/ai-gateway-route
npx wrangler --version

Ожидаемый результат — 4.132.0. Запустите авторизацию устройства с доступом к данным аккаунта и Workers AI:

npx wrangler login --device --browser=false --scopes account:read user:read ai:write

Откройте показанную ссылку, введите код и авторизуйте нужный учебный аккаунт. Затем проверьте структурированные данные о личности:

npx wrangler whoami --json

Убедитесь, что loggedIn: true. Создайте уникальный идентификатор шлюза и связанное с ним имя токена. Замените YOUR_ACCOUNT_ID фактическим идентификатором длиной 32 символа, который отображается для нужного аккаунта:

GATEWAY_ID="labex-c09-g01-$(openssl rand -hex 6)"
TOKEN_NAME="$GATEWAY_ID-token"
cat > .labex/state.json <<JSON
{
  "accountId": "YOUR_ACCOUNT_ID",
  "gatewayId": "$GATEWAY_ID",
  "tokenName": "$TOKEN_NAME"
}
JSON
cat .labex/state.json

Случайный суффикс предотвращает конфликты имён. Файл состояния содержит идентификаторы ресурсов, а не учётные данные, и позволяет всем последующим командам обращаться именно к ресурсам, созданным этой лабораторной работой.

Создание аутентифицированного шлюза и токена вызывающей стороны

На этом шаге вы создадите контрольный узел и учётные данные, которые позволят обращаться к нему и просматривать его данные.

Откройте Cloudflare Dashboard и выберите AI → AI Gateway → Create gateway → Custom gateway. В качестве имени шлюза используйте значение gatewayId из .labex/state.json. Оставьте следующие настройки:

  • журналирование запросов: включено;
  • аутентификация шлюза: включена;
  • кэширование, ограничение частоты запросов, ограничения расходов и повторы: выключены;
  • биллинг Workers AI: Standard.

Биллинг Standard сохраняет использование Workers AI в рамках обычной квоты. Unified Billing — это другой способ оплаты, который не рассматривается в этой вводной лабораторной работе.

После открытия нового ресурса Cloudflare используйте цепочку навигации и выбранную вкладку Overview, чтобы убедиться, что вы находитесь именно в созданном временном шлюзе, а не в общем списке шлюзов аккаунта.

Обзор нового шлюза с уникальным идентификатором и пустыми показателями первых запросов

После создания откройте Settings. Убедитесь, что отображаемый идентификатор шлюза в точности совпадает с сохранённым идентификатором, а журналирование и аутентификация включены.

Теперь выберите Create an AI Gateway authentication token. В качестве имени укажите сохранённое значение tokenName, выберите только нужный учебный аккаунт и добавьте следующие разрешения:

  • AI Gateway — Run позволяет вызывающей стороне войти в аутентифицированный шлюз;
  • AI Gateway — Edit позволяет лабораторной работе читать и удалять ресурсы AI Gateway через API управления.

Не добавляйте этому токену разрешение Workers AI. Доступ к Workers AI по-прежнему будет предоставляться отдельными краткоживущими учётными данными Wrangler.

Форма разрешений токена с областями AI Gateway Run и Edit

Создавайте токен только после проверки аккаунта и разрешений. Cloudflare покажет его значение один раз. Сохраните его в закрытом виде, не выводя на экран:

bash -c '
while :; do
  read -rsp "Paste the AI Gateway token: " GATEWAY_TOKEN
  printf "\n"
  [ -n "$GATEWAY_TOKEN" ] && break
  printf "Token cannot be empty; paste it again.\n" >&2
done
umask 077
printf "%s" "$GATEWAY_TOKEN" > .labex/gateway-token
unset GATEWAY_TOKEN
chmod 600 .labex/gateway-token
'

В подготовленном терминале интерактивно используется zsh, поэтому этот блок запускает короткий дочерний процесс Bash для скрытого prompt команды read. Пустая вставка отклоняется до возврата команды в оболочку. Токен остаётся только в дочернем процессе и закрытом файле.

Токен намеренно не сохраняется в конфигурации и не выводится в результатах команд. Проверьте настоящий шлюз через аутентифицированный API управления:

ACCOUNT_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).accountId')
GATEWAY_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).gatewayId')
GATEWAY_TOKEN=$(cat .labex/gateway-token)
curl --http1.1 -fsS \
  -H "Authorization: Bearer $GATEWAY_TOKEN" \
  "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-gateway/gateways/$GATEWAY_ID" \
  | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const b=JSON.parse(s),g=b.result||{};console.log(JSON.stringify({success:b.success,id:g.id,collect_logs:g.collect_logs,authentication:g.authentication},null,2))})'
unset GATEWAY_TOKEN

Ожидайте собственный идентификатор, а также значения true для collect_logs и authentication. Секретные данные не выводятся.

Представление Settings шлюза с включёнными аутентификацией, журналами и биллингом Standard

Маршрутизация одного запроса Workers AI через шлюз

На этом шаге вы отправите один небольшой запрос через шлюз, а не напрямую в Workers AI.

URL шлюза в формате провайдера содержит аккаунт, шлюз, провайдера и модель. Два заголовка авторизации намеренно остаются раздельными:

caller → cf-aig-authorization → AI Gateway → Authorization → Workers AI model

Получите текущий краткоживущий токен Workers AI от Wrangler в виде структурированных данных, а затем выполните запрос. Команда записывает на диск только JSON-ответ; ни один из токенов не выводится:

ACCOUNT_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).accountId')
GATEWAY_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).gatewayId')
MODEL='@cf/meta/llama-3.3-70b-instruct-fp8-fast'
GATEWAY_TOKEN=$(cat .labex/gateway-token)
UPSTREAM_TOKEN=$(npx wrangler auth token --json | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>process.stdout.write(JSON.parse(s).token))')
curl --http1.1 -fsS \
  -H "cf-aig-authorization: Bearer $GATEWAY_TOKEN" \
  -H "Authorization: Bearer $UPSTREAM_TOKEN" \
  -H 'Content-Type: application/json' \
  --data '{"prompt":"In one sentence, explain why an AI gateway is useful.","max_tokens":64}' \
  "https://gateway.ai.cloudflare.com/v1/$ACCOUNT_ID/$GATEWAY_ID/workers-ai/$MODEL" \
  > .labex/valid-response.json
unset GATEWAY_TOKEN UPSTREAM_TOKEN
node -e 'const b=require("./.labex/valid-response.json"); console.log(b.result?.response ?? b.result)'

Формулировка ответа может отличаться, поскольку генерация недетерминирована. Проверка учитывает только то, что провайдер вернул непустой успешный результат через принадлежащий вам шлюз.

Выделение границы аутентификации шлюза

На этом шаге вы оставите действительные учётные данные Workers AI, но замените только учётные данные шлюза.

Контролируемый отрицательный тест должен изменять одно условие за раз. Если сделать недействительными оба набора учётных данных, ошибка HTTP не покажет, какая система отклонила запрос. В этом запросе сохраняется действительный токен вышестоящего сервиса Wrangler, а в cf-aig-authorization передаётся заведомо недействительное значение:

ACCOUNT_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).accountId')
GATEWAY_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).gatewayId')
MODEL='@cf/meta/llama-3.3-70b-instruct-fp8-fast'
UPSTREAM_TOKEN=$(npx wrangler auth token --json | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>process.stdout.write(JSON.parse(s).token))')
STATUS=$(curl --http1.1 -sS -o .labex/invalid-response.json -w '%{http_code}' \
  -H 'cf-aig-authorization: Bearer deliberately-invalid' \
  -H "Authorization: Bearer $UPSTREAM_TOKEN" \
  -H 'Content-Type: application/json' \
  --data '{"prompt":"This request must not reach the model.","max_tokens":8}' \
  "https://gateway.ai.cloudflare.com/v1/$ACCOUNT_ID/$GATEWAY_ID/workers-ai/$MODEL")
unset UPSTREAM_TOKEN
printf '%s\n' "$STATUS" | tee .labex/invalid-status.txt

Ожидайте 401 или 403. Не выводите тело ответа: самого статуса достаточно как доказательства, а ограничение вывода ошибки снижает вероятность раскрытия деталей запроса.

Связывание запроса с журналом шлюза

На этом шаге вы используете наблюдаемость, чтобы связать поведение во время выполнения с видимой записью шлюза.

Наблюдаемость означает сбор достаточного количества данных, чтобы понять, что система сделала после отправки запроса вызывающей стороной. Журнал шлюза может показать провайдера, модель, статус, задержку и использование токенов, не требуя повторно обращаться к модели. Журналы могут появиться с небольшой задержкой.

Прочитайте существующие журналы через аутентифицированный API управления. Это проверка только для чтения; новый запрос к модели не отправляется:

ACCOUNT_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).accountId')
GATEWAY_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).gatewayId')
GATEWAY_TOKEN=$(cat .labex/gateway-token)
curl --http1.1 -fsS \
  -H "Authorization: Bearer $GATEWAY_TOKEN" \
  "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-gateway/gateways/$GATEWAY_ID/logs?per_page=50" \
  > .labex/logs.json
unset GATEWAY_TOKEN
node - <<'NODE'
const body = require('./.labex/logs.json')
const model = '@cf/meta/llama-3.3-70b-instruct-fp8-fast'
const matches = (body.result || []).filter(row =>
  row.provider === 'workers-ai' && row.model === model
)
console.log(matches.map(row => ({
  id: row.id,
  provider: row.provider,
  model: row.model,
  success: row.success,
  created_at: row.created_at
})))
if (!matches.some(row => row.success === true)) process.exit(2)
NODE

Ожидайте одну запись с provider: "workers-ai", нужной моделью и success: true. Если команда завершится без такой записи, подождите примерно 20 секунд и повторно выполните этот же блок только для чтения, вместо отправки новых запросов к модели.

Откройте представление Logs шлюза в Dashboard. Найдите успешную строку Workers AI для @cf/meta/llama-3.3-70b-instruct-fp8-fast. Перед открытием панели подробностей убедитесь, что указаны успешный статус, нужный провайдер и модель.

Таблица Logs шлюза с выделенным успешным запросом Workers AI

Точные значения длительности, количества токенов и сгенерированного текста могут отличаться. Они описывают этот запрос и не являются точными целевыми значениями. Никогда не помещайте учётные данные или личную информацию в prompt только для того, чтобы упростить поиск записи в журнале.

Открытая панель подробностей записи журнала с моделью, статусом, задержкой и использованием токенов

Удаление временного шлюза

На этом шаге вы удалите облачный ресурс, пока учётные данные для управления им ещё доступны.

Очистка должна обращаться к точному принадлежащему вам идентификатору, а результат необходимо подтвердить через аутентифицированный список ресурсов. Исчезновение страницы из-за выхода из аккаунта или сбоя сети не доказывает удаление.

ACCOUNT_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).accountId')
GATEWAY_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).gatewayId')
GATEWAY_TOKEN=$(cat .labex/gateway-token)
curl --http1.1 -fsS -X DELETE \
  -H "Authorization: Bearer $GATEWAY_TOKEN" \
  "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-gateway/gateways/$GATEWAY_ID" \
  | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const b=JSON.parse(s);if(!b.success)process.exit(1);console.log("gateway deletion accepted")})'
unset GATEWAY_TOKEN

GATEWAY_TOKEN=$(cat .labex/gateway-token)
curl --http1.1 -fsS \
  -H "Authorization: Bearer $GATEWAY_TOKEN" \
  "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-gateway/gateways" \
  > .labex/gateways-after-delete.json
unset GATEWAY_TOKEN
node -e 'const b=require("./.labex/gateways-after-delete.json"),id=process.argv[1],found=(b.result||[]).some(g=>g.id===id);console.log("gateway absent:",!found);if(found)process.exit(1)' "$GATEWAY_ID"

Ожидайте gateway absent: true. Второй запрос с действительной авторизацией перечисляет шлюзы и завершается ошибкой, если принадлежащий вам идентификатор всё ещё существует. Другие шлюзы вашего аккаунта не изменяются.

Удаление токена и выход из системы

На этом шаге вы удалите два независимых набора учётных данных в обратном порядке.

В Cloudflare Dashboard откройте My Profile → API Tokens. Найдите точное имя токена, сохранённое в .labex/state.json, откройте его меню Actions, выберите Delete, проверьте подтверждение и удалите только этот токен. Удаление токена немедленно отзывает его доступ. Теперь его можно безопасно удалить, поскольку шлюз уже удалён.

Удалите локальную копию, а затем завершите отдельную авторизацию Wrangler на виртуальной машине:

shred -u .labex/gateway-token
npx wrangler logout
npx wrangler whoami --json || true

Ожидайте структурированный вывод с loggedIn: false. Сеанс браузера Dashboard является отдельным и останется активным. Выполните финальную локальную проверку:

test ! -e .labex/gateway-token && echo "local gateway token removed"

Сообщение подтверждает отсутствие копии на виртуальной машине. Кнопка Check в LabEx независимо повторяет проверки локального файла и выхода Wrangler; её серверный скрипт намеренно не входит в проект учащегося.

Теперь вы удалили шлюз, удалили токен вызывающей стороны и управления, стёрли локальную копию токена и отключили новую виртуальную машину. После завершения лабораторной работы LabEx уничтожит эту временную виртуальную машину, а не сохранит её. Однако очистка облачных ресурсов по-прежнему важна: одно только уничтожение виртуальной машины не отзывает токен Cloudflare и не удаляет шлюз.

Итоги

Вы создали аутентифицированный Cloudflare AI Gateway и направили через него настоящий запрос инференса Workers AI. Вы отделили авторизацию шлюза от авторизации вышестоящей модели, изменили только одни учётные данные, чтобы определить отклоняющую запрос границу, и связали успешный запрос с журналом шлюза. В конце вы подтвердили удаление ресурса через аутентифицированный запрос, затем удалили токен и вышли из системы на виртуальной машине.

Следующая лабораторная работа продолжает изучение этого наблюдаемого пути запроса. Вы добавите небольшие метаданные, не содержащие секретов, проследите за намеренно вызванной ошибкой и будете использовать данные шлюза, а не предположения, чтобы определить место сбоя запроса.