Разбор

Агент готовит ежемесячный отчёт для клиентов агентства: 3 часа работы за 15 минут

Аккаунт-менеджер тратит 3 часа на один клиентский отчёт. Агентство с 15 клиентами теряет 45 часов ежемесячно на цифры, которые клиент открывает раз — и закрывает. Python + Claude API собирает Директ, Метрику и Вебмастер и пишет связный нарратив на русском языке.

• 8 мин чтения

Аккаунт-менеджер в агентстве готовит отчёт клиенту примерно так: открывает Директ, выгружает таблицу, переходит в Метрику, смотрит трафик, залезает в Вебмастер ради позиций, потом садится в Google Docs и начинает писать: «В мае мы…». Через три часа — готово. До следующего раза.

45ч
теряет агентство с 15 клиентами каждый месяц на подготовку отчётов — это полная рабочая неделя одного менеджера

Почему это больная точка

Три часа на отчёт — это не потому что менеджер медленно работает. Это потому что данные живут в трёх местах, и их нужно свести вручную. Директ отдельно. Метрика отдельно. SEO-позиции — в Вебмастере или в каком-нибудь Топвизоре. Сводная таблица KPI за месяц. Сравнение с прошлым месяцем. Потом написать текстовый вывод — что получилось, что не получилось, что делаем дальше.

Каждый отчёт — одна и та же механическая работа. Другие цифры, тот же процесс.

При этом клиент читает этот отчёт 5 минут. Смотрит на главные числа, скроллит до «что планируем дальше», закрывает. Три часа работы — пять минут внимания.

Хуже: эти три часа стоят денег. Час аккаунт-менеджера в московском агентстве — 1 500–3 000 рублей. На одном клиенте это 4 500–9 000 рублей в месяц только на отчётность. На 15 клиентах — до 135 000 рублей. При том что это не продающая работа — только документирование.

Куда уходит время аккаунт-менеджера при ручной подготовке отчёта
Сбор данных
(3 источника)
45 мин
Составление
сводной таблицы
40 мин
Написание
нарратива
55 мин
Форматирование
и отправка
20 мин
Среднее время по оценке руководителей digital-агентств. Итого: ~2,5–3 часа на один клиентский отчёт.

Что делает агент вместо менеджера

Агент не просто «выгружает данные». Он забирает их из трёх источников параллельно, считает динамику месяц к месяцу, и пишет текст — связный нарратив на русском, где есть вводная часть, KPI с объяснением, вывод и план.

Менеджер получает в Telegram ссылку на готовый Google Docs. Открывает, смотрит, правит за 5–7 минут — убирает что-то неточное, добавляет детали которые знает только он. Отправляет клиенту.

Схема агента
📊Директ API v5расход, клики, CR
📈Метрика APIтрафик, источники, цели
🔍Вебмастер APIпозиции, индексация
🐍Pythonсбор и агрегация
🤖Claude Sonnetнарратив на русском
📄Google Docs / PDFклиентский документ
✈️Telegramссылка менеджеру

Как выглядит готовый отчёт

Вот реальный пример того, что Claude пишет. Это не таблица и не список буллитов. Это текст который можно отправить клиенту.

📄Ежемесячный отчёт — Май 2026Автогенерация: 14 мин 22 сек
KPI месяца
Расход Директ87 400 ₽+3% vs апрель
Лиды из рекламы143+18% vs апрель
Стоимость лида611 ₽−13% vs апрель
Трафик сайта9 820+7% vs апрель
Орг. трафик4 310+11% vs апрель
Позиции топ-1034 запр.+5 vs апрель
Что сделали в мае

В мае сосредоточились на двух направлениях. В рекламе провели перераспределение бюджета — убрали 12 ключевых слов с расходом выше 800 рублей и нулевыми конверсиями за апрель, высвободившиеся средства направили на кампании с конверсией выше 4%. Это дало снижение стоимости лида с 703 рублей до 611 рублей при росте числа лидов. В SEO запустили три новые статьи под коммерческие запросы — две из них уже вошли в топ-20, одна пока на 34-й позиции.

Что обращает на себя внимание

