Introduzione: L'Arte di Comunicare con gli LLM
Il Prompt Engineering e la disciplina che studia come formulare richieste efficaci ai Large Language Models. Non e un'arte mistica: e un insieme di tecniche sistematiche, testabili e riproducibili che possono migliorare drasticamente la qualità degli output.
La differenza tra un prompt generico e un prompt ben ingegnerizzato può essere la differenza tra una risposta mediocre e un risultato professionale. Questo articolo copre le tecniche più efficaci, dal livello base a pattern avanzati come chain-of-thought e ReAct.
Cosa Imparerai in Questo Articolo
- Le tecniche fondamentali: zero-shot, few-shot e one-shot
- Chain-of-Thought (CoT) e Tree-of-Thought (ToT) per il ragionamento complesso
- System prompt e persona injection per controllare il comportamento
- Structured output: ottenere JSON, tabelle e formati specifici
- Il pattern ReAct per integrare ragionamento e azione
- Strategie di prompt versioning e A/B testing
Fondamentali: Zero-Shot, One-Shot e Few-Shot
La distinzione più basilare nel prompt engineering riguarda il numero di esempi forniti al modello prima della richiesta effettiva.
Zero-Shot: Nessun Esempio
Nel zero-shot prompting, si chiede direttamente al modello di eseguire un compito senza fornire alcun esempio. Funziona bene per compiti semplici e ben definiti dove il modello ha già appreso il pattern durante il training.
# Zero-shot: richiesta diretta senza esempi
from anthropic import Anthropic
client = Anthropic()
# Zero-shot classification
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=50,
messages=[{
"role": "user",
"content": "Classifica il sentimento di questa recensione come POSITIVO, NEGATIVO o NEUTRO:\n\n'Il prodotto e arrivato in anticipo e funziona perfettamente. Consigliatissimo!'"
}]
)
print(response.content[0].text) # POSITIVO
Few-Shot: Esempi come Guida
Il few-shot prompting fornisce 2-5 esempi di input/output prima della richiesta. Questo "insegna" al modello il pattern desiderato attraverso esempi concreti, migliorando significativamente la coerenza e l'accuratezza dell'output.
# Few-shot: fornire esempi prima della richiesta
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=100,
messages=[{
"role": "user",
"content": """Estrai le entità dal testo nel formato ENTITA: TIPO.
Esempio 1:
Testo: "Mario Rossi lavora a Roma per Acme Corp"
Output:
- Mario Rossi: PERSONA
- Roma: LUOGO
- Acme Corp: ORGANIZZAZIONE
Esempio 2:
Testo: "Il 15 gennaio Apple ha presentato il nuovo iPhone a San Francisco"
Output:
- 15 gennaio: DATA
- Apple: ORGANIZZAZIONE
- iPhone: PRODOTTO
- San Francisco: LUOGO
Testo: "Luca Bianchi partecipera alla conferenza Google I/O 2026 a Mountain View"
Output:"""
}]
)
print(response.content[0].text)
Chain-of-Thought: Ragionamento Passo dopo Passo
Il Chain-of-Thought (CoT) e una delle tecniche più potenti del prompt engineering. Invece di chiedere al modello una risposta diretta, lo si invita a "ragionare passo dopo passo", esplicitando il processo di ragionamento. Questo migliora significativamente le performance su compiti che richiedono logica, matematica o ragionamento multi-step.
# Chain-of-Thought: ragionamento esplicito
# SENZA CoT - risposta spesso errata su problemi complessi
response_no_cot = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=200,
messages=[{
"role": "user",
"content": "Un negozio vende mele a 2 euro al kg. Se compro 3.5 kg e pago con una banconota da 20 euro, quanto resto ricevo? E se applico uno sconto del 10%?"
}]
)
# CON CoT - ragionamento passo dopo passo
response_cot = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=500,
messages=[{
"role": "user",
"content": """Un negozio vende mele a 2 euro al kg. Se compro 3.5 kg e pago con una banconota da 20 euro, quanto resto ricevo? E se applico uno sconto del 10%?
Ragiona passo dopo passo prima di dare la risposta finale."""
}]
)
print(response_cot.content[0].text)
# Il modello esplicitera:
# 1. Costo base: 3.5 * 2 = 7 EUR
# 2. Resto senza sconto: 20 - 7 = 13 EUR
# 3. Sconto 10%: 7 * 0.10 = 0.70 EUR
# 4. Costo con sconto: 7 - 0.70 = 6.30 EUR
# 5. Resto con sconto: 20 - 6.30 = 13.70 EUR
Quando Usare Chain-of-Thought
Il CoT e particolarmente efficace per: problemi matematici multi-step, analisi logiche con premesse multiple, decisioni con criteri multipli da bilanciare, debugging di codice dove serve tracciare il flusso, e qualsiasi compito dove il ragionamento intermedio e importante quanto la risposta finale.
System Prompt e Persona Injection
Il system prompt e un messaggio speciale che pre-condiziona il comportamento del modello per l'intera conversazione. E il modo più efficace per definire il ruolo, il tono, le restrizioni e il formato di output desiderato.
# System prompt: definire ruolo e comportamento
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=500,
system="""Sei un esperto senior di architettura software con 20 anni di esperienza.
REGOLE:
1. Rispondi sempre in modo conciso e pratico
2. Usa esempi di codice Python quando utile
3. Evidenzia trade-off e considerazioni di performance
4. Se una domanda e ambigua, chiedi chiarimenti
5. Non inventare librerie o API inesistenti
FORMATO OUTPUT:
- Risposta breve (2-3 frasi)
- Pro e Contro (se applicabile)
- Esempio di codice (se rilevante)
- Riferimenti a pattern noti""",
messages=[{
"role": "user",
"content": "Dovrei usare un monolite o microservizi per una startup con 3 sviluppatori?"
}]
)
print(response.content[0].text)
Persona Injection
La persona injection e una variante del system prompt dove si assegna al modello un'identità specifica. Non e role-playing: e una tecnica per attivare conoscenze e stili comunicativi specifici nel modello.
- "Sei un DBA PostgreSQL senior": attiva conoscenze specifiche su ottimizzazione query, indici, EXPLAIN ANALYZE
- "Sei un tech lead che fa code review": focus su manutenibilità, naming, SOLID principles
- "Sei un copywriter per landing page B2B": tono professionale, focus su conversione, CTA efficaci
Structured Output: JSON, Tabelle e Formati Specifici
Una delle applicazioni più pratiche del prompt engineering e ottenere output in formati strutturati che possono essere facilmente parsati dal codice.
import json
# Richiedere output JSON strutturato
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=500,
messages=[{
"role": "user",
"content": """Analizza questa descrizione di un bug e restituisci un JSON con questa struttura esatta:
{
"severity": "critical|high|medium|low",
"component": "stringa",
"steps_to_reproduce": ["step1", "step2"],
"expected_behavior": "stringa",
"actual_behavior": "stringa",
"suggested_fix": "stringa"
}
Bug report: "Quando clicco su 'Salva' nel form di profilo con un'immagine maggiore di 5MB, la pagina va in crash con errore 500. Dovrebbe mostrare un messaggio di errore sulla dimensione massima."
Rispondi SOLO con il JSON, senza testo aggiuntivo."""
}]
)
# Parsing sicuro del JSON
try:
bug_data = json.loads(response.content[0].text)
print(f"Severita: {bug_data['severity']}")
print(f"Componente: {bug_data['component']}")
except json.JSONDecodeError:
print("Errore nel parsing JSON")
Il Pattern ReAct: Ragionamento + Azione
ReAct (Reason + Act) e un pattern avanzato dove il modello alterna fasi di ragionamento e fasi di azione. Il modello pensa a cosa fare, esegue un'azione (come chiamare un tool), osserva il risultato, e ripete il ciclo fino a completare il compito.
# Simulazione del pattern ReAct
react_prompt = """Sei un assistente che risolve problemi usando il pattern ReAct.
Per ogni step, usa il formato:
Thought: [ragionamento su cosa fare]
Action: [azione da eseguire]
Observation: [risultato dell'azione]
... (ripeti fino alla soluzione)
Final Answer: [risposta finale]
Strumenti disponibili:
- search(query): cerca informazioni
- calculate(expression): calcola espressioni matematiche
- lookup(database, key): cerca in un database
Domanda: Qual è il costo totale se compro 15 licenze software a 49.99 EUR ciascuna con IVA al 22%?"""
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=500,
messages=[{"role": "user", "content": react_prompt}]
)
print(response.content[0].text)
# Il modello produrra:
# Thought: Devo calcolare il costo base e poi aggiungere l'IVA
# Action: calculate(15 * 49.99)
# Observation: 749.85
# Thought: Ora devo aggiungere l'IVA al 22%
# Action: calculate(749.85 * 1.22)
# Observation: 914.817
# Final Answer: Il costo totale e 914.82 EUR (749.85 + 164.97 IVA)
Tecniche Avanzate di Ottimizzazione
Oltre alle tecniche fondamentali, esistono strategie avanzate per massimizzare la qualità degli output.
Delimitatori e Struttura
Usare delimitatori chiari per separare le diverse parti del prompt riduce l'ambiguità e migliora la comprensione del modello:
- Triple quotes (
""") per racchiudere testo da analizzare - XML tags (
<context>,<instructions>) per strutturare sezioni - Hash markers (
###) per separare sezioni logiche - Numerazione esplicita per istruzioni multi-step
Negative Prompting
Specificare cosa non fare e altrettanto importante quanto specificare cosa fare. Le istruzioni negative aiutano il modello a evitare pattern comuni indesiderati:
- "NON iniziare con 'Certo!' o 'Certamente!'"
- "NON inventare dati o statistiche se non li hai nel contesto"
- "NON ripetere la domanda nella risposta"
- "Se non conosci la risposta, dillo. NON inventare."
Self-Consistency
La tecnica di self-consistency consiste nel generare multiple risposte allo stesso prompt (con temperature > 0) e selezionare la risposta più frequente o quella su cui il modello converge. Questo e particolarmente utile per compiti con una risposta corretta definita.
Prompt Engineering Checklist
- Definisci il ruolo con un system prompt chiaro
- Usa few-shot per compiti specifici o formati custom
- Attiva CoT per ragionamento complesso ("Ragiona passo dopo passo")
- Specifica il formato di output desiderato (JSON, lista, tabella)
- Usa delimitatori per separare contesto, istruzioni e input
- Includi istruzioni negative per evitare pattern indesiderati
- Testa con diversi temperature per trovare il bilanciamento ottimale
- Versiona i tuoi prompt e traccia le performance con A/B test
Prompt Versioning e Testing
In produzione, i prompt sono codice. Vanno versionati, testati e monitorati come qualsiasi altro componente software. Una modifica apparentemente innocua al prompt può cambiare drasticamente il comportamento del modello.
# Sistema semplice di prompt versioning e testing
from dataclasses import dataclass
from typing import Callable
@dataclass
class PromptVersion:
version: str
system_prompt: str
template: str
evaluator: Callable[[str], float]
# Definisci versioni del prompt
v1 = PromptVersion(
version="1.0",
system_prompt="Sei un assistente utile.",
template="Riassumi questo testo: {text}",
evaluator=lambda output: len(output.split()) / 100 # brevita
)
v2 = PromptVersion(
version="2.0",
system_prompt="Sei un editor esperto. Produci riassunti concisi e informativi.",
template="""Riassumi il seguente testo in massimo 3 bullet point.
Ogni punto deve contenere un'informazione chiave con dati specifici.
Testo:
\"\"\"{text}\"\"\"
Formato output:
- [punto 1]
- [punto 2]
- [punto 3]""",
evaluator=lambda output: len(output.split()) / 50 # brevita più stretta
)
# A/B test
def test_prompt(version: PromptVersion, test_texts: list) -> float:
scores = []
for text in test_texts:
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=300,
system=version.system_prompt,
messages=[{"role": "user", "content": version.template.replace("{text}", text)}]
)
score = version.evaluator(response.content[0].text)
scores.append(score)
return sum(scores) / len(scores)
Conclusioni
Il prompt engineering non e un'abilita statica: evolve con i modelli e con la comprensione delle loro capacità. Le tecniche presentate in questo articolo - zero-shot, few-shot, CoT, system prompt, structured output, ReAct - formano un toolkit completo per interagire efficacemente con qualsiasi LLM.
Il punto chiave e trattare i prompt come codice: versionarli, testarli, iterare. Un prompt ben ingegnerizzato può eliminare la necessità di fine-tuning in molti casi, risparmiando tempo e costi significativi.
Nel prossimo articolo esploreremo il Fine-Tuning: quando il prompt engineering non basta e serve adattare il modello stesso ai tuoi dati, usando tecniche efficienti come LoRA, QLoRA e PEFT.







