← Cloudflare Workflows / workflows / examples
Značení obrázků s lidským dohledem pomocí waitForEvent
Tento příklad ukazuje, jak použít waitForEvent() API v Cloudflare Workflows, kterým se do procesu zavede krok human-in-the-loop. Workflow se spustí nahráním obrázku, při kterém se metadata uloží do databáze D1. Workflow poté čeká na schválení uživatelem a po schválení pomocí Workers AI vygeneruje tagy obrázku, které se uloží do databáze. Přiložená frontendová aplikace Next.js usnadňuje nahrávání obrázků a proces schvalování.
Přehled Workflow
V tomto Workflow simulujeme scénář, kdy nahraný obrázek vyžaduje před zpracováním pomocí AI schválení člověkem. Obrázek se nahraje do R2 a Workflow poté provede následující kroky:
- Ukládá metadata obrázků do databáze D1.
- Pozastaví spuštění pomocí
waitForEvent()a čeká na externí událost odeslanou z frontendu Next.js, která signalizuje schválení nebo zamítnutí. - Pokud je schváleno, Workflow pomocí Workers AI vygeneruje štítky obrázku a uloží je do databáze D1.
- Pokud je zamítnuto, Workflow skončí bez dalšího zásahu.
Tento vzor se hodí ve scénářích, kdy určité operace nemají pokračovat bez výslovného souhlasu člověka, což přidává další vrstvu kontroly a bezpečnosti.
Frontendová integrace
Tento příklad obsahuje frontendovou aplikaci v Next.js, která zajišťuje nahrávání a schvalování obrázků. Frontend nabízí rozhraní pro nahrávání obrázků, jejich kontrolu a jejich schválení nebo zamítnutí. Po nahrání obrázku aplikace spustí Cloudflare Workflow, který následně řídí další kroky: čeká na schválení uživatelem a po schválení provede AI tagování obrázku.
Viz /nextjs-workflow-frontend složku v Repozitář GitHub ↗ pro úplnou implementaci frontendu a podrobnosti nasazení.
Workflow index.ts
index.ts soubor definuje základní logiku Cloudflare Workflow, který zajišťuje zpracování nahraných obrázků, čekání na schválení člověkem a po schválení provedení označení obrázku pomocí AI. Rozšiřuje WorkflowEntrypoint třídu a implementuje run() metoda.
Úplnou implementaci index.ts souboru se podívejte na Repozitář GitHub ↗.
export class MyWorkflow extends WorkflowEntrypoint {
db;
async run(event, step) {
this.db = new DatabaseService(this.env.DB);
const { imageKey } = event.payload;
await step.do("Insert image name into database", async () => {
await this.db.insertImage(imageKey, event.instanceId);
});
const waitForApproval = await step.waitForEvent(
"Wait for AI Image tagging approval",
{
type: "approval-for-ai-tagging",
timeout: "5 minute",
},
);
const approvalPayload = waitForApproval.payload;
if (approvalPayload?.approved) {
const aiTags = await step.do("Generate AI tags", async () => {
const image = await this.env.workflow_demo_bucket.get(imageKey);
if (!image) throw new Error("Image not found");
const arrayBuffer = await image.arrayBuffer();
const uint8Array = new Uint8Array(arrayBuffer);
const input = {
image: Array.from(uint8Array),
prompt: AI_CONFIG.PROMPT,
max_tokens: AI_CONFIG.MAX_TOKENS,
};
const response = await this.env.AI.run(AI_CONFIG.MODEL, input);
return response.description;
});
await step.do("Update DB with AI tags", async () => {
await this.db.updateImageTags(event.instanceId, aiTags);
});
}
}
}export class MyWorkflow extends WorkflowEntrypoint<Env, WorkflowParams> {
private db!: DatabaseService;
async run(event: WorkflowEvent<WorkflowParams>, step: WorkflowStep) {
this.db = new DatabaseService(this.env.DB);
const { imageKey } = event.payload;
await step.do('Insert image name into database', async () => {
await this.db.insertImage(imageKey, event.instanceId);
});
const waitForApproval = await step.waitForEvent('Wait for AI Image tagging approval', {
type: 'approval-for-ai-tagging',
timeout: '5 minute',
});
const approvalPayload = waitForApproval.payload as ApprovalRequest;
if (approvalPayload?.approved) {
const aiTags = await step.do('Generate AI tags', async () => {
const image = await this.env.workflow_demo_bucket.get(imageKey);
if (!image) throw new Error('Image not found');
const arrayBuffer = await image.arrayBuffer();
const uint8Array = new Uint8Array(arrayBuffer);
const input = {
image: Array.from(uint8Array),
prompt: AI_CONFIG.PROMPT,
max_tokens: AI_CONFIG.MAX_TOKENS,
};
const response = await this.env.AI.run(AI_CONFIG.MODEL, input);
return response.description;
});
await step.do('Update DB with AI tags', async () => {
await this.db.updateImageTags(event.instanceId, aiTags);
});
}
}
}Workflow wrangler.jsonc
Konfigurace Workflow je definována v wrangler.jsonc soubor. Tento soubor obsahuje vazby pro R2 bucket, databázi D1, Workers AI a samotný Workflow. Zajistěte, aby všechny potřebné vazby a proměnné prostředí byly správně nastaveny tak, aby odpovídaly vašemu účtu Cloudflare a používaným službám.
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "workflows-waitforevent",
"main": "src/index.ts",
// Set this to today's date
"compatibility_date": "2026-08-28",
"observability": {
"enabled": true,
"head_sampling_rate": 1,
},
"ai": {
"binding": "AI"
},
"workflows": [
{
"name": "workflows-starter",
"binding": "MY_WORKFLOW",
"class_name": "MyWorkflow"
}
],
"r2_buckets": [
{
"bucket_name": "workflow-demo",
"binding": "workflow_demo_bucket"
}
],
"d1_databases": [
{
"binding": "DB",
"database_name": "workflows-demo-d1",
"database_id": "66e4fbe9-06ac-4548-abba-2dc42088e13a"
}
]
}"$schema" = "node_modules/wrangler/config-schema.json"
name = "workflows-waitforevent"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-08-28"
[observability]
enabled = true
head_sampling_rate = 1
[ai]
binding = "AI"
[[workflows]]
name = "workflows-starter"
binding = "MY_WORKFLOW"
class_name = "MyWorkflow"
[[r2_buckets]]
bucket_name = "workflow-demo"
binding = "workflow_demo_bucket"
[[d1_databases]]
binding = "DB"
database_name = "workflows-demo-d1"
database_id = "66e4fbe9-06ac-4548-abba-2dc42088e13a"Zdrojový kód, pokyny k nasazení a referenční architekturu najdete na Repozitář GitHub ↗. Tento zdroj poskytuje všechny nástroje a informace potřebné k efektivní implementaci Workflow a frontendové aplikace Next.js.