Python & AI Tutorials Logo
LangChain & LangGraph

4. Progettare Prompt Riutilizzabili con i Template

Nel Capitolo 3, abbiamo costruito una CLI di chat in streaming funzionante dove i prompt erano incorporati direttamente nel nostro codice Python. Questo funziona per prototipi rapidi, ma man mano che le tue applicazioni AI crescono, i prompt hardcoded diventano un incubo di manutenzione. Immagina di aggiornare la stessa logica di prompt in più file, o di provare a testare A/B diverse variazioni di prompt senza ridistribuire il codice.

Questo capitolo ti insegna come progettare prompt riutilizzabili e manutenibili usando il sistema di template di LangChain. Imparerai a separare la logica dei prompt dal codice dell'applicazione, sfruttare i messaggi basati sui ruoli per un migliore controllo dell'LLM, esternalizzare i prompt in file YAML per la collaborazione del team e validare i template prima dell'esecuzione per individuare gli errori in anticipo.

Cosa Copre Questo Capitolo (e Cosa Non Copre):

In questo capitolo, lavoreremo con template e prompt manualmente—renderizzerai esplicitamente i template in messaggi, poi invierai quei messaggi all'LLM usando llm.invoke(). Questo approccio pratico ti aiuta a capire esattamente cosa fanno i template e come funzionano.

Nel Capitolo 6, imparerai LCEL (LangChain Expression Language), che ti permette di comporre template e LLM in pipeline usando l'operatore |. Per ora, ci stiamo concentrando sui fondamenti dei template senza quel livello di orchestrazione.

Alla fine di questo capitolo, avrai un sistema robusto di gestione dei prompt che scala da semplici chatbot a complessi workflow multi-agente.

4.1) Separazione delle Responsabilità: Disaccoppiare il Codice dai Prompt

Perché Separare i Prompt dal Codice?

Quando hardcodi i prompt direttamente nella logica della tua applicazione, crei un accoppiamento stretto che porta a diversi problemi:

Onere di Manutenzione: Cambiare un prompt richiede di modificare il codice Python, eseguire test e ridistribuire. Le modifiche ai prompt tipicamente avvengono molto più frequentemente delle modifiche al codice, rendendo questo ciclo modifica-codice-test-ridistribuzione altamente inefficiente per quelle che dovrebbero essere semplici modifiche di testo.

Sfide di Controllo Versione: Quando codice e prompt sono mescolati insieme, il controllo versione diventa difficile. I conflitti di merge sono più probabili, e ogni conflitto richiede risoluzione manuale e refactoring.

Attrito nella Collaborazione: I membri del team non tecnici (product manager, esperti di dominio) non possono modificare direttamente i prompt che vivono nei file .py e devono fare affidamento sull'assistenza degli sviluppatori. Questa dipendenza rende i cicli di miglioramento dei prompt significativamente più lenti.

Complessità nei Test: Testare diverse variazioni di prompt significa copiare codice, modificare stringhe e gestire più branch—rendendo gli esperimenti lenti e soggetti a errori.

Pensa ai prompt come alle query SQL nelle applicazioni tradizionali. Non hardcoderesti stringhe SQL in tutto il tuo codice Python—useresti un ORM o almeno centralizzeresti le query. I prompt meritano la stessa disciplina architetturale.

Il Sistema di Template di LangChain

LangChain fornisce le classi PromptTemplate e ChatPromptTemplate per separare la struttura fissa del tuo prompt dai dati che cambiano. Scrivi il tuo prompt una volta con {segnaposto}, poi inserisci valori diversi ogni volta—niente più ricostruzione di prompt con f-string o concatenazione.

Sintassi e Utilizzo dei Template

Sintassi dei Segnaposto

I template usano {nome_variabile} come segnaposto. A runtime, fornisci un dizionario con chiavi corrispondenti:

python
from langchain_core.prompts import PromptTemplate
 
# Definisci il template con segnaposto
template = PromptTemplate.from_template(
    "Traduci {content} da {source_lang} a {target_lang}"
)
 
