Przejdź do treści

Wykrywanie anomalii SEO z Google Search Console, BigQuery i AI

Zobacz, jak zbudować system wykrywania anomalii SEO na danych Search Console, regułach SQL, BigQuery ML i generatywnym AI.

Maciej Sala

Founder StriveLab

11 min czytaniaOpublikowano 4 czerwca 2026 (Aktualizacja 23 lipca 2026)

Dlaczego sam raport Search Console nie wystarcza do automatycznych alertów

Search Console udostępnia widok ostatnich 24 godzin, porównania okresów i raport Insights. Te funkcje pomagają w ręcznej analizie, ale nie zastępują alertów dopasowanych do architektury konkretnego serwisu. Średnia dla całej domeny może ukryć problem w jednej sekcji, a człowiek nadal musi pamiętać o otwarciu raportu i ustawieniu właściwych filtrów.

Najgorsze są spadki częściowe. Strona główna trzyma się dobrze, blog rośnie, ale katalog /poradniki/ traci widoczność po wdrożeniu nowego layoutu. W raporcie domeny wszystko wygląda „prawie normalnie”. W przychodach już nie.

Dlatego detekcję anomalii trzeba przenieść bliżej danych.

Dane z Google Search Console: czego szukać w eksporcie do BigQuery

Bulk Data Export z Google Search Console zapisuje dane do BigQuery codziennie. W praktyce najczęściej pracujesz na dwóch tabelach:

  • searchdata_site_impression: dane zagregowane na poziomie właściwości, z zapytaniami, krajem, typem wyszukiwania i urządzeniem.
  • searchdata_url_impression: dane na poziomie URL, dobre do analizy katalogów, szablonów i konkretnych stron.
  • ExportLog: rejestr udanych zapisów do każdej z tabel danych.

Do pierwszej wersji systemu alertów biorę tabelę URL. Daje mniej „ładny” obraz, ale lepiej odpowiada na pytanie, które naprawdę pada w firmie: co dokładnie spadło?

Załóżmy, że tabela nazywa się:

Code
my_project.searchconsole.searchdata_url_impression

W danych masz między innymi datę, URL, kraj, urządzenie, typ wyszukiwania, kliknięcia, impresje i sumę pozycji. CTR i średnią pozycję liczysz samodzielnie, bo eksport przechowuje liczniki potrzebne do ponownej agregacji.

Warto znać kilka szczegółów, zanim zaczniesz pisać alerty:

  • data_date jest datą danych w czasie Pacific Time, więc nie próbuj dopasowywać jej co do godziny do polskiej strefy czasowej,
  • eksport jest przyrostowy i może zawierać powtarzające się klucze, dlatego prawie zawsze ponownie agregujesz metryki,
  • ExportLog zapisuje wyłącznie udane eksporty, a tabele URL i site mogą zakończyć zapis o różnych porach,
  • tekst zapytania może zostać ukryty z powodów prywatności,
  • dane URL i site mają różną semantykę agregacji, więc ich sumy i średnie pozycje nie zawsze są bezpośrednio porównywalne,
  • metryki są zwykle przypisywane do adresu kanonicznego wybranego przez Google, a nie zawsze do URL-a, na który ostatecznie trafił użytkownik.

W tabeli URL Google używa pola sum_position, a w tabeli site pola sum_top_position. W obu przypadkach pozycja jest liczona od zera, więc średnią pozycję w stylu raportu GSC liczysz jako SUM(sum_position) / SUM(impressions) + 1.

Bulk Data Export zaczyna gromadzić dane dopiero po konfiguracji. Nie uzupełnia automatycznie wcześniejszej historii. Dane pozostają w BigQuery, dopóki ich nie usuniesz albo nie ustawisz wygaśnięcia partycji. Dla detektora korzystającego z kilkumiesięcznego poziomu bazowego okres przechowywania musi być odpowiednio dłuższy niż jego okno analizy.

Dane godzinowe z Search Analytics API a eksport do BigQuery

Bulk Data Export jest dobrym źródłem ukończonych danych dobowych, ale nie jest kanałem czasu rzeczywistego. Search Analytics API obsługuje wymiar godzinowy i stan HOURLY_ALL. Możesz pobrać do 10 dni danych z podziałem na godziny, a najnowsze obserwacje pojawiają się z opóźnieniem kilku godzin.

