Перейти до основного вмісту
megapromotingПоговорімо

Експертиза · Автоматизації маркетингу

Середовище роботи, у якому агент, що займається вашими кампаніями Google Ads і Meta, читає звіти, пропонує зміну з написаним мотивом і виконує лише в межах, які ви йому дали.

Сервіс — це пропозиція, а не реалізація, і ми кажемо це першими: сьогодні в нас немає системи, яка змінює бюджети чи ставки в Google Ads або Meta. У нас побудовані й запущені частини, з яких складається така система — надсилання сигналу реальної конверсії до Google, двигун, що купує платне просування з dry-run, увімкненим за замовчуванням, реєстр, який записує кожну дію, що коштує грошей, і вимикач, який зупиняє orchestrator без обхідного шляху.

Пропозиція, з умовами«offer», а не «delivered», і причину можна перевірити за кілька секунд. Ми шукали в понад 150 власних репозиторіях поля, через які рекламні API рухають гроші — `budget_micros`, `campaignBudget`, `target_cpa`, `target_roas`, `bid_strategy` — і вони ніде не трапляються, ані в Google, ані в Meta. Сьогодні ми не керуємо рекламними бюджетами. Те, що є, і що можна відкрити файл за файлом, — це елементи, які відділяють автоматизацію від аварії: надсилання офлайн-конверсій до Google через Data Manager API, 264 рядки в нашому production-проєкті; мотор платного просування, який купує boosteri, теги та пакети з dry-run, увімкненим за замовчуванням; Bayesian A/B tester, який відмовляється оголошувати переможця за менш ніж 100 показів на варіант, і orchestrator кампаній у MEGA CRM із перемикачем без `--force`. Послуга описана саме цими елементами. Не панеллю, якої не існує.

Агент, що займається кампаніями, — це не кнопка з написом «оптимізуй». Це програма, якій дозволено читати певні звіти, дозволено писати певні поля і більше нічого. Складне не в тому, щоб запропонувати зміну — моделі роблять це добре й швидко. Складне в тому, щоб написати, де вона зупиняється, як вона доводить, що зробила, і що стається, коли вона помиляється. Про це йдеться в роботі, і з цього ми починаємо розмову.

Ми від початку кажемо, чого в нас немає, щоб ви не дізналися це від когось іншого. Сьогодні в нас не працює система, яка піднімає бюджет у Google Ads або змінює ставку в Meta. Те, що працює, і що можна показати рядок за рядком, — це механізми безпеки, які ми написали для інших автоматизацій, що витрачають реальні гроші: dry-run, увімкнений за замовчуванням, лічильник на кожну платну дію, затримка при помилці, щоб не повторювати її в циклі, поріг доказу перед рішенням і вимикач, який відмовляється запускати оркестратор.

Побудована нами частина — не зміна бюджету, а сигнал — і саме з неї ми пропонуємо починати. Google навчається з того, що ви називаєте конверсією. Якщо ви скажете йому «форма надіслана», він шукатиме людей, які надсилають форми. Ми написали інтеграцію, яка каже йому «запит реально потрапив у систему клієнта», з ідентифікатором кліку та ідентифікаторами користувача, переданими через SHA-256, через Data Manager API. Це можна починати негайно, бо це існуючий код, а не обіцянка.

Інша частина середовища — шар, що читає звіти, шар, що пропонує, шар, що виконує — будується за тією самою схемою, яку ми використали там, де вже витрачали гроші через код. Спочатку лише читання, потім пропозиції без виконання, і тільки наприкінці відкривається виконання, поле за полем, з лімітом, написаним для кожного.

Що входить

Робота, за складовими

Читає звіти, а не панель

Від самого початку визначається, що агент читає і звідки: витрати по кампанії та групі оголошень, атрибутовані конверсії, вартість за залучення, частка показів, втрачена через бюджет, на відміну від втраченої через позицію — дві різні цифри, які потребують різних рішень. Читання відбувається через API, окремим service account, а не через скриншоти з інтерфейсу. Шар читання постачається і запускається першим, окремо, без жодних прав на запис у акаунт.

Пропонує зміну з написаним поруч мотивом