# Riempi i segnaposto con un dizionario
result = template.invoke({
    "content": "Hello world",
    "source_lang": "English", 
    "target_lang": "Korean"
})
 
print(result.text)

Output:

Traduci Hello world da English a Korean

Regole Chiave:

  • I nomi dei segnaposto devono corrispondere esattamente alle chiavi del dizionario
  • Tutti i segnaposto devono essere forniti (chiavi mancanti sollevano KeyError)
  • Le chiavi extra del dizionario vengono ignorate
  • Usa invoke() per renderizzare il template con i tuoi valori

PromptTemplate vs ChatPromptTemplate

PromptTemplate: Restituisce una stringa semplice (avvolta in StringPromptValue)

  • Per semplici completamenti di testo o modelli legacy
  • Output: Singola stringa come "Riassumi: {content}"

ChatPromptTemplate: Restituisce messaggi strutturati con ruoli (avvolti in ChatPromptValue)

  • Per modelli di chat moderni (GPT-4, Claude, Gemini)
  • Output: Messaggi separati per ruolo (system/user/assistant)
  • Scelta preferita: Migliore per mantenere le istruzioni di sistema separate dall'input dell'utente

Quando usare quale?

  • Di default usa ChatPromptTemplate per i modelli di chat—è più chiaro e più manutenibile
  • Usa PromptTemplate solo per semplici completamenti o quando la separazione dei ruoli non è necessaria
python
# PromptTemplate - output stringa singola
from langchain_core.prompts import PromptTemplate
 
template1 = PromptTemplate.from_template("Riassumi: {content}")
result1 = template1.invoke({"content": "LangChain è un framework..."})  
print(result1)

Output:

text='Riassumi: LangChain è un framework...'
python
# ChatPromptTemplate - messaggi basati sui ruoli
from langchain_core.prompts import ChatPromptTemplate
 
template2 = ChatPromptTemplate.from_messages([
    ("system", "Sei un assistente utile"),
    ("user", "{question}")
])
result2 = template2.invoke({"question": "Cos'è LangChain?"})
print(result2)

Output:

messages=[SystemMessage(content='Sei un assistente utile'), HumanMessage(content='Cos\'è LangChain?')]

Il template è definito una volta. Puoi riutilizzarlo con valori diversi senza modificare la definizione del template. Sia PromptTemplate.invoke() che ChatPromptTemplate.invoke() restituiscono valori di prompt pronti per essere inviati direttamente a un LLM.

Dalla Formattazione di Stringhe ai Template

Rifacciamo un prompt hardcoded per usare i template. Ecco la versione "prima" dal Capitolo 3:

python
# Approccio hardcoded (stile Capitolo 3)
from langchain_openai import ChatOpenAI
 
llm = ChatOpenAI(model="gpt-4o-mini")
 
user_input = "Spiega il quantum computing"
# Logica del prompt mescolata con il codice
prompt = f"Sei un assistente utile. Rispondi a questa domanda: {user_input}"
 
response = llm.invoke(prompt)
print(response.content)

Ora con i template—usando l'approccio passo-passo che praticheremo in tutto questo capitolo:

python
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
 
# Template definito separatamente
template = ChatPromptTemplate.from_messages([
    ("system", "Sei un assistente utile."),
    ("user", "{user_input}")
])
 
# Logica dell'applicazione - esecuzione passo-passo
llm = ChatOpenAI(model="gpt-4o-mini")
 
user_input = "Spiega il quantum computing"
 
# Passo 1: Renderizza il template in messaggi
messages = template.invoke({"user_input": user_input})
 
# Passo 2: Invia i messaggi all'LLM
response = llm.invoke(messages)
print(response.content)

Cosa è cambiato?

  1. Definizione del Template: La struttura del prompt è definita una volta in template, separata dalla logica di esecuzione.
  2. Sintassi dei Segnaposto: {user_input} è un segnaposto che viene riempito a runtime.
  3. Esecuzione Passo-Passo: Renderizziamo esplicitamente il template (template.invoke()), poi inviamo il risultato all'LLM (llm.invoke()). Questo processo in due passi ti aiuta a capire cosa fanno effettivamente i template.
  4. Riutilizzabilità: Lo stesso template può essere usato per qualsiasi domanda dell'utente senza modifiche.
  5. Struttura dei Messaggi: template.invoke() restituisce un ChatPromptValue formattato correttamente che l'LLM si aspetta.

