---
title: "Ottimizzazione Prompt LLM: Guida Pratica con GEPA e Python"
date: "2026-06-07"
category: "Intelligenza Artificiale"
tags: ["gepa", "intelligenza artificiale", "llm", "prompt engineering", "python"]
author: "Giuseppe Carruezzo"
description: "Scopri come automatizzare l'ottimizzazione dei prompt con GEPA per migliorare le prestazioni degli LLM in compiti matematici complessi. Tutorial completo."
source: "https://ainsights.it/blog/ottimizzazione-prompt-llm-gepa-python"
---

# Ottimizzazione Prompt LLM: Guida Pratica con GEPA e Python

![Ottimizzazione Prompt LLM: Guida Pratica con GEPA e Python](/blogai/backend/uploads/6a26090388fd5.webp)

L'ottimizzazione dei prompt per i modelli linguistici di grandi dimensioni (LLM) sta evolvendo da un processo manuale basato su tentativi ed errori a un approccio scientifico e automatizzato. In questo tutorial, vedremo come utilizzare GEPA, un framework di evoluzione riflessiva, per trasformare prompt deboli in istruzioni altamente performanti per risolvere complessi problemi aritmetici.

## Configurazione dell'ambiente: GEPA e LiteLLM

Il primo passo consiste nell'installazione delle librerie necessarie. Utilizzeremo **GEPA** per gestire il ciclo evolutivo del prompt e **LiteLLM** per standardizzare le chiamate alle API di diversi modelli linguistici, garantendo flessibilità tra i vari provider.

```python
!pip install -q gepa litellm
import os, re, json, random, getpass, textwrap
import litellm
import gepa.optimize_anything as oa
from gepa.optimize_anything import (
   optimize_anything, GEPAConfig, EngineConfig, ReflectionConfig,
)
litellm.suppress_debug_info = True
if not os.environ.get("OPENAI_API_KEY"):
   os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API key: ")
TASK_LM        = "openai/gpt-4o-mini"
REFLECTION_LM  = "openai/gpt-4.1"
MAX_METRIC_CALLS = 100
```

In questa configurazione, distinguiamo due ruoli fondamentali:

-   **Task LM:** Il modello (es. GPT-4o-mini) incaricato di risolvere effettivamente il problema matematico.
    
-   **Reflection LM:** Un modello tipicamente più potente (es. GPT-4.1 o modelli di ragionamento avanzati) che analizza i fallimenti del Task LM per riscrivere e migliorare il prompt.
    

Il budget `MAX_METRIC_CALLS` è essenziale per limitare il numero di iterazioni di ottimizzazione, mantenendo i costi e i tempi di esecuzione sotto controllo.

## Costruzione di un benchmark aritmetico deterministico

Per ottimizzare un prompt in modo efficace, serve un set di dati affidabile. Creiamo un generatore di problemi aritmetici "deterministici" (ovvero con una soluzione univoca e calcolabile programmaticamente) che copra diverse casistiche: sconti commerciali, calcolo di distanze di viaggio, gestione del portafoglio e operazioni a catena.

```python
def make_problems(n, seed=0):
   rng = random.Random(seed)
   out = []
   for _ in range(n):
       t = rng.choice(["discount", "travel", "wallet", "chain"])
       if t == "discount":
           unit  = rng.choice([40, 60, 80, 120])
           qty   = rng.choice([5, 6, 8, 10])
           disc  = rng.choice([10, 20, 25, 50])
           total = unit * qty
           gold  = total - total * disc // 100
           q = (f"A shop sells notebooks at {unit} rupees each. You buy {qty} "
                f"notebooks and get a {disc}% discount on the total bill. "
                f"How many rupees do you pay in total?")
       elif t == "travel":
           s1, h1 = rng.choice([40, 50, 60]), rng.choice([2, 3])
           s2, h2 = rng.choice([30, 45, 70]), rng.choice([1, 2, 3])
           gold = s1 * h1 + s2 * h2
           q = (f"A car drives at {s1} km/h for {h1} hours, then at {s2} km/h "
                f"for {h2} hours. What is the total distance travelled, in km?")
       elif t == "wallet":
           tens   = rng.choice([3, 5, 7, 9])
           fifties= rng.choice([2, 4, 6])
           spent  = rng.choice([50, 80, 110, 150])
           gold = tens * 10 + fifties * 50 - spent
           q = (f"You have {tens} ten-rupee notes and {fifties} fifty-rupee "
                f"notes. You spend {spent} rupees. How many rupees are left?")
       else:
           x = rng.choice([6, 9, 12, 15]); y = rng.choice([4, 7, 10]); z = rng.choice([3, 8, 11])
           gold = x * 2 - y + z
           q = (f"Start with the number {x}. Double it, then subtract {y}, "
                f"then add {z}. What number do you end with?")
       out.append({"question": q, "answer": gold})
   return out

all_problems = make_problems(18, seed=42)
random.Random(1).shuffle(all_problems)
trainset = all_problems[:12]
valset   = all_problems[12:]
print(f"Dataset: {len(trainset)} train / {len(valset)} val problems\n")
```