Nie mieszaj ich bez oznaczenia z zamkniętymi dniami w BigQuery. Ostatnie godziny są wstępne i mogą się zmienić. Najbezpieczniejszy układ składa się z dwóch osobnych alarmów:

  1. Wczesny alarm godzinowy porównuje ukończone godziny z tymi samymi godzinami wcześniejszych dni.
  2. Alarm dobowy potwierdza problem dopiero po udanym zapisie odpowiedniej tabeli w ExportLog.

Jeśli biznes nie wymaga reakcji tego samego dnia, sam eksport dobowy będzie prostszy i mniej podatny na fałszywe alarmy.

Segment URL przed alertem: jak ograniczyć fałszywe alarmy SEO

Alert na całą domenę jest kuszący. Jest też mało użyteczny.

Jeśli ruch spada o 12% w całym serwisie, nie wiesz jeszcze nic. Jeśli spada o 42% w katalogu /blog/ na mobile w Polsce, zaczyna się praca. Możesz sprawdzić ostatnie wdrożenia, crawl, indeksację, szablon, linkowanie wewnętrzne i nowe błędy w HTML.

Dlatego pierwszy krok to prosta klasyfikacja URL-i. Segmentuj ścieżkę, a nie cały URL, aby parametr zapytania zawierający /blog/ nie przypisał strony do niewłaściwej grupy.

Code
CREATE OR REPLACE VIEW `my_project.seo.url_daily` AS
WITH source AS (
  SELECT
    data_date,
    url,
    COALESCE(
      REGEXP_EXTRACT(url, r'^https?://[^/]+(/[^?#]*)'),
      '/'
    ) AS path,
    country,
    device,
    clicks,
    impressions,
    sum_position
  FROM `my_project.searchconsole.searchdata_url_impression`
  WHERE search_type = 'web'
    AND url IS NOT NULL
)
SELECT
  data_date AS date,
  url,
  CASE
    WHEN path = '/blog' OR STARTS_WITH(path, '/blog/') THEN 'blog'
    WHEN path = '/uslugi' OR STARTS_WITH(path, '/uslugi/') THEN 'services'
    WHEN path = '/produkty' OR STARTS_WITH(path, '/produkty/') THEN 'products'
    WHEN path = '/kategorie' OR STARTS_WITH(path, '/kategorie/') THEN 'categories'
    ELSE 'other'
  END AS page_group,
  country,
  device,
  SUM(clicks) AS clicks,
  SUM(impressions) AS impressions,
  SAFE_DIVIDE(SUM(clicks), SUM(impressions)) AS ctr,
  -- przechowujemy surową sumę pozycji; średnią liczymy dopiero po finalnej agregacji
  SUM(sum_position) AS sum_position
FROM source
GROUP BY date, url, page_group, country, device;

Zwróć uwagę na pozycję. Eksport GSC nie daje gotowej średniej, tylko sum_position. Średnią liczysz dopiero przy odczycie za pomocą SAFE_DIVIDE(SUM(sum_position), SUM(impressions)) + 1. Widok przechowuje surową sumę, ponieważ ponowne uśrednianie gotowych średnich dałoby niepoprawny wynik.

To nie musi być piękne. Ma być czytelne i łatwe do zmiany, gdy dodasz nowy katalog albo typ strony.

Prosta detekcja anomalii SEO na kompletnych danych

Na start nie potrzebujesz uczenia maszynowego. Możesz porównać ostatni pomyślnie wyeksportowany dzień z medianą ośmiu wcześniejszych wystąpień tego samego dnia tygodnia. Poniedziałek porównujesz z poniedziałkami, a nie z niedzielą.

Samo CURRENT_DATE() - 3 jest kruche. Eksport może pojawić się później, a korekta starszych danych może zostać zapisana ponownie. Datę raportową wybierz z ExportLog. Następnie zbuduj pełną siatkę segmentów i poprawnych dat. Bez tego segment, który spadł do zera i nie ma żadnego wiersza w tabeli, zniknie również z wyniku alertu.