Perché l'Approccio Passo-Passo?

In tutto questo capitolo, vedrai questo pattern ripetutamente:

python
messages = template.invoke(inputs)  # Passo 1: Renderizza il template
response = llm.invoke(messages)     # Passo 2: Invia all'LLM

Stiamo usando questo approccio in due passi intenzionalmente per l'apprendimento—mostra esattamente cosa fanno i template: trasformare i dati di input in messaggi strutturati. Nel Capitolo 6, imparerai il pattern di produzione del mondo reale: combinare questi passi con le pipeline LCEL (template | llm). Ma capire prima ogni passo separatamente costruisce una solida base.

Validazione dei Template

I template individuano gli errori in anticipo. Se fai riferimento a un segnaposto che non esiste, LangChain solleva un errore prima di effettuare una chiamata API:

python
template = PromptTemplate.from_template("Riassumi: {text}")
 
# Questo fallirà - chiave 'text' mancante
try:
    template.invoke({"content": "Un po' di testo"})  # Nome chiave sbagliato
except KeyError as e:
    print(f"Errore template: {e}")

Output:

Errore template: "Input to PromptTemplate is missing variables {'text'}.  Expected: ['text'] Received: ['content']

Questa validazione avviene al momento della renderizzazione del template, non durante l'esecuzione dell'LLM—risparmiandoti sia tempo che costi API.

4.2) Template di Prompt Consapevoli dei Ruoli (System, User, Assistant)

Comprendere i Ruoli dei Messaggi

Gli LLM moderni (GPT-4, GPT-5, Claude, Gemini) comprendono la struttura conversazionale attraverso i ruoli dei messaggi. Ogni messaggio ha un ruolo specifico che dice al modello come interpretarlo.

I Tre Ruoli Principali:

System: Definisce come l'IA dovrebbe comportarsi

  • Scopo: Imposta la personalità, l'expertise e le regole operative dell'IA
  • Esempio: "Sei un esperto Python che scrive esempi di codice concisi"
  • Quando si applica: Impostato una volta all'inizio, influenza tutte le risposte
  • Pensalo come: Il manuale di istruzioni dell'IA

User: Rappresenta l'input umano

  • Scopo: Fa domande o richieste
  • Esempio: "Come leggo un file in Python?"
  • Quando si applica: Ogni volta che un umano invia un messaggio
  • Pensalo come: Le domande che fai

Assistant: Rappresenta le risposte precedenti dell'IA

  • Scopo: Fornisce la cronologia della conversazione
  • Esempio: "Puoi usare la funzione open() per leggere i file"
  • Quando si applica: Quando hai bisogno di conversazioni multi-turno
  • Pensalo come: La memoria dell'IA delle risposte precedenti

Messaggi di Sistema: Il Meccanismo di Controllo

Il messaggio di sistema dice all'IA chi è e come dovrebbe operare—prima di qualsiasi interazione con l'utente.

Cosa Puoi Controllare:

  1. Expertise: "Sei uno sviluppatore Python senior"
  2. Formato di Output: "Rispondi sempre in formato JSON"
  3. Regole Comportamentali: "Se non sei sicuro, di' 'Non lo so'"
  4. Stile di Risposta: "Sii conciso e tecnico"

Perché Questo È Importante:

Senza un messaggio di sistema → risposte generiche e verbose

Con un messaggio di sistema → comportamento coerente e personalizzato

Messaggi di Sistema in Azione

Vediamo l'impatto reale dei messaggi di sistema confrontando la stessa domanda con e senza uno. Presta attenzione a come cambia drammaticamente la risposta—non solo in lunghezza, ma in tono, complessità e approccio didattico.

Senza Messaggio di Sistema:

python
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
 
template = ChatPromptTemplate.from_messages([
    ("user", "Cos'è Python?")
])
 
llm = ChatOpenAI(model="gpt-4o-mini")
messages = template.invoke({})
response = llm.invoke(messages)
print(response.content)