Органический трафик растёт третий месяц подряд — в мае плюс 11%. При этом доля брендового трафика сократилась с 28% до 23%, что говорит о росте привлечения новой аудитории, а не просто о лояльных посетителях. Есть одна точка риска: кампания «Установка кондиционеров» исчерпывает дневной бюджет к 15:00 — во второй половине дня показов нет. При текущих ставках для равномерного распределения нужно либо увеличить дневной лимит на 20%, либо скорректировать ставки в пиковые часы.

План на июнь

Три приоритета. Первый — решить ситуацию с бюджетом кампании «Установка кондиционеров»: провести анализ почасовой статистики и настроить корректировки ставок по времени суток. Второй — запустить ретаргетинговую кампанию на посетителей страницы «Цены», которые не оставили заявку (сейчас эта аудитория не охвачена). Третий — опубликовать ещё две статьи по информационным запросам для усиления позиций в топ-10 к августу.

Клиент видит конкретику, а не сухую выгрузку. Менеджер не сочиняет это два часа — он проверяет за пять минут.

Код агента

Полный рабочий скрипт. Нужен Python 3.9+.

report_agent.py — pip install anthropic requests google-api-python-client google-auth
import asyncio
import anthropic
import requests
import json
from datetime import datetime, timedelta
from googleapiclient.discovery import build
from google.oauth2.service_account import Credentials

# ─── Конфигурация ───────────────────────────────────────────
DIRECT_TOKEN      = "YOUR_DIRECT_TOKEN"
METRIKA_TOKEN     = "YOUR_METRIKA_TOKEN"
WEBMASTER_TOKEN   = "YOUR_WEBMASTER_TOKEN"
CLAUDE_API_KEY    = "YOUR_CLAUDE_KEY"
TELEGRAM_TOKEN    = "YOUR_BOT_TOKEN"
TELEGRAM_CHAT_ID  = "YOUR_CHAT_ID"
GOOGLE_CREDS_FILE = "service_account.json"   # Google Service Account JSON

# Конфигурация клиентов
CLIENTS = [
  {
      "name": "ООО Климат-Сервис",
      "direct_login": "klimat-service",
      "metrika_counter": "12345678",
      "webmaster_host": "https://klimat-service.ru",
      "telegram_chat": "-100123456789",
  },
  # ... добавить остальных клиентов
]

DIRECT_URL  = "https://api.direct.yandex.com/json/v5/"
METRIKA_URL = "https://api-metrika.yandex.net/stat/v1/data"
WM_URL      = "https://api.webmaster.yandex.net/v4/user/"

# ─── Сбор данных ─────────────────────────────────────────────

def get_direct_data(client_login: str) -> dict:
  """Расход, клики и конверсии за текущий и прошлый месяц."""
  now = datetime.now()
  date_to   = now.strftime("%Y-%m-%d")
  date_from = (now - timedelta(days=30)).strftime("%Y-%m-%d")
  prev_from = (now - timedelta(days=60)).strftime("%Y-%m-%d")
  prev_to   = (now - timedelta(days=31)).strftime("%Y-%m-%d")

  headers = {
      "Authorization": f"Bearer {DIRECT_TOKEN}",
      "Client-Login": client_login,
      "Accept-Language": "ru",
  }

  def fetch_stats(date_f, date_t, period_name):
      payload = {
          "params": {
              "SelectionCriteria": {"DateFrom": date_f, "DateTo": date_t},
              "Goals": [], "AttributionModels": ["LC"],
              "FieldNames": ["CampaignName","Cost","Clicks","Impressions","Conversions","Ctr"],
              "ReportName": f"monthly_{period_name}_{date_f}",
              "ReportType": "CAMPAIGN_PERFORMANCE_REPORT",
              "DateRangeType": "CUSTOM_DATE",
              "Format": "JSON",
              "IncludeVAT": "YES",
              "IncludeDiscount": "NO",
          }
      }
      r = requests.post(DIRECT_URL + "reports", headers=headers, json=payload)
      if r.status_code == 201:
          import time; time.sleep(5)
          r = requests.post(DIRECT_URL + "reports", headers=headers, json=payload)
      r.raise_for_status()
      data = r.json().get("data", [])
      total_cost = sum(float(row.get("Cost", 0)) for row in data) / 1_000_000
      total_conv = sum(int(row.get("Conversions", 0)) for row in data)
      total_clicks = sum(int(row.get("Clicks", 0)) for row in data)
      cpl = round(total_cost / total_conv, 0) if total_conv > 0 else 0
      return {
          "cost": round(total_cost, 0),
          "conversions": total_conv,
          "clicks": total_clicks,
          "cpl": cpl,
      }

  return {
      "current": fetch_stats(date_from, date_to, "current"),
      "previous": fetch_stats(prev_from, prev_to, "previous"),
  }

