In queste lezioni abbiamo potuto apprezzare l'importanza del prompt come meccanismo che serve, al contempo, ad inoltrare richieste al Large Language Model, dargli chiare indicazioni (più dettagliate possibile) sul tipo di risultati che desideriamo e fornirgli eventuale materiale su cui lavorare. Tutto ciò emergerà ancora più chiaro al momento di prototipare i più comuni tipi di applicazioni con Intelligenza Artificiale quali chatbot, RAG e agenti.
Considerando però che un esempio parla più di mille definizioni, mostriamo in questa lezione come un particolare tipo di prompt permetta di spiegare ad un LLM quale tipo di risultato ci interessa: ecco il FewShotPromptTemplate
.
Pianificare un prompt ad esempi
Prendiamo un caso di studio. Diciamo di avere un modello (nel nostro caso è OpenAI ma l'esempio è adattabile a qualsiasi LLM), al quale chiediamo al volo qualcosa riguardo un personaggio storico italiano famoso:
from langchain_openai import ChatOpenAI
model = ChatOpenAI(
api_key="..."
)
print(model.invoke("Chi fu il primo presidente della repubblica italiana? Dimmi anno di nascita e di morte").content)
La risposta che otteniamo è:
Il primo presidente della Repubblica Italiana fu Enrico De Nicola. È nato il 9 novembre 1877 e morto il 1° ottobre 1959.
Perfetto ma diciamo che potremmo essere interessati ad una risposta in formato diverso come ad esempio Enrico De Nicola (1877-1959) ovvero nome e cognome seguito dagli anni di nascita e morte tra parentesi. Prendere l'output ed interpretarlo in Python per formattarlo non sarebbe facilissimo ma soprattutto non sarebbe nello stile AI! Sarebbe invece molto più attuale scrivere un prompt in grado di spiegare esattamente al modello il formato di output che desideriamo.
Qui impariamo a farlo fornendogli degli esempi per mostrargli di che tipo vogliamo la risposta.
Preparazione degli esempi
Questi sono gli esempi che proponiamo:
examples = [
{
"question": "Chi fu il primo presidente del consiglio del Regno d'Italia?",
"answer": "Camillo Benso Conte di Cavour (1810-1861)"
},
{
"question": "Chi guidò la spedizione dei Mille?",
"answer": "Giuseppe Garibaldi (1807-1882)"
},
{
"question": "Chi scrisse 'I Promessi Sposi'?",
"answer": "Alessandro Manzoni (1785-1873)"
}
]
ed inventiamo un semplice prompt che sarebbe in grado di formattarli uno alla volta:
from langchain.prompts import PromptTemplate
example_prompt = PromptTemplate(
input_variables=["question", "answer"],
template="Domanda: {question}\nRisposta: {answer}"
che provato con:
print(example_prompt.format_prompt(
question= "Chi scrisse 'I Promessi Sposi'?",
answer="Alessandro Manzoni (1785-1873)").text)
restituirebbe:
Domanda: Chi scrisse 'I Promessi Sposi'?
Risposta: Alessandro Manzoni (1785-1873)
Finora abbiamo semplicemente scritto esempi e preparato un prompt per mostrarli, niente di più. Tra l'altro il prompt può avere un testo a piacere purché mostri un output chiaro, per il modello, che illustri come vogliamo che i risultati siano esposti.
Creare un secondo prompt in grado di usare il primo
Ora creiamo un secondo prompt che sia in grado di usare il primo per incorporare nella richiesta all'LLM gli esempi che vogliamo fornirgli affinché intenda quale output vogliamo:
from langchain.prompts import FewShotPromptTemplate
few_shot_prompt = FewShotPromptTemplate(
examples=examples,
example_prompt=example_prompt,
prefix="Rispondi alle seguenti domande di storia italiana. Le risposte devono contenere nome e cognome del personaggio e le date di nascita e morte racchiuse tra parentesi come mostriamo nei seguenti esempi:",
suffix="L'utente ha chiesto di sapere: {user_input}\n Qual è la risposta nel formato degli esempi?",
input_variables=["user_input"],
example_separator="\n\n"
)
Il prompt è composto di varie parti:
examples
introduce la struttura dati che contiene gli esempi;example_prompt
è il prompt con cui formatteremo ogni singolo esempio;prefix
è un'introduzione (molto importante) che spiega al modello cosa dovrà farci con gli esempi che seguono;suffix
è la porzione di testo che segue gli esempi e introduce tipicamente la richiesta dell'utente che vorrà una risposta dal modello;example_separator
indica cosa vogliamo ci sia tra un esempio e l'altro, il separatore di esempi in pratica.
Questo prompt:
ai_prompt = few_shot_prompt.format(
user_input="Chi fu il principale teorico dell'unità d'Italia?"
)
print(ai_prompt)
genera un output del genere:
Rispondi alle seguenti domande di storia italiana. Le risposte devono contenere nome e cognome del personaggio e le date di nascita e morte racchiuse tra parentesi come mostriamo nei seguenti esempi:
Domanda: Chi fu il primo presidente del consiglio del Regno d'Italia?
Risposta: Camillo Benso Conte di Cavour (1810-1861)Domanda: Chi guidò la spedizione dei Mille?
Risposta: Giuseppe Garibaldi (1807-1882)Domanda: Chi scrisse 'I Promessi Sposi'?
Risposta: Alessandro Manzoni (1785-1873)L'utente ha chiesto di sapere: Chi fu il principale teorico dell'unità d'Italia?
Qual è la risposta nel formato degli esempi?
La prova del prompt
E' arrivato il momento di provare il tutto, fornendo richiesta utente e prompt al nostro modello:
ai_prompt = few_shot_prompt.format(
user_input="Chi affrescò la Cappella Sistina?"
)
res=model.invoke(ai_prompt)
print(res.content)
L'output che otteniamo:
Michelangelo Buonarroti (1475-1564)
dimostra come grazie ai nostri esempi il modello sappia chiaramente come mostrare il formato. Il prompt non è solo il modo in cui facciamo le richieste ma è un potente strumento per determinare il formato dell'output che desideriamo.
Questo tipo di approccio è estremamente interessante ed istruttivo per il programmatore stesso: la sperimentazione può essere proseguita in proprio modificando i due prompt coinvolti e applicando il tutto a modelli diversi come quelli in locale di Ollama.