Code
DECLARE report_date DATE DEFAULT (
  SELECT MAX(data_date)
  FROM `my_project.searchconsole.ExportLog`
  WHERE namespace = 'searchdata_url_impression'
);
 
ASSERT report_date IS NOT NULL AS 'Brak udanego eksportu tabeli URL';
 
WITH exported_dates AS (
  SELECT data_date AS date
  FROM `my_project.searchconsole.ExportLog`
  WHERE namespace = 'searchdata_url_impression'
    AND data_date BETWEEN DATE_SUB(report_date, INTERVAL 70 DAY) AND report_date
  GROUP BY date
),
comparison_dates AS (
  SELECT date
  FROM exported_dates
  WHERE EXTRACT(DAYOFWEEK FROM date) = EXTRACT(DAYOFWEEK FROM report_date)
  QUALIFY ROW_NUMBER() OVER (ORDER BY date DESC) <= 9
),
daily_raw AS (
  SELECT
    date,
    page_group,
    COALESCE(country, 'UNKNOWN') AS country,
    COALESCE(device, 'UNKNOWN') AS device,
    SUM(clicks) AS clicks,
    SUM(impressions) AS impressions,
    SUM(sum_position) AS sum_position
  FROM `my_project.seo.url_daily`
  JOIN comparison_dates USING (date)
  WHERE date BETWEEN DATE_SUB(report_date, INTERVAL 70 DAY) AND report_date
  GROUP BY 1, 2, 3, 4
),
segments AS (
  SELECT DISTINCT page_group, country, device
  FROM daily_raw
),
daily AS (
  SELECT
    d.date,
    s.page_group,
    s.country,
    s.device,
    COALESCE(r.clicks, 0) AS clicks,
    COALESCE(r.impressions, 0) AS impressions,
    SAFE_DIVIDE(r.clicks, r.impressions) AS ctr,
    SAFE_DIVIDE(r.sum_position, r.impressions) + 1 AS avg_position
  FROM comparison_dates d
  CROSS JOIN segments s
  LEFT JOIN daily_raw r
    ON r.date = d.date
    AND r.page_group = s.page_group
    AND r.country = s.country
    AND r.device = s.device
),
baseline_medians AS (
  SELECT
    page_group,
    country,
    device,
    APPROX_QUANTILES(clicks, 100)[OFFSET(50)] AS median_clicks,
    APPROX_QUANTILES(impressions, 100)[OFFSET(50)] AS median_impressions,
    APPROX_QUANTILES(ctr, 100)[OFFSET(50)] AS median_ctr,
    APPROX_QUANTILES(avg_position, 100)[OFFSET(50)] AS median_position
  FROM daily
  WHERE date < report_date
  GROUP BY page_group, country, device
  HAVING COUNT(*) >= 6
),
baseline AS (
  SELECT
    m.*,
    APPROX_QUANTILES(ABS(d.clicks - m.median_clicks), 100)[OFFSET(50)]
      AS mad_clicks
  FROM baseline_medians m
  JOIN daily d USING (page_group, country, device)
  WHERE d.date < report_date
  GROUP BY
    m.page_group,
    m.country,
    m.device,
    m.median_clicks,
    m.median_impressions,
    m.median_ctr,
    m.median_position
),
current_day AS (
  SELECT *
  FROM daily
  WHERE date = report_date
)
SELECT
  report_date,
  c.page_group,
  c.country,
  c.device,
  c.clicks,
  b.median_clicks,
  SAFE_DIVIDE(c.clicks - b.median_clicks, b.median_clicks) AS clicks_delta,
  SAFE_DIVIDE(
    c.clicks - b.median_clicks,
    1.4826 * NULLIF(b.mad_clicks, 0)
  ) AS clicks_robust_z,
  c.impressions,
  b.median_impressions,
  SAFE_DIVIDE(c.impressions - b.median_impressions, b.median_impressions) AS impressions_delta,
  c.ctr,
  b.median_ctr,
  c.ctr - b.median_ctr AS ctr_delta,
  c.avg_position,
  b.median_position,
  c.avg_position - b.median_position AS position_delta,
  GREATEST(b.median_clicks - c.clicks, 0) AS click_gap_vs_baseline
