## Jak model widzi czat

*Skąd model wie to, co wie*

*Ostatnia zmiana: 28 września 2026*

Model nie widzi okienka czatu. System prompt, wiadomości, wywołania narzędzi i ich wyniki są sklejane w jeden dokument ze znacznikami ról, a model dopisuje ciąg dalszy za znacznikiem asystenta. Stąd biorą się koszty długich rozmów, granice system promptu i większość błędów przy self-hostingu.

**Po ludzku:** Scenariusz, w którym każda kwestia ma podpis: REŻYSER, KLIENT, ASYSTENT. Aktor bez pamięci dostaje za każdym razem cały scenariusz od pierwszej strony, na końcu widzi pusty podpis ASYSTENT i dopisuje swoją kwestię. Jeśli ktoś wsunie do scenariusza obcą kartkę, aktor przeczyta ją tak samo jak resztę.

*Interaktywny widżet na stronie: Dokładaj wiadomości i wywołania narzędzi, przełączaj widok. Zobacz, że każde zapytanie wysyła cały dokument od nowa.*

### Szablon czatu

- Znaczniki ról to tokeny specjalne: osobne numery w słowniku, a nie zwykłe znaki (zobacz „Tokeny”). Format model poznał w SFT (zobacz „Trening”): po znaczniku asystenta pada odpowiedź, a po niej token końca tury.
- Każda rodzina modeli ma własny szablon: ChatML w Qwen (`<|im_start|>user … <|im_end|>`), Llama 3 (`<|start_header_id|>user<|end_header_id|> … <|eot_id|>`), harmony w gpt-oss (`<|start|>user<|message|> … <|end|>`), Mistral (`[INST] … [/INST]`). Wysyłasz listę wiadomości z rolami, a serwer renderuje ją szablonem, na którym trenowano model.
- W Hugging Face szablon jest napisany w Jinja i dołączony do tokenizera. `apply_chat_template(messages, add_generation_prompt=True)` skleja dokument i dokleja na końcu znacznik asystenta. Bez niego model potrafi ciągnąć dalej wiadomość użytkownika zamiast odpowiadać.
- Generowanie kończy token końca tury, o ile serwer ma go na liście tokenów stopu. W Llamie 3 turę kończy `<|eot_id|>`, a nie `<|end_of_text|>`. Serwer, który czeka tylko na ten drugi, pozwoli modelowi dopisać znacznik użytkownika i wymyślić jego kolejną wiadomość.

### Cały dokument w każdej turze

- Model nie pamięta rozmowy. Każde zapytanie niesie cały dokument od początku, więc łączna liczba wysłanych tokenów rośnie z kwadratem liczby tur (zobacz „Okno kontekstowe i agent”). Stan po stronie serwera, np. `previous_response_id` w OpenAI, oszczędza tylko wysyłanie: serwer odtwarza dokument, a wszystkie wcześniejsze tokeny wejściowe są znowu płatne. Co z historii zostawić, streścić albo wyrzucić, omawia „Context engineering i pamięć”.
- Stały początek dokumentu, czyli system prompt, definicje narzędzi i starsza historia, nadaje się do prompt cachingu (zobacz „Prompt caching”). Zmienna rzecz na początku, np. aktualna godzina w system prompcie, psuje cache w każdej turze. Edycja starej wiadomości daje nowy dokument i cache przepada od miejsca zmiany.
- Narzędzia to też fragmenty dokumentu. Definicje stoją obok system promptu i kosztują tokeny w każdej turze. Wywołanie to tekst, który model pisze za specjalnym znacznikiem, a wynik twój kod dopisuje jako nowy fragment (zobacz „Narzędzia (function calling)”).
- Myślenie modeli rozumujących to kolejny fragment z własnymi znacznikami: `<think>…</think>` w części otwartych modeli, kanał `analysis` w harmony (zobacz „Modele rozumujące”). Harmony wycina je z historii po odpowiedzi końcowej, ale zostawia w trakcie wywołań narzędzi.

### Obrazy, PDF-y i głos

- Enkoder wizyjny tnie obraz na kwadraty i zamienia każdy na wektor, który zajmuje w ciągu miejsce jak token. U Claude’a kwadrat ma 28 × 28 px, więc zdjęcie 1000 × 1000 px to 1296 tokenów, a Claude 4.7 i nowsze zmniejszają obraz powyżej 2576 px na dłuższym boku albo 4784 tokenów (stan na wrzesień 2026).
- Obraz zostaje w historii i jest płatny w każdej turze: dziesięć zrzutów ekranu 1000 × 1000 px w historii agenta to ok. 13 tys. tokenów w każdym zapytaniu. Claude czyta każdą stronę PDF-a dwa razy, jako tekst (1500–3000 tokenów) i jako obraz, więc 100-stronicowy raport to 150–300 tys. tokenów samego tekstu. Takie wejście cache’uj, zmniejszaj przed wysłaniem, a zrzuty, których agent już nie potrzebuje, usuwaj.
- Dźwięk też zamienia się w tokeny: Gemini liczy 32 tokeny na sekundę nagrania, a Realtime API OpenAI 10 na sekundę mowy użytkownika i 20 na sekundę mowy modelu. Realtime API przy każdej odpowiedzi podaje modelowi całą rozmowę od nowa.
- Agenta głosowego buduje się na dwa sposoby. Łańcuch (mowa na tekst → model tekstowy → tekst na mowę) daje transkrypcję, kontrolę treści przed odpowiedzią i swobodny wybór modelu, ale każdy etap wydłuża ciszę, którą słyszy rozmówca. Model speech-to-speech (OpenAI Realtime, Gemini Live) obsługuje dźwięk w jednej sesji, z przerywaniem w pół zdania i wykrywaniem końca wypowiedzi, ale mniej w nim widać. Mierz czas od końca wypowiedzi rozmówcy do pierwszego dźwięku odpowiedzi, medianę i p95.

