Inferența AI mutată la margine

Până în 2023, rularea unui model de limbă mare era aproape obligatorie comunicați cu un API extern (OpenAI, Anthropic, Google) sau implementați GPU-uri scumpe pe infrastructura dedicată. Latența rețelei la aceste puncte finale centralizate a adăugat 200-800 ms la fiecare solicitare, iar costurile GPU erau prohibitive pentru aplicații cu volum redus.

AI muncitori a schimbat acest scenariu. Cloudflare a distribuit Hardware de inferență AI (GPU-uri specializate) în zeci de centre de date din întreaga lume. Modelele rulează pe același hardware care rulează Worker-ul dvs., eliminând rețea dus-întors către furnizorii externi. Rezultatul este inferența latenței redus, facturat la consum fără administrarea infrastructurii.

Ce vei învăța

  • Șabloane disponibile în Workers AI: LLM, viziune, vorbire, încorporare
  • Generare de text cu Llama 3.1 și streaming de răspuns
  • Modele de vedere: analiza imaginii cu LLaVA
  • Vorbire în text cu Whisper
  • Încorporarea textului pentru căutare semantică
  • AI Gateway: stocarea în cache, limitarea ratei și observabilitatea solicitărilor AI
  • Limite, costuri și strategii de optimizare

Prezentare generală a modelelor disponibile

Workers AI oferă o selecție de modele open-source optimizate pentru inferență pe hardware-ul Cloudflare. Modelele sunt indicate cu prefix @cf/ o @hf/ (Hugging Face găzduit):

Categorie Principalele modele Caz de utilizare
Generarea textului @cf/meta/llama-3.1-8b-instruct, @cf/mistral/mistral-7b-instruct-v0.2 Chatbot, rezumat, întrebări și răspunsuri, generare de cod
Generare text (mare) @cf/meta/llama-3.3-70b-instruct-fp8-fast Raționament complex, analiză avansată
Viziune @cf/llava-hf/llava-1.5-7b-hf, @cf/unum/uform-gen2-qwen-500m Subtitrări, întrebări vizuale și răspunsuri
Vorbire în text @cf/openai/whisper, @cf/openai/whisper-large-v3-turbo Transcriere audio
Încorporarea textului @cf/baai/bge-base-en-v1.5, @cf/baai/bge-large-en-v1.5 Căutare semantică, similaritate, RAG
Clasificarea imaginilor @cf/microsoft/resnet-50 Clasificarea imaginilor
Traducere @cf/meta/m2m100-1.2b Traducerea în peste 100 de limbi
Generare de imagini @cf/stabilityai/stable-diffusion-xl-base-1.0 Text-to-image

Configurație: AI Binding în wrangler.toml

Pentru a utiliza Workers AI, trebuie doar să adăugați legarea [ai] in configuratie:

# wrangler.toml
name = "ai-worker"
main = "src/worker.ts"
compatibility_date = "2024-09-23"

# Binding per Workers AI
[ai]
binding = "AI"

Tipul TypeScript al legării trebuie declarat în interfață Env:

// types.ts - dichiarazione del binding AI
interface Env {
  AI: Ai; // Tipo fornito da @cloudflare/workers-types
}

Generarea textului cu Llama 3.1

Cel mai frecvent caz de utilizare este generarea de text cu un șablon instrucțiune-răspuns. Să vedem cum să implementăm un punct final de chat:

// src/worker.ts - endpoint di chat con Llama 3.1

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (request.method !== 'POST' || new URL(request.url).pathname !== '/chat') {
      return new Response('POST /chat required', { status: 400 });
    }

    const { messages, stream = false } = await request.json<ChatRequest>();

    // Valida l'input
    if (!Array.isArray(messages) || messages.length === 0) {
      return Response.json({ error: 'messages array required' }, { status: 400 });
    }

    if (stream) {
      // Streaming response: il modello restituisce token man mano
      const aiStream = await env.AI.run('@cf/meta/llama-3.1-8b-instruct', {
        messages,
        stream: true,
        max_tokens: 1024,
        temperature: 0.7,
      });

      // Trasforma lo stream AI in Server-Sent Events
      return new Response(aiStream, {
        headers: {
          'Content-Type': 'text/event-stream',
          'Cache-Control': 'no-cache',
          'Connection': 'keep-alive',
        },
      });
    }

    // Risposta sincrona: attende il completion completo
    const result = await env.AI.run('@cf/meta/llama-3.1-8b-instruct', {
      messages,
      max_tokens: 1024,
      temperature: 0.7,
    });

    return Response.json({
      response: (result as AiTextGenerationOutput).response,
      usage: {
        // Workers AI non espone ancora i token counts nella risposta base
        model: '@cf/meta/llama-3.1-8b-instruct',
      },
    });
  },
};

