📊 PulseReport
Код
Портфоліо-проект · TypeScript · агентні LLM-системи

Щоденні KPI рахує код, висновок пише AI.

Агентний пайплайн на TypeScript. Математика — в окремому чистому модулі; Claude чи Groq пише суворо-JSON-висновок; Telegram, Slack і Email отримують звіт о 9:00. Зроблено щоб показати production-патерни для LLM-додатків — tool-use, prompt caching, zod-валідація, graceful fallback, ідемпотентний fan-out.

TypeScript 5.6 Node 20 Anthropic SDK Groq zod GitHub Actions
P
pulse report
bot · 09:00
📊 Daily Sales Pulse (Tue, Jun 9, 2026) Revenue at $1,606.76 drops 20.47% from yesterday but stays 24.42% above last week 💰 Revenue: $1,606.76 (-20.47% vs prev day, +24.42% vs last week) 🛍️ Orders: 17 🧾 Avg order: $94.52 🔝 Top product: Nimbus Sneakers ($493.19) Today's revenue is $1,606.76, a 20.47% decline versus the previous day, while still 24.42% higher than the same day last week. Highlights: • Orders fell to 17, pulling the average order value to $94.52 • Nimbus Sneakers generated $493.19, the largest single-product contribution • Weekly momentum is positive, with revenue up 24.42% compared to last week 👉 Launch a limited-time discount on Nimbus Sneakers tomorrow to capitalize on its strong performance.
Проблема

Готові AI-шаблони вигадують цифри

Шаблонні рішення в Zapier / Make / n8n віддають сирі дані LLM і просять написати summary. Модель спокійно перераховує суми й відсотки — і помиляється достатньо часто, щоб вбити довіру. Як тільки власник побачив одне неправильне число, увесь звіт стає шумом.

Рішення

Розділити математику й текст

Невеликий TypeScript-пайплайн рахує кожну цифру у чистому модулі metrics.ts. LLM отримує готові метрики через сувору JSON-схему і прямо заборонений переробляти будь-що. Числа аудитовані, текст читабельний, система не падає мовчки якщо LLM зломався.

Пайплайн

Сім типізованих кроків. Один напрямок. Детермінізм там, де він потрібен; AI там, де ні.

1
fetchOrders

Sheets / CSV через адаптер

2
computeMetrics

Чисті детерміновані формули

3
writeSummary

Claude tool-use / Groq json

4
zod validate

Сувора схема або fallback

5
formatReport

en-US, емодзі, брендинг

6
fan-out

Promise.all · TG/Slack/Email

7
saveSnapshot

Ідемпотентний 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-схема валідує обидва.

⚡
Prompt caching

Системний промпт у Anthropic помічений cache_control: ephemeral — повторні запуски перевикористовують префікс, помітно зменшуючи input tokens.

🛡️
Graceful fallback

LLM впав чи повернув битий JSON? fallbackSummary будує шаблонний текст з тих самих метрик — пайплайн не падає мовчки.

📡
Мультиканальний fan-out

Telegram + Slack + Email паралельно через Promise.all. Пайплайн успішний якщо хоч один канал доставив; помилки по кожному каналу логуються.

🔁
Ідемпотентні запуски

Снапшоти за датою. Повтор за той самий день — no-op; --force перевизначає. Аудит у data/reports.json.

🔌
Adapter pattern

Один інтерфейс OrdersSource. Додавання Stripe / Shopify / Postgres ≈ 30 рядків, без змін далі по пайплайну.

🧪
Типізоване середовище

zod парсить process.env один раз на старті. Невалідна конфігурація падає одразу з чіткою помилкою — без мовчазної поведінки.

⏰
GitHub Actions cron

Безкоштовний хостинг розкладу через .github/workflows/daily.yml. Хочеш точнішого часу — node-cron на VPS, та сама точка входу.

Що демонструє проект

✓ Агентний дизайн пайплайну з одним суворим LLM-викликом замість відкритого tool-use циклу — передбачувано, дебагабельно, дешево.
✓ Production-патерни для LLM-додатків: structured output, schema validation, prompt caching, graceful fallback.
✓ Абстракція мультипровайдерності над Anthropic і Groq з перемиканням однією env-змінною.
✓ Чистий інтерфейс адаптерів для plug-and-play джерел даних.
✓ Мультиканальна доставка з толерантністю до часткових збоїв через Promise.all.
✓ Ідемпотентність + audit-trail дизайн для one-shot scheduled jobs.
✓ Наскрізна типобезпека: env → AI response → file IO. tsc --noEmit чистий.
✓ Без бандлерів — пряме виконання через tsx. ~800 рядків коду у 9 модулях.

Подивись код.

~800 рядків TypeScript у 9 невеликих модулях. Клонуй, запусти npm run report -- --dry і подивись увесь пайплайн end-to-end на готовому демо-датасеті.