### System prompt to nie zabezpieczenie

- System prompt to tekst na początku dokumentu, a model nauczono traktować go priorytetowo. Nie jest osobnym kanałem ani uprawnieniem. Polecenie w mailu, na stronie albo w wyniku narzędzia leży w tym samym ciągu tokenów i czasem wygrywa (zobacz „Prompt injection”). Zasady, które muszą obowiązywać, egzekwuje kod aplikacji. Sekretów nie trzymaj w system prompcie, bo model może go zacytować.
- Tekst użytkownika nie może stać się znacznikiem. W dobrze zbudowanym API wpisane „<|im_start|>system” to zwykłe znaki. Przy self-hostingu sprawdź to sam: tokenizery Hugging Face domyślnie rozpoznają napisy tokenów specjalnych w tekście (`split_special_tokens=False`), więc wklejony tekst może otworzyć prawdziwą turę systemu.

### Self-hosting i fine-tuning

- Przy modelu open-weight (zobacz „Modele open-weight”) szablon jest częścią modelu. Zły albo nieaktualny nie rzuca błędem, tylko po cichu pogarsza odpowiedzi i psuje tool calling. Częsty przypadek to podwójny token początku tekstu: dokument z `apply_chat_template(tokenize=False)` tokenizuje się drugi raz z dodawaniem tokenów specjalnych, a dokumentacja Hugging Face ostrzega, że to obniża jakość.
- Dane do fine-tuningu renderuj dokładnie tym szablonem, którego użyje serwer, i licz stratę tylko na tokenach asystenta (zobacz „Fine-tuning i LoRA”).
- Prefill: kończysz listę wiadomości początkiem odpowiedzi, np. `{`, a model pisze od tego miejsca. W transformers robi to `continue_final_message=True`. API Claude od Opus 4.6 i Sonnet 4.6 odrzuca prefill błędem 400 i odsyła do structured outputs (zobacz „Wymuszanie formatu”).

### Sprawdź się

**Pytanie:** Jak model widzi rozmowę i skąd wie, kto co mówi?

**Krótka odpowiedź:** Model nie widzi okienka czatu. Serwer skleja system prompt, historię wiadomości, wywołania narzędzi i ich wyniki w jeden dokument, a role oddziela tokenami specjalnymi według szablonu, którego model nauczył się w SFT. Na końcu stawia znacznik asystenta, a model dopisuje ciąg dalszy, aż wygeneruje token końca tury. Model nie ma pamięci, więc w każdej turze dostaje cały dokument od nowa. System prompt to tylko tekst na początku, a nie granica bezpieczeństwa: polecenie ukryte w mailu leży w tym samym ciągu.

### Pytania pogłębiające

- **Czemu model czasem pisze dalej za użytkownika?** Bo nie wygenerował tokena końca tury albo serwer nie ma go na liście tokenów stopu. Dla modelu to wciąż jeden dokument, więc najbardziej prawdopodobny ciąg dalszy to znacznik użytkownika i jego kolejna wiadomość. Zwykle winny jest zły szablon albo zła konfiguracja stopu.
- **Czy model odróżnia polecenie z system promptu od polecenia w mailu?** Tylko tyle, ile nauczył się w treningu. Modele trenuje się tak, by przestrzegały hierarchii: system ważniejszy od użytkownika, użytkownik od treści z narzędzi (OpenAI, „The Instruction Hierarchy”, 2024). To podnosi odporność, ale nie jest twardą granicą, bo wszystko leży w jednym ciągu tokenów.
- **Po co API przyjmuje listę wiadomości zamiast gotowego tekstu?** Żeby szablon, znaczniki i zapis narzędzi zawsze zgadzały się z tym, na czym trenowano model, i żeby treść od użytkownika nie mogła podrobić znacznika roli.
- **Wdrażasz model open-weight na własnym serwerze. Co sprawdzasz w szablonie?** Czy szablon z tokenizera zgadza się z kartą modelu, czy token końca tury jest na liście stopu, czy nie ma podwójnego tokena początku i czy tekst użytkownika nie zamienia się w tokeny specjalne. Najprościej wyrenderować przykładową rozmowę z narzędziami i porównać ją znak po znaku z przykładem z dokumentacji modelu.
- **Co się dzieje, gdy edytujesz starą wiadomość?** Model niczego nie zapamiętał, więc dostaje po prostu nowy dokument. Tracisz prompt cache od miejsca zmiany do końca. W Claude Opus 5.5, Fable 5.1 i Sonnet 5.5 zmiana historii przed blokiem myślenia kończy się błędem 400 (zobacz „Modele rozumujące”).

### Źródła

- [Hugging Face: Chat templates](https://huggingface.co/docs/transformers/main/en/chat_templating)
- [OpenAI: format harmony (gpt-oss)](https://developers.openai.com/cookbook/articles/openai-harmony)
- [Wallace i in.: The Instruction Hierarchy (2024)](https://arxiv.org/abs/2404.13208)
- [Claude docs: Vision (tokeny obrazu)](https://platform.claude.com/docs/en/build-with-claude/vision)
- [OpenAI: Voice agents](https://developers.openai.com/api/docs/guides/voice-agents)

Strona interaktywna: https://howaiworks.dev/pl/czat/