Output:

Python è un linguaggio di programmazione di alto livello, interpretato, noto per la sua leggibilità e semplicità.
È stato creato da Guido van Rossum e rilasciato per la prima volta nel 1991.
Python enfatizza la leggibilità del codice, permettendo ai programmatori di esprimere concetti in meno righe di codice rispetto a linguaggi come C++ o Java.
 
Caratteristiche chiave di Python includono:
...

Con Messaggio di Sistema:

python
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
 
# Controlla la persona e lo stile di output con il messaggio System
template = ChatPromptTemplate.from_messages([
    ("system", """Sei un istruttore Python senior con 15 anni di esperienza nell'insegnamento.
I tuoi studenti sono principianti completi che non hanno mai programmato prima.
 
Stile di insegnamento:
- Usa analogie semplici e quotidiane
- Evita il gergo tecnico
- Mostra esempi pratici dalla vita quotidiana
- Sii incoraggiante e paziente"""),
    ("user", "Cos'è Python?")
])
 
llm = ChatOpenAI(model="gpt-4o-mini")
messages = template.invoke({})
response = llm.invoke(messages)
print(response.content)

Output:

Ottima domanda! Pensa a Python come a uno strumento davvero utile nella tua cassetta degli attrezzi.
Proprio come un martello o un cacciavite ti aiuta a costruire o riparare cose in casa, Python ti aiuta a creare software o automatizzare compiti su un computer.
 
Immagina di voler fare una torta.
Hai bisogno di una ricetta da seguire, giusto? In questa analogia, Python è come quella ricetta.
Dice al computer quali passi seguire per raggiungere un obiettivo, che si tratti di fare calcoli, organizzare file o persino eseguire un gioco.
...

La Differenza:

Senza Messaggio di Sistema:

  • L'IA usa il suo comportamento predefinito: educato, informativo, ma generico
  • Le risposte sono enciclopediche e formali—ottimizzate per un pubblico ampio
  • Nessuna persona coerente: ogni risposta potrebbe variare in tono e stile
  • Nessun vincolo: l'IA decide da sola quanto essere dettagliata o tecnica

Con Messaggio di Sistema:

  • L'IA segue le tue istruzioni specifiche: persona, stile e regole che hai definito
  • Le risposte sono coerenti e prevedibili—ogni risposta corrisponde ai tuoi requisiti
  • Persona chiara mantenuta: agisce come il ruolo che hai assegnato (insegnante, esperto, assistente)
  • Vincoli espliciti applicati: formato di output, livello di linguaggio e confini comportamentali che hai impostato

Intuizione Chiave: Senza un messaggio di sistema, ottieni la modalità predefinita dell'IA. Con un messaggio di sistema, ottieni la tua IA—personalizzata per le esigenze della tua applicazione. Il messaggio di sistema trasforma l'IA da uno strumento generico in un assistente specializzato che si comporta esattamente come vuoi, ogni singola volta.

Ruoli User e Assistant: Costruire Conversazioni

Singola Domanda (Solo User):

python
template = ChatPromptTemplate.from_messages([
    ("system", "Sei un esperto Python."),
    ("user", "{question}")
])
 
llm = ChatOpenAI(model="gpt-4o-mini")
messages = template.invoke({"question": "Come leggo un CSV?"})
response = llm.invoke(messages)

Funziona bene per domande indipendenti.

Multi-Turno con Contesto (User + Assistant):

Senza cronologia:

python
template = ChatPromptTemplate.from_messages([
    ("system", "Sei un esperto Python."),
    ("user", "Come funziona?")  # "funziona" = ???
])

L'IA non sa a cosa si riferisce "funziona".

Con cronologia:

python
template = ChatPromptTemplate.from_messages([
    ("system", "Sei un esperto Python."),
    ("user", "Cos'è la libreria pandas?"),
    ("assistant", "Pandas è una libreria di analisi dati."),
    ("user", "Come funziona?")  # Ora "funziona" = pandas
])

La cronologia della conversazione (domanda precedente dell'utente + risposta dell'assistant) fornisce il contesto. L'IA ora capisce che "funziona" significa pandas.

