Schema.org dla AEO na WordPressie: Jak wdrożyć FAQ, HowTo i Article pod Google AI Overviews i ChatGPT

Zastanawiasz się, dlaczego modele takie jak Perplexity, SearchGPT czy Google AI Overviews (SGE) cytują w swoich podsumowaniach konkurencję, całkowicie pomijając Twoje obszerne i merytoryczne artykuły?

Schema.org dla AEO na WordPressie: Jak wdrożyć FAQ, HowTo i Article pod Google AI Overviews i ChatGPT

Odpowiedź rzadko leży w długości tekstu. Kluczem jest Answer Engine Optimization (AEO) i to, w jaki sposób serwujesz dane maszynom.

W erze „agentic browsing”, gdzie boty nie tylko indeksują tekst, ale wręcz starają się „zrozumieć” relacje między pojęciami, poleganie wyłącznie na czystym HTML-u to proszenie się o bycie zignorowanym. Duże modele językowe (LLM) uwielbiają determinizm. W tym wpisie pokażę Ci, jak jako programista poprawnie wstrzyknąć Schema.org w formacie JSON-LD z poziomu PHP 8.3. Wykorzystamy natywne bloki Gutenberga, aby stać się wiarygodnym węzłem wiedzy (entity) dla algorytmów AI.

Dlaczego klasyczne SEO to za mało dla AI Overviews?

Tradycyjny Googlebot parsuje DOM, ocenia tagi <h1>, <h2>, analizuje profil linków i przypisuje wagi do słów kluczowych. Agent AI działa inaczej. Kiedy użytkownik zadaje złożone pytanie (np. „Jakie są wymogi EAA dla stron e-commerce?”), LLM w ułamku sekundy musi wyciągnąć precyzyjną odpowiedź z zaufanych źródeł.

Zamiast tracić zasoby obliczeniowe na „zgadywanie”, gdzie na Twojej stronie kończy się wstęp, a zaczyna właściwa instrukcja, bot woli sięgnąć do twardych danych ustrukturyzowanych:

  • Widzi FAQPage? Wie, że ma przed sobą gotowe pary pytanie-odpowiedź.
  • Widzi HowTo? Zna dokładną sekwencję kroków i narzędzia potrzebne do wykonania zadania.
  • Widzi Article połączone przez @id z Organization? Wie, kto jest autorem i zyskuje pewność co do autorytetu źródła.

Zastosowanie poprawnego grafu wiedzy w JSON-LD drastycznie obniża „koszt poznawczy” (tokenizację) dla modelu, co jest bezpośrednim czynnikiem rankingowym w AEO.

@graph – dlaczego płaska struktura to architektoniczny błąd?

Większość popularnych, darmowych wtyczek SEO „wypluwa” na stronę kilka niezależnych tagów <script type="application/ld+json">. Jeden dla artykułu, drugi dla okruszków (breadcrumbs), trzeci dla FAQ.

Dla systemów AI to jak dostarczenie puzzli w rozsypanych pudełkach. Nowoczesne AEO wymaga użycia struktury @graph. Pozwala ona zadeklarować jeden, spójny graf encji, w którym elementy odwołują się do siebie nawzajem za pomocą unikalnych identyfikatorów @id. Strona internetowa (WebPage) jest częścią witryny (WebSite), na stronie znajduje się artykuł (Article), który zawiera sekcję pytań (FAQPage), a autorem całości jest organizacja (Organization).

Takie powiązanie to dla agenta AI jasny sygnał wysokiej jakości inżynieryjnej.

JSON-LD bez wtyczek: wstrzykiwanie Schema w PHP 8.3

Poniżej znajdziesz kompletny, zoptymalizowany pod kątem wydajności skrypt. Wyciąga on treść bezpośrednio z obiektu postu i strukturyzuje ją pod wymogi AEO. Zrezygnowaliśmy ze zbędnych zapytań do bazy, opierając się na natywnych funkcjach WordPressa.

PHP

<?php
declare(strict_types=1);

namespace DesignSolutions\Schema;

use WP_Post;

class AeoSchemaGenerator {
    public function __construct() {
        // Wpinamy się w sekcję <head> tylko na pojedynczych wpisach i stronach
        add_action('wp_head', [$this, 'inject_graph_schema'], 5);
    }

