# PRD — Claudio Opuscoli: Hackapizza 2.0

## 1. Vision

Un sistema multi-agente che simula una **brigata di cucina** per gestire autonomamente un ristorante galattico. Ogni agente ha un ruolo specializzato, tool dedicati e comunica con gli altri via message bus interno e stato condiviso su DB. Il sistema deve funzionare **senza intervento umano** durante la Golden Run.

---

## 2. Strategia di gioco

### 2.1 Il target è una conseguenza, non una scelta a priori

Il target del ristorante **dipende dai piatti nel menu**. Non si sceglie un archetipo cliente per poi comprare gli ingredienti — si calibrano ricette e ingredienti, e questi attireranno naturalmente una determinata clientela.

**Target preferenziale: Famiglie Orbitali**
- Profilo equilibrato ("medio-medio"): osservano prezzo e qualità, hanno molto tempo
- Più facile da gestire se non si ottengono gli ingredienti perfetti all'asta
- Menu con piatti di prestigio medio-alto, prezzi bilanciati, buon rapporto qualità/prezzo
- Fallback naturale: se otteniamo ingredienti premium → il menu scala verso Astrobaroni/Saggi; se otteniamo ingredienti comuni → restiamo sulle Famiglie

### 2.2 Menu ideale (da ricette analizzate)

| Ricetta | Prestigio | Tempo | Ingredienti |
|---|---|---|---|
| Portale Cosmico: Sinfonia di Gnocchi del Crepuscolo... | 100 | 5.2s | Shard di Materia Oscura, Uova di Fenice, Gnocchi del Crepuscolo, Plasma Vitale, Essenza di Tachioni |
| Sinfonia Temporale di Fenice e Xenodonte... | 95 | 4.0s | Polvere di Crononite, Uova di Fenice, Carne di Xenodonte, Pane degli Abissi, Plasma Vitale |
| Sinfonia del Multiverso Calante | 85 | 4.0s | Shard di Prisma Stellare, Granuli di Nebbia Arcobaleno, Fusilli del Vento, Pane degli Abissi, Lacrime di Unicorno |
| Sinfonia Cosmica di Proteine Interstellari | 77 | 3.0s | Carne di Balena spaziale, Carne di Mucca, Carne di Xenodonte, Pane degli Abissi, Funghi dell'Etere |

**Ingredienti condivisi chiave**: Pane degli Abissi (3 ricette), Uova di Fenice (2), Carne di Xenodonte (2), Plasma Vitale (2). Versatilità alta: se un piatto salta, gli ingredienti si riciclano.

### 2.3 Fallback adattivo

Dopo la fase di acquisti, lo Chef ricalibra il menu in base a ciò che è stato ottenuto:
- Il menu determina il target, non viceversa → lo Chef compone il miglior menu possibile con gli ingredienti disponibili
- Se otteniamo ingredienti premium → il menu scala verso alto prestigio (attrarrà Astrobaroni/Saggi)
- Se otteniamo ingredienti comuni → menu equilibrato (attrarrà Famiglie Orbitali)
- Se non è possibile assemblare nessun piatto → chiudere il ristorante per il turno (proteggere la reputazione)

### 2.4 Strategia di scalping (ricavo secondario)

Oltre a preparare piatti, il Responsabile Magazzini può operare come intermediario sul mercato:
- Usare lo storico prezzi delle aste (`bid_history`) per identificare ingredienti sotto-prezzati
- Comprare ingredienti a basso costo all'asta o sul mercato
- Rivenderli a prezzo maggiorato ad altri ristoranti tramite `create_market_entry` (SELL)
- Fonte di ricavo aggiuntiva che non dipende dal servizio clienti

---

## 3. Architettura multi-agente: la brigata

### 3.1 Patron (Orchestratore / Manager)

**Ruolo**: Cervello strategico del ristorante. Governa le transizioni di fase, attiva gli agenti, prende decisioni di alto livello.

**Tool di gioco**:
- `update_restaurant_is_open` — apre/chiude il ristorante
- Tutti i GET endpoints — visione d'insieme

**Tool DB**: read/write `turn_summary`

