Prompt engineering · · 7 min czytania
Chain-of-thought vs structured outputs: kiedy czego używać
Kiedy potrzebujesz rozumowania, a kiedy sztywnego JSON-a. Napięcie, koszty i przewodnik decyzyjny.
Chain-of-thought (CoT) i structured outputs to dwa narzędzia, które ciągną model w przeciwne strony. CoT chce, żeby model myślał na głos — rozwlekle, w wolnej formie, krok po kroku. Structured output chce, żeby model zamknął odpowiedź w sztywnym kształcie — JSON, który przejdzie walidację schematu. To napięcie jest realne i kosztuje pieniądze, latencję oraz niezawodność, jeśli wybierzesz źle. Ten tekst rozkłada oba podejścia na czynniki pierwsze i daje konkretny przewodnik decyzyjny dla osób budujących na API.
Czym jest chain-of-thought i kiedy realnie pomaga
CoT to prompt, który prosi model o wygenerowanie kroków rozumowania przed finalną odpowiedzią. Klasyczne „think step by step”, ale dziś sięga znacznie dalej: modele rozumujące (reasoning models) produkują osobny strumień myślenia, zanim wydadzą odpowiedź dla użytkownika.
CoT pomaga wtedy, gdy zadanie wymaga wieloetapowego rozumowania: matematyka i arytmetyka, debugowanie logiki („dlaczego ten kod zwraca undefined”), planowanie sekwencji akcji agenta, porównywanie wielu warunków, wnioskowanie prawne czy diagnostyka. Tam, gdzie odpowiedź jest funkcją łańcucha pośrednich wniosków, wymuszenie tego łańcucha realnie podnosi trafność. Model nie „zgaduje” końca — rozwija go.
CoT nie pomaga przy zadaniach jednokrokowych: klasyfikacja krótkiego tekstu, ekstrakcja pola z faktury, decyzja tak/nie na podstawie jednej reguły. Tu rozumowanie dodaje tokeny, latencję i czasem szum, który obniża jakość. Generowanie 400 tokenów „przemyśleń”, żeby zwrócić jedną etykietę, to marnotrawstwo.
Czym są structured outputs
Structured outputs to mechanizmy, które zmuszają model do zwrócenia danych w przewidywalnym kształcie. Trzy główne formy:
- JSON mode — model gwarantuje syntaktycznie poprawny JSON, ale bez kontroli nad konkretnymi polami.
- response_format ze schematem (np. JSON Schema / constrained decoding) — model gwarantuje JSON zgodny z
{"type":"object","required":["score"]}. To grammar-constrained decoding: sampler odrzuca tokeny, które złamałyby schemat. - Function calling / tool schemas — model wybiera narzędzie i wypełnia jego argumenty zgodnie z deklaracją, np.
get_weather(city: string).
Korzyść jest oczywista: pipeline produkcyjny dostaje dane, które bez parsowania regexem wpadają prosto do JSON.parse i walidacji Zodem. Zero „model dopisał zdanie przed nawiasem klamrowym”. Niezawodność na poziomie kontraktu, nie nadziei.
Na czym polega napięcie
Tu robi się ciekawie. Constrained decoding ogranicza przestrzeń tokenów, które model może wygenerować — w danym kroku dozwolone są tylko te, które prowadzą do poprawnego JSON-a. Ale rozumowanie z natury jest wolnoformowe. Jeśli zmusisz model, żeby od pierwszego tokena produkował {"answer":, odbierasz mu miejsce na myślenie. Efekt: na trudnych zadaniach matematycznych czy logicznych jakość potrafi spaść, bo model „skacze do odpowiedzi” zamiast ją wyprowadzić.
To nie jest teoria. Każdy, kto wpiął response_format ze sztywnym schematem do zadania wymagającego analizy, widział, jak odpowiedzi stają się płytsze. Model dostaje kształt, ale traci proces. A na zadaniach reasoningowych proces jest jakością.
Jak nowoczesne modele pozwalają to połączyć
Dobra wiadomość: nie musisz wybierać. Są trzy sprawdzone wzorce łączenia jednego z drugim.
Wzorzec 1: rozumowanie, potem pole strukturalne
Schemat z dwoma polami: {"reasoning": string, "answer": ...}, gdziereasoning jest pierwsze. Model wypełnia je wolnym tekstem (ma miejsce na CoT), a potem — już „po przemyśleniu” — produkuje ustrukturyzowanyanswer. Kolejność pól w schemacie ma znaczenie: rozumowanie musi być przed odpowiedzią, bo model generuje sekwencyjnie. Pole reasoning poanswer jest bezużyteczne — to racjonalizacja, nie rozumowanie.
Wzorzec 2: reasoning tokens + ustrukturyzowana odpowiedź
Modele rozumujące rozdzielają „myślenie” od „odpowiedzi” na poziomie API. Model myśli swobodnie w osobnym strumieniu, a finalna odpowiedź dla użytkownika może być ograniczona schematem. Dostajesz pełne CoT i czysty JSON na wyjściu, bez kompromisu. To dziś najmocniejsza opcja dla trudnych zadań, które muszą wpaść do pipeline'u.
Wzorzec 3: dwa wywołania
Wywołanie A: wolnoformowe CoT, zwraca tekst. Wywołanie B: bierze ten tekst i „tłumaczy” go na ścisły schemat (tania, jednokrokowa ekstrakcja). Droższe i wolniejsze (dwa round-tripy), ale daje pełną kontrolę i działa na każdym modelu, także tym bez natywnego trybu reasoning. Dobry fallback dla starszych modeli i pełna izolacja błędów: jak parsowanie padnie, wiesz, że winne jest wywołanie B, nie rozumowanie.
Latencja i koszt
Każdy token rozumowania to czas i pieniądz. CoT potrafi 5–20-krotnie zwiększyć liczbę tokenów wyjściowych w porównaniu do gołej odpowiedzi. Przy reasoning models reasoning tokeny są zwykle rozliczane jak output i bywają drogie. Trzy konsekwencje:
- Latencja: dłuższe wyjście to dłuższy czas do ostatniego tokena. Dla UI synchronicznego (czat, autouzupełnianie) to bywa zabójcze.
- Koszt: zadanie z CoT potrafi kosztować kilkukrotnie więcej za to samo wejście. Przy milionie żądań miesięcznie to realna pozycja w budżecie.
- Wzorzec dwóch wywołań podwaja round-tripy sieciowe — doliczasz narzut latencji połączenia, nie tylko generacji.
Reguła: nie płać za rozumowanie tam, gdzie go nie potrzebujesz. Klasyfikacja na małym modelu bez CoT bywa 10× tańsza i szybsza, a jakość identyczna.
Niezawodność w pipeline'ach produkcyjnych
Produkcja ma inny próg niż prototyp. Tu liczy się, ile żądań na 10 000 wymaga ręcznej interwencji. Czysty CoT zwracający tekst jest kruchy: musisz parsować wolną formę regexem, a model co jakiś czas zmieni formatowanie i parser się wywróci. Structured output z constrained decoding eliminuje tę klasę błędów — JSON jest poprawny z definicji.
Dlatego dla każdego wyjścia, które trafia do kolejnego systemu (baza, kolejka, kolejny krok agenta), domyślnie wybieraj structured output. Wolny tekst zostaw dla wyjścia, które czyta człowiek. A gdy potrzebujesz i jakości reasoningu, i niezawodności kształtu — łącz je wzorcem 1 lub 2. Zawsze waliduj wynik Zodem na granicy systemu, nawet przy constrained decoding: schemat dostawcy i twój kontrakt domenowy to nie to samo.
Przewodnik decyzyjny
Używaj CoT, gdy: zadanie jest wieloetapowe (matematyka, logika, debugowanie, planowanie agenta), trafność rośnie z jawnym rozumowaniem, a wyjście czyta człowiek lub masz budżet na tokeny i latencję. Sygnał ostrzegawczy: jeśli nie potrafisz wskazać kroków pośrednich, CoT prawdopodobnie nic nie da.
Używaj structured output, gdy: wynik wpada do pipeline'u, zadanie jest jednokrokowe (ekstrakcja, klasyfikacja, routing, wypełnienie argumentów narzędzia), a kluczowa jest niezawodność parsowania i niska latencja. Mały model + schemat to często najtańsze i najpewniejsze rozwiązanie.
Łącz, gdy: potrzebujesz rozumowania i maszynowo czytelnego wyjścia. Wybierz reasoning tokens + structured answer (wzorzec 2), jeśli model to wspiera. W przeciwnym razie schemat {reasoning, answer} z rozumowaniem na początku (wzorzec 1). Wzorzec dwóch wywołań trzymaj jako fallback dla starszych modeli lub gdy potrzebujesz pełnej izolacji błędów.
Częste błędy
- Pole
reasoningpoanswerw schemacie. Model już odpowiedział — reszta to ściema. Rozumowanie zawsze pierwsze. - Sztywny schemat na trudnym zadaniu bez miejsca na myślenie. Dostajesz kształt, tracisz jakość. Dodaj pole na CoT albo użyj reasoning tokens.
- CoT na klasyfikacji za 400 tokenów, gdzie wystarczy jedna etykieta. Płacisz za szum.
- Brak walidacji Zodem „bo schemat dostawcy gwarantuje JSON”. Gwarantuje składnię, nie twój kontrakt domenowy ani zakresy wartości.
TL;DR
CoT podnosi jakość na zadaniach wieloetapowych, ale kosztuje tokeny i latencję. Structured output daje niezawodność parsowania, ale sztywny schemat dławi rozumowanie. Dla jednokrokowych zadań w pipeline — structured. Dla rozumowania czytanego przez człowieka — CoT. Gdy potrzebujesz obu, łącz je: reasoning tokens + structured answer, albo schemat z polemreasoning przed answer. I zawsze waliduj wynik na granicy systemu.