Automatyzacja AI · Picvelo, Jubiler Pagacz · 2026 · 3 miesiące (rozwijany)
Google Ads prowadzone kodem - worker z twardymi limitami
Kampania powstaje z pliku ze specyfikacją i trafia na konto jako wstrzymana - żaden skrypt nie może przypadkiem zacząć wydawać pieniędzy.
- Node.js
- Google Ads API
- Anthropic API
- SQLite
- Express
- React
- Python
Wyzwanie
Konto reklamowe małego SaaS-a albo lokalnego salonu to za mało pracy na agencję i za dużo, żeby zaglądać do niego raz na tydzień. Kampania Performance Max zjadała budżet na ruchu z sieci reklamowej przy kilkunastu groszach za kliknięcie i zerze rejestracji, a rekomendacje systemu sprowadzały się do podniesienia stawek. Do tego dochodzi pytanie, na które panel reklamowy nie odpowiada: która rejestracja w aplikacji naprawdę przyszła z reklamy.
Nasze rozwiązanie
Konto prowadzi się kodem. Definicja kampanii siedzi w pliku ze specyfikacją, skrypt tworzy ją przez API jako wstrzymaną, a każdy zapis idzie najpierw jako walidacja bez zapisu. Do tego worker w cyklu: pobiera dane kampanii i wyszukiwanych fraz, prosi model o listę akcji, przepuszcza je przez warstwę twardych limitów i dopiero potem dotyka konta. Atrybucja nie opiera się na tagu w przeglądarce, tylko na identyfikatorze kliknięcia zapisywanym przy rejestracji po stronie serwera.
Co zbudowaliśmy
- 01 Kampania opisana w pliku i tworzona przez API jako wstrzymana
- 02 Walidacja bez zapisu przed każdą zmianą na koncie
- 03 Worker w cyklu: pobranie danych, propozycje modelu, limity, zapis
- 04 Guard: limit budżetu dziennego, maksymalna zmiana 20%, trzy mutacje na kampanię dziennie
- 05 Whitelist akcji: negatywy, pauza, wznowienie, budżet, flaga do przeglądu
- 06 Blokada podniesienia budżetu przy zerze konwersji przez siedem dni
- 07 Tryby PAUSED i EMERGENCY_STOP - stop dla wszystkich zmian
- 08 Kampania z ręczną flagą, której automat nie dotyka
- 09 Kilka kont w jednym narzędziu, każde z własnymi limitami i kontekstem biznesowym
- 10 Tryb obserwacji: pełny cykl bez żadnej zmiany na koncie
- 11 Panel React: kampanie, decyzje, akcje ręczne, ustawienia
- 12 Powiadomienia na Telegram, log decyzji i snapshoty w SQLite
Spis treści +
- Punkt wyjścia
- Decyzje, które ukształtowały projekt
- Kampania jako plik, nie jako formularz
- Walidacja przed zapisem, zawsze
- Guard, a nie zaufanie do modelu
- Kampania, której automat nie dotyka
- Log decyzji zamiast czarnej skrzynki
- Prawda o konwersjach po stronie serwera
- Praktyka: przebudowa konta
- Drugie konto: salon jubilerski
- Dwa mniejsze narzędzia obok
- Co z tego wyszło
- Technicznie
Punkt wyjścia
Picvelo - nasza platforma dla fotografów - sprzedaje abonament za 30 i 65 zł miesięcznie. Przy takiej cenie budżet reklamowy jest mały, a każda przepalona setka to realny procent kosztów miesiąca. Zostawić konta samego się nie da: wyszukiwane frazy zbierają śmieci, Performance Max rozchodzi się po sieci reklamowej, a system sam z siebie podpowiada głównie zwiększanie budżetu.
Agencja przy tej skali nie ma sensu ekonomicznego. Klikanie w panelu raz na tydzień oznacza tydzień palenia pieniędzy, zanim ktokolwiek zauważy problem.
Decyzje, które ukształtowały projekt
Kampania jako plik, nie jako formularz
Struktura kampanii - grupy, frazy, teksty reklam, stawki - jest opisana w pliku ze specyfikacją i tworzona przez API. Dzięki temu widać w jednym miejscu, co poszło na konto, a zmiana jest edycją pliku, a nie serią kliknięć, których po tygodniu nikt nie odtworzy.
Kampania trafia na konto jako wstrzymana. Nawet udany zapis nie kosztuje ani grosza, dopóki ktoś świadomie jej nie włączy.
Walidacja przed zapisem, zawsze
Google Ads API pozwala wysłać zmianę w trybie samej walidacji: przechodzi pełne sprawdzenie po stronie systemu, łącznie z limitami znaków w tekstach reklam, ale nic nie zmienia. Każda operacja idzie najpierw tak, a zapis jest drugim, świadomym krokiem.
Guard, a nie zaufanie do modelu
Worker pobiera dane, model dostaje je i zwraca propozycje akcji, ale nie ma dostępu do konta. Między nim a API stoi warstwa reguł: maksymalny budżet dzienny, maksymalna jednorazowa zmiana budżetu o 20 procent, najwyżej trzy zmiany na kampanię dziennie, lista dozwolonych typów akcji. Wszystko poza listą zostaje odrzucone i zapisane z powodem odrzucenia.
Osobna reguła blokuje podnoszenie budżetu kampanii, która od siedmiu dni nie ma konwersji. To dokładnie ten ruch, który podpowiada automatyka systemu reklamowego i który przy małym koncie boli najbardziej.
Model nie musi być nieomylny. Musi być ograniczony.
Kampania, której automat nie dotyka
Każda kampania ma flagę ręcznego sterowania. Jeśli człowiek grzebał w niej z panelu, worker ją omija. Bez tego każda ręczna korekta żyje do najbliższego cyklu, a narzędzie, które cofa decyzje właściciela, ląduje w koszu po tygodniu.
Log decyzji zamiast czarnej skrzynki
Do bazy trafia każda propozycja razem z werdyktem i uzasadnieniem. Panel pokazuje to jako listę: co miało być zrobione, co zostało wykonane, co odrzucone i dlaczego. Automat, po którym nie widać, co zrobił, jest gorszy od braku automatu, bo trzeba mu wierzyć na słowo.
Prawda o konwersjach po stronie serwera
Rejestracja jest przypisywana do reklamy na podstawie identyfikatora kliknięcia zapisanego przy zakładaniu konta, a nie na podstawie tego, czy tag w przeglądarce zdążył wystrzelić. Panel właściciela pokazuje więc rejestracje z reklam niezależnie od blokerów, zgód na ciasteczka i błędów po stronie przeglądarki. To liczba, na której podejmuje się decyzje o budżecie.
Praktyka: przebudowa konta
Narzędzia powstały po to, żeby ich użyć. Performance Max zostało wstrzymane, a na jego miejsce weszła kampania w sieci wyszukiwania zbudowana ze specyfikacji: aktywna jedna grupa zamiast trzech, stawki na maksymalizację kliknięć z sufitem 5 zł, budżet 15 zł dziennie. Kampania kieruje na zwykłą stronę produktową, nie na dedykowany landing - o tym, kiedy taki landing page pod Google Ads w ogóle się opłaca, piszemy osobno; tu ruch jest na razie za mały, żeby ten koszt się zwrócił. Do tego reguła wyjścia ustalona z góry: przy określonej kwocie wydanej bez ani jednej rejestracji kampania idzie do wyłączenia.
Ustawiony z góry warunek stopu jest tu równie ważny jak sama kampania. Bez niego test „czy reklama działa” nigdy się nie kończy.
Drugie konto: salon jubilerski
Pod koniec czerwca narzędzie dostało obsługę wielu kont. Każde ma własny plik z limitami, osobne dane dostępowe i opis biznesu, który trafia do promptu: co firma sprzedaje, czego nie sprzedaje, jakie frazy są trafne. Worker i panel pracują tylko na kontach oznaczonych jako włączone. Dziś aktywne jest konto sklepu jubilerskiego, przejęte 3 lipca.
Reguły dla jubilera są inne niż dla SaaS-a i dlatego siedzą w konfiguracji, a nie w kodzie. Sklep był w fazie rozbiegu, więc zero konwersji nie wyzwala cięć ani pauzy. Łączny budżet wszystkich kampanii ma sufit 32 zł dziennie, ustalony z właścicielem. Telefon z reklamy liczy się jako konwersja o wartości 40 zł, bo salon domyka sprzedaż przez telefon. Frazy typu „na zamówienie” czy „z grawerem” są chronione przed dodaniem do wykluczeń: jubiler robi biżuterię na indywidualne zamówienie, a model bez tej wiedzy uznawał je za nietrafione.
Przed przejęciem konta worker przez trzy dni działał w trybie obserwacji: pełny cykl, propozycje i werdykty w logu, zero zmian na koncie. Tak widać, co automat zrobiłby na cudzym koncie, zanim zrobi cokolwiek naprawdę.
Dwa mniejsze narzędzia obok
Pierwsze to warstwa wiersza poleceń do Google Ads i Meta Marketing API: weryfikacja połączenia i podgląd kampanii z terminala, bez klikania w dwa panele. Drugie to kalendarz publikacji i pipeline newsletterów w Pythonie na samej bibliotece standardowej, z panelem pokazującym kliknięcia, CTR i pozycje z Search Console w oknach 7, 28 i 90 dni. Dostęp do Search Console jest tylko do odczytu.
Żadne z nich nie jest produktem. To narzędzia pod konkretne firmy, w rozmiarze, w którym da się je utrzymać.
Co z tego wyszło
Narzędzie obsługuje dwa konta: nasze (Picvelo) i klienta (jubiler). Żadne nie jest publiczne, więc nie da się tego zweryfikować z zewnątrz. W kodzie i logu decyzji widać dyscyplinę: żaden zapis nie omija walidacji, żadna zmiana budżetu nie przekracza limitu z konfiguracji konta, a na koncie Picvelo kampania bez konwersji przez siedem dni nie dostaje podwyżki.
Czego nie wiemy: czy przebudowana kampania Picvelo w sieci wyszukiwania faktycznie przynosi rejestracje taniej niż wcześniejszy Performance Max. Reguła wyjścia (określona kwota wydana bez konwersji kończy kampanię) jest ustawiona, ale nie mamy jeszcze wyniku, który dałoby się tu uczciwie podać. Te narzędzia mierzą i ograniczają ryzyko, nie gwarantują wyniku kampanii. To jeden z projektów automatyzacji AI dla firm, które budujemy.
Technicznie
Node.js 20 w ESM, google-ads-api do odczytu i zmian na koncie, @anthropic-ai/sdk (Claude Sonnet) z tool use do proponowania akcji, SQLite na snapshoty i decyzje. API na Expressie z logowaniem sesyjnym, panel w React i Vite, całość za nginxem na VPS, cykl workera odpalany timerem systemd. Powiadomienia idą na Telegram. Konfiguracja kont, limitów i kontekstu biznesowego siedzi w plikach JSON, po jednym na konto.
Zrzuty ekranu

Panel bota: tryb pracy, model i interwał cyklu na wierzchu, pod spodem konta z limitami opisanymi w osobnych plikach.
Podobny problem do rozwiązania?
Napisz krótko, o co chodzi, wycena wraca w 24 godziny.
Zapytaj o wycenę →