**Quando è attivo**: Sempre. Reagisce agli eventi di fase.

**Comportamento per fase**:
- `game_started` → inizializza il turno, legge stato dal DB
- `speaking` → attiva PR per negoziazioni, attiva Chef per pianificazione menu
- `closed_bid` → attiva Acquisti con brief strategico (budget, priorità ingredienti ricevute dallo Chef)
- `waiting` → attiva Chef per revisione menu post-asta, decide se aprire il ristorante
- `serving` → delega completamente al Maître, monitora solo tramite DB
- `stopped` → attiva Evaluator per analisi turno, poi scrive turn_summary nel DB

**Regola di escalation**: gli agenti scalano al Patron solo per decisioni strategiche che impattano la direzione del ristorante (es. alleanze, decisioni di chiusura, cambio radicale di strategia).

### 3.2 Chef

**Ruolo**: Pianifica il menu e definisce il fabbisogno ingredienti. È il ponte tra strategia e operatività.

**Tool di gioco**:
- `save_menu` — imposta/aggiorna il menu

**Tool DB**:
- read `bid_history` — storico prezzi per capire cosa è realistico ottenere
- read `turn_summary` — performance passate
- read `service_log` — quali piatti hanno funzionato
- read `known_intolerances` — evitare piatti problematici

**Quando è attivo**:
- `speaking` → pianifica menu ideale, comunica al Responsabile Acquisti la lista ingredienti necessaria
- `waiting` → rivede il menu in base agli ingredienti effettivamente ottenuti, chiama `save_menu`

**Flusso decisionale**:
1. Legge inventario attuale (GET `/restaurant/14`)
2. Legge ricette disponibili (GET `/recipes`)
3. Consulta storico turni dal DB (cosa ha funzionato, cosa no)
4. Seleziona 3-4 ricette ottimali (target Famiglie Orbitali preferenziale, adattivo al contesto)
5. Comunica al Responsabile Acquisti: "servono questi ingredienti, priorità X"
6. Dopo l'asta: rivede la selezione in base a ciò che è arrivato, minimizzando sprechi

### 3.3 Responsabile Magazzini (ex Responsabile Acquisti)

**Ruolo**: Gestisce aste cieche, operazioni di mercato e magazzino. Non si limita a comprare: gestisce anche la vendita di surplus e lo scalping di ingredienti.

**Tool di gioco**:
- `closed_bid` — offerte all'asta cieca
- `create_market_entry` — crea offerte di acquisto/vendita sul mercato
- `execute_transaction` — accetta offerte di mercato (deve essere reattivo: le offerte sono pubbliche e gli altri team possono catturarle prima)
- `delete_market_entry` — rimuove proprie offerte

**Tool DB**:
- read/write `bid_history` — storico aste (dati oltre i 2 turni del server)
- read/write `market_history` — storico transazioni mercato

**Quando è attivo**:
- `closed_bid` → riceve dal Chef la lista ingredienti e priorità, consulta `bid_history` per calibrare le offerte, invia bid
- Tutte le fasi (tranne stopped) → monitora il mercato con polling attivo, vende surplus, compra ciò che manca, cattura offerte vantaggiose

**Strategia d'asta**:
- L'LLM analizza lo storico prezzi dal DB e decide il bid
- Ha tool per leggere `bid_history` degli ultimi turni + il proprio storico completo dal DB
- Ha tool per leggere inventario attuale (GET `/restaurant/14`)
- Ha tool per leggere le richieste dello Chef (dal message bus interno)
- Calibra le offerte: media storica + margine di sicurezza, con budget cap dal Patron

**Logica post-asta**:
- Controlla inventario ottenuto
- Se mancano ingredienti critici → verifica mercato (GET `/market/entries`) e compra se conviene
- Se ha surplus → crea entry SELL sul mercato
- Comunica allo Chef l'esito

**Strategia di scalping**:
- Monitora storico prezzi per identificare ingredienti sotto-prezzati
- Compra a basso costo all'asta → rivende a margine sul mercato
- Fonte di ricavo parallela al servizio clienti

