Разбор
Агент готовит ежемесячный отчёт для клиентов агентства: 3 часа работы за 15 минут
Аккаунт-менеджер тратит 3 часа на один клиентский отчёт. Агентство с 15 клиентами теряет 45 часов ежемесячно на цифры, которые клиент открывает раз — и закрывает. Python + Claude API собирает Директ, Метрику и Вебмастер и пишет связный нарратив на русском языке.
Аккаунт-менеджер в агентстве готовит отчёт клиенту примерно так: открывает Директ, выгружает таблицу, переходит в Метрику, смотрит трафик, залезает в Вебмастер ради позиций, потом садится в Google Docs и начинает писать: «В мае мы…». Через три часа — готово. До следующего раза.
Почему это больная точка
Три часа на отчёт — это не потому что менеджер медленно работает. Это потому что данные живут в трёх местах, и их нужно свести вручную. Директ отдельно. Метрика отдельно. SEO-позиции — в Вебмастере или в каком-нибудь Топвизоре. Сводная таблица KPI за месяц. Сравнение с прошлым месяцем. Потом написать текстовый вывод — что получилось, что не получилось, что делаем дальше.
Каждый отчёт — одна и та же механическая работа. Другие цифры, тот же процесс.
При этом клиент читает этот отчёт 5 минут. Смотрит на главные числа, скроллит до «что планируем дальше», закрывает. Три часа работы — пять минут внимания.
Хуже: эти три часа стоят денег. Час аккаунт-менеджера в московском агентстве — 1 500–3 000 рублей. На одном клиенте это 4 500–9 000 рублей в месяц только на отчётность. На 15 клиентах — до 135 000 рублей. При том что это не продающая работа — только документирование.
(3 источника)
сводной таблицы
нарратива
и отправка
Что делает агент вместо менеджера
Агент не просто «выгружает данные». Он забирает их из трёх источников параллельно, считает динамику месяц к месяцу, и пишет текст — связный нарратив на русском, где есть вводная часть, KPI с объяснением, вывод и план.
Менеджер получает в Telegram ссылку на готовый Google Docs. Открывает, смотрит, правит за 5–7 минут — убирает что-то неточное, добавляет детали которые знает только он. Отправляет клиенту.
Как выглядит готовый отчёт
Вот реальный пример того, что Claude пишет. Это не таблица и не список буллитов. Это текст который можно отправить клиенту.
В мае сосредоточились на двух направлениях. В рекламе провели перераспределение бюджета — убрали 12 ключевых слов с расходом выше 800 рублей и нулевыми конверсиями за апрель, высвободившиеся средства направили на кампании с конверсией выше 4%. Это дало снижение стоимости лида с 703 рублей до 611 рублей при росте числа лидов. В SEO запустили три новые статьи под коммерческие запросы — две из них уже вошли в топ-20, одна пока на 34-й позиции.
Органический трафик растёт третий месяц подряд — в мае плюс 11%. При этом доля брендового трафика сократилась с 28% до 23%, что говорит о росте привлечения новой аудитории, а не просто о лояльных посетителях. Есть одна точка риска: кампания «Установка кондиционеров» исчерпывает дневной бюджет к 15:00 — во второй половине дня показов нет. При текущих ставках для равномерного распределения нужно либо увеличить дневной лимит на 20%, либо скорректировать ставки в пиковые часы.
Три приоритета. Первый — решить ситуацию с бюджетом кампании «Установка кондиционеров»: провести анализ почасовой статистики и настроить корректировки ставок по времени суток. Второй — запустить ретаргетинговую кампанию на посетителей страницы «Цены», которые не оставили заявку (сейчас эта аудитория не охвачена). Третий — опубликовать ещё две статьи по информационным запросам для усиления позиций в топ-10 к августу.
Клиент видит конкретику, а не сухую выгрузку. Менеджер не сочиняет это два часа — он проверяет за пять минут.
Код агента
Полный рабочий скрипт. Нужен Python 3.9+.
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 дня. Для отчёта за месяц это несущественно.
Есть вопрос по теме?
Пришлю чеклист аудита рекламных кампаний и разберу ваш кейс
Сколько часов это реально экономит
Честная математика: было 45 часов на 15 клиентов, стало ~5 часов. Экономия — 40 часов в месяц. Эти 40 часов — это один аккаунт-менеджер который теперь работает с клиентами, а не копирует цифры между вкладками браузера.
Сколько времени уходит на отчётность у вас
Быстрая оценка
3 вопроса — посчитаем потери вашего агентства
Как обработать 20+ клиентов за ночь
Скрипт выше обрабатывает клиентов последовательно. При 20 клиентах это занимает 40–50 минут, что вполне приемлемо для ночного запуска. Если нужно быстрее — легко распараллелить через asyncio с батчами по 5 клиентов.
Ограничение Яндекс.Директ API — 5 запросов в секунду на токен агентства. При параллельной обработке нужен простой asyncio.Semaphore(5). Метрика и Вебмастер менее строгие.
Запуск через cron в 02:00 в ночь с последнего числа на первое — к 8 утра все менеджеры видят готовые ссылки. К обеду отчёты у клиентов.
Одно приложение для всех трёх API: Директ, Метрика, Вебмастер. Зайти на dev.ya.ru, создать приложение, выставить права на все три сервиса. Токен одного приложения работает для всех.
dev.ya.ru → Создать приложение → выбрать нужные APIВ Google Cloud Console создать проект, включить Docs API и Drive API, создать сервисный аккаунт, скачать JSON с ключами. В Google Drive создать папку и дать сервисному аккаунту права редактора.
console.cloud.google.com → IAM → Service AccountsЧетыре библиотеки: anthropic, requests, google-api-python-client, google-auth. Python 3.9+. На сервере понадобится виртуальное окружение.
pip install anthropic requests google-api-python-client google-authДля каждого клиента: логин Директа, ID счётчика Метрики, URL хоста в Вебмастере, chat_id Telegram для уведомлений менеджеру. Менеджеры получают уведомления каждый в свой чат.
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 часов. Дальше агент работает сам.
Есть вопрос по теме?
Пришлю чеклист аудита рекламных кампаний и разберу ваш кейс
Похожие кейсы
Источники
AI-агенты · Персональная карта
4 часа потратил — не работает?
Покажу где ты пошёл не туда и как сделать правильно за 2 недели
Получить разбор бесплатно →AI-агенты · 10 мест
Ты работаешь до полуночи — AI-агент будет работать вместо тебя
Покажу какой агент закроет твою главную операционную боль
Узнать свой маршрут →Есть вопрос по теме?
Пришлю чеклист аудита рекламных кампаний и разберу ваш кейс
Источники
Читайте также
Часто задаваемые вопросы
- Сколько времени занимает подготовка ежемесячного клиентского отчёта в агентстве?
- По оценкам руководителей 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 часов. Весь код — в статье.
Канал «Лёха Маркетолог»
Практика без воды: кейсы, инсайты, разборы. 1–2 поста в неделю.
Пока без комментариев. Будьте первым.