Пропозиція — це не цифра. Це поле, яке змінюється, стара величина, нова величина, вікно даних, на якому ухвалено рішення, і чому. Пропозиції збираються у звіт, який читає людина, у формі «я б підняв денний бюджет кампанії X з A до B, тому що за останні N днів вона була обмежена бюджетом у M% аукціонів, при вартості за залучення нижче узгодженого порога». Якщо причину не можна написати, пропозиція не робиться.

Три пороги: що робить сам, що просить людину, чого не робить ніколи

Кожне поле входить в одну з трьох категорій, записаних до запуску чого-небудь. Самостійно: зміни нижче узгодженого відсотка, в межах добового ліміту, для кампаній, позначених як відкриті. З погодженням: будь-яке перевищення ліміту, будь-яка нова кампанія, будь-яка нова аудиторія, будь-яка зміна ставки. Ніколи: зупинка кампанії, яку не він запустив, зміна способу оплати, доступ до білінгових облікових записів. Перелік записується в контракт, а не в код, і код його читає.

Dry-run увімкнено за замовчуванням — шаблон, який ми приносимо з собою

У нашому моторі платного просування на 999.md функція, що вирішує, чи витрачаються гроші, записана навпаки до звичного: dry-run активний, якщо змінна середовища не має точно значення `false`. Не задана, порожня, `true`, записана з помилкою — усе це зберігає dry-run увімкненим. Це свідомий вибір: помилка в конфігурації не може відкрити кран, вона може лише тримати його закритим. Така сама функція існує в двох незалежних місцях: моторі просування та виконавці перепублікації.

Кожну дію, що коштує грошей, записують у реєстр

У системі просування будь-яке виконання потрапляє до таблиці `agent_costs` з оголошенням, користувачем, типом дії, сумою в леях, ознакою успішності, сирою відповіддю постачальника та позначкою `dry_run`. Запис робиться в тій самій транзакції, що й оновлення розкладу, тож не може бути витрати без сліду або сліду без витрати. Коли це dry-run, записана сума дорівнює нулю, але рядок існує — видно точно, що б сталося.

Сигнал реальної конверсії — вже наявний елемент

Коли хтось приходить із платного кліку та надсилає запит, сигнал іде до Google лише після того, як запит потрапляє в систему клієнта, а не під час натискання кнопки. Інтеграція використовує `https://datamanager.googleapis.com/v1/events:ingest`, передає ідентифікатор кліку, момент, значення та валюту, а також ідентифікатори користувача, пропущені через SHA-256 — нормалізовану адресу малими літерами, телефон, приведений до міжнародного формату. Без ідентифікатора кліку надсилання зупиняється саме і позначається як пропущене, бо не було б атрибутованим.

Згоду ставлять перед тегами, а не після

Consent Mode v2 ініціалізується з усіма цілями маркетингу та аналітики на `denied` ще до запуску будь-якого скрипта Google, із вікном очікування 500 ms для оновлення. На `granted` переходять лише під час взаємодії людини з банером, без перезавантаження сторінки. Функціональне та безпекове сховище залишаються увімкненими, бо без них сторінка не працює. Це записано один раз і працює на кожному візиті.

Рішення між двома варіантами ухвалюють на основі доказів, а не враження

A/B-тестер, який ми побудували, моделює кожен варіант як Beta-Binomial з рівномірним prior і оцінює ймовірність того, що один кращий за інший, через 10.000 вибірок Monte Carlo. Він не оголошує переможця до 100 показів на варіант і до ймовірності 0,95. Якщо за 168 годин рішення не ухвалено, він закриває експеримент і каже, що він сплив, замість того щоб називати переможця, якого не може підтвердити.

Вимикач, який неможливо обійти

Оркестратор кампаній у нашій внутрішній системі перевіряє змінну середовища перед будь-чим. Якщо вона встановлена, він відмовляється запускатися — немає `--force`, немає аргументу, що це обійде. Його додали після реального інциденту з дубльованими надсиланнями, а не для декору. Такий самий шаблон застосовується і тут: перемикач на рівні облікового запису, що зупиняє всі записи, залишаючи читання та звітування працювати далі.

Як це виглядає

Шлях, крок за кроком.

01

Інвентар облікових записів і прав