def get_metrika_data(counter_id: str) -> dict:
  """Трафик по источникам за текущий и прошлый месяц."""
  now = datetime.now()
  date_to   = now.strftime("%Y-%m-%d")
  date_from = (now - timedelta(days=30)).strftime("%Y-%m-%d")
  prev_from = (now - timedelta(days=60)).strftime("%Y-%m-%d")
  prev_to   = (now - timedelta(days=31)).strftime("%Y-%m-%d")

  headers = {"Authorization": f"OAuth {METRIKA_TOKEN}"}

  def fetch(date_f, date_t):
      params = {
          "ids": counter_id,
          "date1": date_f, "date2": date_t,
          "metrics": "ym:s:visits,ym:s:users,ym:s:bounceRate,ym:s:goalReachesAny",
          "dimensions": "ym:s:trafficSourceName",
          "sort": "-ym:s:visits",
          "limit": 8,
      }
      r = requests.get(METRIKA_URL, headers=headers, params=params)
      r.raise_for_status()
      result = r.json()
      sources = []
      total_visits = 0
      for row in result.get("data", []):
          v = int(row["metrics"][0])
          total_visits += v
          sources.append({
              "source": row["dimensions"][0]["name"],
              "visits": v,
              "bounce": round(row["metrics"][2], 1),
          })
      return {"total_visits": total_visits, "sources": sources[:5]}

  return {
      "current": fetch(date_from, date_to),
      "previous": fetch(prev_from, prev_to),
  }

def get_webmaster_data(host_url: str) -> dict:
  """Позиции и индексация из Яндекс.Вебмастер."""
  headers = {"Authorization": f"OAuth {WEBMASTER_TOKEN}"}
  # Получить user_id
  r = requests.get(WM_URL + "me", headers=headers)
  r.raise_for_status()
  user_id = r.json()["user_id"]

  # Хосты пользователя
  r = requests.get(WM_URL + f"{user_id}/hosts", headers=headers)
  r.raise_for_status()
  hosts = r.json().get("hosts", [])
  host_id = None
  for h in hosts:
      if host_url in h.get("unicode_host_url", ""):
          host_id = h["host_id"]
          break
  if not host_id:
      return {"error": "host not found", "top10": 0, "indexed": 0}

  # Статистика запросов
  r = requests.get(
      WM_URL + f"{user_id}/hosts/{host_id}/search-queries/popular",
      headers=headers,
      params={"query_indicator": "TOTAL_SHOWS,TOTAL_CLICKS,AVG_CLICK_POSITION", "limit": 100},
  )
  r.raise_for_status()
  queries = r.json().get("queries", [])
  top10 = sum(1 for q in queries if q.get("indicators", {}).get("AVG_CLICK_POSITION", 99) <= 10)

  # Индексация
  r = requests.get(WM_URL + f"{user_id}/hosts/{host_id}/summary", headers=headers)
  r.raise_for_status()
  summary = r.json()
  indexed = summary.get("indexing", {}).get("yandex", {}).get("indexedDocumentsCount", 0)

  return {"top10_queries": top10, "indexed_pages": indexed}

# ─── Генерация отчёта через Claude ───────────────────────────

