Alejandro Rioja.
AI Agents

De Agent-Stack die Ik Gebruik voor 30+ Productie-Agents (Zonder Python)

Alejandro Rioja
Alejandro Rioja
5 min lezen
TL;DR

Ik draai 30+ productie-AI-agents met TypeScript, Cloudflare Workers/Queues/KV en Claude-modellen — geen Python, geen agent-framework. De stack is bewust eenvoudig: Workers verwerken scheduling en queuing, KV slaat de staat op, en de Anthropic SDK stuurt de modelaanroepen direct aan. De bepalende beperking is niet de AI-laag — het is de infrastructuur eromheen.

Gratis nieuwsbrief

Elke woensdag. 28.400+ operators. Geen opvulling.

Inhoudsopgave

Bijgewerkt mei 2026.

TL;DR: Ik draai 30+ productie-AI-agents met TypeScript, Cloudflare Workers/Queues/KV en Claude-modellen — geen Python, geen agent-framework. De stack is bewust eenvoudig: Workers verwerken scheduling en queuing, KV slaat de staat op, en de Anthropic SDK stuurt de modelaanroepen direct aan. De bepalende beperking is niet de AI-laag — het is de infrastructuur eromheen.

[Operator-perspectief] Ik run twee bedrijven: een AI-consultancymerk en Pickleland — een pickleballfaciliteit in Pflugerville, TX. Samen hebben ze vandaag meer dan 30 agents in productie. Dit is de echte stack, geen demo.

Waarom geen Python

Het eerlijke antwoord: ik schrijf elke dag TypeScript voor mijn website- en productwerk. Een tweede taal toevoegen voor agents betekent twee runtimes, twee afhankelijkheidsbomen, twee deployment-pipelines. De productiviteitskosten zijn niet theoretisch — ik heb ze betaald bij eerdere projecten en besloten het niet opnieuw te doen.

De tweede reden is Cloudflare. Workers draait TypeScript native op de edge, met ingebouwde Queues, KV, Durable Objects en Cron Triggers. De volledige agent-infrastructuur die ik nodig heb — scheduling, staat, asynchrone jobverwerking — is één wrangler deploy verwijderd. Er bestaat geen Python-equivalent met dezelfde operationele eenvoud.

De derde reden: de meeste “Python-is-beter-voor-AI”-argumenten betekenen eigenlijk “Python heeft meer ML-bibliotheken.” Ik train geen modellen. Ik roep APIs aan. De Anthropic SDK is eersteklas TypeScript. LangChain en vergelijkbare tools zijn complexiteit die ik niet wil. Als je agents deployt in plaats van onderzoekt, wint eenvoud.

De kern-infrastructuur: drie Cloudflare-primitieven

Elke agent die ik run raakt minimaal een van deze drie:

Cloudflare Workers — de rekenlaag. Een Worker is de runtime van de agent: hij ontvangt een trigger (cron, Queue-bericht, HTTP), voert de modelaanroepen uit en schrijft outputs ergens naartoe. Cold start onder 5ms. Uitvoerlimiet 30 seconden CPU-tijd op het gratis plan, 15 minuten op betaald. Bijna alles wat ik bouw past in 30 seconden; wat niet past, gebruikt Queues voor fan-out.

Cloudflare Queues — asynchrone jobverwerking. Als een taak langer kan duren dan een request, of als ik fan-out nodig heb (12 vertalingen parallel genereren), duw ik berichten in een Queue en laat gebonden consumers ze onafhankelijk verwerken. Geen polling, geen setTimeout-hacks.

Cloudflare KV — lichtgewicht staat. Agent-uitvoeringsgeschiedenis, laatste verwerkte tijdstempels, gecachte API-antwoorden. KV is uiteindelijk consistent, wat prima is voor agents — ik voer geen transacties uit. Het geeft me een eenvoudige sleutel-waardeopslag die ik vanuit elke Worker kan lezen/schrijven zonder een database op te starten.

De modellaag: Anthropic SDK, twee modellen

Ik gebruik precies twee Claude-modellen:

De Anthropic SDK in TypeScript is rechtdoorzee. Dit is het patroon dat ik gebruik voor elke modelaanroep:

typescript
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic({ apiKey: env.ANTHROPIC_API_KEY });

async function runAgent(prompt: string, systemPrompt: string): Promise<string> {
  const message = await client.messages.create({
    model: "claude-sonnet-4-6",
    max_tokens: 2048,
    system: systemPrompt,
    messages: [{ role: "user", content: prompt }],
  });

  const block = message.content[0];
  if (block.type !== "text") throw new Error("Unexpected content type");
  return block.text;
}

