Alejandro Rioja.
AI Agents

Мой Стек Агентов для Запуска 30+ Продакшн-Агентов (Без Python)

Alejandro Rioja
Alejandro Rioja
5 мин чтения
TL;DR

Я запускаю 30+ продакшн-ИИ-агентов с помощью TypeScript, Cloudflare Workers/Queues/KV и моделей Claude — без Python, без агентного фреймворка. Стек намеренно простой: Workers занимаются планированием и очередями, KV хранит состояние, а Anthropic SDK напрямую управляет вызовами модели. Важное ограничение — не слой ИИ, а инфраструктура вокруг него.

Бесплатная рассылка

Каждую среду. 28 400+ читателей. Никакой воды.

Содержание

Обновлено май 2026.

TL;DR: Я запускаю 30+ продакшн-ИИ-агентов с помощью TypeScript, Cloudflare Workers/Queues/KV и моделей Claude — без Python, без агентного фреймворка. Стек намеренно простой: Workers занимаются планированием и очередями, KV хранит состояние, а Anthropic SDK напрямую управляет вызовами модели. Важное ограничение — не слой ИИ, а инфраструктура вокруг него.

[Взгляд оператора] Я управляю двумя бизнесами: брендом ИИ-консалтинга и Pickleland — площадкой для пиклбола в Пфлюгервилле, штат Техас. Вместе они дают более 30 агентов, работающих в продакшне прямо сейчас. Это реальный стек, а не демо.

Почему без Python

Честный ответ: я пишу TypeScript каждый день для работы с сайтом и продуктами. Добавить второй язык для агентов означает два рантайма, два дерева зависимостей, два деплой-пайплайна. Стоимость производительности не теоретическая — я заплатил её на прошлых проектах и решил больше не повторять.

Вторая причина — Cloudflare. Workers запускает TypeScript нативно на эдже, со встроенными Queues, KV, Durable Objects и Cron Triggers. Вся агентная инфраструктура, которая мне нужна — планирование, состояние, асинхронная обработка задач — доступна одной командой wrangler deploy. Нет Python-эквивалента с такой же операционной простотой.

Третья причина: большинство аргументов “Python-лучше-для-ИИ” на самом деле означают “у Python больше ML-библиотек.” Я не обучаю модели. Я вызываю API. Anthropic SDK — это TypeScript первого класса. LangChain и подобные — это сложность, которую я не хочу. Когда вы деплоите агентов, а не исследуете их, простота побеждает.

Основная инфраструктура: три примитива Cloudflare

Каждый агент, который я запускаю, затрагивает как минимум один из этих трёх:

Cloudflare Workers — вычислительный слой. Worker — это рантайм агента: он получает триггер (cron, сообщение из Queue, HTTP), выполняет вызовы модели и записывает выходные данные куда-то. Cold start менее 5мс. Лимит выполнения — 30 секунд CPU на бесплатном плане, 15 минут на платном. Почти всё, что я строю, помещается в 30 секунд; то, что не помещается, использует Queues для fan-out.

Cloudflare Queues — асинхронная обработка задач. Когда задача может занять больше времени, чем запрос, или когда нужен fan-out (генерация 12 переводов параллельно), я отправляю сообщения в Queue и позволяю привязанным consumers обрабатывать их независимо. Без поллинга, без хаков с setTimeout.

Cloudflare KV — лёгкое состояние. История запусков агентов, последние обработанные временные метки, кешированные ответы API. KV — eventually consistent, что подходит для агентов — я не выполняю транзакции. Это простое key-value хранилище, которое я могу читать/писать из любого Worker без запуска базы данных.

Слой моделей: Anthropic SDK, две модели

Я использую ровно две модели Claude:

Anthropic SDK на TypeScript прямолинеен. Вот паттерн, который я использую для каждого вызова модели:

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

Это весь интерфейс модели. Никаких абстракций поверх. Когда нужен tool use, добавляю массив tools. Когда нужен стриминг, заменяю messages.create на messages.stream. Никакой фреймворк не управляет этим за меня — и я этого не хочу.

Реальный агент: контентный пайплайн

Самый сложный агент, который я запускаю, — это контентный пайплайн. Он генерирует посты в блоге, переводит их на 12 языков, рендерит SVG OG-карточек и составляет промо для LinkedIn — всё в виде черновиков, заблокированных до моего просмотра.

Точка входа Worker выглядит так:

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

    // Шаг 1: сгенерировать пост EN
    const enPost = await generatePost(topic, env);
    await env.CONTENT_KV.put(`draft:${slug}:en`, enPost);

    // Шаг 2: fan-out переводов через 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 });
  },
};

Каждый перевод выполняется в собственном вызове Worker. Если один падает, Queue автоматически повторяет попытку. Я получаю 12 параллельных переводов без самостоятельного управления потоками, промисами или rate-limit backoff.

Реальный агент: промоутер событий

Pickleland проводит события по пиклболу. Я создал агента, который сканирует платформу бронирования на события в следующие 4 дня, составляет посты для групп Facebook по каждому событию и представляет их на проверку перед публикацией.

Системный промпт:

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

Ключевое ограничение здесь — не модель, а рабочий процесс. Агент запускается ежедневно в 8 утра по cron-триггеру. Черновые посты попадают в очередь проверки. Я одобряю или редактирую, затем срабатывает отдельный Worker публикации. Ни одно событие не публикуется без просмотра человеком.

Как управлять 30+ агентами без потери рассудка

Дашборд Cloudflare — мой плоскость управления. Каждый Worker показывает мне количество вызовов, частоту ошибок и время CPU. Каждая Queue показывает пропускную способность сообщений и сбои. KV показывает использование хранилища.

Помимо этого:

Дисциплина не техническая. Это решение о том, что агент может делать автономно, а что требует моего одобрения. Черновики контента: автономно. Всё, что касается клиента: проверка человеком. Всё, что перемещает деньги: не работа агента.

Что бы я изменил, начиная сегодня

Одно: я бы настроил структурированные выходные данные (режим JSON) с первого дня, а не добавлял их задним числом к уже задеплоенным агентам. Парсинг свободного текста Claude — это налог. Когда вы определяете схему Zod и передаёте её как ожидаемую форму ответа, вы получаете типизированные данные, и downstream Workers не нужно гадать.

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

Вывод оператора

Стек агентов, который работает в продакшне, — это тот, который можно дебажить в 22:00, когда что-то сломалось. Для меня это TypeScript + Cloudflare + Anthropic SDK — не потому что это самая гламурная комбинация, а потому что каждый слой наблюдаем, разворачиваем и заменяем независимо. Фреймворки — это ставки на абстракции. Я предпочитаю владеть сантехникой.

Читать дальше

Получайте ИИ-руководство на почту

Каждую среду. 28 400+ читателей. Никакой воды.

↵ — все результаты esc esc — закрыть