Esempio: Costruire una Conversazione con Cronologia

Ora costruiamo un esempio che ricorda gli scambi precedenti. Questa funzione mantiene la cronologia della conversazione e la passa all'IA con ogni nuova domanda:

python
llm = ChatOpenAI(model="gpt-4o-mini")
 
def chat_with_history(user_input: str, history: list):
    messages = [("system", "Sei un esperto Python.")]
    
    # Aggiungi la cronologia
    for msg in history:
        messages.append((msg["role"], msg["content"]))
    
    # Aggiungi l'input corrente
    messages.append(("user", user_input))
    
    # Usa il formato mustache per evitare errori quando il contenuto contiene {parentesi graffe}
    template = ChatPromptTemplate.from_messages(messages, template_format="mustache")
    
    formatted = template.format()
    response = llm.invoke(formatted)
    return response.content
 
# Utilizzo
history = []
 
# Turno 1
resp1 = chat_with_history("Cos'è un dizionario Python?", history)
print(resp1)
 
history.append({"role": "user", "content": "Cos'è un dizionario Python?"})
history.append({"role": "assistant", "content": resp1})
 
# Turno 2 - usa il contesto
resp2 = chat_with_history("Mostra un esempio.", history)
print(resp2)

Regole sull'Ordine dei Messaggi

Gli LLM si aspettano una struttura conversazionale specifica: System → User → Assistant → User → Assistant → ...

Perché Questo Ordine?

Questo pattern rispecchia le conversazioni naturali umano-IA:

  1. System viene prima (opzionale): Perché imposta le regole comportamentali che si applicano all'intera conversazione, deve essere definito prima che inizi qualsiasi interazione. Proprio come briefi qualcuno prima che inizi a lavorare, non nel mezzo di un compito.

  2. User poi Assistant si alternano: Nelle conversazioni reali, gli umani parlano (User), l'IA risponde (Assistant), gli umani fanno seguito (User), l'IA risponde di nuovo (Assistant). Questo pattern di alternanza è come l'IA è stata addestrata, quindi si aspetta questa struttura.

  3. Deve finire con User: L'IA genera una risposta all'ultimo messaggio User. Se la conversazione finisce con Assistant, non c'è niente a cui l'IA possa rispondere.

Esempi Validi:

python
# System + singolo User
[("system", "..."), ("user", "...")]
 
# System + conversazione
[("system", "..."), ("user", "..."), ("assistant", "..."), ("user", "...")]

Pattern Problematici:

python
# Assistant prima di User - l'IA si confonde sul contesto
[("system", "..."), ("assistant", "..."), ("user", "...")]
# L'IA vede una risposta senza una domanda. Potrebbe allucinare a quale domanda
# stava rispondendo, portando a risposte irrilevanti o confuse.
python
# Due messaggi User di seguito - risposta IA mancante
[("system", "..."), ("user", "..."), ("user", "...")]
# L'IA non sa a quale messaggio User rispondere, o potrebbe unirli
# in modo goffo. Perde il flusso conversazionale.
python
# Finisce con Assistant - niente a cui rispondere
[("system", "..."), ("user", "..."), ("assistant", "...")]
# La conversazione è completa. L'IA non ha niente da generare poiché non c'è
# una domanda User in sospeso. Probabilmente produrrà un errore o una risposta vuota.

Punto Chiave: Questi pattern non causano sempre errori hard, ma confondono l'IA perché rompono la logica conversazionale su cui è stata addestrata. L'IA potrebbe generare risposte, ma saranno inaffidabili o insensate. Segui sempre il pattern atteso per un comportamento prevedibile.

Oltre la Semplice Cronologia: Pattern di Produzione (Anteprima)

Nota Importante: Il pattern di cronologia della conversazione che hai appena imparato è un'ottima base, ma i sistemi di produzione usano approcci più sofisticati.

Il Problema con la Cronologia Grezza:

Passare semplicemente tutta la cronologia della conversazione all'IA ha limitazioni:

  1. Spreco di token: Ogni messaggio (anche quelli vecchi) conta verso il tuo limite di token e i costi
  2. Perdita di focus: L'IA potrebbe distrarsi con conversazioni precedenti irrilevanti
  3. Nessun compito esplicito: L'IA deduce cosa fare dalla cronologia, piuttosto che ricevere istruzioni chiare

Un Approccio Migliore:

I sistemi di produzione separano il contesto dalle istruzioni:

Approccio con cronologia semplice (quello che abbiamo appena imparato):

python
messages = [
    ("system", "Sei un esperto Python."),
    ("user", "Cos'è un dizionario?"),
    ("assistant", "Un dizionario è una struttura dati chiave-valore."),
    ("user", "Mostra un esempio.")
]

Approccio di produzione (nei capitoli successivi):

python
messages = [
    ("system", "Sei un esperto Python."),
    ("user", """Contesto: L'utente ha precedentemente chiesto dei dizionari Python e ha imparato che sono strutture chiave-valore.
 
Compito: Fornisci un esempio di codice che dimostri l'uso dei dizionari.""")
]

La Differenza:

  • Cronologia grezza: L'IA vede l'intera conversazione e capisce cosa fare
  • Pattern di produzione: L'IA riceve contesto riassunto + istruzione esplicita

Vantaggi della separazione:

  • Meno token (costo inferiore, risposte più veloci)
  • Comportamento più affidabile (istruzioni chiare)
  • Migliore controllo (decidi tu quale contesto è importante)

Dove imparerai questo:

  • Capitolo 8: Gestire lo stato della conversazione e la memoria
  • Capitolo 11: Recupero Contestuale (combinare RAG con la memoria della conversazione)
  • Capitolo 16: Routing dinamico basato sul contesto della conversazione

Per ora, capire la cronologia grezza è essenziale—è la base per questi pattern avanzati. Ma tieni a mente: quello che hai appena imparato è uno strumento didattico, non la soluzione finale.

4.3) Esternalizzare i Prompt: Gestire i File Template (.yaml)

Perché Esternalizzare i Prompt?

Man mano che la tua applicazione AI cresce, gestire i prompt nel codice Python diventa ingombrante. Esternalizzare i prompt in file YAML fornisce:

Collaborazione Non Tecnica: Product manager, esperti di dominio e ingegneri dei prompt possono modificare i file YAML senza toccare il codice Python o comprendere concetti di programmazione.

Chiarezza nel Controllo Versione: Traccia le modifiche ai prompt separatamente dalle modifiche al codice. Niente più commit misti dove le modifiche ai prompt e gli aggiornamenti logici appaiono insieme.

Prompt Specifici per Ambiente: Prompt diversi per sviluppo, staging e produzione senza modifiche al codice.

Test A/B: Testa variazioni di prompt caricando file diversi—nessuna modifica al codice necessaria.

Pensa ai file YAML dei prompt come ai file di configurazione nelle applicazioni tradizionali—definiscono il comportamento senza richiedere modifiche al codice o ridistribuzione.

Cos'è YAML?

YAML è un formato dati leggibile dall'uomo comunemente usato per i file di configurazione. Se non hai mai visto YAML prima, pensalo come un'alternativa più pulita a JSON—usa l'indentazione invece delle parentesi ed è più facile da leggere e modificare.

Struttura dei Prompt YAML

LangChain definisce una struttura standard di file YAML per i prompt. Vediamo degli esempi:

Esempio 1: Prompt senza variabili

Quando un prompt non ha bisogno di valori runtime, imposta input_variables su una lista vuota:

yaml
# prompts/system_prompt.yaml
_type: prompt
input_variables: []
template: |
  Sei un assistente utile.
  Per favore rispondi con un tono amichevole e incoraggiante.

Il simbolo | ti permette di scrivere testo multi-linea, e le interruzioni di riga sono preservate.

Esempio 2: Prompt con variabili

Quando un prompt ha bisogno di valori runtime, elencali in input_variables:

yaml
# prompts/user_prompt.yaml
_type: prompt
input_variables:
  - user_input
template: |
  Domanda dell'utente: {user_input}
  Per favore fornisci una risposta chiara.

