Recuperación con un modelo alternativo

CloudflareBeginner
Practicar Ahora

Introducción

Un modelo de IA puede no estar disponible temporalmente, estar sobrecargado o recibir una entrada que no puede interpretar. Un modelo alternativo proporciona a la aplicación una opción planificada en lugar de devolver un error de inmediato. Una conmutación alternativa útil debe estar limitada: debe tener una lista breve y ordenada, además de un punto claro de detención. No debe reintentar indefinidamente ni llamar a todos los modelos después del primer resultado correcto.

Implementará un Cloudflare Worker pequeño con dos rutas. La ruta de recuperación envía deliberadamente una entrada con formato de chat a un modelo de embeddings, captura esa incompatibilidad predecible y después llama a un único modelo de chat. La ruta saludable llama a un modelo de chat principal compatible y se detiene. Cada intento pasa por el mismo AI Gateway, por lo que sus registros muestran qué modelo falló y qué modelo completó la solicitud.

Si accedió directamente a este curso, complete primero Conecte LabEx a su cuenta de Cloudflare. Este laboratorio le enseña a usar el terminal de LabEx, la autorización de dispositivos de Wrangler, la selección de la cuenta y el ID de cuenta. Complete también primero Enrute inferencias a través de un gateway, ya que este laboratorio se basa en sus conceptos de gateway y Workers AI.

El laboratorio utiliza modelos de Workers AI alojados por Cloudflare y el binding de Workers AI. No necesita Workers Paid, una clave de proveedor externo ni el Universal Endpoint obsoleto. El intento fallido controlado se rechaza antes de la inferencia, y cada ruta correcta genera solo una respuesta breve. Si la asignación diaria compartida de Workers AI no está disponible, deténgase en lugar de reintentar repetidamente.

La configuración instala Node.js 22.22.0 y Wrangler 4.132.0 local para el proyecto en /home/labex/project/ai-gateway-fallback. Prepara comprobaciones independientes, pero no autoriza Wrangler, crea recursos en la nube, implementa un Worker ni envía tráfico a los modelos. LabEx destruye la máquina virtual después del laboratorio; aun así, deberá eliminar el Worker remoto, el gateway y el token de API, porque destruir una máquina virtual no elimina los recursos de la nube.

Autorice la máquina virtual y asigne un nombre a la ruta de recuperación

Cada laboratorio comienza en una máquina virtual nueva. En este paso, autorizará a Wrangler para usar su cuenta de aprendizaje y guardará nombres únicos para un gateway, un Worker y un token temporal.

cd /home/labex/project/ai-gateway-fallback
npx wrangler --version
npx wrangler login --device --browser=false

Abra el enlace que se muestra, introduzca el código y autorice la cuenta de aprendizaje correcta. Wrangler solicita los permisos de implementación del Worker y de Workers AI que necesitará más adelante en este laboratorio; revise la cuenta mostrada antes de aprobar. Después, consulte los datos de identidad estructurados:

npx wrangler whoami --json

Compruebe que aparezcan Wrangler 4.132.0 y loggedIn: true. Sustituya YOUR_ACCOUNT_ID por el ID real de 32 caracteres que se muestra para la cuenta correcta:

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

Estos identificadores no son secretos. Guardarlos permite que las comprobaciones y la limpieza posteriores se dirijan únicamente a los recursos de este laboratorio.

Cree un AI Gateway observable

En este paso, creará el punto de control compartido que registrará ambas rutas de modelo.

Un AI Gateway es un punto de control con nombre entre una aplicación y las llamadas a modelos. Ofrece un único lugar para consultar registros y metadatos de varios intentos, incluso cuando la aplicación cambia de modelo.

Abra el Cloudflare Dashboard y seleccione AI → AI Gateway → Create a custom gateway. Use el gatewayId guardado. Mantenga activadas las opciones Collect Logs y Authenticated Gateway. Mantenga desactivados el almacenamiento en caché, los límites de velocidad, los reintentos y los límites de gasto, y mantenga la facturación de Workers AI en Standard. Después, cree el gateway.

El gateway guardado mantiene activados los registros y el acceso autenticado

Abra My Profile → API Tokens, seleccione Create Token → Create Custom Token y use el tokenName guardado. Añada los permisos de cuenta AI Gateway — Edit y AI Gateway — Run, limitados a la cuenta de aprendizaje correcta. Este token temporal permite que el laboratorio lea y elimine posteriormente únicamente su gateway; el binding de AI del Worker implementado no lo incluye.

Después de crear el token, copie únicamente el valor situado después de Bearer en el comando de verificación de un solo uso de Cloudflare y guárdelo mediante una entrada oculta:

bash -c '
while :; do
  read -ersp "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
'

Consulte únicamente las configuraciones importantes que no son secretas:

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" \
  > .labex/gateway.json
unset GATEWAY_TOKEN
node -e 'const g=require("./.labex/gateway.json").result; console.log({id:g.id,collect_logs:g.collect_logs,authentication:g.authentication})'

Deberá ver el ID del gateway guardado, con ambos valores establecidos en true.

