Wnioskowanie AI przeniesione na krawędź

Do 2023 r. prowadzenie modelu wielkojęzykowego było niemal obowiązkowe komunikować się z zewnętrznym API (OpenAI, Anthropic, Google) lub wdrażać drogie procesory graficzne na dedykowanej infrastrukturze. Opóźnienie sieci dla tych scentralizowanych punktów końcowych dodawał 200–800 ms do każdego żądania, a koszty procesora graficznego były zaporowe aplikacje o małej objętości.

AI pracowników zmienił ten scenariusz. Cloudflare został rozprowadzony Sprzęt do wnioskowania AI (wyspecjalizowane procesory graficzne) w dziesiątkach centrów danych na całym świecie. Modele działają na tym samym sprzęcie, na którym działa Twój Robotnik, co eliminuje objazd sieci do dostawców zewnętrznych. Rezultatem jest wnioskowanie o opóźnieniu obniżone, rozliczane według zużycia bez zarządzania infrastrukturą.

Czego się nauczysz

  • Szablony dostępne w Workers AI: LLM, wizja, mowa, osadzanie
  • Generowanie tekstu za pomocą Lamy 3.1 i przesyłanie strumieniowe odpowiedzi
  • Modele widzenia: analiza obrazu za pomocą LLaVA
  • Zamiana mowy na tekst za pomocą funkcji Whisper
  • Osadzanie tekstu na potrzeby wyszukiwania semantycznego
  • AI Gateway: buforowanie, ograniczanie szybkości i obserwowalność żądań AI
  • Limity, koszty i strategie optymalizacji

Przegląd dostępnych modeli

Workers AI oferuje wybór modeli typu open source zoptymalizowanych pod kątem wnioskowania na sprzęcie Cloudflare. Modele są oznaczone przedrostkiem @cf/ o @hf/ (gospodarz „Przytulona twarz”):

Kategoria Główne modele Użyj przypadku
Generacja tekstu @cf/meta/llama-3.1-8b-instruct, @cf/mistral/mistral-7b-instruct-v0.2 Chatbot, podsumowanie, pytania i odpowiedzi, generowanie kodu
Generowanie tekstu (duże) @cf/meta/llama-3.3-70b-instruct-fp8-fast Złożone rozumowanie, zaawansowana analiza
Wizja @cf/llava-hf/llava-1.5-7b-hf, @cf/unum/uform-gen2-qwen-500m Podpisy do zdjęć, wizualne pytania i odpowiedzi
Zamiana mowy na tekst @cf/openai/whisper, @cf/openai/whisper-large-v3-turbo Transkrypcja dźwięku
Osadzanie tekstu @cf/baai/bge-base-en-v1.5, @cf/baai/bge-large-en-v1.5 Wyszukiwanie semantyczne, podobieństwo, RAG
Klasyfikacja obrazu @cf/microsoft/resnet-50 Klasyfikacja obrazu
Tłumaczenie @cf/meta/m2m100-1.2b Tłumaczenie na ponad 100 języków
Generowanie obrazu @cf/stabilityai/stable-diffusion-xl-base-1.0 Tekst do obrazu

Konfiguracja: Powiązanie AI w pliku wrangler.toml

Aby korzystać z Workers AI, po prostu dodaj powiązanie [ai] w konfiguracji:

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

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

Typ powiązania TypeScript musi być zadeklarowany w interfejsie Env:

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

Generowanie tekstu za pomocą Lamy 3.1

Najczęstszym przypadkiem użycia jest generowanie tekstu za pomocą szablonu instrukcja-odpowiedź. Zobaczmy, jak zaimplementować punkt końcowy czatu:

// 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;
}

Bardziej kompletny przykład implementujący asystenta podpowiedzi systemowych i solidna obsługa błędów:

// 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 wizji: analiza obrazu

Modele wizyjne pozwalają na analizę obrazów wejściowych wraz z pytaniem tekstowy. Jest to przydatne do moderowania treści i wydobywania informacji ze zeskanowanych dokumentów i funkcji ułatwień dostępu:

// 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;
}

Zamiana mowy na tekst za pomocą szeptu