Перелічуються рекламні облікові записи, хто має доступ сьогодні і з якою роллю, які конверсії визначені та які з них насправді є дублями тієї самої події. Визначаються три списки полів — самостійно, з погодженням, ніколи. Ми здаємо: письмовий інвентар, список конверсій із тим, що кожна з них насправді вимірює, і узгодження щодо трьох списків.

02

Шар читання, увімкнений сам

Підключається читання через API з обліковим записом, який не має права запису, і запускається щоденний звіт. На цьому етапі в облікових записах нічого не змінюється — видно лише те, що бачить агент. Саме тоді виявляються конверсії, які рахували неправильно, кампанії, обмежені бюджетом, про що ніхто не знав, і аудиторії, що перекриваються. Ми здаємо: щоденний звіт і список того, що ми знайшли під час читання.

03

Сигнал реальної конверсії

Збирається ідентифікатор кліку на сторінці входу, прив’язується до запиту у вашій системі й надсилається до Google лише тоді, коли запит підтверджено далі по ланцюгу, а не під час натискання кнопки. Запускається повторна спроба для невдалих надсилань, у вікні 90 днів. Поставляємо: функціональну інтеграцію, режим перевірки, який надсилає без запису, і журнал із тим, що пішло, що було пропущено та чому.

04

Шар пропозицій, без виконання

Агент починає писати пропозиції: поле, старе значення, нове значення, причина, вікно даних. Нічого не виконується. Це працює так, доки пропозиції не стають нудними — тобто доки людина, яка їх читає, усе одно натиснула б «так» майже для всіх. Поставляємо: потік пропозицій, з усім, що було запропоновано, і тим, що ви затвердили або відхилили, щоб було видно, де він помиляється.

05

Запуск виконання, поле за полем

Виконання відкривається для першого поля зі списку «singur», з dry-run, увімкненим за замовчуванням, і з денним лімітом. У журналі перевіряється, що записане є тим, що було запропоновано. Потім друге поле. Поставляємо: журнал дій, перемикач, який зупиняє всі записи, залишаючи читання увімкненим, і письмову процедуру на випадок, коли агент помилився.

Шлях одного рішення1citire prin API cuобліковий запис без права наscriere2пропозиція з полем,valoare veche,нове значення і причина3filtrul celor treiliste (singur / cuзатвердження / ніколи)4виконання з dry-runувімкнено за замовчуванням іplafon zilnic5рядок у реєстрівитрати, записано вта сама транзакціяВимикач зупинки перериває виконання і залишає читання увімкненим.
Шлях одного рішення

Дані

Чого торкаємося, де воно лежить і скільки лишається

Запитання, які ставить кожен, у кого є відповідальний за захист даних, — поставлені тут раніше, ніж їх поставить він.

Які дані воно зачіпає
Звіти кампанії, група оголошень і ключове слово, прочитані через API. Ідентифікатор кліку реклами, зібраний з адреси сторінки входу й збережений у запиті клієнта. Події конверсії з моментом, вартістю та валютою. На явний запит — адреса email і телефон особи, яка конвертувала, але лише у вигляді криптографічного резюме.
Що йде до Google і в якій формі
Ідентифікатор кліку йде у відкритому вигляді — він і є ідентифікатором кліку. Email і телефон ідуть лише як SHA-256, обчислений локально перед надсиланням: email очищено від пробілів і переведено в малі літери, телефон приведено до міжнародного формату. Поштова адреса не надсилається взагалі, тому що схема Google вимагає її повністю з поштовим кодом і кодом регіону, а поштовий код не є послідовним у реальних даних — причина записана в коді, а не припущена.
Де вони зберігаються і хто їх бачить
Дані кампанії та події зберігаються в базі вашого проєкту, на погодженій інфраструктурі. Креденціали рекламних облікових записів зберігаються у файлі середовища, який читає системний сервіс, не в коді й не в репозиторії джерел. Токен доступу тримається в пам’яті максимум 55 хвилин, хоча постачальник дає йому дійсність 3.600 секунд — запас навмисний, щоб ніколи не використати прострочений.
Скільки часу ще може бути надіслано подію
Практичне вікно — 90 днів від моменту конверсії — саме стільки приймає Google для офлайн-конверсій. Повторна спроба для невдалих надсилань працює точно в цьому вікні, пакетами, максимум із п’ятьма спробами на запит. Після п’яти спроб запит лишають у спокої й позначають як ненадісланий, замість того щоб повторювати безкінечно.
Що ми не робимо з даними
Ми не робимо публічними ваші списки клієнтів без письмової правової підстави й без того, щоб це було зазначено в інформуванні, яке бачить особа. Ми не передаємо дані між обліковими записами двох різних клієнтів. Ми не зберігаємо копії звітів кампанії поза погодженою системою. Реєстр обробок заповнюється до першого надсилання, а не після.