FROM current_day c
JOIN baseline b USING (page_group, country, device)
WHERE b.median_clicks >= 20
  AND b.median_clicks - c.clicks >= 10
  AND (
    SAFE_DIVIDE(c.clicks - b.median_clicks, b.median_clicks) <= -0.30
    OR SAFE_DIVIDE(
      c.clicks - b.median_clicks,
      1.4826 * NULLIF(b.mad_clicks, 0)
    ) <= -3
  )
ORDER BY clicks_delta ASC;

Zapytanie łączy trzy zabezpieczenia. Wymaga sensownego poziomu bazowego, minimalnej bezwzględnej różnicy oraz spadku procentowego albo dużego odchylenia względem medianowego odchylenia bezwzględnego, czyli MAD. Współczynnik 1.4826 skaluje MAD do wartości zbliżonej do odchylenia standardowego przy rozkładzie normalnym. Nie zamienia to prostej reguły w dowód statystyczny, ale ogranicza alarmy wywołane zwykłym szumem.

click_gap_vs_baseline jest różnicą względem poziomu bazowego, a nie wiarygodnym szacunkiem utraconego ruchu. Popyt, sezonowość, święta, kampanie marki i zmiany w wynikach wyszukiwania również wpływają na liczbę kliknięć.

Lepszy alert SEO: rozbij przyczynę na typ spadku

Spadek kliknięć to objaw. Przyczyna siedzi głębiej.

Jeśli impresje spadły, problem może dotyczyć indeksacji, pozycji, sezonowości albo popytu. Jeśli impresje stoją w miejscu, ale CTR leci w dół, sprawdź title, description, rich results, zmianę intencji zapytań i wygląd SERP. Jeśli pozycja spada, zacznij od zmian w treści, linkowaniu, wydajności i konkurencji.

W SQL możesz dodać prostą etykietę:

Code
CASE
  WHEN impressions_delta <= -0.25 THEN 'visibility_drop'
  WHEN ctr < median_ctr * 0.75 THEN 'ctr_drop'
  WHEN position_delta >= 2 THEN 'ranking_drop'
  ELSE 'mixed'
END AS anomaly_type

To nie jest diagnoza. To skrót, który mówi zespołowi, od którego obszaru zacząć. Warto też liczyć click_gap_vs_baseline, ponieważ alerty sortowane wyłącznie po procencie spadku promują małe segmenty. Różnicę względem poziomu bazowego traktuj jako miarę priorytetu, a nie dowód utracenia konkretnej liczby kliknięć przez błąd SEO.

AI w monitoringu SEO: autor notatki, nie sędzia

Wrzucenie danych do modelu z pytaniem „czy mamy problem?” nie tworzy wiarygodnego detektora. Model może dopowiedzieć wzorzec, którego nie ma w danych. Reguła SQL też może być źle zaprojektowana, ale pozostaje jawna, testowalna i łatwa do audytu.

Lepszy układ wygląda tak:

  1. SQL wykrywa segmenty, które przekroczyły progi.
  2. Query dopisuje typ anomalii i metryki.
  3. AI dostaje tylko wyniki alertu i pisze krótką notatkę dla człowieka.

Przykładowy prompt:

Code
Napisz krótką notatkę SEO po polsku.
Nie diagnozuj ponad dane.
Użyj 4 sekcji:
- co spadło
- skala spadku
- najbardziej prawdopodobny obszar do sprawdzenia
- pierwsze 3 kroki
 
Dane:
page_group: blog
country: POL
device: MOBILE
clicks_delta: -0.41
impressions_delta: -0.38
ctr_delta: -0.04
position_delta: +0.6
anomaly_type: visibility_drop

Wartość ctr_delta w tym kontrakcie jest różnicą zapisaną jako ułamek. -0.04 oznacza spadek o 4 punkty procentowe, a nie o 4%. Jednostki powinny być jawne zarówno w tabeli alertów, jak i w prompcie.

Wynik powinien być konkretny i rozdzielać obserwację od hipotezy. Model może napisać, że spadły wyświetlenia, a następnie wskazać indeksację, zmiany szablonu i popyt jako obszary do sprawdzenia. Nie powinien stwierdzać, że znalazł przyczynę.