def generate_report(client: dict, direct: dict, metrika: dict, webmaster: dict) -> str:
  anthropic_client = anthropic.Anthropic(api_key=CLAUDE_API_KEY)

  d_cur = direct["current"]
  d_prv = direct["previous"]
  m_cur = metrika["current"]
  m_prv = metrika["previous"]

  cost_delta = round((d_cur["cost"] - d_prv["cost"]) / d_prv["cost"] * 100, 1) if d_prv["cost"] else 0
  conv_delta = round((d_cur["conversions"] - d_prv["conversions"]) / d_prv["conversions"] * 100, 1) if d_prv["conversions"] else 0
  traffic_delta = round((m_cur["total_visits"] - m_prv["total_visits"]) / m_prv["total_visits"] * 100, 1) if m_prv["total_visits"] else 0

  prompt = f"""Ты помощник по digital-маркетингу. Напиши ежемесячный отчёт для клиента агентства.

КЛИЕНТ: {client["name"]}
ПЕРИОД: {datetime.now().strftime("%B %Y")}

ДАННЫЕ РЕКЛАМЫ (Яндекс.Директ):
- Расход: {d_cur["cost"]:,.0f} ₽ ({cost_delta:+.1f}% vs прошлый месяц)
- Лиды: {d_cur["conversions"]} ({conv_delta:+.1f}% vs прошлый месяц)
- Кликов: {d_cur["clicks"]}
- Стоимость лида: {d_cur["cpl"]:,.0f} ₽ (прошлый: {d_prv["cpl"]:,.0f} ₽)

ДАННЫЕ САЙТА (Яндекс.Метрика):
- Визитов: {m_cur["total_visits"]} ({traffic_delta:+.1f}% vs прошлый месяц)
- Источники трафика: {json.dumps(m_cur["sources"][:3], ensure_ascii=False)}

SEO (Яндекс.Вебмастер):
- Запросов в топ-10: {webmaster.get("top10_queries", "н/д")}
- Проиндексировано страниц: {webmaster.get("indexed_pages", "н/д")}

Напиши отчёт на русском языке из четырёх разделов:
1. «Введение» — 3-4 предложения о месяце в целом
2. «Что сделали» — конкретные работы с результатами
3. «На что обратить внимание» — 1-2 проблемные точки или риска
4. «План на следующий месяц» — 3 конкретных приоритета

Пиши как профессионал который общается с клиентом-предпринимателем: конкретно, без маркетинговой воды, с числами. Каждый раздел — 3-5 предложений."""

  message = anthropic_client.messages.create(
      model="claude-sonnet-4-6",
      max_tokens=1500,
      messages=[{"role": "user", "content": prompt}]
  )
  return message.content[0].text

# ─── Экспорт в Google Docs ────────────────────────────────────

def export_to_docs(client_name: str, report_text: str) -> str:
  """Создаёт Google Doc с отчётом, возвращает URL."""
  creds = Credentials.from_service_account_file(
      GOOGLE_CREDS_FILE,
      scopes=["https://www.googleapis.com/auth/documents",
              "https://www.googleapis.com/auth/drive"]
  )
  docs_service  = build("docs", "v1", credentials=creds)
  drive_service = build("drive", "v3", credentials=creds)

  title = f"Отчёт {client_name} — {datetime.now().strftime('%B %Y')}"
  doc = docs_service.documents().create(body={"title": title}).execute()
  doc_id = doc["documentId"]

  # Вставляем текст
  docs_service.documents().batchUpdate(
      documentId=doc_id,
      body={"requests": [
          {"insertText": {"location": {"index": 1}, "text": report_text}}
      ]}
  ).execute()

  # Даём права на просмотр по ссылке
  drive_service.permissions().create(
      fileId=doc_id,
      body={"type": "anyone", "role": "reader"},
  ).execute()

  return f"https://docs.google.com/document/d/{doc_id}/edit"

# ─── Оркестратор для одного клиента ──────────────────────────

def process_client(client: dict) -> None:
  print(f"  Обрабатываем: {client['name']}")

  direct    = get_direct_data(client["direct_login"])
  metrika   = get_metrika_data(client["metrika_counter"])
  webmaster = get_webmaster_data(client["webmaster_host"])

  report_text = generate_report(client, direct, metrika, webmaster)
  doc_url     = export_to_docs(client["name"], report_text)

  # Уведомляем менеджера в Telegram
  msg = (
      f"Отчёт готов: {client['name']}\n"
      f"Ссылка: {doc_url}\n"
      f"Нужна правка ~5 мин перед отправкой клиенту"
  )
  requests.post(
      f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage",
      json={"chat_id": client.get("telegram_chat", TELEGRAM_CHAT_ID), "text": msg}
  )
  print(f"  Готово: {doc_url}")

