Novità
Una voce per data, la più recente in alto. Ogni voce dice cosa è cambiato, a chi può servire e come si adotta. Il contratto completo è nella documentazione dell'API.
Versione del contratto: 1.0. Un nome di modello non cambia mai il suo comportamento: se cambierà il modello, cambierà il nome.
Registro completo per gli integratori: docs/NOVITA.md nel repository, allineato a questa pagina.
30/9/2026I tre piani col prezzo su richiesta, e una richiesta di licenza che non si perde.
Cosa è cambiato
- La landing diceva «prezzo al minuto, in definizione» e per chiedere una chiave c'era solo un
mailto:. Misurato: 0 dei tre nomi dei piani e 0 «su richiesta» nelle due facce, 0 richieste registrate — e la landing non era servita da nessuno (dal posto di chi apre il browser: 0 su 1). - Tre piani, con lo stesso contenuto in italiano e in inglese:
prova(ambititranscribeestream,quota_s_month7 200 s al mese — due ore,rate_per_min10 al minuto, 30 giorni),standard(anchebatch, quota e limite su misura, scritti sulla licenza alla firma) edenterprise(ambiti su misura anchemetrics, installazione dedicata). Prezzo su richiesta in tutti e tre: nessuna cifra. - I numeri vengono da una misura: la capacità dichiarata di un motore è 1 440 ore di audio al mese e il piano di prova ne è lo 0,14 %. I numeri veri stanno sulla licenza e si fissano all'emissione: il piano è il modello dei valori predefiniti, non una promessa di capacità.
POST /v1/richieste-licenza, rotta pubblica:201col numero da citare. La riga si scrive prima di provare qualunque recapito, e chi deve leggerla la riceve da un giro che segna fatto solo a consegna riuscita: nessuna richiesta si perde perché un recapito era giù. Rifiuti col nome:LICENSE_REQUEST_INVALIDdice quale campo,413oltre 4096 byte,429RATE_LIMITcolRetry-After(massimo 5 al minuto per indirizzo).- Privacy della richiesta: dell'indirizzo solo lo SHA-256; nel registro del servizio numero, id e piano — mai il nome, mai l'email, mai il testo di quello che hai scritto.
- Il modulo in entrambe le facce: se la rotta risponde male il messaggio è leggibile, senza codici, e quello che hai scritto resta nei campi. Il
mailto:resta una strada alternativa, non l'unica. - La landing è servita con la configurazione vera del repo, e si misura dal posto dell'utente: 7 elementi su 8. Manca il confronto col leader di mercato coi numeri veri, per una ragione sola: la sua chiave non è ancora nel caveau.
A chi serve
A chi non ha ancora una chiave e vuole capire cosa costa e come si comincia.
Come si adotta
Niente da fare per chi ha già una chiave. Il contratto resta 1.0: solo aggiunte, nessuna risposta di prima cambia.
30/9/2026Ogni chiave ha un nome: sai quale stai revocando.
Cosa è cambiato
- Tre chiavi che lo stesso amministratore si fa per tre usi diversi (produzione, collaudo, portatile) erano indistinguibili: stesso
owner, stessi scope, stessa quota. Misurato: 0 righe su 3 dicevano a cosa serve la chiave, e l'unico campo che le distingueva era l'id. Un nome mandato alla creazione veniva buttato via in silenzio, con un201. - Il campo
nomesuPOST /v1/console/keys, nell'elenco e nella risposta: non èowner(di chi è la chiave), è a cosa serve. Unico fra le chiavi attive della tua azienda, maiuscole e spazi indifferenti: un nome già in uso è409 KEY_NAME_TAKENe dice quale chiave lo porta; vuoto, oltre 60 caratteri o con caratteri di controllo è400 KEY_NAME_INVALID, e la chiave non nasce. Il nome di una chiave revocata si libera. - La rotazione porta il nome, come la scadenza: il
key_idcambia, il nome no. - Si cerca:
GET /v1/console/keys?nome=collaudo(nome intero, non il suo inizio); un nome che non combacia torna vuoto, non tutte le chiavi. Misurato: da 3 chiavi su 3 a 1, righe da leggere a mano da 3 a 0. PUT /v1/console/keys/{key_id}/nome: rinomina una chiave che c'è già (solo se attiva), e la risposta dice com'era prima.- Il registro dice come si chiama la chiave (
nome_chiave): «revoke ab12cd34» non diceva niente a nessuno. Il nome non è copiato nel registro ma risolto alla lettura, e quello di una chiave di un'altra azienda non compare mai — nemmeno sulla riga di un tentativo rifiutato.
A chi serve
A chi ha più di una integrazione: la revoca vuole in ingresso l'id della chiave, e chi non le distingue revoca a caso.
Come si adotta
Nessun cambiamento obbligato: una chiave senza nome funziona come prima e il contratto resta 1.0. Il nome lo leggono la tua azienda e noi: non è un posto per un dato di una persona.
30/9/2026Il registro dice anche cosa abbiamo fatto NOI alle tue chiavi, e si cerca.
Cosa è cambiato
- Quando la regia interviene sulle chiavi di un'azienda (una revoca, una rotazione, una chiave emessa per lei) la riga ora è nel registro dell'azienda, e dice di chi è chi ha agito (
attore_tenant,attore_e_della_mia_azienda). Misurato: da 0 operazioni su 4 viste a 4 su 4. - Tre filtri su
GET /v1/console/audit:?key_id=,?persona=(id o email, e guarda sia chi ha subito l'operazione sia chi l'ha fatta) e?operazione=. Un filtro che non combacia torna vuoto, non tutto. Misurato: da 0 domande su 3 a 3 su 3, e le righe da leggere a mano da 100 a 0. - Il tetto è dichiarato:
limiteda 1 a 1000, 100 di serie. Primalimite=1000000tornava il registro intero (20.124 righe, 2.604 kB) e un limite negativo pure; ora è400 AUDIT_LIMIT_INVALID, e la risposta dicequanterighe combaciano e se ètroncato: niente tagli silenziosi. - Mai un token, mai un'impronta, mai un'email nelle righe: era già così, adesso è misurato a ogni giro.
A chi serve
A chi deve rispondere «chi ha spento questa chiave, e quando»: al cliente per le sue integrazioni, a noi per il supporto.
Come si adotta
Nessun cambiamento obbligato: i filtri e i campi sono aggiunti, il predefinito di limite resta 100. Se un tuo script chiedeva un limite enorme per scaricare tutto, ora prende 400: usa i filtri o leggi a pagine.
30/9/2026I numeri del servizio non sono i numeri di un cliente.
Cosa è cambiato
- Dalla console self-service un'azienda si dà solo gli scope che compra —
transcribe,stream,batch: chiederemetrics(il quadro di vigilanza) otestè ora403 SCOPE_NOT_SELF_SERVICE, e quelle chiavi le emette la regia. Prima bastava chiederselo: 4 richieste su 4 accettate. GET /v1/quadrosi ferma al perimetro della chiave e lo scrive (perimetro): la tua azienda se la chiave ha un tenant, il servizio intero solo per la regia. Misurato: da 3 aziende su 3 e 4 chiavi non tue (con nomi, prodotti e secondi d'audio degli altri clienti) a 1 azienda e 0 chiavi non tue.GET /metricssono i contatori del servizio e non hanno un'etichetta per azienda: a una chiave con un tenant rispondono403 METRICS_TENANT_FORBIDDENe dicono dove stanno i suoi numeri.
A chi serve
A ogni azienda sul servizio: quanto lavora, con quali prodotti e con quali volumi non si legge dal posto di un altro cliente.
Come si adotta
Nessun cambiamento per chi usa l'API: i numeri della tua azienda sono dove erano — GET /v1/console/keys (chiavi, secondi per chiave, totale dell'azienda, piano, percentuale di quota) e GET /v1/usage per chiave. Se un tuo cruscotto usa una chiave metrics, continua a funzionare e vede la tua azienda.
30/9/2026Le chiavi di chi ha lasciato l'azienda: ora si vedono, e si tolgono in una mossa (fronte).
- Una persona che si era fabbricata delle chiavi dalla console e poi lascia l'azienda lasciava credenziali vive e invisibili: sospenderla le toglieva la console, non le sue chiavi. Misurato: 3 chiavi su 3 ancora attive e trascriventi dopo la sospensione, e dall'elenco l'amministratore ne riconosceva come sue 0 su 3 (l'
ownerera unu_…opaco, l'email non compariva). - L'elenco dice di chi è ogni chiave:
GET /v1/console/keysportaowner_email,owner_statoeowner_e_personaaccanto all'owner— una chiave attiva di una persona sospesa si vede a occhio. Mai il token di una persona, mai la sua impronta. Misurato dopo: 3 su 3 attribuite con la sola risposta dell'elenco. - Il filtro per persona:
?owner=accetta l'id, l'email (maiuscole indifferenti) o il nome di un prodotto; unownerche non combacia con niente torna un elenco vuoto, non tutte le chiavi. - Le sue chiavi si tolgono in una mossa:
POST /v1/console/keys/revoke-by-ownerinvece di una revoca per chiave sapendo già quali sono (3 chiamate → 1). Lo chiama un amministratore, e ognuno sulle proprie. Un nome senza chiavi è 404OWNER_NOT_FOUND. - Nessuna sospensione revoca niente da sola: la chiave è dell'azienda, non della persona, e dentro c'è l'integrazione che quella persona aveva messo in piedi. La scelta è tua ed è esplicita (
chiavi: "lascia"di serie,"revoca"su richiesta) e finisce nel registro in entrambi i casi. - La revoca per owner non ha grazia, al contrario della rotazione: la rotazione ti consegna un segreto nuovo, una revoca no — una finestra terrebbe valido solo il segreto di chi è uscito (misurato: trascriveva ancora per tutte le 24 ore). Se l'integrazione ti serve viva, ruota la chiave. Il contratto della trascrizione è invariato.
30/9/2026Il token con cui entri in console si può cambiare, e un invito non vale per sempre (fronte).
- Il token
agt_…di una persona è la credenziale del self-service. Misurato quanto vale se finisce in una chat: con un token di persona rubato si fanno 9 operazioni di console su 9 — comprese le chiavi del tenant, che chi lo possiede si fabbrica da sé e che trascrivono l'audio; con una chiave rubata, 0 su 9. - I rimedi erano 0 su 3: sospendere la persona fermava anche lei, e riattivarla le restituiva lo stesso segreto. Ora
POST /v1/console/users/{id}/rotate-tokendà un segreto nuovo senza cambiare identità — stesso id, stesso ruolo, stessa storia, stesse chiavi — e chi resta col vecchio legge 403TOKEN_ROTATED. Misurato dopo: il ladro da 3 su 3 a 0, la persona col nuovo 3 su 3. - Il tuo lo cambi tu, anche se sei un utente semplice: chi si è visto uscire il token non aspetta l'amministratore. Sulle persone di un'altra azienda è 403
TENANT_FORBIDDEN. - Di serie il vecchio muore subito (
grazia_ore: 0), al contrario della rotazione di una chiave che tiene la vecchia viva un giorno: una chiave si ruota per igiene, un token di persona perché è uscito. La grazia si può chiedere, per il token dentro uno script. - Un invito non accettato scade: 7 giorni di serie (
scadenza_giorniper accorciarlo, 90 il massimo), poi 403INVITE_EXPIRED. Prima, un invito mandato a un indirizzo vecchio entrava ancora in console 400 giorni dopo. La scadenza è dell'invito, non della persona: quando l'invito viene accettato viene cancellata. Il contratto della trascrizione è invariato.
30/9/2026Le persone della tua azienda le inviti tu (fronte).
- La console self-service sapeva già dare le chiavi del tuo tenant; le persone no. Ruoli, stati e isolamento fra aziende c'erano già dentro il servizio, ma senza una porta: per far entrare un collega serviva una richiesta a noi. Misurato: delle sei cose che l'amministratore di un'azienda deve poter fare da solo, 0 su 6 si potevano fare dal suo posto; ora 6 su 6.
- Le rotte:
POST /v1/console/users(invita),POST /v1/console/users/accept(chi è stato invitato accetta col proprio token),GET /v1/console/users(le tue persone con ruolo e stato),POST /v1/console/users/{id}/suspend,.../reactivate,PUT /v1/console/users/{id}/role. - Il token dell'invito si vede una volta sola, come il segreto di una chiave: dopo resta solo la sua impronta, e non c'è modo di rileggerlo — né dall'elenco, né dal registro.
- Invitare, sospendere e assegnare ruoli è dell'amministratore: a un utente semplice rispondiamo 403
ADMIN_REQUIRED(l'elenco lo legge). Le persone di un'altra azienda sono 403TENANT_FORBIDDEN, non una lista vuota. - La tua azienda non può chiudersi fuori da sola: l'ultimo amministratore attivo non si può sospendere né declassare (409
LAST_ADMIN). Prima un clic la lasciava con zero amministratori e nessuno che potesse invitare. - Due clic sull'invito non fanno due persone: la stessa email invitata di nuovo è 409
USER_EXISTS. Ogni operazione lascia una riga nel registro con chi l'ha fatta e su chi: l'id della persona, mai il suo token e mai la sua email. Il contratto è invariato.
30/9/2026Una raffica d'audio non fa più aspettare chi vuole il finale (motore v0.3.3).
- Il motore calcola un parziale ogni tanto mentre parli: è un'anteprima del testo e costa un ASR intero sul pool condiviso da tutte le telefonate, quindi la regola è «un parziale per volta». La regola valeva finché l'audio arrivava a ritmo d'orologio, come lo manda un telefono; non valeva su una raffica — un cliente che recupera dopo un buco di rete, un file spinto in streaming — perché la guardia si alzava quando il calcolo cominciava e non quando il parziale entrava in coda.
- Misurato sul box (motore di prova, modello
basesu CPU, 2 lavoratori ASR, 4 s di parlato per telefonata): 7 parziali chiesti per telefonata a raffica contro 1–3 a ritmo; e il finale che chiedi con lostopsi metteva dietro a tutti loro — con 8 telefonate insieme il più tardivo dopo 33,0 s invece di 12,8 s. - Ora la guardia si alza quando il parziale entra in coda: a raffica ne parte uno, e il finale non aspetta lavoro che stava per sostituire lui stesso.
- A ritmo di telefonata non cambia niente: i parziali che ricevi sono gli stessi di prima, con gli stessi tempi, e il contratto è invariato.
- Nel diario del motore la riga di chiusura porta
chiesti=N, i parziali messi in coda: non sono quelli mandati (partials=) né quelli scartati a vuoto (scartati=). Il log dicevapartials=1mentre in coda ce n'erano sette.
30/9/2026Quando il fronte si riavvia, chi sta parlando non se lo sente dire dal silenzio (fronte).
- Il fronte si riavvia a ogni aggiornamento, e un riavvio arriva mentre delle persone stanno parlando. Fino a ieri era un socket che moriva: 0 telefonate su 3 ricevevano il finale di ciò che avevano già detto — un testo che il servizio aveva in mano — e 0 su 3 un avviso.
- Ora chi parla riceve
{"type":"closing","code":"SHUTTING_DOWN","retry_after_s":10}, poi il finale di ciò che aveva già detto, poi la chiusura col codice 1012 («il servizio riparte»), che le librerie WebSocket riprovano da sole. Misurato: da 0 su 3 a 3 su 3. - I lavori lunghi in coda o in corso vanno in
failedconSHUTTING_DOWNsubito e non si addebitano: vivono solo in RAM, un riavvio li perde, e saperlo vale più di un404che arriva dopo. - Chi arriva mentre il fronte si ferma prende 503
SHUTTING_DOWNcolRetry-After, eGET /healthrisponde 503 condraining: true: chi bilancia lo toglie dal giro prima che l'indirizzo diventi muto. Misurato: indirizzo muto da 1,06 s a 0,08 s. - Nulla da cambiare nei prodotti: il
closingè un messaggio in più, il 1012 è il codice standard, il contratto resta 1.0. Finestra 15 s (AGILEASCOLTO_CHIUSURA_S,0= come prima): oltre, il fronte esce comunque.
29/9/2026La quota conta anche l'audio che sta entrando adesso (fronte).
- Una telefonata registra i suoi secondi quando finisce, e un file quando il motore ha risposto: nel frattempo, fino a ieri, quei secondi non esistevano per nessuno. La quota mensile (e il piano dell'azienda) non era quindi un tetto sull'audio ascoltato, ma un tetto su una telefonata per volta.
- Misurato: tre linee aperte insieme passavano 18 s su un tetto di 6 (3,0x, sia sulla chiave sia sul piano del tenant) e un file mandato mentre si parlava non vedeva niente (2,0x). Dopo il rimedio, stesso banco: 1,0x in tutti e tre i casi.
GET /v1/usageporta due campi nuovi,in_corso_setenant_in_corso_s: i secondi entrati e non ancora registrati (telefonate aperte, file in lavorazione, lavori in coda). Campi aggiunti: il contratto resta 1.0.- Una sessione lunga rilegge i consumi del mese ogni 15 s (
AGILEASCOLTO_RINFRESCO_QUOTA_S), e il blocco d'audio rifiutato per quota finita non si paga più (prima: 6,5 s addebitati su una quota di 6). - Nulla da cambiare nei prodotti: chi sta dentro la quota non vede differenza, e senza quota né piano non cambia nulla del tutto.
29/9/2026Quando il servizio dice di no, dice anche fra quanto riprovare (fronte).
- I rifiuti che si possono riprovare portano ora
Retry-After(secondi interi) e lo stesso numero inerror.retry_after_s: i 429 (RATE_LIMIT,QUOTA_EXCEEDED,TENANT_RATE_LIMIT,TENANT_QUOTA_EXCEEDED) e il 503CAPACITY. Sul WebSocket, dove le intestazioni non esistono, c'è il campo nel messaggio d'errore. Campi aggiunti: il contratto resta 1.0. - I tre numeri dicono cose diverse: sul tetto al minuto è l'istante esatto in cui si libera un posto (la finestra è scorrevole); sulla quota mensile è l'inizio del mese prossimo, perché prima non passerà comunque; sul pieno è un consiglio di pochi secondi, spalmato apposta (
AGILEASCOLTO_RIPROVA_PIENO_S,AGILEASCOLTO_RIPROVA_SPREAD_S) perché tutti i rifiutati insieme non tornino insieme. - Gli altri rifiuti — 401, 403, 404, 413, 501, 502 — non lo portano: sono no sul merito, e riprovare non cambierebbe niente.
- Nulla da cambiare nei prodotti:
Retry-Afterlo leggono da sole le librerie HTTP e i client OpenAI. - Perché: misurato con un cliente che riprova come riprovano le librerie. Senza il numero, per far passare 6 richieste attraverso un tetto di 3 al minuto bussava a vuoto 7 volte invece di 1 e ci metteva 75,8 s invece dei 60 del minimo; con la quota già finita mandava 5 richieste inutili in 15 s. Con il numero: 1 bussata, 62,6 s, e una sola richiesta (0,03 s) a quota finita.
29/9/2026Con la seconda orecchia i posti raddoppiano davvero (fronte).
- Il fronte può stare davanti a più motori, e aggiungerne uno serve a servire più clienti. Finché i tetti di capienza erano del fronte, però, i posti erano in comune: i clienti di un motore riempivano il tetto di quelli di un altro motore fermo, e la macchina in più non dava niente a nessuno.
- Ora i tetti stanno dove sta la capienza che proteggono. Per motore: i flussi (
AGILEASCOLTO_MAX_STREAM), le richieste batch (AGILEASCOLTO_MAX_BATCH) e i lavori lunghi in attesa (AGILEASCOLTO_MAX_JOBS_CODA), con una coda e un lavoratore per ogni motore. Del fronte restano l'audio in RAM (AGILEASCOLTO_MAX_CODA_S) e le richieste in volo (AGILEASCOLTO_MAX_IN_VOLO), perché la RAM è una sola per quanti motori ci siano. - Con un motore solo non cambia niente: gli stessi numeri di prima (8 flussi, 8 lavori in attesa, un lavoro per volta). È il caso di tutti i dispiegamenti di oggi.
- Con due motori i posti sono il doppio e il motore pieno non fa più rifiutare chi chiede l'altro. Il 503
CAPACITYora nomina il motore pieno, epositiondi un lavoro in coda è la posizione nella fila del suo motore. GET /healthporta campi nuovi dentro ogni motore:streams_active,streams_max,jobs_running,jobs_queued,jobs_queued_max;jobs.runningdice quanti lavori stanno andando ora su tutto il fronte (con N orecchie possono essere N). Gli stessi numeri in/metrics, una riga per motore configurato. Campi aggiunti: il contratto resta 1.0.- Perché: misurato con due orecchie finte e clienti veri. Prima la seconda orecchia aggiungeva 0 flussi, chi chiedeva il motore fermo prendeva 503
CAPACITY, il batch dell'orecchia libera aspettava 2,63 s invece di 0,13 s e il lavoro lungo dell'orecchia libera 2,53 s invece di 0,06 s. Dopo: 4 posti su due orecchie, batch 0,12 s, lavoro lungo 0,13 s.
29/9/2026Un tetto sulle richieste con audio in volo (fronte).
- Il tetto del giorno prima limita ciò che la coda tiene; questo limita il picco: quante richieste possono tenere audio in RAM nello stesso momento, su
POST /v1/audio/jobse suPOST /v1/audio/transcriptions. Ora sonoAGILEASCOLTO_MAX_IN_VOLO, di serie 4. - Serve perché una richiesta che sta entrando ha già in memoria il corpo dell'upload e il PCM decodificato, e la durata del file si conosce solo dopo averlo decodificato: l'ammissione non può fermarla prima. Quante ne arrivassero insieme lo decideva il cliente.
- Per chi integra non cambia niente fino a 4 richieste per volta. Oltre il tetto la richiesta aspetta il proprio turno (fino a 30 s) e solo allora riceve 503
CAPACITY: in una raffica il file non va rimandato. Aspettare costa 64 KiB, contro i 45 MB di chi è entrato. GET /healthportainflighteinflight_max: le richieste che tengono audio in RAM ora e il loro tetto. Gli stessi numeri in/metricse in/v1/quadro.- Perché: misurato col client in un altro processo, 12 richieste insieme da 600 s valgono 736 MB di picco (45,1 MB per richiesta) che nessun tetto contava. Sotto un tetto di RAM quelle stesse 12 uccidono il fronte con 0 lavori ammessi e nessun 503 a nessuno. Col semaforo, stessa raffica e stesso tetto: 12 ammesse, 0 rifiutate, fronte in piedi.
29/9/2026La coda dei lavori ha un tetto sull'audio in RAM (fronte).
POST /v1/audio/jobstiene in RAM tutto l'audio del file finché il lavoro non finisce, e la coda si misurava in lavori (8 in attesa): gli stessi otto posti valgono 1,7 MB se i file durano 6 secondi e 3110 MB se durano le 3 ore che il contratto promette.- Ora c'è un tetto anche sui secondi d'audio in RAM (
AGILEASCOLTO_MAX_CODA_S, di serie 21 600 s = 6 h = 691 MB): il doppio della durata massima di un lavoro, così un file lungo quanto il contratto dice entra sempre in una coda vuota. - Oltre il tetto arriva un 503
CAPACITYche dice quanti secondi sono liberi; un file che da solo non entrerebbe in nessuna coda, nemmeno vuota, riceve 413AUDIO_TOO_LONGe non un «riprova» che non arriverebbe mai. GET /healthportaqueue_audio_sequeue_audio_max_snella coda dei lavori: i secondi d'audio in RAM e il loro tetto. A lavori finitiqueue_audio_sè 0 — l'audio muore col lavoro.- Perché: senza un tetto sulla somma, quando la RAM finisce non fallisce una richiesta — muore il fronte, e i lavori, che vivono solo in RAM, spariscono tutti e di tutti i clienti. Con i limiti di prima e un fronte da 160 MB il settimo lavoro faceva uccidere il processo senza un solo 503; col tetto, il sesto riceve un 503 e il fronte resta in piedi. Chi manda file corti non si accorge di niente.
29/9/2026Fatti ridettare un pezzo del codice, non tutto: ricomponi + blocco (fronte).
- Quando un codice non regge e non c'è una riparazione, non serve far rileggere tutto: chiedi un blocco — del codice fiscale
cognome,nome,nascita,comune,controllo; dell'IBANpaese,banca,sportello,conto; di POD e PDRtestaecoda(telefono e pratica sono corti: si rileggono interi). - Manda il turno della risposta con
ricomponi=<il codice di prima>eblocco=cognome— nello stream il messaggio{"type":"ricomponi","sentito":"…","blocco":"cognome"}, che arma il finale successivo. Tornaricompostocol codice rimesso insieme e ricontrollato. - Guarda
giudicabile: dice se il controllo di quel tipo ha un checksum e può fare da giudice della ricomposizione. Su 33 ricomposizioni sbagliate di IBAN e codice fiscale quelle che passano il controllo sono 0; sui POD, che hanno solo una forma, 12 su 12 passano — congiudicabile: falseil codice va riletto alla persona. - Il blocco lo scegli tu, che stai parlando con lei: il fronte non sa dove sia l'errore, e la regola che glielo faceva indovinare azzeccava 33 blocchi su 77. Misurato su 107 codici sbagliati: sapendo quale blocco è rotto, un blocco solo ne rimette a posto 57.
- La scala: se un blocco non basta, chiedi il dopo, nell'ordine del codice, e lascia che sia il controllo a dirti quando fermarti. Funziona perché il primo innesto riporta il codice alla lunghezza canonica: arrivano in fondo 30 codici fiscali su 36 e 29 IBAN su 29 senza ridettare niente per intero (mediana 4 domande da 3–5 caratteri), POD e PDR tutti in 2 domande, 0 ricomposizioni sbagliate accettate.
- Conviene perché il pezzo corto si sente e il codice intero no: sugli stessi referti un codice fiscale dettato per intero esce esatto 9 volte su 90 e un IBAN 23 su 90, mentre un codice da 7–10 caratteri sta fra il 74 % e il 93 %.
- Rotta nuova
GET /v1/codici/blocchi?tipo=cf&sentito=…: i blocchi di quel tipo con la frase da dire e, per ciascuno,posizionabile— un blocco si innesta solo se il codice sentito è lungo abbastanza, e chiederne uno che non si innesta butta via un turno (sarebbero 44 domande su 180 sui 36 codici fiscali sbagliati dell'archivio). I blocchi che non si innestano si saltano e tornano in gioco al giro dopo: è quello che porta l'IBAN da 27/29 a 29/29. - Chi non manda
ricomponivede esattamente quello di prima.
29/9/2026Puoi dire al fronte che codice hai chiesto: atteso (fronte).
- Il fronte riconosce i codici per forma: se l'orecchio perde un carattere la forma non regge più e il codice esce col tipo sbagliato o senza tipo (un codice fiscale di 14 caratteri esce
sconosciuto). Chi cercava il suo tipo non trovava niente da dire alla persona. - Con
atteso=cf(batch, lavori,?atteso=cfo"atteso":"cf"nellostartdello stream, o il messaggio{"type":"atteso","tipo":"cf"}a metà sessione) il candidato che risponde alla domanda portaatteso: true; se nessuno ha più quella forma, il più vicino viene adottato e ricontrollato come quel tipo (adottato: true), col motivo da dire: «lunghezza: 14 caratteri, il CF ne ha 16». - Un candidato valido non si adotta mai (in quella frase è il codice di qualcun altro) e l'adozione non rende valido niente: il controllo resta quello vero del tipo. Misurato sui referti del banco: 186 codici sbagliati senza nessun candidato del tipo atteso, 48 adottati, 47 azzeccati, 0 adozioni sul parlato.
- Chi non manda
attesovede esattamente quello di prima.
29/9/2026Un codice che non regge dice anche come si ripara (fronte).
- Con
codici=1, un codice che non passa la verifica porta due campi in più quando esiste una sola riparazione che lo fa reggere:"riparato": "IT881E26716388", "regola": "pod: E mancante". Due regole e solo due: laEdel POD che l'orecchio perde fra le cifre, e il paese dell'IBAN sentito «ID» per «IT». validorestafalse: la riparazione è una proposta da rileggere e far confermare («mi risulta IT881E26716388, confermo?»), non un codice accettato. Al telefono si risparmia una ridettatura di 27 caratteri senza rischiare di consegnare il codice di un'altra persona.- Misurato sui referti del banco (12 proposte, 12 giuste, 0 sbagliate, 0 sul parlato) e dal vivo col modello di serie (12 audio, 2 riparazioni, 2 giuste). Due regole più libere — ricalcolare il carattere di controllo del codice fiscale, indovinare il CIN dell'IBAN — misurate e scartate: inventavano codici formalmente perfetti.
- Chi non legge i campi nuovi vede esattamente quello di prima.
28/9/2026Lo stream dice quale orecchio ti sta ascoltando (fronte).
- Il messaggio
{"type":"started"}con cui lo stream WebSocket risponde allostartporta ora il campomodel: il nome dell'orecchio che trascriverà questa sessione —{"type":"started","model":"agileascolto-turbo"}. È il nome nostro, lo stesso che compare in/v1/usage, in/v1/modelse nel registro, anche se hai chiesto l'aliaswhisper-1o non hai chiesto nessun modello. - Serve a chi deve poter dire chi ha ascoltato una chiamata: prima lo dicevano solo il batch (
verbose_json.model) e lo stato di un lavoro, mentre sullo stream l'alias e il modello predefinito nascondevano l'orecchio. Il campo c'è sempre, non si chiede, e con esso si ritrova la sessione nei consumi della chiave. - Niente altro cambia: chi non lo legge vede il flusso di prima, identico.
23/9/2026Diario dei parziali scartati (motore v0.3.2).
- Il motore scrive nel suo diario ogni parziale che l'ASR scarta a vuoto (
empty,ghost(...),degen[...]): una rigapartial scartato: <motivo> (<ms> di parlato, asr <ms>)e un contatorescartati=nella riga di chiusura. - Nessun testo nel log: il contratto resta invariato. Serve a capire perché, a ritmo reale, il primo parziale usciva in ritardo — il primo tentativo torna vuoto e prima non si vedeva.
23/9/2026Parole con i tempi (motore v0.3.1).
words=1(otimestamp_granularities[]=word, come OpenAI) su/v1/audio/transcriptionse sui lavori: inverbose_jsonogni parola ha inizio e fine in secondi, in cima e dentro ogni segmento. Sullo stream WebSocket:{"type":"start","words":true}e ogni finale portawords.- I sottotitoli
srt/vttchiesti conwords=1prendono i tempi veri delle parole: i tagli cadono sulle pause fra le parole e ogni didascalia va dalla prima all'ultima delle sue parole. Senza, il tempo si divide in proporzione ai caratteri, come prima. - È la base per «chi ha parlato quando» sulle registrazioni d'aula.
22/9/2026Sottotitoli SRT e WebVTT, lavori sui file lunghi.
response_format=srtovttnel batch,?format=srt|vtt|textsui lavori: didascalie di al massimo due righe da 42 caratteri e 7 secondi, frasi lunghe spezzate ai confini di parola.verbose_jsonporta ora inizio e fine di ogni segmento.POST /v1/audio/jobs: file fino a 3 ore in coda, uno per volta, con avanzamento (motore v0.2.1);keep=0= risultato consegnato una volta sola e mai conservato.
22/9/2026Verifica dei codici dettati e motore che non parla con nessuno.
Cosa è cambiato
- Con
codici=1ogni finale porta anchecodici: i codici trovati nel testo con il tipo (IBAN, codice fiscale, POD, PDR, telefono, pratica),valido(checksum mod 97 per l'IBAN, carattere di controllo per il codice fiscale, forma per gli altri) e unmotivoin parole («lunghezza: 25 caratteri, IT ne ha 27»). Il prodotto chiede di ripetere solo quando serve, e solo il blocco che serve. - Il motore non contatta nessun servizio esterno: telemetria della libreria di inferenza spenta, modello caricato dalla cache locale. Verificato con un audit ripetibile (strace + socket), zero connessioni.
A chi serve
A chi raccoglie IBAN, codici fiscali, POD e PDR al telefono; a chi deve poter dire dove finiscono i dati.
Come si adotta
Nessun cambiamento obbligato: codici compare solo con codici=1. Regole per il dialogo di conferma nella documentazione.
22/9/2026Vigilanza del servizio.
Cosa è cambiato
- Il servizio si controlla da solo: il fronte legge
/healthzogni minuto e alza la voce solo quando serve —AGILEASCOLTO ALLARMEalla caduta (del fronte o di un singolo motore),ANCORA GIU'ogni 30 minuti,RIENTROcon i minuti al ritorno. Con tutto ok tace. - In
/healthzentranoengine_down_total(i503 ENGINE_DOWNdall'avvio) euptime_s: se il contatore cresce fra due controlli è allarme anche a motori tornati su, così una caduta di pochi secondi dentro una telefonata non sparisce. - Nessun rimedio automatico: il vecchio resta acceso.
A chi serve
A chi opera il servizio e vuole essere avvisato quando qualcosa cade, senza rumore quando va tutto bene.
Come si adotta
Niente da fare: la vigilanza è interna al servizio, non richiede nessuna configurazione da parte dei client.
21/9/2026Pagina d'uso per chiave e post-processo dei codici dettati.
Cosa è cambiato
GET /uso: incolli la chiave e vedi quota, consumi per mese, operazione e modello; con lo scopemetricsil quadro di tutte le chiavi e lo stato dei motori. La chiave non passa mai per l'indirizzo.codici=1(form del batch,?codici=1ostart{"codici":true}nello stream): IBAN, codici fiscali, POD e pratiche dettati lettera per lettera tornano compatti («bi esse trattino 1 5 7 6 7»→BS-15767). Solo sui finali, a richiesta.GET /v1/quadroper chi vigila (scopemetrics).
A chi serve
A chi tiene d'occhio i consumi di un prodotto o di un cliente; a chi trascrive codici dettati al telefono.
Come si adotta
Niente da cambiare nei client: la pagina si apre col browser; codici è un'opzione in più.
16/9/2026Contratto 1.0: API da file compatibile OpenAI, streaming WebSocket, chiavi con quota in secondi.
Cosa è cambiato
POST /v1/audio/transcriptionscon il formato OpenAI (json,text,verbose_json).WS /v1/audio/stream: parziali e finali in streaming, audio a 8 o 16 kHz.- Chiavi per prodotto e per cliente, con modelli consentiti, limite al minuto, quota in secondi al mese, rotazione;
GET /v1/usage. - Nessun ripiego silenzioso su un altro modello o su un altro servizio: se il modello sta su più macchine il fronte prova le altre (stessi pesi), e quando non risponde nessuna,
503 ENGINE_DOWN.
A chi serve
A chi usa già un'API di trascrizione compatibile OpenAI o Deepgram e vuole tenere l'audio in Europa.
Come si adotta
Chiedi una chiave, cambia base_url, usa model="agileascolto-turbo".