interface ChatRequest {
  messages: Array<{ role: 'system' | 'user' | 'assistant'; content: string }>;
  stream?: boolean;
}

interface Env {
  AI: Ai;
}

Un exemplu mai complet care implementează un asistent prompt de sistem și gestionarea robustă a erorilor:

// src/assistant-worker.ts

const SYSTEM_PROMPT = `Sei un assistente tecnico esperto in cloud computing e edge computing.
Rispondi in modo conciso e tecnico. Se non conosci la risposta, dillo chiaramente.
Non inventare informazioni. Rispondi sempre in italiano a meno che l'utente non scriva in un'altra lingua.`;

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (request.method !== 'POST') {
      return new Response('Method Not Allowed', { status: 405 });
    }

    let body: AssistantRequest;
    try {
      body = await request.json<AssistantRequest>();
    } catch {
      return Response.json({ error: 'Invalid JSON body' }, { status: 400 });
    }

    if (!body.question?.trim()) {
      return Response.json({ error: 'question field is required' }, { status: 400 });
    }

    try {
      const result = await env.AI.run('@cf/meta/llama-3.1-8b-instruct', {
        messages: [
          { role: 'system', content: SYSTEM_PROMPT },
          { role: 'user', content: body.question },
        ],
        max_tokens: 2048,
        temperature: 0.3, // Bassa temperatura per risposte piu deterministiche
      }) as AiTextGenerationOutput;

      return Response.json({
        answer: result.response,
        model: '@cf/meta/llama-3.1-8b-instruct',
        timestamp: new Date().toISOString(),
      });
    } catch (err) {
      console.error('AI inference error:', err);
      return Response.json(
        { error: 'AI inference failed', details: (err as Error).message },
        { status: 500 }
      );
    }
  },
};

interface AssistantRequest {
  question: string;
}

interface Env {
  AI: Ai;
}

Modele de vedere: Analiza imaginii

Modelele de vedere vă permit să analizați imaginile de intrare împreună cu o întrebare textuală. Acest lucru este util pentru moderarea conținutului, extragerea de informații din documente scanate și caracteristici de accesibilitate:

// src/vision-worker.ts - analisi immagini con LLaVA

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (request.method !== 'POST') {
      return new Response('Method Not Allowed', { status: 405 });
    }

    // Accetta immagine come Base64 o URL
    const body = await request.json<VisionRequest>();

    let imageData: number[];

    if (body.imageUrl) {
      // Scarica l'immagine e converti in array di byte
      const imgResponse = await fetch(body.imageUrl);
      if (!imgResponse.ok) {
        return Response.json({ error: 'Failed to fetch image' }, { status: 400 });
      }
      const buffer = await imgResponse.arrayBuffer();
      imageData = Array.from(new Uint8Array(buffer));
    } else if (body.imageBase64) {
      // Decodifica Base64
      const binaryString = atob(body.imageBase64);
      imageData = Array.from({ length: binaryString.length }, (_, i) =>
        binaryString.charCodeAt(i)
      );
    } else {
      return Response.json({ error: 'imageUrl or imageBase64 required' }, { status: 400 });
    }

    const prompt = body.prompt ?? 'Descrivi questa immagine in dettaglio in italiano.';

    const result = await env.AI.run('@cf/llava-hf/llava-1.5-7b-hf', {
      image: imageData,
      prompt,
      max_tokens: 512,
    }) as AiTextGenerationOutput;

    return Response.json({
      description: result.response,
      prompt,
      model: '@cf/llava-hf/llava-1.5-7b-hf',
    });
  },
};

interface VisionRequest {
  imageUrl?: string;
  imageBase64?: string;
  prompt?: string;
}

interface Env {
  AI: Ai;
}

Vorbire în text cu șoaptă

Workers AI include Whisper pentru transcrierea audio. Modelul acceptă audio în format ArrayBuffer și returnează transcrierea marcată de timp optional:

// src/transcribe-worker.ts - Speech-to-text con Whisper

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (request.method !== 'POST') {
      return new Response('Method Not Allowed', { status: 405 });
    }

    const contentType = request.headers.get('Content-Type') ?? '';

    // Accetta audio come multipart/form-data o application/octet-stream
    let audioBuffer: ArrayBuffer;

    if (contentType.includes('multipart/form-data')) {
      const formData = await request.formData();
      const audioFile = formData.get('audio') as File | null;
      if (!audioFile) {
        return Response.json({ error: 'audio field required in form data' }, { status: 400 });
      }
      audioBuffer = await audioFile.arrayBuffer();
    } else {
      // Raw binary audio
      audioBuffer = await request.arrayBuffer();
    }

    if (audioBuffer.byteLength === 0) {
      return Response.json({ error: 'Empty audio data' }, { status: 400 });
    }

    // Limita a 25MB (limite Whisper)
    if (audioBuffer.byteLength > 25 * 1024 * 1024) {
      return Response.json({ error: 'Audio file too large (max 25MB)' }, { status: 413 });
    }

    const result = await env.AI.run('@cf/openai/whisper', {
      audio: Array.from(new Uint8Array(audioBuffer)),
    }) as AiSpeechRecognitionOutput;

    return Response.json({
      text: result.text,
      wordCount: result.text.split(/\s+/).filter(Boolean).length,
      model: '@cf/openai/whisper',
    });
  },
};

interface Env {
  AI: Ai;
}

Încorporarea textului pentru căutarea semantică

Înglobările sunt vectori numerici care reprezintă semnificația semantică a unui text. Workers AI include modele BGE optimizate pentru căutare semantică. Combinate cu Vectorize (baza de date vectorială a Cloudflare), acestea vă permit să construiți Conducta RAG complet la margine:

// src/embedding-worker.ts - generazione embeddings + ricerca semantica

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    if (url.pathname === '/embed' && request.method === 'POST') {
      const { texts } = await request.json<EmbedRequest>();

      if (!Array.isArray(texts) || texts.length === 0) {
        return Response.json({ error: 'texts array required' }, { status: 400 });
      }

      // BGE genera embedding di 768 dimensioni (base) o 1024 (large)
      const result = await env.AI.run('@cf/baai/bge-base-en-v1.5', {
        text: texts,
      }) as AiTextEmbeddingsOutput;

      return Response.json({
        embeddings: result.data,
        dimensions: result.data[0]?.length ?? 0,
        count: result.data.length,
        model: '@cf/baai/bge-base-en-v1.5',
      });
    }

    if (url.pathname === '/search' && request.method === 'POST') {
      const { query, topK = 5 } = await request.json<SearchRequest>();

      // 1. Genera l'embedding per la query
      const queryEmbed = await env.AI.run('@cf/baai/bge-base-en-v1.5', {
        text: [query],
      }) as AiTextEmbeddingsOutput;

      // 2. Ricerca semantica su Vectorize
      const results = await env.VECTORIZE.query(queryEmbed.data[0], {
        topK,
        returnMetadata: 'all',
      });

      return Response.json({
        query,
        results: results.matches.map((match) => ({
          id: match.id,
          score: match.score,
          metadata: match.metadata,
        })),
      });
    }

    return new Response('Not Found', { status: 404 });
  },
};

interface EmbedRequest {
  texts: string[];
}

interface SearchRequest {
  query: string;
  topK?: number;
}

interface Env {
  AI: Ai;
  VECTORIZE: VectorizeIndex;
}

AI Gateway: observabilitate și stocare în cache

Cloudflare AI Gateway este un proxy transparent care da poziții înaintea apelurilor AI (atât Workers AI, cât și furnizori externi, cum ar fi OpenAI). Adaugă memorarea în cache semantică, limitarea ratei, înregistrarea în jurnal și alternativă automată:

// src/worker-with-gateway.ts - Workers AI via AI Gateway

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const { prompt } = await request.json<{ prompt: string }>();

    // Usa il gateway invece del binding diretto
    // Il gateway aggiunge: caching, retry, logging, rate limiting
    const response = await fetch(
      `https://gateway.ai.cloudflare.com/v1/${env.CF_ACCOUNT_ID}/${env.AI_GATEWAY_ID}/workers-ai/@cf/meta/llama-3.1-8b-instruct`,
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${env.CF_API_TOKEN}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          messages: [{ role: 'user', content: prompt }],
          max_tokens: 512,
        }),
      }
    );

    if (!response.ok) {
      const error = await response.text();
      return Response.json({ error }, { status: response.status });
    }

    const result = await response.json();
    return Response.json(result);
  },
};

interface Env {
  AI: Ai;
  CF_ACCOUNT_ID: string;
  CF_API_TOKEN: string;
  AI_GATEWAY_ID: string;
}