Випадок

Кампанія, яка платила за форми, а не за запити

Ситуація

Платформа з Кишинева, що отримує онлайн-запити, купувала трафік із пошуку. Конверсія, визначена в рекламному обліковому записі, була «відправлена форма». Алгоритм ставок робив саме те, про що його просили: приводив людей, які надсилають форми. Скільки з них ставали реальними запитами далі по ланцюгу, ніколи не поверталося назад до рекламної платформи.

Що ми збудували

Я переніс момент конверсії далі в ланцюзі. Ідентифікатор кліку знімається на сторінці входу і зберігається в запиті. Запит іде далі до системи нижче за ланцюгом; якщо вона повертає власний ідентифікатор — тобто справді ввійшла — лише тоді подія надсилається до Google через Data Manager API, з моментом, значенням, валютою та ідентифікаторами користувача, переданими через SHA-256. Без ідентифікатора кліку надсилання зупиняється саме і позначається як пропущене. Я додав job повторної спроби, який шукає запити з ідентифікатором кліку та без позначки надсилання, за останні 90 днів, партіями по 50, не більше п’яти спроб.

Що вийшло

Сигнал, який отримує рекламна платформа, тепер описує запити, що доходять до пункту призначення, а не натискання кнопки. Усі операції є інертними, якщо інтеграцію не налаштовано або її вимкнено — функція перевіряє всі сім креденціалів перед будь-чим і повертає «пропущено», якщо бракує хоча б одного. Помилки журналюються, але не поширюються далі: якщо Google не відповідає, запит клієнта йде далі без змін.

Чого цей випадок не каже

Це інтеграція сигналу, а не керування кампанією. Вона не змінює ані бюджет, ані ставку. Вплив на вартість за залучення нами не вимірювався і ми його не публікуємо — у коді є письмове очікування автора, а не вимір, і між цими двома є вся різниця.

Питання

Про що нас питають, перш ніж зателефонувати

Хто несе відповідальність, коли агент збільшує бюджет і витрати зростають?

Ви. І саме тому середовище побудоване так, щоб ви могли нести відповідальність усвідомлено, а не щоб вас застати зненацька. Конкретно: агент не має жодного дозволу на запис, якого йому не дав хтось на ім’я, письмово, поле за полем; кожне відкрите поле має стелю, понад яку пропозиція йде на людське затвердження; кожна виконана дія записується до реєстру зі старим значенням, новим значенням і причиною, в тій самій транзакції, що й виконання; і є перемикач, який зупиняє всі записи, не зупиняючи читання. Ми відповідаємо за те, що побудували — що межі, які ви прописали, дотримуються кодом, що реєстр повний, що вимкнення працює. Ми не відповідаємо за комерційний результат зміни, яку ви затвердили, так само як не відповідає й агентство, яке натискає кнопку вручну. Якщо хтось обіцяє вам інше, запитайте, що в нього написано в договорі в розділі обмеження відповідальності.

Чи маєте ви сьогодні систему, яка керує кампаніями Google Ads або Meta?

Ні. Ніде в нашому коді немає жодного поля, через яке змінюється бюджет, ставка або стратегія ставок у Google чи Meta. Ми перевірили, шукаючи саме ці назви полів у всіх репозиторіях. Те, що ми маємо, — це надсилання конверсій до Google, механізм, який купує платне просування на іншій платформі, і механізми безпеки навколо них. Сервіс будується, виходячи з цього, і позначений як пропозиція саме для того, щоб не було плутанини.

Тоді що саме ви вже побудували і витрачає реальні гроші?

