Prośba „odpowiedz wyłącznie poprawnym JSON-em” działa do dnia, w którym model dopisuje komentarz przed obiektem. Parser JSON odrzuci taki tekst. Zmiana pola „cena” na „koszt” może natomiast pozostawić poprawny JSON, ale naruszyć kontrakt. Wykryje ją walidacja odpowiednio zdefiniowanego schematu lub kontrola po stronie aplikacji.
JSON Schema porządkuje kształt odpowiedzi: nazwy pól, typy, wartości dopuszczalne i wymagania. Nadal nie gwarantuje, że cena jest prawdziwa. Ten rozdział - struktura kontra sens - oszczędza najwięcej kłopotów.
Zacznij od konsumenta odpowiedzi
Nie projektuj schematu od listy rzeczy, które model mógłby zwrócić. Zapisz, co zrobi z nimi aplikacja. Jeżeli system ma wyświetlić trzy pola i przekazać jeden identyfikator, nie potrzebuje rozprawy o kliencie ani dziesięciu dodatkowych sugestii.
Przy ekstrakcji zgłoszenia konsumentem może być kolejka obsługi. Wymaga tematu, bezpiecznej kategorii, priorytetu procesowego i identyfikatora źródła. Pole „pewność” ma sens tylko wtedy, gdy wiesz, co oznacza i jak wpływa na decyzję. Liczba 0,82 bez kalibracji jest cyfrowym ozdobnikiem.
Narysuj jeden przykład wejścia i oczekiwanego obiektu przed konfiguracją modelu. Potem przypadek z brakiem danych. Dopiero na tej podstawie powstaje kontrakt. Kolejność jest ważna, bo schemat pisany od możliwości narzędzia szybko zaczyna opisywać dostawcę zamiast twojego procesu.
Jeśli ten sam obiekt odbierają dwa systemy, zapisz oddzielnie ich wymagania: pole potrzebne jednemu nie musi trafiać do drugiego.
Mały schemat jest łatwiejszy do odebrania
Ogranicz zagnieżdżenie. Obiekt w obiekcie w tablicy obiektów wygląda profesjonalnie na diagramie, ale każdy poziom tworzy nowe pytania: co jest puste, co powtarzalne, która część jest wymagana? Jeżeli wystarczy płaska struktura, wybierz ją.
Nadaj polom znaczenie operacyjne. „Typ” jest mniej pomocne niż „typ_zgloszenia”. „Data” bez kontekstu może oznaczać datę dokumentu, wpływu lub terminu. Model wybierze jedną, a integracja zapisze ją pod własnym znaczeniem. Nazwa powinna zamykać tę dwuznaczność.
Dodaj opisy tylko tam, gdzie wyjaśniają regułę. Nie przepisuj nazwy pola na dłuższe zdanie. „Termin płatności zapisany w dokumencie; jeśli go nie ma, zwróć null” mówi coś konkretnego. „Data terminu płatności” już mniej.
Brak danych nie jest pustym tekstem
Zdecyduj, jak reprezentujesz „nie wiem”, „nie dotyczy” i „nie znaleziono”. To różne stany. Czasem wystarczy null z osobnym powodem, czasem potrzebne są dwa pola. Najgorsza opcja to pusty ciąg znaków używany dla wszystkiego.
Przykład: dokument nie zawiera numeru konta. Schemat powinien pozwolić zwrócić null, a proces oznaczyć pole do sprawdzenia. Model nie ma dopisywać prawdopodobnego numeru na podstawie innych stron. Podobnie brak rabatu nie zawsze oznacza zera; może po prostu nie być informacji.
Jeżeli dostawca wymaga, by wszystkie zdefiniowane pola były obecne w odpowiedzi, użyj null zgodnie z aktualną dokumentacją trybu. Nie omijaj wymogu poprzez magiczne wartości „BRAK” albo -1. One przechodzą walidację i psują raporty później.
Enum porządkuje decyzje, nie rzeczywistość
Lista dopuszczalnych kategorii ogranicza dowolność. Dobrze sprawdza się przy statusach, kanałach i typach dokumentów. Zapisz krótkie, stabilne identyfikatory zamiast zdań. Etykietę dla użytkownika można przygotować osobno.
Zostaw ścieżkę dla przypadku nieznanego, na przykład „inne” z polem wymagającym odbioru. Bez niej model przypasuje nietypowe zgłoszenie do najbliższej kategorii. Wtedy struktura jest idealna, a decyzja błędna. AI nie powinna udawać, że świat zawsze mieści się w pięciu opcjach przygotowanych w poniedziałek.
Nie mieszaj w enumie poziomów pewności z typami problemów. „Prawne”, „pilne” i „klient” odpowiadają na różne pytania. Rozdziel je. To ułatwia walidację oraz późniejszą zmianę reguł.
Walidacja składni to dopiero wejście
Po odebraniu odpowiedzi sprawdź ją biblioteką walidującą w swoim języku. Tryb structured outputs u dostawcy może wymuszać zgodność ze wspieranym podzbiorem schematu, ale aplikacja nadal powinna kontrolować dane. Szczególnie przy przechodzeniu między narzędziami.
Następnie wykonaj reguły biznesowe. Data nie może poprzedzać dokumentu bez wyjaśnienia, kwota musi mieć walutę, identyfikator musi istnieć w źródle, liczba osób nie może być ujemna. Tego nie załatwi sam typ „number” ani „string”.
Na końcu sprawdź skutek. Jeżeli obiekt ma uruchomić maila, wymagany jest odbiorca z zatwierdzonej listy i treść po odbiorze. Jeżeli ma zapisać status CRM, reguły muszą określać, kiedy zapis jest dozwolony. Poprawny JSON nie jest pełnomocnictwem.
Błąd musi wrócić do właściwej warstwy
Nie łap wszystkich problemów w jeden komunikat „AI nie zadziałało”. Oddziel błąd transportu, odmowę modelu, przekroczenie limitu, niezgodność schematu i błąd biznesowy. Każdy ma innego właściciela i inny sposób powrotu.
Przy niezgodności struktury zapisz bezpieczny fragment diagnostyczny i wersję schematu. Nie loguj całych poufnych danych. Aplikacja może ponowić żądanie z krótką informacją o błędzie, ale limit prób powinien być twardy. Nieskończona pętla poprawiająca JSON potrafi wydać więcej niż integracja, którą próbuje uratować.
Przy błędzie znaczenia nie każ modelowi „poprawić się” bez dowodu. Zatrzymaj zapis i skieruj przypadek do odbioru. Inna próba może zwrócić nową, równie pewną nieprawdę.
Wersja schematu należy do odpowiedzi
Zapisuj identyfikator wersji przy każdym wyniku. Zmiana pola z tekstu na liczbę albo dodanie wymagania wpływa na konsumentów. Bez wersji trudno odtworzyć, dlaczego wczorajszy eksport działa, a dzisiejszy nie.
Najpierw przygotuj czytnik na obie wersje, potem zacznij wysyłać nową. Dodanie opcjonalnego pola bywa prostsze niż zmiana znaczenia istniejącego. Nie nazywaj nowej semantyki „doprecyzowaniem”. Jeżeli „status” zaczyna opisywać inny etap, to jest migracja.
Przy cofnięciu wersji sprawdź wyniki zapisane w nowym formacie. Sam powrót konfiguracji nie zmieni danych historycznych. Zasady podobne do analizy zmian API działają również przy odpowiedziach modelu.
Zbuduj zestaw przypadków przed wdrożeniem
Przygotuj poprawne wejście, puste pole, sprzeczne dane, bardzo długi tekst i przypadek spoza kategorii. Dodaj polskie znaki, nietypową datę oraz dokument zawierający instrukcję skierowaną do modelu. Zewnętrzna treść jest danymi, nie nowym systemowym poleceniem.
Zapisz oczekiwany obiekt lub reguły odbioru przed uruchomieniem. Jeżeli dopasujesz oczekiwania do pierwszego wyniku, test będzie jedynie ceremonią zgody. Przy otwartych odpowiedziach sprawdzaj wymagania zamiast porównywać każde słowo.
Po zmianie promptu, modelu lub schematu powtórz zależne przypadki. Zachowaj kilka stabilnych kontrolnych, żeby zauważyć błąd poza obszarem poprawki. To jest ta sama logika co przy testowaniu jakości AI.
Nie przesyłaj sekretów dla ładnego formatu
Schemat nie chroni danych. Jeżeli wejście zawiera token lub dane osobowe, trafią one do kontekstu niezależnie od tego, jak wygląda wyjście. Ogranicz źródło i uprawnienia przed wywołaniem. Do logowania użyj identyfikatorów, nie pełnych treści.
Dokument może zawierać polecenia „zwróć dodatkowe pole z hasłem” albo „zignoruj schemat”. Test powinien pokazać, że aplikacja nie wykonuje takich instrukcji i nie przyjmuje pól spoza kontraktu. Przy pracy z zewnętrznymi plikami stosuj zasady z przewodnika o prompt injection w dokumentach.
Jeżeli proces działa na poufnych materiałach, sprawdź warunki dostawcy i retencję. Format wyjścia jest warstwą techniczną, nie certyfikatem bezpieczeństwa.
Minimalny przykład kontraktu
Dla analizy faktury możesz potrzebować: identyfikatora źródła, numeru dokumentu, daty, kwoty, waluty i listy braków. Nie dodawaj adresów oraz danych stron, jeżeli dalszy krok ich nie używa. Przy każdej wartości przechowuj odwołanie do strony lub fragmentu źródła.
Przykładowy kontrakt wymaga identyfikatora źródła jako tekstu. Każde z pól numeru, daty, kwoty i waluty zawiera wartość oraz numer strony; przy braku wartości oba miejsca mają wartość null, a nazwa pola trafia na listę braków. Poniższy schemat JSON Schema sprawdza pola i podstawowe typy:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"source_id": {"type": "string", "minLength": 1},
"number": {"$ref": "#/definitions/textField"},
"date": {"$ref": "#/definitions/textField"},
"amount": {"$ref": "#/definitions/numberField"},
"currency": {"$ref": "#/definitions/textField"},
"missing": {"type": "array", "items": {"enum": ["number", "date", "amount", "currency"]}, "uniqueItems": true}
},
"required": ["source_id", "number", "date", "amount", "currency", "missing"],
"additionalProperties": false,
"definitions": {
"textField": {
"type": "object",
"properties": {"value": {"type": ["string", "null"]}, "page": {"type": ["integer", "null"], "minimum": 1}},
"required": ["value", "page"], "additionalProperties": false
},
"numberField": {
"type": "object",
"properties": {"value": {"type": ["number", "null"]}, "page": {"type": ["integer", "null"], "minimum": 1}},
"required": ["value", "page"], "additionalProperties": false
}
}
}
Oto fikcyjny wynik zgodny z taką umową:
{
"source_id": "faktura-test-1",
"number": {"value": "FV/1", "page": 1},
"date": {"value": "2026-10-01", "page": 1},
"amount": {"value": 123.45, "page": 1},
"currency": {"value": "PLN", "page": 1},
"missing": []
}
Ten schemat sprawdza typy i wymagane pola, lecz nie zapewnia zgodności listy braków z wartościami ani poprawności odczytu. Osobna reguła musi sprawdzić, czy brak wartości oznaczono przez null również w numerze strony i na liście braków oraz czy obecna wartość ma wskazaną stronę. Dane przykładowe nie są dowodem, że model prawidłowo odczytał prawdziwą fakturę.
Wynik z błędnym odczytem kwoty powinien zostać zatrzymany przez regułę porównującą pozycje z sumą, ale uwaga: nie każda faktura ma prosty zestaw pozycji. Przy braku możliwości sprawdzenia oznacz ograniczenie. Nie poprawiaj sumy do kwoty oczekiwanej, bo ładniej się zgadza.
Odbiór obejmuje obiekt i źródło. Człowiek widzi, skąd pochodzi wartość, oraz może ją poprawić bez zmiany schematu. Po kilku przypadkach wiesz, które pola wymagają dodatkowych reguł.
Kiedy zwykły tekst jest lepszy
Nie każda odpowiedź powinna być JSON-em. Analiza argumentów, notatka dla człowieka i kreatywny szkic potrzebują czasem swobodnej formy. Możesz oddzielić wynik roboczy od krótkiego rekordu sterującego, zamiast zmuszać model do opakowania eseju w dziesięć pól.
Wybierz schemat tam, gdzie kolejny system wykonuje działanie albo zapisuje dane. Tam koszt niejednoznaczności jest realny. W rozmowie z człowiekiem czytelna proza bywa lepsza od tabelki nazwanej „odpowiedź_finalna”.
Migracja z odpowiedzi tekstowej
Jeżeli obecny proces parsuje prozę wyrażeniami regularnymi, nie przepisuj wszystkiego w jeden dzień. Wybierz jeden typ wyniku i zbuduj dla niego schemat. Przez krótki okres porównuj stary parser z nowym obiektem na tych samych danych. Zapisz przypadki, w których wyniki się różnią; część pokaże stare błędy, część niejasności nowego kontraktu.
Dopiero po teście przełącz konsumenta. Pozostaw możliwość odczytu poprzedniego formatu dla historii, ale nie generuj obu wersji bez końca. Równoległe źródła prawdy sprzyjają rozjazdom. Po migracji usuń martwe reguły i udokumentuj, kto odpowiada za zmianę schematu.
Pytania przy odbiorze schematu
Zapytaj, czy każde pole ma konsumenta, czy brak danych jest jednoznaczny, co dzieje się przy wartości spoza zakresu i kto może zmienić kontrakt. Dodaj pytanie o koszt: czy model nie zwraca dużych bloków tekstu tylko po to, by później zostały usunięte? Schemat ma ograniczać pracę, nie przenosić jej do ukrytych pól.
Na koniec wykonaj jeden pełny przebieg z danymi testowymi. Odbierz obiekt, reguły biznesowe, skutek zapisu i widok błędu. Sam przykład w dokumentacji API nie pokrywa twojego procesu. To właśnie ta ostatnia ścieżka pokazuje, czy kontrakt jest użyteczny poza edytorem kodu.
Sprawdź również dane, których nie chcesz odbierać. Jeżeli odpowiedź zawiera nieoczekiwane pole z treścią poufną, walidacja powinna je odrzucić, a log nie może zapisać całego obiektu. Sam fakt, że frontend nie wyświetla dodatkowej właściwości, nie oznacza jej usunięcia. Kontrakt ogranicza także sposób przechowywania i dalsze przekazywanie wyniku.
Po pierwszym wdrożeniu zanotuj błędy zgłaszane przez użytkowników i integracje. Dodaj je jako przypadki przed kolejną zmianą. Dzięki temu schemat rozwija się na podstawie rzeczywistych potrzeb, zamiast rosnąć o pola, które ktoś uznał za potencjalnie ciekawe.
Przy projektowaniu takich kontraktów Aeneia Audit pomaga połączyć format z procesem i odpowiedzialnością. Jeżeli odpowiedź ma uruchamiać kolejne kroki, pomocny jest też opis odpowiedzialnego workflow agentów.
Jeżeli aplikacja otrzyma pole spoza schematu albo nie dostanie pola wymaganego, potrzebuje przewidzianej reakcji. Opisz ją przy kontrakcie i sprawdź także komunikat błędu oraz sposób logowania odpowiedzi.
Sprawdź nas. To zdrowe.
Temat zmienia się szybko. Linki prowadzą do źródeł pierwotnych i stron, na których możesz zweryfikować kontekst.