Alternativ, puteți configura gateway-ul direct în legarea AI a wrangler.toml:

# wrangler.toml con AI Gateway
[ai]
binding = "AI"
# Il gateway viene usato automaticamente per tutte le chiamate
# Configurato nella dashboard Cloudflare

Limitări și considerații de cost

Model Liber (unități neuronale) Plătit ($ per 1.000 neuroni) Latența tipică
Lama 3.1 8B 10K NU/zi gratuit 0,011 USD / 1K NU ~500ms-2s (depinde de jetoane)
Lama 3.3 70B FP8 Inclus în planul plătit 0,050 USD / 1K NU ~1-5s
Şoaptă 10K NU/zi gratuit 0,011 USD / 1K NU ~1-3s pe minut de sunet
înglobări BGE 10K NU/zi gratuit 0,011 USD / 1K NU ~50-200 ms
Difuziune stabilă XL 10K NU/zi gratuit 0,020 USD / imagine ~3-10s

Timeout-uri și limite CPU

Workers AI operează în afara bugetului normal al CPU al lucrătorului (plan plătit de 30 de ani). Cu toate acestea, modelele mari precum Llama 70B pot dura 5-15 secunde raspunde. În aceste cazuri, asigurați-vă că utilizați streaming pentru a returna token-urile pe măsură ce mergeți și pentru a nu depăși timpul de expirare HTTP al clientului. Pentru concluzii lungi, luați în considerare utilizarea unei cozi de așteptare (Workers Queues) și notificați clientul când a terminat.

Model de producție: RAG at the Edge

Un model din ce în ce mai comun este cel RAG (Recuperare-Augmented Generation) complet la margine: Vectorize pentru recuperare, Workers AI pentru încorporare și generare.

// src/rag-worker.ts - RAG completo all'edge

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (request.method !== 'POST') return new Response('POST only', { status: 405 });

    const { question } = await request.json<{ question: string }>();

    // Step 1: Genera l'embedding della domanda
    const queryEmbedding = await env.AI.run('@cf/baai/bge-base-en-v1.5', {
      text: [question],
    }) as AiTextEmbeddingsOutput;

    // Step 2: Recupera i chunk rilevanti dal vector store
    const relevant = await env.DOCS.query(queryEmbedding.data[0], {
      topK: 3,
      returnMetadata: 'all',
    });

    // Step 3: Costruisce il contesto dai chunk recuperati
    const context = relevant.matches
      .map((m) => m.metadata?.['text'] as string ?? '')
      .filter(Boolean)
      .join('\n\n---\n\n');

    // Step 4: Genera la risposta con il contesto
    const answer = await env.AI.run('@cf/meta/llama-3.1-8b-instruct', {
      messages: [
        {
          role: 'system',
          content: `Rispondi alla domanda basandoti SOLO sul contesto fornito.
Se il contesto non contiene informazioni sufficienti, dillo esplicitamente.
Contesto:
${context}`,
        },
        { role: 'user', content: question },
      ],
      max_tokens: 1024,
      temperature: 0.1,
    }) as AiTextGenerationOutput;

    return Response.json({
      question,
      answer: answer.response,
      sources: relevant.matches.map((m) => ({
        id: m.id,
        score: m.score,
        title: m.metadata?.['title'],
      })),
    });
  },
};

interface Env {
  AI: Ai;
  DOCS: VectorizeIndex;
}

Concluzii și pașii următori

Workers AI reprezintă o schimbare de paradigmă în accesarea inferenței AI: fără GPU de gestionat, fără furnizori externi de integrat, facturare pentru consum cu tier gratuit generos. Creșterea de 4000% față de T1 2026 reflectă adoptare rapidă de către dezvoltatorii care caută o cale mai simplă spre AI în produsele lor.

Combinația Workers AI + Vectorize + Durable Objects (pentru gestionarea istoricul conversațiilor) vă permite să construiți asistenți AI completi pe platforma Cloudflare, fără dependențe externe.

Următoarele articole din serie

  • Articolul 6: Vercel Edge Runtime — Advanced Middleware, Geolocalizare și testare A/B: cum folosește Vercel edge runtime cu Next.js pentru personalizare și steaguri de caracteristici.
  • Articolul 7: Rutarea geografică la margine — Personalizare Conținut și conformitate GDPR: construiți o logică bazată pe geometrie fără a atinge serverul principal.
  • Articolul 8: API-ul cache și strategiile de invalidare în Cloudflare Lucrători: CDN programabil cu TTL, învechit în timp ce se revalidează și curățare per-cheie.