A runtime, il segnaposto {user_input} viene sostituito con il valore effettivo.

Componenti chiave:

  • _type: prompt: Identifica questo come un template di prompt
  • input_variables: Elenca tutti i segnaposto usati nel template (lista vuota [] se nessuno)
  • template: Il testo effettivo del prompt con {segnaposto}

Caricare e Usare i Prompt YAML

Caricamento Base:

Ora carichiamo i file YAML che abbiamo creato e usiamoli con un LLM:

python
from langchain_core.prompts import load_prompt, ChatPromptTemplate
from langchain_openai import ChatOpenAI
 
# Carica i prompt dai file YAML
system_prompt_template = load_prompt("prompts/system_prompt.yaml")
user_prompt_template = load_prompt("prompts/user_prompt.yaml")
 
# Combina i prompt caricati in un template di chat
chat_template = ChatPromptTemplate.from_messages([
    ("system", system_prompt_template.template),
    ("user", user_prompt_template.template)
])
 
llm = ChatOpenAI(model="gpt-4o-mini")
 
# Passo 1: Renderizza il template con valori runtime
messages = chat_template.invoke({"user_input": "Cos'è LangChain?"})
 
# Passo 2: Invia all'LLM
response = llm.invoke(messages)
print(response.content)

Verificare i Template Caricati:

Prima di usare un template, verifica che sia stato caricato correttamente:

python
from langchain_core.prompts import load_prompt
 
# Carica il template
user_prompt_template = load_prompt("prompts/user_prompt.yaml")
 
# Controlla quali variabili si aspetta
print("Variabili di input:", user_prompt_template.input_variables)
 
# Vedi il testo del template
print("Template:", user_prompt_template.template)

Output:

Variabili di input: ['user_input']
Template: Domanda dell'utente: {user_input}
Per favore fornisci una risposta chiara.

Errori Comuni in YAML

Errore 1: Indentazione inconsistente

YAML richiede un'indentazione consistente (tipicamente 2 spazi). Ogni livello deve usare la stessa quantità di spaziatura:

Sbagliato:

yaml
_type: prompt
input_variables:
- user_input      # Sbagliato: gli elementi della lista dovrebbero essere indentati
  - question      # Sbagliato: livelli di indentazione misti

Corretto:

yaml
_type: prompt
input_variables:
  - user_input    # Corretto: entrambi gli elementi allo stesso livello di indentazione
  - question

Errore 2: Mancata corrispondenza dei segnaposto

I segnaposto in template devono corrispondere a input_variables:

Sbagliato:

yaml
input_variables:
  - user_input
template: "Domanda: {question}"  # 'question' non è in input_variables!

Corretto:

yaml
input_variables:
  - user_input
template: "Domanda: {user_input}"

LangChain solleverà un errore se i segnaposto non corrispondono alle variabili dichiarate.

4.4) Anteprima e Validazione dei Template prima dell'Esecuzione

Perché Visualizzare in Anteprima i Template?

L'ingegneria dei prompt è iterativa. Modifichi la formulazione, aggiusti la struttura, aggiungi esempi—e ogni iterazione costa token API e tempo. Visualizzare in anteprima i template prima dell'esecuzione ti permette di:

Risparmiare Tempo e Denaro: Individuare gli errori prima di effettuare costose chiamate API.

Verificare la Correttezza: Assicurarsi che le variabili siano riempite correttamente e la formattazione sia come previsto.

Debug Efficiente: Vedere il prompt esatto inviato all'LLM, con tutte le variabili riempite e la formattazione applicata.

Pensa all'anteprima del template come al debug con print—ispezioni lo stato intermedio prima dell'esecuzione per verificare la correttezza.

Anteprima Base del Template

Ispezionare la Struttura del Template:

Prima di usare un template con un LLM, ispeziona la sua struttura e visualizza in anteprima come si renderizza con dati di esempio:

python
from langchain_core.prompts import ChatPromptTemplate
 
template = ChatPromptTemplate.from_messages([
    ("system", "Sei un {role}."),
    ("user", "{user_input}")
])
 