Defina un Worker con dos intentos

En este paso, escribirá la política de recuperación en código normal de Worker. La política contiene dos llamadas explícitas en lugar de un bucle, por lo que su coste y latencia máximos son fáciles de identificar.

La primera llamada de la ruta de recuperación utiliza un modelo de embeddings. Los modelos de embeddings convierten texto en vectores numéricos; no aceptan messages de chat. Proporcionar una entrada con formato de chat crea un fallo de compatibilidad seguro y determinista antes de la inferencia. El bloque catch registra ese fallo y realiza una llamada a un modelo de chat compatible.

GATEWAY_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).gatewayId')
WORKER_NAME=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).workerName')
cat > wrangler.jsonc <<JSON
{
  "name": "$WORKER_NAME",
  "main": "src/index.js",
  "compatibility_date": "2026-09-17",
  "ai": { "binding": "AI" }
}
JSON
cat > src/index.js <<JS
export default {
  async fetch(request, env) {
    const healthy = new URL(request.url).pathname === "/healthy";
    const attempts = [];
    const gateway = {
      gateway: {
        id: "$GATEWAY_ID",
        metadata: {
          lab: "g05-fallback",
          mode: healthy ? "healthy" : "fallback",
          synthetic: true
        }
      }
    };

    if (!healthy) {
      try {
        await env.AI.run(
          "@cf/baai/bge-small-en-v1.5",
          { messages: [{ role: "user", content: "Reply with ROUTE OK" }] },
          gateway
        );
        attempts.push({ model: "@cf/baai/bge-small-en-v1.5", status: "unexpected-success" });
      } catch (error) {
        attempts.push({
          model: "@cf/baai/bge-small-en-v1.5",
          status: "failed",
          reason: String(error).slice(0, 180)
        });
      }
    }

    const selectedModel = healthy
      ? "@cf/meta/llama-3.3-70b-instruct-fp8-fast"
      : "@cf/meta/llama-3.2-3b-instruct";
    const result = await env.AI.run(
      selectedModel,
      { prompt: "Reply with exactly: ROUTE OK", max_tokens: 12 },
      gateway
    );
    attempts.push({ model: selectedModel, status: "succeeded" });

    return Response.json({
      mode: healthy ? "healthy-primary" : "fallback-recovery",
      usedFallback: !healthy,
      selectedModel,
      attempts,
      response: result.response
    });
  }
};
JS
npx wrangler deploy --dry-run

El binding de AI proporciona al Worker acceso directo a Workers AI. La opción gateway dirige cada llamada a través del gateway guardado y adjunta únicamente metadatos sintéticos; nunca incluye una instrucción, una credencial ni un identificador personal.

Implemente el Worker y pruebe la ruta de recuperación

En este paso, implementará el Worker y activará una vez el caso de recuperación controlada.

Implemente el Worker y guarde la salida de Wrangler para que la prueba utilice la URL exacta asignada a su cuenta:

npx wrangler deploy 2>&1 | tee .labex/deploy-output.txt
WORKER_URL=$(grep -Eo 'https://[^ ]+\.workers\.dev' .labex/deploy-output.txt | tail -1)
printf '%s\n' "$WORKER_URL" | tee .labex/worker-url.txt

La ruta workers.dev puede tardar unos segundos en propagarse después de una implementación correcta. Consulte la URL sin enviar tráfico adicional a los modelos: un 404 solo indica que la ruta perimetral aún no está lista, y el bucle se detiene en la primera respuesta 200.

WORKER_URL=$(cat .labex/worker-url.txt)
for attempt in $(seq 1 12); do
  STATUS=$(curl --http1.1 -sS -o .labex/fallback-response.json -w '%{http_code}' "$WORKER_URL/fallback")
  printf 'attempt %s: HTTP %s\n' "$attempt" "$STATUS"
  [ "$STATUS" = 200 ] && break
  [ "$attempt" -eq 12 ] && exit 1
  sleep 5
done
python3 -m json.tool < .labex/fallback-response.json

Deberá ver usedFallback: true, dos intentos, el modelo de embeddings marcado como failed y @cf/meta/llama-3.2-3b-instruct marcado como succeeded. No se evalúa exactamente el texto generado; se evalúa la decisión de enrutamiento.

Demuestre que un modelo principal saludable se detiene pronto

En este paso, demostrará que un modelo principal correcto evita una llamada alternativa innecesaria.

Una conmutación alternativa solo es correcta si no interviene cuando la ruta principal funciona. La ruta /healthy comienza con un modelo de chat compatible, por lo que debe producir un intento y detenerse.

WORKER_URL=$(cat .labex/worker-url.txt)
curl --http1.1 -fsS "$WORKER_URL/healthy" \
  | tee .labex/healthy-response.json \
  | python3 -m json.tool

Deberá ver usedFallback: false, @cf/meta/llama-3.3-70b-instruct-fp8-fast como selectedModel y exactamente un intento correcto. Este es el comportamiento de cortocircuito: el resultado correcto finaliza la ruta inmediatamente.

