AgileTavs — Guida operativa (HELP)

Questa guida permette di verificare tutto senza leggere il codice. È servita

anche su /help. Base URL pubblica: https://agiletavs.agile.software (in locale:

http://localhost:8000). Negli esempi usiamo BASE come segnaposto.

---

Percorso di giuria (in una sola sessione, dalla landing page)

Il copione della seduta finale. Un utente demo è già pronto: nome utente demo

(accettato anche demo@agile.software), password AgileTavs. La giuria NON si

registra: entra.

1. Landing page → Accedi. Apri https://agiletavs.agile.software/: è la landing

commerciale (cos'è, cosa fa, invito ad accedere). Clicca «Accedi» e inserisci

demo / AgileTavs. Via API:

```bash

curl -X POST "$BASE/account/login" -H "Content-Type: application/json" \

-d '{"utente":"demo","password":"AgileTavs"}'

# -> {"ok":true,"email":"demo@agile.software","nome":"Demo","username":"demo","token":"..."}

```

**Il token restituito dal login (o dalla registrazione, che fa auto-accesso) va

passato alle chiamate del conto** (/account/avatar, /account/conto): senza

token rispondono 401. Password errata → 401 (non 200) con il motivo.

2. Galleria degli avatar. Dopo il login sei nell'area del conto (/account): la

galleria mostra le tre persone digitali inventate (volto fotorealistico, nome,

ruolo, tono, voce) con ritratto e breve video di presentazione, e i tuoi avatar

salvati. Per ciascuna c'è il pulsante «Conversa» che apre la videochat.

3. Crea il tuo avatar (salvato). Dal pulsante «Crea il mio avatar» si apre la

sala di registrazione nel sito (/registra): registra 20–30 s di video parlando

(webcam + microfono), oppure carica una foto (campo foto) con un eventuale

campione voce (campo audio, alias accettato voce: prima il solo voce

veniva ignorato in silenzio), e con un clic il sistema clona volto e voce e **salva

l'avatar nel conto**, con un nome scelto da te. Riaprendo il sito l'avatar è ancora

lì. Via API (da una sola foto, con o senza campione voce; TOKEN è quello del

login):

```bash

curl -X POST "$BASE/account/avatar" \

-F "email=demo@agile.software" -F "token=$TOKEN" -F "nome=Il mio avatar" \

-F "foto=@foto.jpg" -F "audio=@voce.wav"

# -> {"ok":true,"avatar":{"id":"av_...","nome":"Il mio avatar",...}, "voci":{...}}

curl -s "$BASE/account/avatar?email=demo@agile.software&token=$TOKEN"

# -> {"ok":true,"avatar":[...]}

```

Cancellare un avatar dal conto (volto + voce + derivati, senza sporcare la

galleria):

```bash

curl -X DELETE "$BASE/account/avatar/AV_ID?email=demo@agile.software&token=$TOKEN"

# -> {"ok":true,"avatar":"av_..."}

```

Pulire il conto demo (rimuove avatar e chiavi di collaudo, idempotente):

```bash

curl -X POST "$BASE/account/pulisci-demo"

# -> {"ok":true,"avatar_rimossi":N}

```

4. Conversa con gli avatar. Dalla galleria (i tre + il tuo) scegli un avatar e

conversa a voce in videochat (/demo): ascolta, risponde in video con

sottotitoli, e la vista (webcam) è attiva — l'avatar tiene conto di ciò che vede.

Il tuo avatar parla con il tuo volto e la tua voce. Con un avatar salvato la

sessione si apre così:

```bash

curl -X POST "$BASE/v1/sessioni" -H "X-API-Key: <tua-chiave>" \

-F "avatar=AV_ID" -F "voce=if_sara" -F "lingua=it"

# -> {"id":"sess_...","stato":"pronto"}

```

5. Scarica la API key e usala da applicazioni esterne. Nell'area conto, sezione

API: genera/scarica la propria chiave e trova esempi pronti in curl, Python

e JavaScript (browser, CORS abilitato) per elenco persone, sessione, parla, conversa

e vede. La chiave è mostrata una sola volta.

---

Inizia in 5 minuti

1. Ottieni una chiave API: il percorso pubblico è self-service (vedi sotto

«Percorso utente self-service»): registrati, applica il codice promo AGILE2026

e genera la chiave dal tuo conto. Per il collaudo esiste già una chiave di prova

seminata: agiletavs-demo-2026.

2. Apri una sessione avatar:

```bash

curl -X POST "$BASE/v1/sessioni" -H "X-API-Key: agiletavs-demo-2026" \

-F "persona=aria-volta" -F "voce=if_sara" -F "lingua=it"

# -> {"id":"sess_...","stato":"pronto"}

```

3. Fai parlare l'avatar (testo → video):

```bash

curl -X POST "$BASE/v1/sessioni/SESS_ID/parla" -H "X-API-Key: agiletavs-demo-2026" \

-F "testo=Ciao! Sono AgileTavs." -o parla.mp4

```

4. Fai conversare l'avatar (audio → risposta video):

```bash

curl -X POST "$BASE/v1/sessioni/SESS_ID/conversa" -H "X-API-Key: agiletavs-demo-2026" \

-F "audio=@banco/dataset/domanda.wav" -o conversa.mp4

```

5. Prova la vista (fotogramma → descrizione filtrata):

```bash

curl -X POST "$BASE/v1/sessioni/SESS_ID/vede" -H "X-API-Key: agiletavs-demo-2026" \

-F "fotogramma=@banco/dataset/persona.jpg"

# -> {"descrizione":"Rilevo una persona di fronte alla telecamera."}

curl -X POST "$BASE/v1/sessioni/SESS_ID/vede" -H "X-API-Key: agiletavs-demo-2026" \

-F "fotogramma=@banco/dataset/documento.jpg"

# -> {"descrizione":"Rilevo un documento, non ne leggo il contenuto."}

```

---

Percorso utente self-service (in un'unica sessione)

Non serve l'amministratore: un visitatore del sito si registra da solo, sceglie la

persona digitale, la «acquista» con un codice promo (checkout simulato, nessun

pagamento reale) e genera la propria API key. Tutto dalla pagina /account (UI)

oppure via curl.

1. Registrati (email + nome, dati fittizi ammessi):

```bash

curl -X POST "$BASE/account/registra" -H "Content-Type: application/json" \

-d '{"email":"cliente@esempio.it","nome":"Mario Rossi"}'

# -> {"ok":true,"email":"cliente@esempio.it","nome":"Mario Rossi",...}

```

2. Scegli la persona fra le tre inventate (senza chiave):

```bash

curl -s "$BASE/account/persone"

# -> {"persone":[{"id":"aria-volta","nome":"Aria Volta","ruolo":"assistente commerciale",...}, ...]}

```

3. Acquista con il codice promo AGILE2026 (azzera il prezzo di listino 19 EUR/mese):

```bash

curl -X POST "$BASE/account/acquista" -H "Content-Type: application/json" \

-d '{"email":"cliente@esempio.it","persona":"aria-volta","codice_promo":"AGILE2026"}'

# -> {"ok":true,"prezzo":"0 EUR (azzerato dal promo)",...}

```

4. Genera la tua API key (dal tuo conto, dopo l'acquisto; mostrata una sola volta):

```bash

curl -X POST "$BASE/account/chiave" -H "Content-Type: application/json" \

-d '{"email":"cliente@esempio.it"}'

# -> {"ok":true,"chiave":"agiletavs_...","quota":1000}

```

5. Usala su /v1 (persone, sessione, parla, conversa, vede):

```bash

curl -s "$BASE/v1/persone" -H "X-API-Key: agiletavs_..."

```

Il riepilogo del conto (senza chiavi in chiaro): GET /account/conto?email=...&token=...

(serve il token del login). Il codice promo accettato è AGILE2026 (vedi api/account.py).

---

1. Ottenere una chiave (portale amministratore o self-service)

  • Percorso pubblico (consigliato): registrati dal sito e genera la chiave dal tuo
  • conto (vedi «Percorso utente self-service»): non serve l'amministratore.

  • Portale amministratore (/portale): per lo sviluppatore/tenant che amministra
  • le chiavi. Le credenziali dell'amministratore non sono pubblicate (vengono

    generate al primo avvio e salvate in api/data/portale.json): il percorso pubblico

    è quello self-service, senza admin.

  • La chiave è mostrata una sola volta, salvata come hash; si può revocare in
  • qualsiasi momento. Il registro d'uso mostra ogni chiamata (ora, chiave, percorso, esito).

    Ogni chiamata a /v1/* richiede l'intestazione X-API-Key: <chiave>.

    2. Persone digitali (tutte inventate)

    GET /v1/persone (con chiave) elenca le persone disponibili:

    idnomeruolovoce aria-voltaAria Voltaassistente commercialeif_sara bruno-aldiniBruno Aldinisupporto tecnicoim_nicola clara-neriClara Neriaccoglienza e benvenutoaf_bella
    
    curl -s "$BASE/v1/persone" -H "X-API-Key: agiletavs-demo-2026"
    

    I volti sono sintetici fotorealistici, generati da noi con SDXL base

    (stabilityai, licenza CreativeML OpenRAIL++-M) sul pod GPU: nessuna persona reale

    dietro. Il modello usato e la licenza sono dichiarati anche in docs/LICENZE.md e nel

    README.

    3. Aprire una sessione

    POST /v1/sessioni con form: persona, voce, lingua. Ritorna {"id": ...}.

    Puoi invece passare riferimento (id di un riferimento caricato) per usare una foto tua.

    Caricare un riferimento (resta solo su disco locale, mai fuori):

    
    curl -X POST "$BASE/v1/riferimenti" -H "X-API-Key: agiletavs-demo-2026" \
      -F "file=@foto.jpg"   # -> {"id":"rif_..."}
    

    Chiudere la sessione (cancellazione completa, anche della voce). Il DELETE

    della sessione rimuove volto, voce e ogni derivato dal disco locale (non solo il

    volto): se la sessione è stata aperta con un campione voce (voce_riferimento o

    tramite /v1/registrazione), anche quel campione rif_*.wav viene cancellato.

    
    curl -X DELETE "$BASE/v1/sessioni/SESS_ID" -H "X-API-Key: agiletavs-demo-2026"
    # -> {"stato":"chiusa"}
    

    Verificare che la cancellazione sia avvenuta (endpoint di verifica): `GET

    /v1/riferimenti/{id} risponde 200 con i metadati finché il file esiste, e 404`

    quando è stato cancellato.

    
    curl -s -o /dev/null -w "%{http_code}\n" "$BASE/v1/riferimenti/RIF_ID" \
      -H "X-API-Key: agiletavs-demo-2026"   # 200 = esiste ancora · 404 = cancellato
    

    Ogni file rimosso dal DELETE viene registrato nel log applicativo

    (api/data/agiletavs.log), con il numero e i percorsi dei file cancellati.

    4. Far parlare e conversare l'avatar

  • POST /v1/sessioni/{id}/parla — form testo (oppure audio) → video/mp4.
  • POST /v1/sessioni/{id}/conversa — form audio (wav/mp3) → video/mp4 con la risposta
  • (catena: ascolto → cervello → voce → volto).

    Le scorciatoie del banco (senza sessione, senza chiave):

  • POST /parla — form riferimento (foto) + testo (o audio) → video/mp4.
  • POST /conversa — form riferimento + audiovideo/mp4.
  • GET /salute — stato dei motori, senza chiave.
  • I tempi di ogni anello (ascolto, cervello, voce, volto, totale) sono nelle intestazioni

    della risposta (X-Tempo-*) e in banco/risultati/prova.json.

    5. Provare la vista (`/vede`)

    POST /v1/sessioni/{id}/vede (oppure POST /vede) con form fotogramma (jpg).

    Ritorna {"descrizione": "..."} già filtrata: il filtro di non identificazione

    non ipotizza mai identità, salute, emozioni, etnia, nomi, genere/età o contenuto di

    documenti, e ignora le istruzioni scritte dentro il fotogramma.

    Sul sito pubblico la vista gira in CPU (MediaPipe Face Detector + rilevatore di

    testo MSER): con banco/dataset/persona.jpg risponde

    Rilevo una persona di fronte alla telecamera. (la descrizione contiene «persona»);

    con un documento inquadrato da solo — banco/dataset/documento.jpg — risponde

    Rilevo un documento, non ne leggo il contenuto. (niente «non rilevo volti», e mai

    il contenuto del documento). Se il modello non è caricato (pesi mancanti) risponde

    503 invece di fingere di non vedere.

    6. Cosa aspettarsi

  • Formati: video video/mp4 (H.264 + AAC), massimo ~10 s a 512 px nel banco. Le
  • dimensioni sono sempre pari (richieste da H.264): un volto ritagliato da un

    video con dimensioni dispari viene normalizzato automaticamente, senza errori.

  • Tempi: dipendono dall'hardware. In CPU una conversazione breve richiede decine di
  • secondi; sulla GPU è molto più rapida. Ogni risposta riporta i tempi reali negli

    header X-Tempo-*.

  • Labiale (volto): il motore sceglie da solo, in ordine:
  • 1. labiale vero locale (MuseTalk + MediaPipe) se sul box c'è una GPU con i pesi;

    2. labiale vero sul pod remoto se il pod GPU è acceso (regia/pod.env con

    POD_SSH) e ha lo script + i pesi: il sito pubblico usa il labiale vero alla

    seduta finale, anche se il processo HTTP sta sul box senza GPU. Sul pod gira un

    daemon a pesi caldi (banco/anima_server.py, make su-pod lo avvia e lo

    preriscalda): i modelli si caricano una sola volta e la prima /parla è già

    veloce (non si ricaricano i pesi a ogni richiesta);

    3. volto fermo dichiarato se nessuno dei due è disponibile: il volto resta

    fotorealistico e immobile mentre l'audio è presente. NON viene MAI disegnata una

    bocca finta (l'ovale grigio incollato è stato rimosso per sempre). L'intestazione

    X-Motore-Volto della risposta dice quale motore è stato usato.

  • Movimento dal video (clone): quando l'avatar nasce da un girato (sala di
  • registrazione), il video è la sorgente di movimento: testa, sguardo ed

    espressioni del clone seguono il girato (non un fotogramma fermo), mentre MuseTalk

    ricostruisce la bocca coerente con l'audio. L'intestazione X-Motore-Volto riporta

    MuseTalk+MediaPipe (movimento dal video).

  • Il catalogo è vivo (le tre persone inventate). Anche Aria, Bruno e Clara hanno
  • un video sorgente (<id>-motion.mp4, generato da banco/genera_motion.py):

    cenni di testa, sguardo, respiro e battito di ciglia — non una foto ferma con la

    bocca incollata. Quando si sceglie una persona del catalogo, quel video è usato come

    sorgente di movimento esattamente come il girato del clone, quindi anche il catalogo

    risponde con X-Motore-Volto: MuseTalk+MediaPipe (movimento dal video).

  • La sincronia labiale è misurata (correlazione energia-apertura bocca) e riportata
  • in banco/risultati/prova.json. Con la testa in movimento la metrica a finestra

    fissa cala un po' (la bocca si sposta), ma il labiale è lo stesso MuseTalk vero:

    il valore resta positivo e la qualità si valuta guardando il video.

  • /conversa espone anche il testo: le intestazioni X-Domanda e X-Risposta
  • riportano la domanda trascritta e la risposta generata, così la coerenza è

    verificabile senza aprire il video.

    7. Errori possibili e cosa significano

    CodiceSignificatoCosa fare 401X-API-Key mancante, errata o revocataverificare la chiave su /portale 429quota giornaliera superataattendere (header Retry-After) o aumentare la quota 404sessione o riferimento inesistentericontrollare l'id 400manca un campo obbligatorio (es. riferimento)controllare la richiesta 422richiesta non valida (es. riferimento/video illeggibile)controllare il file inviato 503modello non caricato o generazione non riuscitaeseguire make pesi sul pod; il detail spiega il motivo 500errore interno imprevistoil detail riporta il motivo; la traccia completa è nel log

    Ogni errore di generazione è parlante (riporta il motivo nel campo detail, non

    un 500 muto) e viene registrato su file in api/data/agiletavs.log (lo stesso

    log finisce anche nel journal del servizio systemd). Se qualcosa fallisce, controlla

    lì la traccia completa.

    8. Come si verifica che la rete sia chiusa

    Il sistema è sovrano: a regime non chiama servizi esterni. I pesi si scaricano una

    volta con make pesi, mai a runtime. Sul pod la prova gira a rete chiusa:

    
    ./banco/su-pod.sh        # rsync + make pesi + unshare -n banco/prova.py
    

    unshare -n isola la rete (solo loopback): se la conversazione funziona comunque,

    dimostra che non c'è alcuna chiamata di rete a runtime. banco/risultati/prova.json

    registra rete_bloccata: true e l'uscita di nvidia-smi.

    9. Collaudo robotizzato

    
    make collaudo-pubblico SITO=https://agiletavs.agile.software
    

    Esegue il piano in banco/collaudo-pubblico.yaml (chiave, sessione, parla, conversa,

    vede, revoca→401, quota→429) e stampa verde/rosso per ogni passo.

    10. Landing page e demo videochat (UI)

    Apri / nel browser: è la landing page commerciale (cos'è, cosa fa, invito ad

    accedere), con il pulsante «Accedi» (utente demo demo / AgileTavs). Dopo il

    login sei nell'area del conto (/account) con la galleria, la creazione del tuo

    avatar e la API key.

    La videochat white-label è su /demo: inserisci una chiave, scegli una persona

    (o un tuo avatar), «Apri sessione», poi registra con il microfono per conversare e

    «Vede» per inquadrare la webcam. Il marchio e i colori vengono da demo/tenant.json.

    Arrivando da /account?persona=… o /demo?avatar=…&apikey=… la sessione si apre da

    sola.

    11. Sala di registrazione e clonazione (finale)

    Apri /registra nel browser: è la sala di registrazione integrata. Si registra

    un breve video (10–20 s) con webcam + microfono e lo si carica con un clic: il

    servizio estrae il volto dal fotogramma in cui il volto è più grande del girato

    (il primo piano, non la prima inquadratura larga), ritagliando il volto più grande

    — il soggetto, non chi gli sta accanto — e la voce (traccia audio); poi usa il video

    come sorgente di movimento del clone (testa, sguardo ed espressioni come nel

    girato) e crea una sessione con l'avatar clonato. Formati video accettati:

    mp4/webm (dal browser).

    La stessa cosa via curl (video → sessione clonata):

    
    curl -X POST "$BASE/v1/registrazione" -H "X-API-Key: agiletavs-demo-2026" \
      -F "video=@registrazione.webm"
    # -> {"id":"sess_...","riferimento_id":"rif_...","voce_riferimento_id":"rif_...","motion_id":"rif_..."}
    

    Poi si fa parlare/conversare l'avatar con la voce clonata:

    
    curl -X POST "$BASE/v1/sessioni/SESS_ID/parla" -H "X-API-Key: agiletavs-demo-2026" \
      -F "testo=Ciao, sono io!" -o clonato.mp4
    

    In alternativa si può aprire una sessione con un campione voce separato:

    
    curl -X POST "$BASE/v1/sessioni" -H "X-API-Key: agiletavs-demo-2026" \
      -F "riferimento=RIF_FOTO_ID" -F "voce_riferimento=@voce.wav"
    

    La clonazione della voce usa OpenVoice v2 (MIT) per trasferire il timbro del

    campione, con Kokoro-82M (Apache-2.0) come voce di base italiana (MeloTTS non ha

    l'italiano). Tutto resta su disco locale e viene cancellato con DELETE della

    sessione. I pesi si scaricano con make pesi --solo voce-clone (una sola volta,

    ~100 MB); senza pesi la sessione ripiega onestamente sulla voce Kokoro di default.

    La catena è verificata end-to-end: richiede solo torch (CPU basta), e l'audio

    prodotto è intelligibile (transcritto dal motore di ascolto locale).

    12. Separazione delle voci (diarizzazione) prima della clonazione

    Se il campione voce contiene altre voci (un intervistatore, una domanda fuori

    campo, un sottofondo), il sistema isola la voce del soggetto prima di clonarla:

  • VAD (Silero VAD v6, MIT, bundlato in faster-whisper) trova i segmenti di parlato;
  • embedding del parlante (ReferenceEncoder di OpenVoice v2, MIT) dà un vettore per
  • ogni segmento;

  • clustering (distanza coseno) raggruppa i segmenti per voce e sceglie la
  • dominante (quella con più parlato).

    Nella sala di registrazione (/registra) e nelle risposte di /account/avatar,

    /v1/registrazione e /v1/sessioni il campo voci riporta l'esito, per esempio:

    
    {
      "voci": {"voci": 2, "dominante": 1,
               "parlanti": [{"indice":1,"durata_s":18.4,"percentuale":70.1,"segmenti":5},
                            {"indice":2,"durata_s":7.8,"percentuale":29.9,"segmenti":3}],
               "report": "Ho trovato 2 voci: uso la voce dominante (70.1% del parlato)."}
    }
    

    Di default si usa la voce dominante (il soggetto, che di norma parla di più). Per

    scegliere esplicitamente un'altra voce, passa il campo parlante=<indice> (l'indice è

    quello riportato in voci.parlanti):

    
    # sala di registrazione via API: scegli la voce 2
    curl -X POST "$BASE/v1/registrazione" -H "X-API-Key: agiletavs-demo-2026" \
      -F "video=@registrazione.webm" -F "parlante=2"
    
    # sessione con campione voce separato e scelta della voce
    curl -X POST "$BASE/v1/sessioni" -H "X-API-Key: agiletavs-demo-2026" \
      -F "riferimento=RIF_FOTO_ID" -F "voce_riferimento=@voce.wav" -F "parlante=2"
    

    Per un avatar già salvato nel conto si può cambiare la voce a posteriori:

    
    curl -X POST "$BASE/account/avatar/AV_ID/parlante" -H "Content-Type: application/json" \
      -d '{"parlante": 2}'
    # oppure null per tornare alla scelta automatica (dominante):
    # curl -X POST "$BASE/account/avatar/AV_ID/parlante" -H "Content-Type: application/json" \
    #   -d '{"parlante": null}'
    

    Se il campione ha una sola voce, la diarizzazione lo conferma («Ho trovato 1 voce»)

    e la clonazione usa quella voce. Tutti i modelli usati per separare le voci sono a

    licenza commerciale (MIT) e nessuno scarica nulla a runtime.