Lab 02-03: Chat template e prompt di sistema¶
Obiettivo¶
Vedere con i tuoi occhi il testo che il chat template costruisce a partire dai messaggi, capire perché il template sbagliato distrugge le risposte, e misurare quanto un prompt di sistema cambia il comportamento dello stesso modello sullo stesso input.
Prerequisiti¶
- Lab 02-01 completato;
curlinstallato. - Aver letto la sezione 8 del modulo.
- Facoltativo: il pacchetto Python
ggufdel lab 00-02 per leggere il template dal file.
Materiale¶
| Cosa | Dove | Dimensione |
|---|---|---|
ggml-org/Qwen3.5-0.8B-GGUF (Q8_0, Apache 2.0) |
cache HF | 785 MiB |
| Tre prompt di sistema (li scrivi tu al punto 5) | file sys-*.txt |
– |
Procedura¶
-
Il template così com'è nel GGUF. Il modo più rapido è il log verbose, che ne mostra un esempio reso:
llama cli -hf ggml-org/Qwen3.5-0.8B-GGUF -st -p "Ciao" -n 1 -rea off -v 2>&1 | grep -A6 "example_format"Atteso:
init: chat template, example_format: '<|im_start|>system\n…<|im_end|>\n<|im_start|>user\n…<|im_start|>assistant\n…. Il template completo (Jinja, 7 755 caratteri per Qwen3.5) è nella chiavetokenizer.chat_template; con il pacchettogguf(pip install gguf):python3 - <<'EOF' import glob, os; from gguf import GGUFReader f = glob.glob(os.path.expanduser("~/.cache/huggingface/hub/models--ggml-org--Qwen3.5-0.8B-GGUF/snapshots/*/*.gguf"))[0] r = GGUFReader(f); fld = r.fields["tokenizer.chat_template"] t = bytes(fld.parts[fld.data[0]]).decode() print(len(t)); print(t[:1500]) for k in ["<|im_start|>", "enable_thinking", "<think>", "tools"]: print(k, k in t) EOFIl template comincia con
{%- set image_count = namespace(value=0) %}(conta le immagini nei messaggi) e contiene i ramienable_thinking,<think>,tools: sono i pezzi di programma che-rea off,-syse il tool calling attivano. -
Il prompt reso, senza generare. Avvia il server (modulo 04) su una porta libera e chiedi a
/apply-templatedi trasformare dei messaggi:llama serve -hf ggml-org/Qwen3.5-0.8B-GGUF --port 8080 -c 2048 & sleep 15 curl -s http://127.0.0.1:8080/apply-template -H "Content-Type: application/json" \ -d '{"messages":[{"role":"system","content":"Sei un assistente."},{"role":"user","content":"Ciao"}]}'; echo{"prompt":"<|im_start|>system\nSei un assistente.<|im_end|>\n<|im_start|>user\nCiao<|im_end|>\n<|im_start|>assistant\n<think>\n"}Ripeti (a) senza il messaggio
system, (b) con"chat_template_kwargs": {"enable_thinking": false}nel body, (c) con una conversazione di tre messaggi (user,assistant,user). Osserva: dove finisce il system prompt, come viene chiuso il blocco<think>quando il thinking è spento, come vengono incorniciati i turni passati. Ferma il server conkill %1(o Ctrl+C). -
Il template sbagliato. Forza su Qwen3.5 il template di Llama 2 e poi quello di Gemma:
llama cli -hf ggml-org/Qwen3.5-0.8B-GGUF -st -p "Ciao, chi sei?" -n 40 -rea off --chat-template llama2 llama cli -hf ggml-org/Qwen3.5-0.8B-GGUF -st -p "Ciao, chi sei?" -n 40 -rea off --chat-template gemmaCon
llama2il modello risponde con un finto header di file sorgente («// llama2 // Copyright 2016-2017 // All rights reserved…»): non riconoscendo[INST], completa il testo come se fosse codice. Congemmala risposta è la parola «gemma» ripetuta riga dopo riga fino a esaurire-n. Il modello ha visto<start_of_turn>senza conoscerlo e non trova mai l'EOS giusto: il loop è il sintomo classico del template sbagliato. -
Il template giusto, esplicito.
--chat-template chatmlè il formato di Qwen, e infatti la risposta torna sensata, ma con una differenza: il template incorporatochatmlnon conosceenable_thinking, quindi-rea offnon ha effetto e il modello apre<think>dentro il testo («\nOkay, the user asked in Italian "Ciao, chi sei?"…»). Solo il template Jinja del GGUF sa spegnere il ragionamento. Poi prova --chat-template-filecon una copia del template originale (punto 1) modificata, ad esempio cambiando il messaggio di sistema predefinito. -
Tre prompt di sistema, una domanda. Scrivi tre file:
sys-neutro.txt → Sei un assistente utile. Rispondi in italiano. sys-esperto.txt → Sei un esperto di modelli linguistici. Rispondi in italiano, in una sola frase, con precisione tecnica. sys-bambini.txt → Spiega le cose a un bambino di 8 anni, in italiano, con un esempio concreto, in due frasi.e lancia la stessa domanda con ciascuno:
for s in neutro esperto bambini; do echo "== $s"; llama cli -hf ggml-org/Qwen3.5-0.8B-GGUF -st -rea off -n 80 --temp 0 -sysf sys-$s.txt -p "Cos'è un token?" 2>&1 | sed -n '/^> /,/Prompt:/p' doneCon il prompt "esperto" la risposta è: «Un token è una parte dell'input dell'LLM, che rappresenta una parte della sequenza dei caratteri.»; senza prompt di sistema lo stesso modello parla di token crittografici (lab 02-01). Con
--temp 0il confronto è deterministico: l'unica variabile è il prompt di sistema. -
Quanto costa il prompt di sistema. Ogni token del system prompt viene elaborato a ogni nuova conversazione (nel server: a ogni richiesta che non lo trova in cache). Misura con
/tokenizedel server o guardandoPrompt: N t/se il tempo: un prompt di sistema di 300 parole su CPU può costare diversi secondi al primo messaggio. Usa/apply-templatee/tokenizeper contare i token dei tre prompt.
Verifica del risultato¶
- Hai visto il template Jinja (o il suo
example_format) e riconosci i marcatori ChatML. -
/apply-templatecon e senzaenable_thinking:falseproduce due prompt diversi e sai spiegare la differenza. - Con
--chat-template llama2il modello risponde in modo assurdo; conchatmltorna sensato. - Hai tre risposte diverse alla stessa domanda, una per prompt di sistema, ottenute a
--temp 0. - Sai quanti token costa ciascun prompt di sistema.
Domande e varianti¶
- Perché Gemma 3 non ha un vero ruolo
system? Guarda il template diggml-org/gemma-3-1b-it-GGUFcon lo stesso metodo del punto 1 e trova dove finisce il testo del system prompt. - Cosa succede se in
/apply-templatemetti due messaggiuserconsecutivi? Alcuni template lo rifiutano. - Variante: un template personalizzato che aggiunge la data odierna al system prompt (
--chat-template-file), passando il valore con--chat-template-kwargs '{"oggi":"2026-09-23"}'e usandolo nel Jinja come{{ oggi }}. - Variante:
--no-jinjacon il template incorporato: quali differenze noti nel prompt reso?
Consegna¶
lab-02-03.md con: i quattro prompt resi da /apply-template (punto 2) commentati; la risposta con il template
llama2 e quella con chatml; le tre risposte del punto 5 e il conteggio dei token dei tre prompt di sistema; una
frase su quale prompt di sistema useresti per un assistente di supporto tecnico e perché.