Lea la ruta en los registros del gateway

En este paso, relacionará los resultados JSON del Worker con evidencias independientes de AI Gateway.

La respuesta del Worker describe el comportamiento de la aplicación. Los registros de AI Gateway proporcionan evidencias independientes del lado del proveedor. Los registros pueden tardar unos segundos en aparecer, así que espere brevemente e imprima únicamente los campos relevantes para el enrutamiento:

sleep 8
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 rows=require('./.labex/logs.json').result||[];
const meta=row=>{try{return typeof row.metadata==='string'?JSON.parse(row.metadata):(row.metadata||{})}catch{return {}}};
console.table(rows.filter(row=>meta(row).lab==='g05-fallback').map(row=>({
  mode:meta(row).mode, model:row.model, success:row.success, status:row.status_code
})));
NODE

Abra la página Logs del gateway en el Dashboard. El grupo de recuperación debe contener una fila del modelo de embeddings fallida y una fila del modelo alternativo correcta. El grupo saludable debe contener únicamente el modelo de chat principal correcto.

Los registros del gateway muestran el intento principal fallido seguido del modelo alternativo correcto

La solicitud saludable contiene un único registro correcto del modelo principal

Los metadatos mode relacionan las filas sin incluir una instrucción ni un secreto. El modelo, el resultado correcto y el estado explican la ruta; el texto generado por sí solo no puede hacerlo.

Inspeccione el contrato de recuperación limitada

En este paso, comparará ambas rutas y determinará el número máximo de intentos de modelo.

Ahora dispone de tres formas de evidencia coherentes:

  • el código fuente contiene dos llamadas explícitas a env.AI.run() y ningún bucle de reintento;
  • /fallback informa de un fallo seguido de un resultado correcto;
  • /healthy informa de un resultado correcto y se detiene.

Muestre una comparación compacta a partir de las respuestas guardadas:

node - <<'NODE'
for (const name of ['fallback','healthy']) {
  const body=require(`./.labex/${name}-response.json`);
  console.log(name, {
    usedFallback: body.usedFallback,
    selectedModel: body.selectedModel,
    attemptCount: body.attempts.length,
    statuses: body.attempts.map(item=>item.status)
  });
}
NODE

El máximo es de dos intentos. Si la ruta alternativa también falla, el Worker devuelve un error en lugar de reiniciar la ruta. En una aplicación de producción podría añadir un tiempo de espera, un interruptor de circuito o un error fácil de entender para el usuario, pero cada mecanismo de recuperación adicional debe seguir estando limitado y ser observable de forma independiente.

Elimine los recursos temporales

En este paso, eliminará todos los recursos remotos que pertenezcan a este laboratorio y quitará la autorización local.

Elimine primero el Worker para que no pueda generar tráfico nuevo hacia el gateway. Después, elimine únicamente el gateway registrado en state.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')
WORKER_NAME=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).workerName')
GATEWAY_TOKEN=$(cat .labex/gateway-token)
npx wrangler delete --name "$WORKER_NAME" --force
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" \
  > .labex/delete-gateway.json
unset GATEWAY_TOKEN
node -p 'require("./.labex/delete-gateway.json").success'

Deberá aparecer true. Mientras las dos autorizaciones temporales sigan existiendo en esta máquina virtual, guarde evidencias independientes de la ausencia del Worker y del gateway:

set +e
npx wrangler deployments list --name "$WORKER_NAME" --json \
  > .labex/worker-after-delete.json 2> .labex/worker-absent.err
printf '%s\n' "$?" > .labex/worker-absent-status.txt
set -e
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
GATEWAY_ID="$GATEWAY_ID" node - <<'NODE'
const rows=require('./.labex/gateways-after-delete.json').result||[];
console.log('gateway absent:', !rows.some(row=>row.id===process.env.GATEWAY_ID));
NODE
grep -Ei '10090|10007|script_not_found|does not exist' .labex/worker-absent.err

Deberá ver gateway absent: true y una respuesta que indique la ausencia del Worker, como script_not_found, el código 10090, el código 10007 o does not exist. Wrangler puede utilizar distintas formas de error para el mismo script inexistente; los errores de red y autenticación no demuestran que la eliminación se haya realizado.

Ahora abra My Profile → API Tokens y elimine el tokenName guardado exacto. Por último, elimine su copia de la máquina virtual y cierre la sesión:

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

Deberá aparecer loggedIn: false. Eliminar posteriormente la máquina virtual borra los archivos locales, pero solo estos comandos eliminan los recursos remotos y revocan la autorización.

Resumen

Construyó una ruta de recuperación limitada con dos modelos alojados en Cloudflare. Un intento principal incompatible y controlado falló, un modelo alternativo recuperó la solicitud y un modelo principal saludable se detuvo después de una sola llamada. Los registros de AI Gateway relacionaron las decisiones de la aplicación con evidencias del modelo y del estado en el lado del proveedor. También aprendió por qué los límites explícitos de intentos, los metadatos seguros y una limpieza verificada forman parte de un diseño fiable de recuperación alternativa.