Workers AI zawiera Whisper do transkrypcji dźwięku. Model akceptuje dźwięk w formacie ArrayBuffer i zwraca transkrypcję ze znacznikiem czasu opcjonalnie:

// 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;
}

Osadzanie tekstu w wyszukiwaniu semantycznym

Osadzenia to wektory numeryczne reprezentujące znaczenie semantyczne tekstu. Workers AI obejmuje modele BGE zoptymalizowane pod kątem wyszukiwania semantycznego. W połączeniu z Vectorize (baza danych wektorów Cloudflare) umożliwiają budowanie Rurociąg RAG całkowicie na krawędzi:

// 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;
}

Brama AI: obserwowalność i buforowanie

Brama Cloudflare AI jest to przejrzyste potwierdzenie, że tak pozycje przed wezwaniami AI (zarówno Workers AI, jak i dostawcy zewnętrzni, tacy jak OpenAI). Dodaje buforowanie semantyczne, ograniczanie szybkości, rejestrowanie i automatyczne przywracanie:

// 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;
}

Alternatywnie możesz skonfigurować bramkę bezpośrednio w powiązaniu AI pliku wrangler.toml:

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

Ograniczenia i względy kosztowe

Model Bezpłatnie (jednostki neuronowe) Płatne ($ za 1 tys. neuronów) Typowe opóźnienie
Lama 3.1 8B 10 tys. NU/dzień za darmo 0,011 USD / 1 tys. NU ~500ms-2s (w zależności od tokenów)
Lama 3.3 70B FP8 Zawarte w płatnym planie 0,050 USD / 1 tys. NU ~1-5 s
Szept 10 tys. NU/dzień za darmo 0,011 USD / 1 tys. NU ~1-3 s na minutę dźwięku
Osadzenia BGE 10 tys. NU/dzień za darmo 0,011 USD / 1 tys. NU ~50-200ms
Stabilna dyfuzja XL 10 tys. NU/dzień za darmo 0,020 USD / obraz ~3-10 s

Limity czasu i limity procesora

Workers AI działa poza normalnym budżetem procesora pracownika (płatny plan 30s). Jednak w przypadku dużych modeli, takich jak Llama 70B, może to zająć 5–15 sekund odpowiedzieć. W takich przypadkach pamiętaj o użyciu przesyłanie strumieniowe aby zwracać tokeny na bieżąco i nie przekraczać limitu czasu HTTP klienta. W przypadku długich wniosków rozważ użycie kolejki (kolejki robocze) i powiadom klienta po zakończeniu.

Schemat produkcji: RAG na krawędzi

Coraz powszechniejszym wzorem jest tzw RAG (generacja wspomagana odzyskiwaniem) całkowicie na krawędzi: wektoryzacja do pobierania, Workers AI do osadzania i generowania.

// 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;
}

Wnioski i dalsze kroki

Sztuczna inteligencja pracowników reprezentuje zmianę paradygmatu w dostępie do wnioskowań AI: brak procesora graficznego do zarządzania, brak zewnętrznych dostawców do integracji i rozliczenia do spożycia z hojnym bezpłatnym poziomem. Odzwierciedla to wzrost o 4000% r/r do I kwartału 2026 r szybkie przyjęcie przez programistów poszukujących prostszej ścieżki w stronę sztucznej inteligencji w swoich produktach.

Kombinacja Workers AI + Vectorize + Trwałe obiekty (do zarządzania historia rozmów) pozwala w całości zbudować kompletnych asystentów AI na platformie Cloudflare, bez zewnętrznych zależności.

Następne artykuły z serii

  • Artykuł 6: Vercel Edge Runtime — zaawansowane oprogramowanie pośredniczące, Geolokalizacja i testy A/B: jak Vercel wykorzystuje środowisko wykonawcze Edge w Next.js do dostosowywania i flag funkcyjnych.
  • Artykuł 7: Trasowanie geograficzne na krawędzi — personalizacja Treść i zgodność z RODO: Twórz logikę opartą na danych geograficznych bez dotykania głównego serwera.
  • Artykuł 8: Cache API i strategie unieważniania w Cloudflare Pracownicy: programowalny CDN z TTL, funkcją sprawdzania stanu aktualnego podczas ponownej weryfikacji i czyszczeniem poszczególnych kluczy.