AI 추론이 엣지로 옮겨졌습니다.

2023년까지는 대규모 언어 모델 실행이 거의 필수였습니다. 외부 API(OpenAI, Anthropic, Google)와 통신하거나 값비싼 GPU를 배포합니다. 전용 인프라에서. 이러한 중앙 집중식 엔드포인트에 대한 네트워크 대기 시간 각 요청에 200~800ms를 추가했으며 GPU 비용은 엄청나게 높았습니다. 저용량 애플리케이션.

노동자 AI 이 시나리오를 변경했습니다. Cloudflare가 배포한 전 세계 수십 개의 데이터 센터에 있는 AI 추론 하드웨어(특수 GPU). 모델은 작업자를 실행하는 동일한 하드웨어에서 실행되므로 외부 공급자에 대한 네트워크 왕복. 결과는 지연 시간 추론입니다. 인프라 관리 없이 소비량에 따라 비용이 절감됩니다.

무엇을 배울 것인가

  • Workers AI에서 사용 가능한 템플릿: LLM, 비전, 음성, 임베딩
  • Llama 3.1 및 응답 스트리밍을 사용한 텍스트 생성
  • 비전 모델: LLaVA를 사용한 이미지 분석
  • 속삭임으로 음성을 문자로 변환
  • 의미론적 검색을 위한 텍스트 임베딩
  • AI 게이트웨이: AI 요청의 캐싱, 속도 제한 및 관찰 가능성
  • 한계, 비용 및 최적화 전략

사용 가능한 모델 개요

Workers AI는 추론에 최적화된 다양한 오픈 소스 모델을 제공합니다. Cloudflare 하드웨어에서. 모델은 접두어로 표시됩니다. @cf/ o @hf/ (Hugging Face 호스팅):

범주 주요 모델 사용 사례
텍스트 생성 @cf/meta/llama-3.1-8b-instruct, @cf/mistral/mistral-7b-instruct-v0.2 챗봇, 요약, Q&A, 코드생성
텍스트 생성(대형) @cf/meta/llama-3.3-70b-instruct-fp8-fast 복잡한 추론, 고급 분석
비전 @cf/llava-hf/llava-1.5-7b-hf, @cf/unum/uform-gen2-qwen-500m 이미지 캡션, 시각적 Q&A
음성-텍스트 @cf/openai/whisper, @cf/openai/whisper-large-v3-turbo 오디오 전사
텍스트 임베딩 @cf/baai/bge-base-en-v1.5, @cf/baai/bge-large-en-v1.5 의미 검색, 유사성, RAG
이미지 분류 @cf/microsoft/resnet-50 이미지 분류
번역 @cf/meta/m2m100-1.2b 100개 이상의 언어 번역
이미지 생성 @cf/stabilityai/stable-diffusion-xl-base-1.0 텍스트를 이미지로

구성: wrangler.toml의 AI 바인딩

Workers AI를 사용하려면 바인딩을 추가하기만 하면 됩니다. [ai] 구성에서:

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

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

바인딩의 TypeScript 유형은 인터페이스에서 선언되어야 합니다. Env:

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

Llama 3.1을 사용한 텍스트 생성

가장 일반적인 사용 사례는 템플릿을 사용하여 텍스트를 생성하는 것입니다. 지시-응답. 채팅 엔드포인트를 구현하는 방법을 살펴보겠습니다.

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

시스템 프롬프트 도우미를 구현하는 보다 완전한 예 강력한 오류 처리:

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

비전 모델: 이미지 분석

비전 모델을 사용하면 질문과 함께 입력 이미지를 분석할 수 있습니다. 텍스트. 이는 콘텐츠 조정, 정보 추출에 유용합니다. 스캔한 문서 및 접근성 기능에서:

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

속삭임으로 음성을 텍스트로 변환

Workers AI에는 오디오 전사를 위한 Whisper가 포함되어 있습니다. 모델이 오디오를 수신합니다. 형식으로 ArrayBuffer 그리고 타임스탬프가 붙은 성적표를 반환합니다. 선택사항:

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

