Workers AI: LLM 推論およびビジョン モデルをエッジで直接使用
Workers AI の推論リクエストは前年比 4000% 増加 2026 年第 1 四半期まで。テキスト、ビジョン、音声モデルを直接実行する方法を学びます。 専用の GPU を使用せずにワーカー内で動作し、レイテンシは 200 ミリ秒未満です。
AI 推論がエッジに移行
2023 年まで、大規模言語モデルの実行はほぼ必須でした 外部 API (OpenAI、Anthropic、Google) と通信するか、高価な GPU を導入する 専用のインフラストラクチャ上で。これらの集中エンドポイントへのネットワーク遅延 各リクエストに 200 ~ 800 ミリ秒が追加され、GPU コストが法外に高くなりました。 少量のアプリケーション。
ワーカーAI がこのシナリオを変更しました。 Cloudflareが配布しています 世界中の数十のデータセンターにある AI 推論ハードウェア (専用 GPU)。 モデルはワーカーを実行するのと同じハードウェア上で実行されるため、 外部プロバイダーへのネットワークの往復。結果はレイテンシ推論です 削減され、インフラストラクチャ管理なしで消費量に応じて請求されます。
何を学ぶか
- Workers AI で利用可能なテンプレート: LLM、ビジョン、スピーチ、埋め込み
- Llama 3.1 によるテキスト生成と応答ストリーミング
- 視覚モデル: LLaVA による画像解析
- ウィスパーによる音声テキスト変換
- セマンティック検索のためのテキスト埋め込み
- AI ゲートウェイ: AI リクエストのキャッシュ、レート制限、可観測性
- 制限、コスト、最適化戦略
利用可能なモデルの概要
Workers AI は、推論用に最適化されたオープンソース モデルのセレクションを提供します
Cloudflareハードウェア上で。モデルは接頭辞で示されます
@cf/ o @hf/ (ハグフェイス主催):
| カテゴリ | 主要機種 | 使用事例 |
|---|---|---|
| テキストの生成 | @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 | 10,000 NU/日無料 | $0.011 / 1K NU | ~500ms-2s (トークンによって異なります) |
| ラマ 3.3 70B FP8 | 有料プランに含まれる | $0.050 / 1K NU | ~1~5秒 |
| ささやき | 10,000 NU/日無料 | $0.011 / 1K NU | 音声は 1 分あたり約 1 ~ 3 秒 |
| BGE エンベディング | 10,000 NU/日無料 | $0.011 / 1K NU | ~50~200ミリ秒 |
| 安定拡散XL | 10,000 NU/日無料 | $0.020/画像 | ~3~10秒 |
タイムアウトとCPU制限
Worker 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 + Vectorize + Durable Objects (管理用) の組み合わせ 会話履歴)を使用して、完全な AI アシスタントを完全に構築できます Cloudflareプラットフォーム上で、外部依存関係なし。
シリーズの次の記事
- 第6条: Vercel Edge ランタイム — アドバンスト ミドルウェア、 地理位置情報と A/B テスト: Vercel が Next.js でエッジ ランタイムを使用する方法 カスタマイズと機能フラグ用。
- 第7条: エッジでの地理的ルーティング — パーソナライゼーション GDPR コンテンツとコンプライアンス: メイン サーバーに触れることなく、地理ベースのロジックを構築します。
- 第8条: CloudflareのキャッシュAPIと無効化戦略 ワーカー: TTL、再検証中の失効、およびキーごとのパージを備えたプログラム可能な CDN。