Dat is de volledige modelinterface. Geen abstracties bovenop. Als ik tool use nodig heb, voeg ik een tools-array toe. Als ik streaming nodig heb, vervang ik messages.create door messages.stream. Geen framework beheert dit voor mij — en dat wil ik ook niet.

Een echte agent: de content-pipeline

De meest complexe agent die ik draai is de content-pipeline. Hij genereert blogposts, vertaalt ze naar 12 talen, rendert OG-card-SVG’s en stelt LinkedIn-promoties op — alles als concepten, geblokkeerd achter mijn beoordeling voordat er iets gepubliceerd wordt.

Het Worker-ingangspunt ziet er zo uit:

typescript
// src/workers/content-pipeline.ts
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const { topic, slug } = await request.json<{ topic: string; slug: string }>();

    // Stap 1: EN-post genereren
    const enPost = await generatePost(topic, env);
    await env.CONTENT_KV.put(`draft:${slug}:en`, enPost);

    // Stap 2: vertalingen fan-outen via Queue
    const locales = ["ar", "de", "es", "fr", "hi", "it", "ja", "ko", "nl", "pt", "ru", "zh"];
    for (const locale of locales) {
      await env.TRANSLATION_QUEUE.send({ slug, locale, content: enPost });
    }

    return Response.json({ status: "queued", slug });
  },
};

Elke vertaling wordt uitgevoerd in zijn eigen Worker-aanroep. Als een mislukt, probeert de Queue het automatisch opnieuw. Ik krijg 12 parallelle vertalingen zonder threads, promises of rate-limit backoff zelf te beheren.

Een echte agent: de eventpromotor

Pickleland organiseert pickleballevenementen. Ik heb een agent gebouwd die het boekingsplatform scant voor evenementen in de komende 4 dagen, Facebook-groepsberichten per evenement opstelt en ze ter beoordeling aanbiedt voordat er iets naar buiten gaat.

De systeemprompt:

typescript
const systemPrompt = `You are a community manager for a pickleball facility.
Write Facebook group posts for upcoming events.
Rules:
- Max 150 words per post
- Lead with what's fun about the event, not the price
- Include the booking URL exactly as provided
- Do not use exclamation marks more than once per post
- Tone: friendly, local, not corporate`;

De bepalende beperking hier is niet het model — het is de workflow. De agent draait dagelijks om 8 uur op een cron-trigger. De conceptberichten landen in een beoordelingswachtrij. Ik keur goed of bewerk, dan viert een aparte publicatie-Worker. Geen evenement wordt gepost zonder dat een mens het heeft gezien.

Hoe ik 30+ agents beheer zonder mijn verstand te verliezen

Het Cloudflare-dashboard is mijn besturingsvlak. Elke Worker toont me aanroeptellingen, foutenpercentage en CPU-tijd. Elke Queue toont berichtdoorvoer en mislukkingen. KV toont opslaggebruik.

Daarnaast:

De discipline is niet technisch. Het is beslissen wat een agent autonoom mag doen versus wat mijn goedkeuring nodig heeft. Contentconcepten: autonoom. Alles wat een klant raakt: menselijke beoordeling. Alles wat geld verplaatst: geen agentwerk.

Wat ik zou veranderen als ik vandaag begon

Eén ding: ik zou gestructureerde outputs (JSON-modus) vanaf dag één instellen in plaats van ze achteraf op al gedeployde agents te zetten. Vrije Claude-tekst parsen is een belasting. Als je een Zod-schema definieert en het doorgeeft als de verwachte antwoordvorm, krijg je getypeerde data terug en hoeven downstream Workers niet te gissen.

typescript
import { z } from "zod";

const EventPostSchema = z.object({
  headline: z.string().max(80),
  body: z.string().max(600),
  bookingUrl: z.string().url(),
  suggestedPostTime: z.enum(["morning", "afternoon", "evening"]),
});

De conclusie van de operator

De agent-stack die in productie werkt, is degene die je om 22 uur kunt debuggen als er iets kapotgaat. Voor mij is dat TypeScript + Cloudflare + Anthropic SDK — niet omdat het de meest glamoureuze combinatie is, maar omdat elke laag onafhankelijk observeerbaar, deploybaar en vervangbaar is. Frameworks zijn weddenschappen op abstracties. Ik heb liever eigenaarschap over de loodgieterij.

Lees verder

Ontvang het AI-playbook in je inbox

Elke woensdag. 28.400+ operators. Geen opvulling.

↵ alle resultaten bekijken esc esc om te sluiten