**Cattura offerte da accordi P2P**:
- Quando il PR conclude un accordo con un altro team, l'altro team pubblica l'ingrediente sul mercato (che è **pubblico**)
- Il Magazzini deve catturare l'offerta con `execute_transaction` prima che un terzo team la intercetti
- Richiede polling attivo su `GET /market/entries`

### 3.4 Maître (Servizio clienti)

**Ruolo**: Interpreta ordini e avvia le preparazioni. Gestisce più clienti in parallelo. Delega il servizio effettivo (serve_dish) alla Brigata middleware.

**Tool di gioco**:
- `prepare_dish` — avvia preparazione (HTTP 200 immediato, la preparazione avviene lato server per N ms)

**Tool DB**:
- write `service_log` — logga ogni ordine e il suo esito
- read `service_log` — consulta pattern di ordini passati
- read `known_intolerances` — verifica intolleranze prima di preparare

**Quando è attivo**: `serving` — completamente autonomo, riceve eventi `client_spawned` direttamente dal middleware SSE.

**Comportamento chiave**: `prepare_dish` **non è bloccante**. La chiamata HTTP ritorna subito 200 OK. Il piatto sarà pronto dopo `preparationTimeMs` millisecondi, quando il server invierà l'evento SSE `preparation_complete`. Il Maître non aspetta: continua a processare nuovi clienti in parallelo.

**Flusso asincrono**:
```
client_spawned (clientName, orderText, client_id)
  → Maître riceve evento (arricchito dal middleware con client_id)
  → LLM interpreta orderText → identifica ricetta dal menu
  → controlla known_intolerances per quel tipo di cliente
  → se OK: chiama prepare_dish(dish_name) → 200 OK immediato
  → se intolleranza nota: rifiuta (non serve, protegge reputazione)
  → registra in pending_orders e passa al prossimo cliente

[meanwhile, in Brigata middleware]
preparation_complete (dish_name)
  → Brigata trova il client in pending_orders che aspetta quel piatto
  → Brigata chiama serve_dish(dish_name, client_id) → deterministico, no LLM
  → Brigata logga in service_log
```

**Stato in memoria** (non su DB, volatile per turno):
```python
pending_orders = {
    "client-uuid": {
        "clientName": "Famiglia Orbitale",
        "orderText": "...",
        "dish": "Sinfonia Temporale...",
        "status": "preparing" | "ready" | "served"
    }
}
```

**Concorrenza**: gestisce più clienti simultaneamente. Gli eventi `client_spawned` arrivano in parallelo — il Maître ha "più chat aperte" contemporaneamente e non serve un cliente alla volta.

**Apprendimento intolleranze**: se un servizio ha esito negativo (il server restituisce errore o il cliente non paga), logga l'associazione `clientName/tipo → ingrediente problematico` nella tabella `known_intolerances` del DB.

### 3.5 Brigata (Middleware serving — NON è un agente LLM)

**Ruolo**: Processo deterministico che resta in ascolto degli eventi SSE `preparation_complete` e invoca `serve_dish` appena il piatto è pronto. Libera il Maître dalla responsabilità di attendere la fine delle preparazioni.

**Tool di gioco**:
- `serve_dish` — serve piatto pronto a un cliente

**Tool DB**:
- write `service_log` — logga esito del servizio
- write `known_intolerances` — registra intolleranze scoperte da fallimenti

**Logica**: puramente deterministica, nessuna chiamata LLM. Quando arriva `preparation_complete`:
1. Cerca in `pending_orders` il primo cliente che aspetta quel piatto
2. Chiama `serve_dish(dish_name, client_id)`
3. Aggiorna `pending_orders` (status → "served")
4. Logga in `service_log`
5. Se `serve_dish` fallisce per intolleranza → scrive in `known_intolerances`

### 3.6 PR (Comunicazioni esterne)

**Ruolo**: Gestisce tutte le comunicazioni con gli altri ristoranti. Fa **triage sui messaggi in entrata**: filtra truffe, proposte inutili e scambi che coinvolgono ingredienti già impegnati nelle nostre ricette.

**Tool di gioco**:
- `send_message` — messaggi ad altri ristoranti

**Tool DB**:
- read/write `messages_log` — storico conversazioni