    public function inject_graph_schema(): void {
        if (!is_singular()) {
            return;
        }

        global $post;
        
        $site_url = home_url();
        $post_url = get_permalink($post);
        $logo_url = wp_get_attachment_image_url(get_theme_mod('custom_logo'), 'full');

        // Podstawowa struktura grafu
        $graph = [
            $this->build_organization_entity($site_url, $logo_url),
            $this->build_website_entity($site_url),
            $this->build_webpage_entity($post, $post_url, $site_url),
            $this->build_article_entity($post, $post_url, $site_url)
        ];

        // Dynamiczne wyciąganie FAQ z bloków Gutenberga
        $faq_entity = $this->extract_faq_from_blocks($post);
        if ($faq_entity !== null) {
            $graph[] = $faq_entity;
        }

        $schema = [
            '@context' => 'https://schema.org',
            '@graph'   => $graph
        ];

        // Renderowanie JSON-LD z flagami chroniącymi polskie znaki i strukturę
        echo '<script type="application/ld+json">' . PHP_EOL;
        echo json_encode($schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
        echo PHP_EOL . '</script>' . PHP_EOL;
    }

    private function build_organization_entity(string $site_url, string|bool $logo_url): array {
        return [
            '@type' => 'Organization',
            '@id'   => $site_url . '#organization',
            'name'  => get_bloginfo('name'),
            'url'   => $site_url,
            'logo'  => [
                '@type' => 'ImageObject',
                '@id'   => $site_url . '#logo',
                'url'   => $logo_url ?: $site_url . '/wp-content/themes/ds-theme/assets/img/default-logo.png',
            ]
        ];
    }

    private function build_website_entity(string $site_url): array {
        return [
            '@type'     => 'WebSite',
            '@id'       => $site_url . '#website',
            'url'       => $site_url,
            'name'      => get_bloginfo('name'),
            'publisher' => ['@id' => $site_url . '#organization']
        ];
    }

    private function build_webpage_entity(WP_Post $post, string $post_url, string $site_url): array {
        return [
            '@type'      => 'WebPage',
            '@id'        => $post_url . '#webpage',
            'url'        => $post_url,
            'name'       => get_the_title($post),
            'isPartOf'   => ['@id' => $site_url . '#website'],
            'datePublished' => get_the_date('c', $post),
            'dateModified'  => get_the_modified_date('c', $post)
        ];
    }

    private function build_article_entity(WP_Post $post, string $post_url, string $site_url): array {
        return [
            '@type'            => 'Article',
            '@id'              => $post_url . '#article',
            'isPartOf'         => ['@id' => $post_url . '#webpage'],
            'mainEntityOfPage' => ['@id' => $post_url . '#webpage'],
            'headline'         => get_the_title($post),
            'datePublished'    => get_the_date('c', $post),
            'dateModified'     => get_the_modified_date('c', $post),
            'author'           => [
                '@type' => 'Organization',
                '@id'   => $site_url . '#organization'
            ],
            'publisher'        => ['@id' => $site_url . '#organization']
        ];
    }

    /**
     * Parsowanie bloków Gutenberga w poszukiwaniu pytań i odpowiedzi
     */
    private function extract_faq_from_blocks(WP_Post $post): ?array {
        $blocks = parse_blocks($post->post_content);
        $questions = [];

        // Przykładowa logika: szukamy natywnych bloków lub własnych (np. ds-blocks/faq-item)
        foreach ($blocks as $block) {
            // Zakładamy, że stworzyłeś własny blok Gutenberga (React) zapisujący dane w atrybutach
            if ($block['blockName'] === 'ds-blocks/faq-item' && !empty($block['attrs']['question'])) {
                $questions[] = [
                    '@type'          => 'Question',
                    'name'           => wp_strip_all_tags($block['attrs']['question']),
                    'acceptedAnswer' => [
                        '@type' => 'Answer',
                        'text'  => wp_strip_all_tags($block['attrs']['answer'])
                    ]
                ];
            }
        }

        if (empty($questions)) {
            return null;
        }

        return [
            '@type'      => 'FAQPage',
            '@id'        => get_permalink($post) . '#faq',
            'isPartOf'   => ['@id' => get_permalink($post) . '#webpage'],
            'mainEntity' => $questions
        ];
    }
}

new AeoSchemaGenerator();
?>

Zwróć szczególną uwagę na metodę extract_faq_from_blocks(). Zamiast zmuszać redaktora do podwójnego wpisywania tych samych danych (raz w treści, raz w polach wtyczki SEO), skrypt za pomocą natywnej funkcji parse_blocks() „w locie” czyta strukturę Reactowych bloków Gutenberga i buduje z nich prawidłowe powiązania FAQPage. Jest to niezwykle doceniane przez algorytmy Google – treść widoczna dla użytkownika (DOM) i treść serwowana botom (JSON) zgadzają się w stu procentach.

Jak AI czyta dane strukturalne (HowTo i powiązania encji)?

Podobny mechanizm co w przypadku FAQPage, należy zastosować przy oznaczaniu instrukcji. Schema HowTo to potężne narzędzie, jeśli Twoja strona dostarcza technicznej wiedzy (np. konfiguracja serwera, wdrożenia).

Dla AI Overview każdy krok (HowToStep) jest analizowany pod kątem precyzji działania. Oczekuje się struktury składającej się z:

  1. name (tytuł kroku).
  2. text (dokładny opis).
  3. url (kotwica, czyli anchor link prowadzący bezpośrednio do tego kroku na stronie, co znacząco zwiększa szansę na cytowanie i kliknięcie).

Kluczowe w AEO jest powiązanie HowTo z głównym artykułem. Nie wrzucaj tego jako osobnego skryptu. W naszym grafie dodalibyśmy po prostu kolejny element do tablicy $graph, gdzie isPartOf wskazywałoby na ['@id' => $post_url . '#article'].

Najczęstsze błędy we wdrażaniu Schema.org pod AEO

Audytując zaawansowane wdrożenia, regularnie zauważamy krytyczne błędy, które całkowicie dyskwalifikują strony z wyników AI:

  • Rozbieżność treści (Unintentional Cloaking): JSON-LD zawiera inną (np. dłuższą lub zoptymalizowaną pod słowa kluczowe) odpowiedź niż ta, którą ostatecznie widzi użytkownik. Nowoczesne crawlery (jak PerplexityBot) korelują tekst wizualny z JSON-em. Jeśli wykryją próbę manipulacji, strona otrzymuje ujemną wagę autorytetu.
  • Brak identyfikatorów (@id): Jeśli Twój Article i FAQPage nie wiedzą o swoim istnieniu, tracisz tzw. „Entity Authority”. Bot widzi dwa oddzielne byty na jednym adresie URL, zamiast spójnej, eksperckiej paczki danych.
  • Złamana walidacja JSON (Trailing Commas): Częsty błąd przy ręcznym generowaniu stringów (np. konkatenacji w PHP zamiast używania json_encode). Pamiętaj, że o ile przeglądarka poradzi sobie z wybrakowanym HTML, o tyle parser JSON w oprogramowaniu bota natychmiast wyrzuci błąd SyntaxError i przerwie analizę całego bloku.
  • Nadużywanie @type: Article na stronach ofertowych: Jeśli prowadzisz stronę usługową, np. oferując pozycjonowanie Warszawa, to nie jest to Article. To Service lub Product. Złe przypisanie typu encji dezorientuje modele językowe, które próbują zrozumieć intencję podstrony (informacyjna vs komercyjna).

Podsumowanie i checklista AEO

Solidna architektura danych to inwestycja w długoterminową widoczność. Kiedy projektujemy duże i wymagające wdrożenia (od systemów rezerwacji po lokalne strony internetowe Wrocław), wdrożenie llms.txt w połączeniu z rygorystycznym Schema.org jest naszym priorytetem.

Zanim uznasz wpis za zoptymalizowany pod AEO, sprawdź:

  1. [ ] Czy cały Schema.org na podstronie renderuje się jako jeden powiązany @graph?
  2. [ ] Czy sekcje pytań i odpowiedzi (Q&A) są poprawnie oskryptowane jako FAQPage połączone z głównym Article lub WebPage?
  3. [ ] Czy encja Organization precyzyjnie definiuje logotyp, nazwę i adres URL firmy, tak by AI miało pewność, kogo podać jako źródło?
  4. [ ] Czy kod JSON jest wolny od błędów składniowych i renderuje poprawne kodowanie znaków (brak encji typu &amp; czy \u0105 utrudniających wektoryzację tekstu)?

W agencji Design Solutions nie używamy gotowych, ociężałych kombajnów SEO do wypluwania kodu. Programujemy te mechanizmy natywnie, zachowując czystość i najwyższą wydajność dla naszych środowisk opartych na PHP i React.