Наш мотор платного просування на платформі оголошень, де публікують приватні оголошення, у Молдові. Він ставить платні мітки, запускає бустери з добовою межею та ціною за клік, активує пакети й републікує оголошення після інтервалу, обчисленого з реальної швидкості категорії. Він відмовляється запускати бустер, чия добова межа не вища за ціну за клік — маленька перевірка, але саме такого типу перевірки зазвичай бракує. Кожна дія проходить dry-run перевірку перед будь-яким викликом, що коштує.

Що він може змінювати сам і що потребує схвалення?

Це вирішується разом, заздалегідь, і записується. Наша відправна точка в обговоренні: сам — коригування нижче погодженого відсотка від добового бюджету, в межах абсолютної добової стелі, для кампаній, які ви позначили як відкриті; з підтвердженням — будь-яке перевищення стелі, нові кампанії, нові аудиторії, будь-який вплив на ставки; ніколи — зупинка кампаній, запущених кимось іншим, способи оплати, білінгові акаунти. Якщо на початку ви хочете все через затвердження, це валідний вибір, і ми його підтримуємо.

Звідки агент знає, що конверсія реальна, а не порожня форма?

Тому що сигнал не йде на натискання кнопки. Він іде лише тоді, коли запит підтверджено нижче за ланцюгом, у системі, яка для вас має значення, — ідентифікатор запиту, отриманий у відповідь від CRM або від партнера. До того моменту не сталося нічого, що варто називати конверсією. Різниця не косметична: алгоритм ставок Google навчається на тому, що ви йому надсилаєте, тож якщо ви надсилатимете форми, він приводитиме вам людей, які заповнюють форми.

Що відбувається з персональними даними тих, хто конвертується?

Email і телефон надсилаються лише як SHA-256 криптографічний підсумок, обчислений локально перед відправленням, із нормалізацією — email без пробілів і в нижньому регістрі, телефон приведений до міжнародного формату. Поштова адреса не надсилається взагалі. Ніщо не надсилається, доки людина не погодиться на маркетингову мету в банері, бо Consent Mode усе запускає з `denied`. Правову підставу й інформування записують до першого надсилання, а не після першого запитання когось.

Що відбувається, якщо виклик до рекламної платформи обривається посередині?

Відмову з необробленою відповіддю реєструють, а дію відкладають — у нашому виконавці повторної публікації, максимум із налаштованого охолодження та п’яти хвилин, явно для того, щоб помилка не перетворилася на цикл викликів. Надсилання невдалих конверсій повторюють через окреме завдання, партіями по 50, не більш як п’ять спроб на запит, у вікні 90 днів. Після п’ятої спроби воно зупиняється і лишається позначеним як ненадіслане, щоб це було видно, а не щоб зникло.

Як його зупинити, повністю і швидко?

Комутатор запису. Читання і звітування тривають, виконання зупиняється. У наших наявних системах це реалізовано двома способами, обидва перевірні: змінною середовища, яка має мати точно значення `false`, щоб витрачати гроші — будь-що інше тримає кран закритим — і вимикачем на оркестраторі кампаній, який відмовляє в запуску і не має `--force`. Ми не залежимо від кнопки в інтерфейсі, який може не завантажитися.

Чи гарантуєте ви зниження вартості за залучення?

Ні, і варто сказати чому, бо в нашому коді є рівно така цифра. У коментарі на початку інтеграції конверсій написане очікування — наскільки могла б знизитися вартість за придбання і наскільки могла б зрости частка показів. Це очікування, написане тим, хто це зібрав, а не вимірювання, і тому ви не знайдете його ніде на цьому сайті як результат. Що ми можемо пообіцяти — це те, що вимірювання стане можливим: з конверсіями, що щось означають, і з реєстром кожної зміни, порівняння до-після стає можливим. Без них — ні.

На чому ґрунтуються твердження вище (20 джерел)

20 з них — це код і файли з наших репозиторіїв. Ми не публікуємо їхні назви чи номери рядків: разом, на одній сторінці, це надто точно описало б, як побудовані системи, які належать не лише нам. Ми проходимо їх разом із вами, у репозиторії, за запитом — перевірка залишається можливою, просто вона відбувається в розмові.

Що вам хотілося б налагодити?

Розкажіть про свій процес. Разом визначимо, що варто збудувати, що можна підключити і як перевіримо результат.

Поговорімо