**Quando è attivo**:
- `speaking` → proattivamente contatta altri ristoranti per negoziazioni
- Reattivo su `new_message` e `message` (broadcast) in tutte le fasi

**Triage messaggi in entrata**: prima di inoltrare una proposta al Magazzini, il PR verifica:
1. L'ingrediente proposto è già impegnato nelle nostre ricette? → rifiuta
2. L'offerta è plausibile rispetto allo storico prezzi? → se no, possibile truffa
3. Il team ha un track record affidabile? (consulta `messages_log`) → se inaffidabile, cautela

**Comunicazione peer-to-peer**: parla **direttamente** con il Responsabile Magazzini (via message bus interno) per valutare proposte, senza passare dal Patron.

**Flusso scambio P2P (come funziona davvero)**:
```
new_message: "Team 6 propone di venderci Radici di Gravità"
  → PR fa triage: ingrediente ci serve? prezzo ragionevole?
  → PR → Magazzini (msg interno): "Team 6 propone scambio, conviene?"
  → Magazzini verifica storico prezzi e bisogni attuali
  → Magazzini → PR: "accetta, digli di pubblicare sul mercato"
  → PR → Team 6 (send_message): "pubblica l'offerta"
  → Team 6 pubblica su mercato (create_market_entry)
  → Magazzini: polling attivo su GET /market/entries
  → Magazzini: cattura l'offerta con execute_transaction PRIMA che altri la prendano
```

**Nota**: i messaggi diretti servono solo per mettersi d'accordo. Lo scambio effettivo avviene sempre tramite il mercato pubblico. Questo significa che c'è un rischio: un terzo team può intercettare l'offerta.

### 3.7 Evaluator (Valutatore di fine turno)

**Ruolo**: Agente LLM che si attiva alla fine della fase serving (o a stopped) per analizzare le performance del turno e produrre insight per il turno successivo.

**Tool di gioco**: nessuno (solo lettura)

**Tool DB**:
- read `service_log` — ordini del turno
- read `bid_history` — aste del turno
- read `market_history` — transazioni del turno
- write `turn_summary` — scrive il riepilogo con raccomandazioni

**Quando è attivo**: `stopped` — analizza dopo che il servizio è concluso.

**Cosa analizza**:
- Per ogni ordine servito: richiesta iniziale, piatto interpretato, tempo impiegato, costo ingredienti vs prezzo di vendita, esito
- Margine effettivo per piatto
- Piatti più richiesti vs piatti mai ordinati
- Efficacia della strategia d'asta (quanto abbiamo speso vs quanto abbiamo guadagnato)
- Intolleranze scoperte

**Output**: scrive in `turn_summary` un riepilogo strutturato + campo `notes` con raccomandazioni strategiche per il turno successivo che il Patron e lo Chef leggeranno.

---

## 4. Infrastruttura tecnica

### 4.1 SSE Middleware (Event Router)

Processo asyncio sempre attivo, **non è un agente LLM**. Routing deterministico:

| Evento SSE | Destinazione |
|---|---|
| `game_started` | Patron |
| `game_phase_changed` | Patron |
| `game_reset` | Patron |
| `client_spawned` | Maître (arricchito con client_id da GET /meals) |
| `preparation_complete` | Brigata middleware (deterministico, chiama serve_dish) |
| `new_message` | PR |
| `message` (broadcast) | PR |
| `heartbeat` | ignorato (o logging) |

### 4.2 Message Bus interno (comunicazione tra agenti)

Ogni agente ha una `asyncio.Queue` come inbox. Qualsiasi agente può inviare messaggi a qualsiasi altro agente per ruolo:

```python
await send_internal("acquisti", {
    "from": "chef",
    "type": "ingredient_request",
    "content": {...}
})
```

**Flussi di comunicazione previsti**:
- Chef → Magazzini: "per il menu servono questi ingredienti"
- Magazzini → Chef: "ecco cosa ho ottenuto, rivedi il menu"
- PR → Magazzini: "proposta di scambio dal team X, conviene?"
- Magazzini → PR: "accetta/rifiuta/controproponi, digli di pubblicare"
- PR → Patron: "proposta di alleanza strategica dal team Y" (escalation)
- Evaluator → turn_summary DB: raccomandazioni per il turno successivo