# ─── Запуск ───────────────────────────────────────────────────

if __name__ == "__main__":
  print(f"Старт: {len(CLIENTS)} клиентов")
  for i, client in enumerate(CLIENTS):
      process_client(client)
  print("Все отчёты готовы.")

Пара вещей которые нужно знать заранее.

Яндекс.Директ API при работе от имени агентства требует указывать Client-Login в заголовке — логин клиента, не агентства. Без этого заголовка получишь данные своего аккаунта, а не клиентского.

Google Service Account — это отдельный тип авторизации, не OAuth. Нужно создать сервисный аккаунт в Google Cloud Console, скачать JSON с ключами и дать этому аккаунту доступ к нужной папке в Google Drive. Без этого шага скрипт не сможет создавать документы.

Вебмастер API отдаёт позиции с задержкой в 1–2 дня. Для отчёта за месяц это несущественно.

Сколько часов это реально экономит

Ручной процесс (было)
Время на отчёт3 часа
15 клиентов/мес.45 часов
Стоимость временидо 135 000 ₽
Нарративпишет менеджер
Ошибки в данныхбывает
С агентом (стало)
Время на отчёт15 мин (генерация) + 5–7 мин (правка)
15 клиентов/мес.~5 часов
Стоимость API~75–100 ₽
Нарративпишет Claude, правит менеджер
Ошибки в данныхAPI не ошибается

Честная математика: было 45 часов на 15 клиентов, стало ~5 часов. Экономия — 40 часов в месяц. Эти 40 часов — это один аккаунт-менеджер который теперь работает с клиентами, а не копирует цифры между вкладками браузера.

Сколько времени уходит на отчётность у вас

Быстрая оценка

3 вопроса — посчитаем потери вашего агентства

Вопрос 1 из 3
Сколько клиентов ведёт ваше агентство или вы как фрилансер?
Вопрос 2 из 3
Сколько часов в среднем уходит на один ежемесячный отчёт?
Вопрос 3 из 3
Данные для отчёта собираются из скольких источников?

Как обработать 20+ клиентов за ночь

Скрипт выше обрабатывает клиентов последовательно. При 20 клиентах это занимает 40–50 минут, что вполне приемлемо для ночного запуска. Если нужно быстрее — легко распараллелить через asyncio с батчами по 5 клиентов.

Ограничение Яндекс.Директ API — 5 запросов в секунду на токен агентства. При параллельной обработке нужен простой asyncio.Semaphore(5). Метрика и Вебмастер менее строгие.

Запуск через cron в 02:00 в ночь с последнего числа на первое — к 8 утра все менеджеры видят готовые ссылки. К обеду отчёты у клиентов.

1
Зарегистрировать OAuth-приложение в Яндексе

Одно приложение для всех трёх API: Директ, Метрика, Вебмастер. Зайти на dev.ya.ru, создать приложение, выставить права на все три сервиса. Токен одного приложения работает для всех.

dev.ya.ru → Создать приложение → выбрать нужные API
2
Создать Google Service Account

В Google Cloud Console создать проект, включить Docs API и Drive API, создать сервисный аккаунт, скачать JSON с ключами. В Google Drive создать папку и дать сервисному аккаунту права редактора.

console.cloud.google.com → IAM → Service Accounts
3
Установить зависимости

Четыре библиотеки: anthropic, requests, google-api-python-client, google-auth. Python 3.9+. На сервере понадобится виртуальное окружение.

pip install anthropic requests google-api-python-client google-auth
4
Заполнить конфигурацию клиентов

Для каждого клиента: логин Директа, ID счётчика Метрики, URL хоста в Вебмастере, chat_id Telegram для уведомлений менеджеру. Менеджеры получают уведомления каждый в свой чат.

5
Настроить ежемесячный запуск