Dividiamo i dati in un **training set** (utilizzato da GEPA per l'ottimizzazione iterativa) e un **validation set** (testato solo alla fine per verificare che il prompt funzioni bene anche su problemi mai visti prima).

## Definizione dell'evaluator e feedback strutturato

Il cuore dell'ottimizzazione riflessiva è la capacità di fornire feedback "azionabile". Non basta dire all'IA che la risposta è sbagliata; bisogna spiegare _perché_. L'evaluator analizza l'output del modello, estrae la risposta finale (identificata dal formato `#### <numero>`) e assegna un punteggio basato sulla precisione del calcolo e del formato.

```python
def build_system_prompt(candidate: dict) -> str:
   return (f"{candidate['instructions']}\n\n"
           f"OUTPUT FORMAT RULES:\n{candidate['format_rules']}")

def parse_answers(text: str):
   formatted = re.search(r"####\s*(-?\d+)", text)
   all_nums  = re.findall(r"-?\d+", text)
   fmt_val   = int(formatted.group(1)) if formatted else None
   last_val  = int(all_nums[-1]) if all_nums else None
   return fmt_val, last_val

def evaluate(candidate: dict, example: dict):
   system = build_system_prompt(candidate)
   raw    = call_task_lm(system, example["question"])
   gold   = example["answer"]
   fmt_val, last_val = parse_answers(raw)
   
   if fmt_val is not None and fmt_val == gold:
       score, fb = 1.0, "Correct and correctly formatted."
   elif fmt_val is not None and fmt_val != gold:
       score, fb = 0.0, (f"WRONG ANSWER. You output '#### {fmt_val}' but the correct answer is {gold}.")
   elif last_val == gold:
       score, fb = 0.5, (f"Right number but FORMAT VIOLATION. Use '#### <integer>'.")
   else:
       score, fb = 0.0, (f"WRONG. Correct answer is {gold}. Likely a reasoning slip.")
   
   side_info = {"feedback": fb, "problem": example["question"], "model_output": raw[:500]}
   return score, side_info
```

Questo feedback strutturato permette a GEPA di capire se il modello fallisce per mancanza di logica (errori matematici) o per inosservanza del formato richiesto.

```mermaid
flowchart TD
    A[Prompt Baseline] --> B[Task LM: Risolve Problemi]
    B --> C[Evaluator: Controlla Risposta e Formato]
    C --> D{Punteggio Massimo?}
    D -- No --> E[Reflection LM: Analizza Feedback]
    E --> F[Generazione Nuovo Prompt]
    F --> B
    D -- Sì --> G[Prompt Ottimizzato]
```

## Esecuzione del ciclo evolutivo con GEPA

Iniziamo con un prompt "seme" estremamente semplice: _"Risolvi il problema matematico. Dai la risposta."_. È un prompt volutamente debole che funge da linea di base (baseline).

```python
seed_candidate = {
   "instructions": "Solve the math problem.",
   "format_rules": "Give the answer.",
}

objective = (
   "Evolve a system prompt so a small LLM reliably solves multi-step arithmetic "
   "word problems AND always ends with exactly '#### <integer>'."
)

config = GEPAConfig(
   engine=EngineConfig(max_metric_calls=MAX_METRIC_CALLS, parallel=True),
   reflection=ReflectionConfig(reflection_lm=REFLECTION_LM),
)

result = optimize_anything(
   seed_candidate=seed_candidate,
   evaluator=evaluate,
   dataset=trainset,
   valset=valset,
   objective=objective,
   config=config,
)
```

Durante l'esecuzione, GEPA tenta diverse varianti del prompt. Se una variante migliora il punteggio medio, diventa la base per la "prole" successiva. Il processo continua fino al raggiungimento dell'obiettivo o all'esaurimento del budget di chiamate.

## Processo di Ottimizzazione con GEPA

1.  **Inizializzazione**: Si definisce un prompt di partenza e si valutano le prestazioni iniziali sui set di train e validazione.
2.  **Ciclo Evolutivo**: GEPA chiama il Task LM sui problemi. L\`evaluator analizza i risultati e produce feedback.
3.  **Riflessione**: Il Reflection LM usa il feedback per identificare punti deboli (es. mancanza di passaggi intermedi) e propone modifiche alle istruzioni.
4.  **Selezione e Validazione**: Il framework seleziona il candidato migliore basato sui punteggi medi e lo valida sul set di dati \`held-out\` per evitare l\`overfitting.

## Analisi dei risultati e validazione finale

Al termine del processo, estraiamo il `best_candidate`. Spesso si nota come il Reflection LM aggiunga istruzioni che forzano il modello a mostrare il proprio ragionamento passo-passo (Chain of Thought) o a eseguire una fase di verifica interna prima di scrivere la riga finale con il formato rigoroso.

```python
best = result.best_candidate
print(" Seed prompt score:", eval_set(seed_candidate, valset, "val-seed")[0])
print(" GEPA prompt score:", eval_set(best, valset, "val-gepa")[0])
```

Il miglioramento delle prestazioni tra il prompt iniziale e quello ottimizzato sul set di validazione dimostra che il sistema ha imparato strategie di istruzione generalizzabili, anziché limitarsi a memorizzare le risposte del training set.

## Punti chiave

-   GEPA automatizza il prompt engineering trasformando il feedback qualitativo in miglioramenti iterativi delle istruzioni.
-   L\`uso di due modelli distinti (Task e Reflection) permette di sfruttare l\`intelligenza superiore di modelli grandi per addestrare modelli più piccoli ed economici.
-   La separazione rigorosa tra istruzioni operative e regole di formato aiuta a ridurre le violazioni strutturali negli output dell\`IA.
-   Validare su un dataset separato (held-out) è fondamentale per garantire che il prompt funzioni in scenari reali non previsti.

### Verdetto Tecnico — 4.5/5

GEPA si conferma uno strumento potente per chiunque debba gestire pipeline LLM complesse. La capacità di evolvere prompt multi-componente basandosi su feedback strutturato riduce drasticamente il tempo dedicato alla scrittura manuale delle istruzioni, portando a risultati più robusti e meno soggetti a errori di formato.

## FAQ

### Posso usare GEPA con modelli locali?

Sì, tramite LiteLLM è possibile collegare GEPA a modelli locali come Llama 3 o Mistral eseguiti tramite server compatibili con le API OpenAI.

### Quanti esempi servono per una buona ottimizzazione?

Anche con pochi esempi (10-20) si possono ottenere miglioramenti significativi, purché i problemi siano rappresentativi della varietà che il modello dovrà affrontare.

### GEPA funziona per compiti non matematici?

Assolutamente sì. Può essere usato per l\`estrazione di dati, la sintesi di testi o la generazione di codice, a patto di definire un evaluator capace di dare un punteggio all\`output.

## Fonti e riferimenti

1.  [Repository Ufficiale GEPA su GitHub](https://github.com/gepa-ai/gepa)
2.  [Tutorial e Notebook Completo Marktechpost](https://github.com/MARKTECHPOST-AI-MEDIA-INC/AI-Agents-Projects-Tutorials/blob/main/LLM%20Evaluation/gepa_reflective_prompt_evolution_feedback_validation_marktechpost.py)

---

*Fonte: [https://ainsights.it/blog/ottimizzazione-prompt-llm-gepa-python](https://ainsights.it/blog/ottimizzazione-prompt-llm-gepa-python)*