Jeśli chcesz wygenerować takie notatki z poziomu BigQuery, możesz użyć AI.GENERATE_TEXT z modelem zdalnym skonfigurowanym w BigQuery ML. Funkcja nadal wysyła prompt do wskazanego modelu w Vertex AI albo obsługiwanej usłudze modelowej, więc nie oznacza to, że dane pozostają wyłącznie w silniku BigQuery. Lokalizacja datasetu, połączenia i endpointu oraz wymagania dotyczące przetwarzania danych muszą być świadomą decyzją.

Model zdalny można utworzyć na przykład tak:

Code
CREATE OR REPLACE MODEL `my_project.seo.gemini_flash`
REMOTE WITH CONNECTION DEFAULT
OPTIONS (ENDPOINT = 'gemini-2.5-flash');

Przed uruchomieniem potrzebujesz aktywnych API, połączenia Cloud Resource oraz odpowiedniej roli dla konta usługi tego połączenia. Dataset i połączenie powinny znajdować się w zgodnej lokalizacji. Globalnego endpointu nie używaj, jeśli musisz kontrolować region przetwarzania.

Code
SELECT *
FROM AI.GENERATE_TEXT(
  MODEL `my_project.seo.gemini_flash`,
  (
    SELECT
      FORMAT(
        '''
        Napisz krótką notatkę SEO po polsku.
        Nie diagnozuj ponad dane.
        Segment: %s / %s / %s.
        Spadek kliknięć: %.0f%%.
        Spadek impresji: %.0f%%.
        Zmiana CTR: %.2f pp.
        Zmiana pozycji: %.2f.
        Typ anomalii: %s.
        Różnica kliknięć względem mediany: %d.
        ''',
        page_group,
        country,
        device,
        clicks_delta * 100,
        impressions_delta * 100,
        ctr_delta * 100,
        position_delta,
        anomaly_type,
        click_gap_vs_baseline
      ) AS prompt
    FROM `my_project.seo_alerts.daily_anomalies`
    WHERE report_date = (
      SELECT MAX(report_date)
      FROM `my_project.seo_alerts.daily_anomalies`
    )
    ORDER BY click_gap_vs_baseline DESC
    LIMIT 5
  ),
  STRUCT(512 AS max_output_tokens, 0.2 AS temperature)
);

Przy większej tabeli najpierw zapisz wybrane alerty do małej tabeli stagingowej, a dopiero potem przekaż ją do AI.GENERATE_TEXT. LIMIT ogranicza liczbę wynikowych promptów, ale nie oznacza, że BigQuery pominie wcześniejsze przetwarzanie całego wejścia. Po wywołaniu kontroluj również status odpowiedzi modelu i obsłuż błędy kwoty zamiast zakładać, że każdy wiersz otrzymał poprawny tekst.

Harmonogram wykrywania anomalii SEO: raz dziennie i do tabeli alertów

BigQuery Scheduled Queries pozwala uruchamiać zapytanie cyklicznie. Dla eksportu GSC wystarczy jedno uruchomienie dziennie. Zapytanie powinno jednak wybrać najnowszą datę potwierdzoną w ExportLog, a nie zakładać stałego opóźnienia dwóch lub trzech dni.

Wyniki zapisuję do tabeli:

Code
my_project.seo_alerts.daily_anomalies

Minimalny schemat może wyglądać tak:

Code
CREATE TABLE IF NOT EXISTS `my_project.seo_alerts.daily_anomalies` (
  report_date DATE,
  page_group STRING,
  country STRING,
  device STRING,
  anomaly_type STRING,
  clicks INT64,
  baseline_clicks INT64,
  clicks_delta FLOAT64,
  clicks_robust_z FLOAT64,
  impressions INT64,
  baseline_impressions INT64,
  impressions_delta FLOAT64,
  ctr FLOAT64,
  baseline_ctr FLOAT64,
  ctr_delta FLOAT64,
  avg_position FLOAT64,
  baseline_position FLOAT64,
  position_delta FLOAT64,
  click_gap_vs_baseline INT64,
  created_at TIMESTAMP
)
PARTITION BY report_date
CLUSTER BY page_group, country, device
OPTIONS (require_partition_filter = TRUE);