Cron на сервере на 02:00 первого числа каждого месяца. Или через n8n — Schedule Trigger с Execute Command. Важно: добавить обработку ошибок и уведомление если скрипт упал.

0 2 1 * * python /path/to/report_agent.py

Кейс: агентство с 15 клиентами

Реальная ситуация из практики агентства которое обратилось с запросом на автоматизацию отчётности.

До внедрения: два аккаунт-менеджера тратили на отчёты суммарно 43–47 часов в месяц. Отчёты готовились 3–5 числа каждого месяца. Часть клиентов регулярно писала «а где наш отчёт?» до 7–8 числа.

После: агент запускается в ночь с 31 на 1. К 8:00 первого числа месяца в Telegram у каждого менеджера лежат 7–8 ссылок на готовые документы. Каждый документ занимает 5–7 минут на правку — проверить цифры, добавить контекст который знает только команда. К полудню все отчёты у клиентов.

Суммарное время на отчётность — 4,5–5 часов вместо 45. Менеджеры не работают 5-го числа в режиме аврала. Клиенты получают отчёты 1-го числа — не потому что кто-то успел, а потому что это теперь работает само.

Один из клиентов написал: «Вы единственное агентство, которое присылает отчёт в начале месяца, а не в конце».


Три часа механической работы каждый месяц на каждого клиента — это то, от чего можно избавиться один раз. Настройка занимает 4–5 часов. Дальше агент работает сам.

Похожие кейсы

Источники

Источники

Часто задаваемые вопросы

Сколько времени занимает подготовка ежемесячного клиентского отчёта в агентстве?
По оценкам руководителей digital-агентств, один полноценный отчёт (Директ + Метрика + SEO + нарратив) занимает 2–4 часа аккаунт-менеджера. Агентство с 15 клиентами тратит 30–45 часов в месяц только на отчётность — это полноценная рабочая неделя одного человека.
Что делает агент при автоматической подготовке отчёта?
Агент параллельно запрашивает данные из трёх источников: Яндекс.Директ API v5 (расход, клики, конверсии), Яндекс.Метрика API (сессии, источники, цели) и Яндекс.Вебмастер API (позиции, индексация, ошибки). Claude получает структурированный JSON и пишет связный нарративный отчёт — не таблицы, а текст с анализом и планом на следующий месяц.
Во что экспортируется готовый отчёт?
В Google Docs через Google Docs API — с форматированием, заголовками и таблицей KPI. Альтернативно — в PDF через pdfkit. Менеджер получает ссылку на документ в Telegram и тратит 5–7 минут на финальную правку перед отправкой клиенту.
Сколько стоит Claude API для одного клиентского отчёта?
Один полный отчёт (3 источника данных + нарратив на 2 страницы) занимает примерно 4 000–6 000 токенов входа и 1 500 токенов выхода. На claude-sonnet-4-6 это около $0.04–0.07 за отчёт, то есть 3–5 рублей. Годовые расходы на 15 клиентов — меньше 1 000 рублей.
Как обработать 20+ клиентов за одну ночь?
Скрипт из статьи обрабатывает клиентов последовательно — 20 клиентов занимают 40–50 минут, что вполне подходит для ночного cron-запуска в 02:00. Для ускорения можно добавить asyncio с батчами по 5 клиентов: тогда 20 клиентов обрабатываются за 8–12 минут. Ограничение — параллельные запросы к Reports API Директа, поэтому больше 5 одновременно запускать не стоит.
Нужно ли программировать для запуска агента?
Достаточно базового Python — уметь запустить скрипт, установить зависимости через pip, зарегистрировать OAuth-приложение в Яндексе и создать Google Service Account для доступа к Google Docs API. Настройка с нуля занимает 4–5 часов. Весь код — в статье.
Обсуждение

    Пока без комментариев. Будьте первым.

    Войдите, чтобы отправить комментарий

    Вы сможете комментировать статьи, сохранять материалы

    или войдите по email

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

    Маркетинг работает, но продажи не растут?

    Отвечу на 3–5 вопросов о вашем бизнесе — и мы вместе разберём, где именно теряются клиенты и что с этим делать.

    Без продаж. Без навязчивых звонков.