Агентний пайплайн на TypeScript. Математика — в окремому чистому модулі; Claude чи Groq пише суворо-JSON-висновок; Telegram, Slack і Email отримують звіт о 9:00. Зроблено щоб показати production-патерни для LLM-додатків — tool-use, prompt caching, zod-валідація, graceful fallback, ідемпотентний fan-out.
Шаблонні рішення в Zapier / Make / n8n віддають сирі дані LLM і просять написати summary. Модель спокійно перераховує суми й відсотки — і помиляється достатньо часто, щоб вбити довіру. Як тільки власник побачив одне неправильне число, увесь звіт стає шумом.
Невеликий TypeScript-пайплайн рахує кожну цифру у чистому модулі metrics.ts. LLM отримує готові метрики через сувору JSON-схему і прямо заборонений переробляти будь-що. Числа аудитовані, текст читабельний, система не падає мовчки якщо LLM зломався.
Сім типізованих кроків. Один напрямок. Детермінізм там, де він потрібен; AI там, де ні.
Sheets / CSV через адаптер
Чисті детерміновані формули
Claude tool-use / Groq json
Сувора схема або fallback
en-US, емодзі, брендинг
Promise.all · TG/Slack/Email
Ідемпотентний JSON-аудит
export async function runDailyReport(flags) {
const cfg = loadConfig();
const orders = await fetchOrders(cfg);
const metrics = computeMetrics(orders, flags.date);
if (!flags.force && await hasReportFor(cfg.SNAPSHOT_PATH, metrics.date)) return;
let summary;
try {
assertLLMReady(cfg);
summary = await writeSummary(cfg, metrics);
} catch (err) {
summary = fallbackSummary(metrics);
}
const text = formatReport(metrics, summary, cfg.BRAND_NAME);
if (flags.dry) return console.log(text);
const [tg, slack, email] = await Promise.all([
enabled.telegram ? sendTelegram(cfg, text) : null,
enabled.slack ? sendSlack(cfg, text) : null,
enabled.email ? sendEmail(cfg, subject, text) : null,
]);
if (![tg, slack, email].some(r => r?.ok)) throw new Error("all channels failed");
await saveSnapshot(cfg.SNAPSHOT_PATH, { date: metrics.date, metrics, summary, ... });
}
Уся математика — в одному чистому metrics.ts. LLM отримує готові цифри і йому в системному промпті заборонена арифметика.
Claude — tools + tool_choice + input_schema; Groq — response_format: json_object. Одна zod-схема валідує обидва.
Системний промпт у Anthropic помічений cache_control: ephemeral — повторні запуски перевикористовують префікс, помітно зменшуючи input tokens.
LLM впав чи повернув битий JSON? fallbackSummary будує шаблонний текст з тих самих метрик — пайплайн не падає мовчки.
Telegram + Slack + Email паралельно через Promise.all. Пайплайн успішний якщо хоч один канал доставив; помилки по кожному каналу логуються.
Снапшоти за датою. Повтор за той самий день — no-op; --force перевизначає. Аудит у data/reports.json.
Один інтерфейс OrdersSource. Додавання Stripe / Shopify / Postgres ≈ 30 рядків, без змін далі по пайплайну.
zod парсить process.env один раз на старті. Невалідна конфігурація падає одразу з чіткою помилкою — без мовчазної поведінки.
Безкоштовний хостинг розкладу через .github/workflows/daily.yml. Хочеш точнішого часу — node-cron на VPS, та сама точка входу.
Promise.all.tsc --noEmit чистий.tsx. ~800 рядків коду у 9 модулях.~800 рядків TypeScript у 9 невеликих модулях. Клонуй, запусти npm run report -- --dry і подивись увесь пайплайн end-to-end на готовому демо-датасеті.