Scheduled Query może zostać uruchomione ponownie po błędzie albo ręcznie. Zwykłe WRITE_APPEND utworzy wtedy duplikaty. Zapisuj wynik przez MERGE z kluczem złożonym z daty i wymiarów albo nadpisuj wyłącznie partycję konkretnego dnia. Do uruchamiania użyj dedykowanego konta usługi z minimalnymi uprawnieniami, a nie poświadczeń pojedynczego pracownika.

epoch_version w ExportLog rośnie, gdy Google poprawia wcześniej zapisane dane. Jeśli materializujesz agregaty, zapamiętuj najwyższą przetworzoną wersję dla pary namespace i data_date. Zmiana wersji powinna uruchomić ponowne przeliczenie odpowiedniej partycji oraz alertów, których poziom bazowy korzysta z poprawionej daty.

Dopiero na tej tabeli budujesz powiadomienie do Slacka, poczty, Looker Studio albo webhooka. Powiadomienie nie powinno uruchamiać się dla każdego wiersza. Lepiej zebrać anomalie w jeden dzienny raport i posortować je po click_gap_vs_baseline, prawdopodobieństwie anomalii albo własnej wartości biznesowej.

Google Cloud pozwala oprzeć alert o metrykę liczby wierszy zwróconych przez Scheduled Query. Zapytanie zwraca wiersze tylko wtedy, gdy są anomalie, a Cloud Monitoring reaguje na wartość większą od zera. Metryka ostatniej liczby wierszy może jednak utrzymywać poprzednią wartość przez wiele tygodni po wyłączeniu albo awarii harmonogramu. Osobno monitoruj completed_runs, historię uruchomień lub logi BigQuery Data Transfer Service. Brak nowego wykonania nie może wyglądać jak poprawny stan systemu.

Co sprawdzić po alercie

Alert mówi „tu jest dym”. Nie mówi jeszcze „tu jest ogień”. Po spadku w segmencie idę tym porządkiem:

  • kompletność ExportLog i status wykonania pipeline'u,
  • dane z analityki lub logów serwera, aby odróżnić problem raportowania od realnej zmiany wejść,
  • ostatnie wdrożenia na szablon lub layout,
  • zmiany w robots.txt, sitemapie, linkach kanonicznych i meta robots,
  • raport indeksowania w GSC dla przykładowych URL-i,
  • logi crawlowania, jeśli masz Cloudflare lub serwerowe access logi,
  • zmiany title i description,
  • nowa treść konkurencji albo zmiana intencji w SERP,
  • problemy z renderowaniem, szczególnie przy React/Next.js.

Kolejność zależy od sygnału. Jeśli problem pojawił się bezpośrednio po wdrożeniu i dotyczy konkretnego szablonu, sprawdzenie kodu ma wysoki priorytet. Jeśli spadły wyświetlenia w całej branży, trzeba uwzględnić sezonowość, popyt i zmiany w wynikach wyszukiwania. Warto przechowywać obok szeregu daty wdrożeń, migracji, kampanii, świąt oraz incydentów. Korelacja czasowa pomaga ustalać kolejność pracy, ale nie dowodzi przyczyny.

Dobry alert SEO nie ma imponować. Ma skrócić drogę od spadku do pierwszej sensownej decyzji.

Kiedy dołożyć model anomalii

Reguły SQL wystarczą długo. Model ma sens, gdy masz dużo segmentów, sezonowość, kilka krajów i różne typy stron. AI.DETECT_ANOMALIES wykorzystuje wbudowany model TimesFM i porównuje wartości docelowe z prognozowanym zakresem. Wynik zawiera między innymi is_anomaly, granice przedziału, prawdopodobieństwo oraz status wywołania.

Przed wywołaniem przygotuj gęstą tabelę z jednym wierszem dla każdej poprawnie wyeksportowanej daty i kombinacji identyfikatorów. Nie przekazuj surowej, nieregularnej tabeli, w której brak wiersza może oznaczać zarówno zero, jak i brak eksportu.