### 4.3 Database condiviso (SQLite)

Il DB conserva solo dati che il server **non fornisce** o che servono come memoria storica oltre i limiti del server (2 turni).

**Tabelle**:

#### `bid_history`
Storico completo delle aste (il server tiene solo gli ultimi 2 turni).
```
turn_id, restaurant_id, ingredient, bid, quantity, won (bool), timestamp
```

#### `service_log`
Ogni ordine ricevuto e il suo esito.
```
turn_id, client_id, client_name, order_text, interpreted_dish, served (bool), outcome, timestamp
```

#### `turn_summary`
Snapshot di fine turno per decisioni strategiche.
```
turn_id, balance, reputation, dishes_served, dishes_failed, ingredients_wasted, revenue, cost, margin, timestamp
```

#### `market_history`
Transazioni completate sul mercato tra ristoranti.
```
turn_id, counterpart_id, side (BUY/SELL), ingredient, quantity, price, timestamp
```

#### `messages_log`
Conversazioni con altri ristoranti.
```
turn_id, counterpart_id, counterpart_name, direction (IN/OUT), text, timestamp
```

#### `known_intolerances`
Intolleranze apprese per esperienza.
```
client_type, ingredient, confidence (low/medium/high), learned_at_turn, source (failure/info)
```

### 4.4 MCP

Due layer MCP:
1. **MCP di gioco** (server Hackapizza): `POST /mcp` — tool di gioco (closed_bid, save_menu, prepare_dish, serve_dish, ecc.)
2. **MCP custom DB**: server MCP locale che espone tool di lettura/scrittura per il database SQLite. Ogni agente ha accesso ai tool DB pertinenti al suo ruolo.

---

## 5. Flusso di un turno completo

```
game_started (turn_id)
│
├─ Patron: inizializza turno, legge turn_summary precedente (con note Evaluator)
│
▼
speaking phase
│
├─ Patron → Chef: "pianifica il menu" (con contesto dalle note Evaluator)
├─ Patron → PR: "fase negoziazioni aperta"
├─ Chef: analizza ricette, inventario, storico → produce lista ingredienti
├─ Chef → Magazzini: "servono: Uova di Fenice x3, Pane degli Abissi x4, ..."
├─ PR: contatta altri ristoranti, esplora opportunità
├─ Magazzini: valuta opportunità di scalping se ci sono fondi extra
│
▼
closed_bid phase
│
├─ Magazzini: consulta storico prezzi (DB), calibra le offerte
├─ Magazzini: chiama closed_bid con la lista di bid
│
▼
waiting phase
│
├─ Magazzini: verifica inventario ottenuto (GET /restaurant/14)
├─ Magazzini → Chef: "ecco cosa abbiamo ottenuto"
├─ Magazzini: se mancano ingredienti critici → polling mercato + cattura offerte
├─ Magazzini: se ha surplus non necessario → crea entry SELL
├─ Chef: rivede il menu in base agli ingredienti disponibili (il menu determina il target)
├─ Chef: chiama save_menu
├─ Patron: decide se aprire il ristorante (update_restaurant_is_open)
│
▼
serving phase
│
├─ Maître: autonomo, riceve client_spawned direttamente, gestisce N clienti in parallelo
├─ Per ogni client_spawned:
│   ├─ LLM interpreta ordine
│   ├─ verifica intolleranze (DB)
│   └─ prepare_dish → 200 OK immediato (non bloccante, passa al prossimo cliente)
├─ Brigata middleware (deterministico, no LLM):
│   ├─ ascolta preparation_complete via SSE
│   ├─ serve_dish(dish, client_id)
│   └─ logga in service_log
├─ PR: continua a gestire messaggi in arrivo
├─ Magazzini: continua polling mercato
│
▼
stopped phase
│
├─ Evaluator: analizza tutti i dati del turno (service_log, bid_history, market_history)
├─ Evaluator: scrive turn_summary con metriche + raccomandazioni strategiche
├─ Magazzini: salva bid_history nel DB
└─ (sistema pronto per il turno successivo)
```

