Alejandro Rioja.
AI Agents

La Stack d'Agents que J'Utilise pour Faire Tourner 30+ Agents en Production (Sans Python)

Alejandro Rioja
Alejandro Rioja
6 min de lecture
TL;DR

Je fais tourner 30+ agents IA en production avec TypeScript, Cloudflare Workers/Queues/KV et des modèles Claude — sans Python, sans framework d'agents. La stack est volontairement simple : les Workers gèrent le scheduling et les files d'attente, KV stocke l'état, et l'Anthropic SDK pilote les appels au modèle directement. La vraie contrainte n'est pas la couche IA — c'est l'infrastructure qui l'entoure.

Newsletter gratuite

Chaque mercredi. 28 400+ opérateurs. Zéro superflu.

Table des matières

Mis à jour mai 2026.

TL;DR: Je fais tourner 30+ agents IA en production avec TypeScript, Cloudflare Workers/Queues/KV et des modèles Claude — sans Python, sans framework d’agents. La stack est volontairement simple : les Workers gèrent le scheduling et les files d’attente, KV stocke l’état, et l’Anthropic SDK pilote les appels au modèle directement. La vraie contrainte n’est pas la couche IA — c’est l’infrastructure qui l’entoure.

[Perspective d’opérateur] Je dirige deux entreprises : une marque de conseil en IA et Pickleland — une installation de pickleball à Pflugerville, TX. Entre les deux, j’ai plus de 30 agents en production aujourd’hui. Voici la stack réelle, pas une démo.

Pourquoi pas Python

La réponse honnête : j’écris du TypeScript chaque jour pour mon site web et mes produits. Ajouter un second langage pour les agents signifie deux runtimes, deux arbres de dépendances, deux pipelines de déploiement. Le coût de productivité n’est pas théorique — je l’ai payé sur des projets précédents et j’ai décidé de ne pas recommencer.

La deuxième raison, c’est Cloudflare. Workers exécute TypeScript nativement à l’edge, avec Queues, KV, Durable Objects et Cron Triggers intégrés. Toute l’infrastructure d’agents dont j’ai besoin — scheduling, état, traitement asynchrone de jobs — est à un wrangler deploy de distance. Il n’existe pas d’équivalent Python avec la même surface opérationnelle.

La troisième raison : la plupart des arguments “Python-est-meilleur-pour-l’IA” signifient en réalité “Python a plus de bibliothèques ML.” Je n’entraîne pas de modèles. J’appelle des APIs. L’Anthropic SDK est du TypeScript de première classe. LangChain et ses semblables représentent une complexité dont je ne veux pas. Quand on déploie des agents plutôt que de les étudier, la simplicité gagne.

L’infrastructure centrale : trois primitives Cloudflare

Chaque agent que je fais tourner touche au moins l’une de ces trois :

Cloudflare Workers — la couche de calcul. Un Worker est le runtime de l’agent : il reçoit un trigger (cron, message Queue, HTTP), exécute les appels au modèle et écrit les sorties quelque part. Démarrage à froid sous 5ms. Limite d’exécution de 30 secondes de temps CPU sur le plan gratuit, 15 minutes sur le plan payant. Presque tout ce que je construis tient en 30 secondes ; ce qui ne tient pas utilise les Queues pour le fan-out.

Cloudflare Queues — traitement asynchrone de jobs. Quand une tâche pourrait prendre plus longtemps qu’une requête, ou quand j’ai besoin de fan-out (générer 12 traductions en parallèle), je pousse des messages dans une Queue et laisse les consumers liés les traiter indépendamment. Pas de polling, pas de bidouilles setTimeout.

Cloudflare KV — état léger. Historique des exécutions d’agents, derniers timestamps traités, réponses API en cache. KV est éventuellement cohérent, ce qui convient parfaitement aux agents — je n’exécute pas de transactions. Ça me donne un store clé-valeur simple que je peux lire/écrire depuis n’importe quel Worker sans démarrer une base de données.

La couche modèle : Anthropic SDK, deux modèles

J’utilise exactement deux modèles Claude :

L’Anthropic SDK en TypeScript est direct. Voici le pattern que j’utilise pour chaque appel au modèle :

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

C’est toute l’interface du modèle. Pas d’abstractions par-dessus. Quand j’ai besoin de tool use, j’ajoute un tableau tools. Quand j’ai besoin de streaming, je remplace messages.create par messages.stream. Aucun framework ne gère ça pour moi — et je n’en veux pas.

Un vrai agent : le pipeline de contenu

L’agent le plus complexe que je fais tourner est le pipeline de contenu. Il génère des posts de blog, les traduit en 12 langues, rend des SVGs de cartes OG et rédige des promos LinkedIn — le tout comme brouillons, bloqués derrière ma révision avant que quoi que ce soit ne soit publié.

Le point d’entrée du Worker ressemble à ceci :

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 }>();

    // Étape 1 : générer le post EN
    const enPost = await generatePost(topic, env);
    await env.CONTENT_KV.put(`draft:${slug}:en`, enPost);

    // Étape 2 : fan-out des traductions 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 });
  },
};

Chaque traduction s’exécute dans sa propre invocation Worker. Si l’une échoue, la Queue la réessaie automatiquement. J’obtiens 12 traductions en parallèle sans gérer les threads, les promises ou le backoff de rate limit moi-même.

Un vrai agent : le promoteur d’événements

Pickleland organise des événements de pickleball. J’ai construit un agent qui scanne la plateforme de réservation pour les événements des 4 prochains jours, rédige des posts pour des groupes Facebook par événement et les présente pour ma révision avant que quoi que ce soit ne soit publié.

Le system prompt :

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`;

La vraie contrainte ici n’est pas le modèle — c’est le workflow. L’agent tourne avec un trigger cron à 8h chaque matin. Les posts brouillons atterrissent dans une file de révision. J’approuve ou modifie, puis un Worker de publication séparé se déclenche. Aucun événement n’est posté sans qu’un humain l’ait vu.

Comment je gère 30+ agents sans perdre la tête

Le dashboard Cloudflare est mon plan de contrôle. Chaque Worker me montre le nombre d’invocations, le taux d’erreurs et le temps CPU. Chaque Queue montre le débit de messages et les échecs. KV montre l’utilisation du stockage.

Au-delà de ça :

La discipline n’est pas technique. C’est décider ce qu’un agent peut faire de façon autonome versus ce qui nécessite mon approbation. Brouillons de contenu : autonome. Tout ce qui touche un client : révision humaine. Tout ce qui déplace de l’argent : pas un travail d’agent.

Ce que je changerais si je recommençais aujourd’hui

Une chose : j’aurais configuré les sorties structurées (mode JSON) dès le premier jour plutôt que de les ajouter rétroactivement sur des agents déjà déployés. Parser du texte libre de Claude est une taxe. Quand on définit un schéma Zod et qu’on le passe comme forme de réponse attendue, on récupère des données typées et les Workers en aval n’ont pas à deviner.

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"]),
});

La conclusion de l’opérateur

La stack d’agents qui fonctionne en production est celle que vous pouvez déboguer à 22h quand quelque chose se casse. Pour moi, c’est TypeScript + Cloudflare + Anthropic SDK — non pas parce que c’est la combinaison la plus glamour, mais parce que chaque couche est observable, déployable et remplaçable indépendamment. Les frameworks sont des paris sur des abstractions. Je préfère posséder la plomberie.

Continuer à lire

Recevez le guide IA dans votre boîte mail

Chaque mercredi. 28 400+ opérateurs. Zéro superflu.

↵ pour voir tous les résultats esc esc pour fermer