Code
SELECT *
FROM AI.DETECT_ANOMALIES(
  (
    SELECT
      date,
      page_group,
      country,
      device,
      clicks
    FROM `my_project.seo.segment_daily_dense`
    WHERE date >= DATE_SUB(
      (
        SELECT MAX(date)
        FROM `my_project.seo.segment_daily_dense`
      ),
      INTERVAL 180 DAY
    )
  ),
  data_col => 'clicks',
  timestamp_col => 'date',
  target_last_n_points => 7,
  id_cols => ['page_group', 'country', 'device'],
  model => 'TimesFM 2.5',
  context_window => 128,
  anomaly_prob_threshold => 0.95
)
WHERE is_anomaly
  AND COALESCE(ai_detect_anomalies_status, '') = '';

Wartości context_window, modelu i progu nie są uniwersalne. Waliduj je na historycznych incydentach i okresach bez problemów. Wyższy anomaly_prob_threshold poszerza przedział i zwykle ogranicza liczbę wykrytych anomalii. Dla segmentu z bardzo małym ruchem nawet poprawnie uruchomiony model może nie dostarczyć użytecznego sygnału.

Dlatego traktuj model jako drugą warstwę:

  • Warstwa pierwsza obejmuje kontrolę danych i reguły SQL. Jest szybka, przewidywalna i zrozumiała dla zespołu.
  • Warstwa druga wykorzystuje model anomalii. Pomaga przy sezonowości i wielu szeregach czasowych.
  • Warstwa trzecia wykorzystuje generatywne AI. Redaguje streszczenie i sugeruje pytania do sprawdzenia.

Kontrola kosztów i jakości danych

Koszt zależy od modelu rozliczeń BigQuery, ilości przetworzonych danych oraz wywołań BigQuery ML i modelu zdalnego. W trybie on-demand LIMIT nie zmniejsza liczby skanowanych bajtów. W praktyce:

  • filtruj bezpośrednio po partycji data_date i sprawdzaj plan zapytania,
  • zapisuj dzienne agregaty do tabeli partycjonowanej i klastrowanej zamiast za każdym razem czytać surowy eksport,
  • używaj progów wolumenu, żeby nie generować alertów i promptów dla mikrosegmentów,
  • sprawdzaj ExportLog, zanim uznasz spadek za problem SEO,
  • materializuj mały zbiór promptów przed wywołaniem modelu,
  • ogranicz max_output_tokens i liczbę segmentów wysyłanych do modelu,
  • używaj dry run oraz maximum bytes billed do kontroli zapytań,
  • ustaw retencję partycji zgodną z potrzebnym oknem historycznym i polityką danych.

To nie jest detal techniczny. Bez kontroli kosztów alert SEO szybko staje się kolejnym raportem, którego nikt nie chce utrzymywać.

Audyt techniczny i optymalizacja pod kątem SEO i GEO.
Audyt techniczny SEO

Często zadawane pytania

Po co wykrywać anomalie SEO w BigQuery, skoro mam raporty w Search Console?

Search Console ma raport skuteczności, widok ostatnich 24 godzin i Insights, ale nie zastępuje własnego procesu monitoringu. BigQuery pozwala automatyzować dzienne alerty, segmentować adresy URL i łączyć wyniki z wdrożeniami lub danymi biznesowymi. Do alarmów wymagających danych godzinowych można użyć Search Analytics API, pamiętając, że najnowsze dane są wstępne.

Czy Bulk Data Export z Search Console ma limit wierszy jak interfejs GSC?

Nie działa jak zwykły eksport z interfejsu. Google opisuje Bulk Data Export jako dzienny zrzut danych do BigQuery, bez typowego limitu wierszy z UI. Nadal obowiązują ograniczenia prywatności. Tekst rzadkich zapytań może być ukryty, a w niektórych typach danych część wymiarów może być pusta. Nie należy też zakładać, że sumy po dowolnych wymiarach będą identyczne z wykresem zagregowanym na poziomie usługi.

Od ilu danych ma sens wykrywanie anomalii SEO?

Nie istnieje uniwersalny próg kliknięć. Liczy się wolumen w pojedynczym monitorowanym segmencie i liczba obserwacji historycznych. Przy małym ruchu lepiej agregować dane tygodniowo, łączyć podobne adresy URL i wymagać minimalnej bezwzględnej zmiany. Dzienny procent dla kilku kliknięć będzie generował głównie szum.