# Anteprima della struttura del template
print("Variabili di input:", template.input_variables)
print("Conteggio messaggi:", len(template.messages))
 
# Anteprima con dati di esempio
prompt_value = template.invoke({
    "role": "esperto di programmazione Python",
    "user_input": "Cos'è Python?"
})
 
print("\nAnteprima:")
for msg in prompt_value.to_messages():
    print(f"{msg.type}: {msg.content}")

Output:

Variabili di input: ['role', 'user_input']
Conteggio messaggi: 2
 
Anteprima:
system: Sei un esperto di programmazione Python.
human: Cos'è Python?

Questo mostra esattamente cosa verrà inviato all'LLM, permettendoti di verificare il prompt prima dell'esecuzione.

Validare i Template: Individuare le Variabili Mancanti

L'errore più comune nei template è la mancanza di variabili richieste. Ecco una funzione di validazione riutilizzabile che individua le variabili mancanti:

python
from langchain_core.prompts import ChatPromptTemplate
 
def preview_template(template: ChatPromptTemplate, inputs: dict):
    """Visualizza in anteprima il template con gli input forniti, individuando gli errori."""
    try:
        prompt_value = template.invoke(inputs)
        
        print("ANTEPRIMA TEMPLATE")
        print("=" * 60)
        
        for i, msg in enumerate(prompt_value.to_messages(), 1):
            print(f"Messaggio {i} ({msg.type.upper()}):")
            print(msg.content)
            print("-" * 60)
                
    except KeyError as e:
        print(f"ERRORE: {e}")
        print(f"Variabili richieste: {template.input_variables}")
 
# Utilizzo
template = ChatPromptTemplate.from_messages([
    ("system", "Sei un {role}."),
    ("user", "{user_input}")
])
 
# Input validi
preview_template(template, {
    "role": "esperto di programmazione Python",
    "user_input": "Cos'è Python?"
})
 
# Variabile mancante
preview_template(template, {
    "user_input": "Cos'è Python?"  # Manca 'role'
})

Output:

ANTEPRIMA TEMPLATE
============================================================
Messaggio 1 (SYSTEM):
Sei un esperto di programmazione Python.
------------------------------------------------------------
Messaggio 2 (HUMAN):
Cos'è Python?
------------------------------------------------------------
 
ERRORE: "Input to ChatPromptTemplate is missing variables {'role'}.
Expected: ['role', 'user_input'] Received: ['user_input']
...
Variabili richieste: ['role', 'user_input']

Workflow di Validazione:

Ecco il tipico processo di validazione del template:

Errori

Validi

No

Definisci Template

Carica Dati di Esempio

Valida Input

Correggi Template/Dati

Anteprima Messaggi

Pronto?

Esegui con LLM

Questo processo iterativo aiuta a individuare gli errori prima di costose chiamate LLM.

Checklist Pre-Esecuzione

Prima di inviare i template in produzione:

  • Tutte le input_variables sono dichiarate in YAML/template
  • I dati di esempio si renderizzano senza errori
  • I prompt multi-linea si visualizzano correttamente
  • I segnaposto corrispondono esattamente ai nomi delle variabili
  • Test con casi limite (stringhe vuote, testo lungo)

Riepilogo del Capitolo:

Hai imparato a progettare prompt manutenibili e riutilizzabili usando il sistema di template di LangChain:

  1. Separazione delle Responsabilità: Disaccoppia i prompt dal codice per una manutenzione e iterazione più facili
  2. Template Consapevoli dei Ruoli: Usa messaggi system, user e assistant per interazioni LLM strutturate con una gerarchia di istruzioni appropriata
  3. Prompt Esternalizzati: Gestisci i prompt in file YAML per la collaborazione non tecnica e il controllo versione
  4. Anteprima e Validazione: Individua gli errori in anticipo e verifica i template prima dell'esecuzione

Prossimi Passi:

Nel Capitolo 5, vedrai come i template abilitano il processo decisionale autonomo negli esempi di agenti in anteprima. Poi nel Capitolo 6, imparerai LCEL (LangChain Expression Language) per comporre questi template in potenti pipeline usando l'operatore |.