---

## 6. Resilienza

### 6.1 Filosofia: Fail Safe, Not Fail Fast

Il sistema non crasha mai. Degrada con grazia.

### 6.2 Gestione errori

| Errore | Strategia |
|---|---|
| Risposta LLM mal formattata | Max 3 retry con backoff incrementale. Se fallisce, usa fallback deterministico. |
| Rate limit (429) | Backoff esponenziale, max 3 retry. |
| Asta fallita (ingredienti non ottenuti) | Chef rivede menu minimizzando ingredienti aggiuntivi da comprare. Tenta acquisto sul mercato. |
| Nessun piatto assemblabile | Patron chiude il ristorante per il turno (proteggere reputazione). |
| Servizio a cliente con intolleranza | Logga nel DB `known_intolerances`, non ripetere in futuro. |
| Connessione SSE persa | Riconnessione automatica con backoff. |

### 6.3 Retry policy

```
Tentativo 1: immediato
Tentativo 2: dopo 1 secondo
Tentativo 3: dopo 3 secondi
Dopo 3 fallimenti: fallback o skip
```

---

## 7. Stack tecnologico

| Componente | Tecnologia |
|---|---|
| Framework agenti | `datapizza-ai` (obbligatorio) |
| LLM inference | Regolo.ai (`gpt-oss-120b` primario, `gpt-oss-20b` per task semplici) |
| Database | SQLite |
| Async runtime | Python asyncio |
| MCP gioco | `POST /mcp` su server Hackapizza |
| MCP DB | Server MCP locale custom |
| Monitoring | Datapizza Monitoring |
| SSE | aiohttp |

### 7.1 Scelta dei modelli per agente

| Agente | Modello suggerito | Motivazione |
|---|---|---|
| Patron | `gpt-oss-120b` | Decisioni strategiche complesse |
| Chef | `gpt-oss-120b` | Analisi ricette e ottimizzazione menu |
| Magazzini | `gpt-oss-20b` | Decisioni numeriche strutturate, velocità |
| Maître | `gpt-oss-120b` | Interpretazione linguaggio naturale degli ordini |
| Brigata | nessuno (deterministico) | Logica pura, nessun LLM |
| PR | `gpt-oss-20b` | Triage messaggi e negoziazioni |
| Evaluator | `gpt-oss-120b` | Analisi qualitativa post-turno |

---

## 8. Dati di riferimento

### 8.1 Il nostro ristorante

- **ID**: 14
- **Nome**: Claudio Opuscoli
- **Saldo iniziale**: 1000
- **Reputazione iniziale**: 100

### 8.2 Competizione

25 team in gioco. Aste cieche su 62 ingredienti unici.

### 8.3 Ingredienti strategici (alta versatilità)

| Ingrediente | Presente in N ricette |
|---|---|
| Carne di Balena spaziale | 65 |
| Carne di Kraken | 62 |
| Pane di Luce | 61 |
| Teste di Idra | 56 |
| Fibra di Sintetex | 54 |
| Carne di Drago | 54 |
| Uova di Fenice | 53 |
| Pane degli Abissi | 48 |

### 8.4 Timing

- **Sabato 12:00-14:00**: Setup (server attivo, nessuna run)
- **Sabato 14:00-17:00**: Run di testing (risultati non contano)
- **Sabato 17:00 - Domenica 10:00**: Run ufficiale (risultati contano)
- **Domenica 10:00**: Stop the coding
- **Domenica 10:00-12:00**: Golden Run (solo agenti, ~10 min/turno)
- **Domenica 14:00+**: Presentazioni

---

## 9. Criteri di valutazione (in ordine di importanza)

1. **Implementazione tecnica** — long term planning, memory, tool call, token efficiency, gestione eventi asincroni, uso di `datapizza-ai`
2. **Risultati di gioco** — fatturato, clienti serviti, reputazione, relazioni strategiche
3. **Pitch & Presentazione** — 2 min micro pitch, eventuale 5 min finale
4. **Creatività/Innovazione** — soluzioni fuori dagli schemi