Czy AI powinno samo decydować, że mamy problem SEO?

Nie. Model może opisać anomalię i zasugerować, gdzie szukać przyczyny. Pierwsza wersja detekcji powinna opierać się na jawnych regułach statystycznych i biznesowych w SQL. Generatywne AI nie powinno samodzielnie ogłaszać przyczyny ani uruchamiać zmian na stronie.

Czy BigQuery AI.DETECT_ANOMALIES zastępuje własne reguły SQL?

Nie w pierwszej wersji systemu. AI.DETECT_ANOMALIES może pomóc przy wielu szeregach czasowych i sezonowości, ale nadal potrzebujesz kompletnych dat, segmentów, progów wpływu, monitoringu samego eksportu i procesu weryfikacji. Funkcja wykorzystuje wbudowany model TimesFM, a jej wynik jest sygnałem statystycznym, nie diagnozą SEO.

Jak często uruchamiać alerty SEO?

Raz dziennie wystarczy w większości projektów. Dane GSC mają opóźnienie, a Bulk Data Export zapisuje dane dobowe. Jeżeli potrzebujesz szybszego sygnału, Search Analytics API udostępnia dane godzinowe z ostatnich dni. Takie dane są wstępne, dlatego alarm godzinowy powinien mieć osobne progi i zostać potwierdzony po zamknięciu dnia.

O autorze

Maciej Sala

Maciej Sala — Product Manager i Frontend Developer z bogatym doświadczeniem w marketingu internetowym oraz SEO. Na co dzień pracuje z Reactem, Next.js i TypeScriptem, a ostatnio także z Astro i narzędziami do automatyzacji procesów AI. Sprawnie łączy perspektywę produktową z praktycznym podejściem do kodu. Przez kilka lat był związany z branżą gier wideo jako project manager i game designer. Absolwent historii na Uniwersytecie Jagiellońskim oraz studiów podyplomowych z marketingu internetowego na AGH w Krakowie. Po godzinach trenuje na siłowni, maluje figurki i rozwijam własne projekty.

Pomagam przekładać takie tematy na konkretne wdrożenia w frontendzie, SEO, analityce i procesie produktowym.

Skontaktuj się ze mną

Biblioteka wiedzy na temat SEO

Czytaj dalej

Zobacz więcej wpisów
Data Science w SQL i wdrażanie modeli AI w BigQuery

Zapomnij o żmudnym eksporcie danych do zewnętrznych narzędzi i skomplikowanych skryptach w Pythonie. Dziś praca analityka zmienia się na naszych oczach: model predykcyjny uruchamiasz jedną komendą w konsoli BigQuery , a wyniki odbierasz w tej samej tabeli, w której zaczynałeś pracę. Sprawdź, jak wykorzystanie AI w SQL rewolucjonizuje analitykę danych w firmach bez konieczności bycia ekspertem od machine learningu.

Maciej Sala

Maciej Sala

Founder StriveLab

AI jako wsparcie analityka w SQL i Google BigQuery

AI może skrócić drogę od pytania biznesowego do zapytania SQL , ale nie zbuduje wiarygodnej analityki na niejednoznacznych danych. Zanim analityk odda modelowi część pracy, firma potrzebuje jednej definicji metryk, opisanego schematu i kontroli nad tym, co trafia do asystenta.

Maciej Sala

Maciej Sala

Founder StriveLab

Optymalizacja Google Search Console w projektach Next.js

Trudno wyobrazić sobie współczesną analitykę bez Google Search Console GSC , czyli podstawowego narzędzia pokazującego dane bezpośrednio z Google Search. PageSpeed Insights mierzy wydajność, Ahrefs śledzi backlinki, ale GSC pokazuje dane o widoczności i problemach dotyczących strony internetowej. Dowiesz się z niego, które strony Google zna i indeksuje, jakie błędy crawlowania występują, na jakie frazy rankujesz oraz jakie wyniki Core Web Vitals osiąga strona podczas rzeczywistych wizyt użytkowników.

Maciej Sala

Maciej Sala

Founder StriveLab