의미론적 검색을 위한 텍스트 임베딩

임베딩은 의미론적 의미를 나타내는 숫자 벡터입니다. 텍스트의. Workers AI에는 의미 검색에 최적화된 BGE 모델이 포함되어 있습니다. Vectorize(Cloudflare의 벡터 데이터베이스)와 결합하면 다음을 구축할 수 있습니다. RAG 파이프라인이 완전히 가장자리에 있습니다.

// 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 게이트웨이: 관찰 가능성 및 캐싱

Cloudflare AI 게이트웨이 그렇습니다. 이는 투명한 프록시입니다. AI 호출보다 앞서 위치합니다(Workers AI 및 OpenAI와 같은 외부 제공자 모두). 의미론적 캐싱, 속도 제한, 로깅 및 자동 폴백을 추가합니다.

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

또는 wrangler.toml의 AI 바인딩에서 직접 게이트웨이를 구성할 수 있습니다.

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

제한 사항 및 비용 고려 사항

모델 무료(뉴런 단위) 유료(뉴런 1,000개당 $) 일반적인 대기 시간
라마 3.1 8B 10K NU/일 무료 $0.011 / 1K 누 ~500ms-2s(토큰에 따라 다름)
라마 3.3 70B FP8 유료 요금제에 포함됨 $0.050 / 1K 누 ~1~5초
속삭임 10K NU/일 무료 $0.011 / 1K 누 오디오 분당 ~1~3초
BGE 임베딩 10K NU/일 무료 $0.011 / 1K 누 ~50-200ms
안정확산XL 10K NU/일 무료 $0.020 / 이미지 ~3~10초

시간 초과 및 CPU 제한

Workers AI는 Worker의 일반 CPU 예산(30초 유료 요금제) 밖에서 작동합니다. 그러나 Llama 70B와 같은 대형 모델은 5~15초 정도 소요될 수 있습니다. 응답하십시오. 이러한 경우에는 반드시 다음을 사용하십시오. 스트리밍 진행하면서 토큰을 반환하고 클라이언트의 HTTP 시간 초과를 초과하지 않습니다. 긴 추론의 경우 대기열(작업자 대기열) 사용을 고려하고 알림 완료되면 클라이언트.

생산 패턴: RAG at the Edge

점점 더 일반적인 패턴은 다음과 같습니다. RAG(검색 증강 생성) 완전히 가장자리에 있습니다. 검색을 위한 벡터화, 삽입 및 생성을 위한 Workers AI.

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

결론 및 다음 단계

Workers AI는 AI 추론에 액세스하는 패러다임의 변화를 나타냅니다. 관리할 GPU 없음, 통합할 외부 공급자 없음, 청구 넉넉한 무료 등급으로 소비할 수 있습니다. 2026년 1분기까지 전년 동기 대비 4000% 성장 반영 더 간단한 경로를 찾는 개발자의 빠른 채택 자사 제품에서 AI를 지향합니다.

Workers AI + 벡터화 + 지속성 객체 조합(관리용) 대화 기록)을 사용하면 완전한 AI 도우미를 완전히 구축할 수 있습니다. 외부 종속성 없이 Cloudflare 플랫폼에서.

시리즈의 다음 기사

  • 제6조: Vercel Edge 런타임 — 고급 미들웨어, 위치정보 및 A/B 테스트: Vercel이 Next.js와 함께 엣지 런타임을 사용하는 방법 사용자 정의 및 기능 플래그용.
  • 제7조: 엣지에서의 지리적 라우팅 — 개인화 GDPR 콘텐츠 및 규정 준수: 기본 서버를 건드리지 않고 지역 기반 논리를 구축합니다.
  • 제8조: Cloudflare의 캐시 API 및 무효화 전략 작업자: TTL, 유효하지 않은 재검증 및 키별 제거 기능을 갖춘 프로그래밍 가능한 CDN입니다.