@amuseapp/caa-generator
Generatore di pittogrammi CAA e di testo facile da leggere per il patrimonio culturale. Nato per gli itinerari accessibili di amuseapp, gira con una sola chiave Google.
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
| castello | torre | edificio | piazza | statua | amare |
Sei pittogrammi del set amuseapp (300 px), con licenza CC BY 4.0: vedi Licenza.
Che cos’è
Due strumenti che lavorano insieme sui contenuti per chi ha difficoltà di lettura:
- Un generatore di pittogrammi. Dato un concetto (per esempio “fossato”) e la frase in cui compare, descrive il disegno, lo genera in uno stile unico, lo fa passare da un cancello automatico di qualità e lo salva in una libreria locale con tutti i dati del controllo.
- Un kit per il testo facile. Il prompt di semplificazione dei testi museali, il glossario di itinerario, un controllo meccanico delle regole di scrittura (L1-L12, con il contatore delle subordinate L13) e la politica dei simboli (quali parole non ricevono mai un pittogramma).
Non è un’app: è una libreria TypeScript con una riga di comando (caa-gen). La persistenza, il modello di testo, quello di immagini e quello di visione sono interfacce: puoi sostituirle con le tue.
Quello che non è incluso: il simbolizzatore end to end (la scelta dei pittogrammi per le parole chiave di un intero itinerario) e la pubblicazione su una webapp o su un database. Il kit espone i pezzi (simplifyPoiText, lintSymbols, shouldSymbolize, ensurePictogram) ma non un comando che li orchestra. Vedi anche i limiti noti.
Perché
- Le licenze dei set esistenti. ARASAAC, il set libero più usato, è CC BY-NC-SA e i suoi termini escludono i prodotti commerciali senza autorizzazione scritta. Widgit è proprietario. Chi costruisce un prodotto per musei, enti o aziende non può usarli come base.
- Il lessico museale manca. Fossato, ponte levatoio, merlatura, affresco, stemma, loggia: i set generalisti non li hanno, o li hanno con disegni che non rendono il concetto. Servono pittogrammi per il vocabolario dei luoghi della cultura.
- La coerenza di stile. Un percorso accessibile funziona se la stessa cosa ha sempre lo stesso disegno e tutti i disegni sembrano della stessa mano. Con un solo stile e un solo cancello ogni nuovo pittogramma entra nella libreria alle stesse condizioni dei precedenti.
Come funziona
lemma + frase di contesto
|
v
descrizione del concetto (TextModel)
classe, descrizione del disegno, alias, parole accettate, distrattori, parole vicine
|
v
immagine nello stile unico "A3" (ImageModel)
|
v
cancello automatico (VisionModel)
ritaglio della cornice, vincoli, riconoscimento a 72 px
|
passa? ---- no ----> una sola rigenerazione con descrizione irrigidita ---- no ----> bozza, mai usata
|
si
v
PNG 300 e 72 px + manifest nello store (FileStore, MemoryStore o il tuo)
La descrizione del concetto
Il modello di testo riceve il lemma e la frase in cui compare (il simbolo rappresenta il concetto che la parola ha in quella frase, non la parola isolata) e restituisce, validato con zod:
- la classe del concetto (oggetto, luogo, parte, materiale, persona o ruolo, azione, astratto), che decide la regola di composizione: una parte di un insieme (il fossato) si disegna in primo piano con il resto attenuato, un’azione con una figura di profilo e linee di movimento, un materiale con l’oggetto tipico fatto di quel materiale;
- una descrizione di una frase di cosa disegnare;
- gli alias in italiano e inglese;
- le parole accettate, cioè i nomi con cui chi guarda il disegno può chiamarlo correttamente. Gli iperonimi generici (edificio, persona, luogo, cosa) sono vietati, così un pittogramma del “duomo” non finisce a servire “edificio” in tutti i contesti;
- quattro distrattori: parole dello stesso campo ma di concetto diverso, contrari compresi, per smascherare un disegno ambiguo;
- le parole vicine (
related): quelle plausibili per un disegno corretto (parti, iperonimi stretti, azioni associate).
Lo stile unico
Lo stile a3-2026-10 è un testo fisso passato a ogni generazione: pittogramma piatto con contorno nero uniforme di spessore medio, forme geometriche, riempimenti piatti, una tavolozza terrosa (terracotta, ocra, verde oliva, azzurro, crema, grigi) con la regola che il colore reale del materiale vince quando identifica il concetto (oro delle monete, bianco del marmo), stile da segnaletica per adulti, un solo soggetto isolato, nessuna figura umana salvo che il concetto sia una persona, un ruolo o un’azione. I vincoli fissi sono: quadrato, sfondo bianco pieno, soggetto all’80 per cento dell’area, nessun testo, nessuna ombra, nessuna cornice, leggibile a 72 px. Il testo è in src/pictogram/style.ts. Se lo cambi, cambia anche l’identificatore: la libreria registra con quale stile è stata generata ogni riga.
Il cancello automatico, senza approvazione umana
È la scelta di progetto più importante: nessuna persona approva i pittogrammi prima dell’uso, quindi il cancello è l’unico arbitro e deve essere severo. Una riga diventa approvato solo se:
- Ritaglio della cornice. Il modello di immagini disegna talvolta una cornice nonostante il divieto (in prove interne di amuseapp circa 4 immagini su 10 con il modello Flash; dato non pubblicato). Un rilevatore ad anelli la trova, la ritaglia senza toccare il soggetto e riporta l’immagine a un quadrato bianco con margine uniforme. Se resta una cornice, anche sottile o grigio chiara, l’immagine è scartata.
- Vincoli. Immagine quadrata, sfondo bianco sul bordo, nessun testo, lettera o numero (lo verifica il modello di visione).
- Riconoscimento a 72 px, in due passi:
- parola libera (trasparenza): al modello di visione si mostra il disegno ridotto a 72 px e si chiede una sola parola; deve essere uguale (dopo normalizzazione, mai per contenimento, mai per iperonimo) a una delle parole accettate;
- scelta multipla (traslucenza): solo se la parola libera cade nel campo del concetto (è tra le parole vicine) e non coincide con un distrattore, si chiede di scegliere tra il lemma, i quattro distrattori e “nessuna di queste”. Passa solo se sceglie il lemma. Se la risposta libera coincide con un distrattore (per esempio “uscire” per “entrare”) il disegno è respinto subito: potrebbe mostrare il concetto opposto.
Se il cancello fallisce si rigenera una sola volta con una descrizione irrigidita (“il disegno è stato letto come X invece di Y”). Se fallisce ancora la riga resta bozza e nessun consumatore la usa: meglio nessun simbolo di un simbolo sbagliato. Non allargare le parole accettate per far passare più immagini: è l’errore che il cancello esiste per impedire.
Il tetto di spesa (SpendMeter) conta ogni generazione e può avere una scadenza: nella CLI il default è 8 immagini per comando (CAA_MAX_GENERATIONS). Le generazioni passano da una coda a singolo slot, perché con più richieste in parallelo la chiave Google riceve 429.
La convenzione ARASAAC, facoltativa
Con --arasaac-hint il generatore chiede a ARASAAC come rappresenta il concetto (quali elementi, quale posa, quali segni come frecce o linee di movimento) e passa solo la frase al modello di testo. ARASAAC è usata come fonte di convenzioni, non di disegni: il PNG scaricato serve a farne scrivere la frase, non viene salvato né dato al generatore di immagini.
I criteri della letteratura che rispetta
Le regole vengono da due fonti principali: le regole europee per l’informazione facile da leggere di Inclusion Europe (“Informazioni per tutti”, edizione italiana 2012, con Anffas) e la guida Widgit al buon contenuto con simboli (“Guide to Good Symbol Content”, 2021). ARASAAC (Governo d’Aragona) è fonte di convenzioni compositive, non di disegni.
Simboli (S1-S10)
| Criterio | Dove vive nel codice |
|---|---|
| S1 il simbolo rappresenta il concetto che la parola ha in quella frase | describeConcept riceve sempre la frase come contesto |
| S2 simboli il più possibile trasparenti | cancello: parola libera a 72 px (trasparenza), poi scelta multipla (traslucenza) solo nel campo del concetto; l’opaco è respinto |
| S3 stesso concetto, stesso simbolo; coppie per gli opposti | una riga di libreria per lemma, con alias; i distrattori includono il contrario; lintSymbols segnala lo stesso lemma con più simboli e lo stesso simbolo per lemmi non sinonimi |
| S4 pochi simboli per frase, solo parole portanti | massimo 3 per frase; parole generiche, numeri, qualificatori e frasi negative esclusi (shouldSymbolize) |
| S5 prova “copri il testo” | per simbolo, è il riconoscimento a 72 px; per frase resta la revisione umana |
| S6 sequenza che segue la frase | withOffsets ordina i simboli come le parole |
| S7 concetto vicino con la parola giusta sotto, senza cambiare il senso | alias solo nella stessa famiglia, iperonimi generici vietati |
| S8 niente qualificatori di plurale o tempo | un solo soggetto, nessun elemento aggiunto |
| S9 adatto agli adulti, chiaro, una cosa sola da guardare | stile da segnaletica per adulti, soggetto isolato, nessuna scena, cornici ritagliate, niente testo |
| S10 nomi propri senza simbolo | collectProperNames e lintSymbols (C5) |
La politica dei simboli (src/text/policy/symbols.ts) elenca anche i controlli C1-C10 di lintSymbols: stesso lemma con più simboli, stesso simbolo per lemmi diversi, nomi propri, qualificatori astratti, frasi negative con simboli, ripetizioni.
Testo facile (L1-L13)
caa-gen lint-text e lintEasyText controllano a macchina un testo italiano. Gli errori (fail) provocherebbero un nuovo tentativo di generazione, gli avvisi (warn) si riportano soltanto.
| Regola | Cosa controlla |
|---|---|
| L1 | lunghezza della frase: avviso oltre 12 parole, errore oltre 15, errore da 13 se c’è una subordinata |
| L2 | numeri: niente numeri romani (“Ercole II”), migliaia con il punto, numeri grandi (gli anni restano), percentuali |
| L3 | forma passiva e impersonale (“è stato costruito”, “si vede”) |
| L4 | negazioni a inizio frase e due frasi di fila con “non” |
| L5 | congiuntivo, condizionale, passato remoto (errori), gerundio a inizio frase (avviso) |
| L6 | parole difficili non spiegate; termini del glossario alla prima comparsa senza “cioè” |
| L7 | metafore e modi di dire (lista) |
| L8 | sinonimi per lo stesso concetto (castello e fortezza, percorso e itinerario) |
| L9 | pronomi a inizio frase con più di un possibile antecedente |
| L10 | nessuna frase che si rivolge al lettore; voce plurale (“noi”, “vi”) vietata |
| L11 | fedeltà all’originale: nomi propri, anni e confronti non devono andare persi |
| L12 | struttura: la prima frase nomina il luogo, la prima tappa invita a cominciare, l’ultima chiude il percorso |
| L13 | countSubordinates: il contatore delle subordinate usato da L1 |
Il prompt di semplificazione (buildSimplifyPrompt) incorpora le stesse regole: una sola idea per frase, al massimo 12 parole, presente o passato prossimo, mai passivo, parole comuni con “cioè” per i termini difficili, stessa parola per lo stesso concetto, niente metafore, informazioni positive prima delle negazioni, anni in cifre e niente numeri romani, solo ciò che dice il testo sorgente.
I limiti noti
- Nessuna validazione con lettori. Le fonti della letteratura (Inclusion Europe, UNE 153101, le linee guida tedesche e nordiche) chiedono che persone con disabilità intellettiva verifichino i contenuti. Questo progetto non lo fa e non può sostituirlo: i controlli automatici stimano la riconoscibilità per un modello, non la comprensione di una persona. Non usare nomi o loghi di standard (per esempio il logo europeo “easy to read”) sui contenuti prodotti.
- Concetti astratti e relazionali deboli. Nomi concreti e oggetti funzionano; verbi, ruoli storici, parti di un insieme e concetti astratti (stile, epoca, potere) passano molto più raramente, e per gli astratti il cancello preferisce non produrre nulla. Gli studi di iconicità sui set esistenti dicono la stessa cosa: sono i concetti meno trasparenti per chiunque.
- Un modello come giudice. Il riconoscimento è fatto da un modello della stessa famiglia che disegna: errori correlati sono possibili (un disegno sbagliato in un modo che il giudice non vede). Per questo il cancello è severo e fallisce chiuso, ma non è un test con persone.
- Dati e prompt in italiano. I prompt, le parole accettate e i distrattori sono in italiano e i lemmi devono essere in alfabeto latino senza lettere come “ł”. Anche i campi dei dati sono in italiano (
classe,stato,approvato,bozza,riconoscimento,vincoli_ok): vengono dal backoffice di amuseapp e potrebbero essere rinominati prima della versione 1.0. Le altre lingue funzionano solo come alias in lettura. - Il simbolizzatore end to end e la pubblicazione non sono inclusi (vedi sopra): il progetto genera e controlla singoli pittogrammi e controlla testi, non produce itinerari completi.
- Il costo e la quota dipendono dalla tua chiave Google. Ogni pittogramma richiede almeno una chiamata di testo, una di immagine e una o due di riconoscimento, e una rigenerazione se il cancello fallisce. Non abbiamo verificato i prezzi: calcola il costo sul listino Google aggiornato. Il tetto
CAA_MAX_GENERATIONSlimita le immagini per comando, non la spesa. - Un pittogramma generato è un punto di partenza: un esperto di CAA dovrebbe rivedere il set prima di un uso con pubblici fragili.
Eseguirlo con la tua chiave Google
Serve Node 20.12 o superiore (per eseguire i test serve Node 22.12 o superiore) e una chiave di Google AI Studio. Una sola chiave copre testo, immagini e riconoscimento.
git clone https://github.com/amuseapp-labs/caa-generator
cd caa-generator
npm install
cp .env.example .env # scrivi la tua chiave in GOOGLE_API_KEY
npm run build
npx caa-gen --help
Genera un pittogramma:
npm run caa-gen -- pictogram fossato --context "Il fossato circonda il castello." --out ./library
Con la convenzione compositiva ARASAAC (facoltativa):
npm run caa-gen -- pictogram entrare --context "Entra dal portone." --arasaac-hint
Il comando scrive in ./library il manifest library.json e i file {id}_300.png e {id}_72.png, poi stampa l’esito: Approvato (exit 0), Bozza, non approvata dal cancello (exit 2) oppure un errore (exit 1). Un lemma già approvato non costa nulla: viene ritrovato in libreria, anche per alias.
Guarda la libreria:
npm run caa-gen -- library list --out ./library
npm run caa-gen -- library sheet --out ./library # foglio di contatto, serve ImageMagick; se manca, lo salta
Controlla un testo facile italiano (una frase per riga o separate dalla punteggiatura). Il file examples/testo-facile-ferrara.txt contiene sei frasi facili su un castello, con un errore voluto: l’ultima usa il passato remoto (“fu costruito”), vietato dalla regola L5. Il comando lo trova e termina con exit 1:
npm run caa-gen -- lint-text examples/testo-facile-ferrara.txt --place-name "Il Castello Estense"
# ERRORE L5 frase 6: Passato remoto "fu": usa il presente o il passato prossimo.
# 6 frasi, 1 errori, 0 avvisi
Con “è stato costruito” al posto di “fu costruito” l’errore sparisce e resta un avviso sulla forma passiva (L3).
npm run build compila in dist/: npx caa-gen (dalla cartella del progetto) esegue la versione compilata, che è anche il comando bin installato da npm. Come libreria:
import { ensurePictogram, FileStore, GeminiTextModel, GeminiImageModel, GeminiVisionModel, SpendMeter } from '@amuseapp/caa-generator'
const result = await ensurePictogram({
lemma: 'fossato',
context: 'Il fossato circonda il castello.',
languageCode: 'it',
meter: new SpendMeter(8),
store: new FileStore('./library'),
text: new GeminiTextModel(), // gemini-2.5-flash
image: new GeminiImageModel(), // gemini-2.5-flash-image
vision: new GeminiVisionModel(), // gemini-2.5-flash
})
// result.status: 'approved' | 'draft' | 'exhausted' | 'error' | 'skipped'
Variabili d’ambiente: GOOGLE_API_KEY (o GOOGLE_GENAI_API_KEY), e facoltativamente CAA_TEXT_MODEL, CAA_IMAGE_MODEL, CAA_MAX_GENERATIONS. Nessuna chiave è scritta nel codice: .env è ignorato da git.
Per portarlo su un database o un bucket implementa PictogramStore (lookup, create, update, list, putImages); per usare un altro provider implementa TextModel, ImageModel o VisionModel.
Come contribuire
- Apri una issue prima di una modifica grande. Le correzioni piccole (liste di parole, regole del lint, bug) vanno bene direttamente in pull request.
npm testenpm run builddevono restare verdi. I test usano modelli simulati: non servono chiavi.- Il cancello è la parte che protegge chi legge: una modifica che fa passare più immagini deve portare un test che dimostri perché sono giuste, e non deve allargare le parole accettate.
- Cambiare lo stile significa un nuovo identificatore (
STYLE_V1.id) e una nota nella pull request: le righe già in libreria restano legate al loro stile. - Contributi benvenuti: lessico (distrattori, iperonimi vietati, metafore), nuove regole di lint con la fonte, store per altri database, supporto ad altre lingue, e soprattutto esperienze di validazione con lettori.
Licenza
- Codice: MIT, Copyright 2026 amuseapp (file
LICENSE). - Sei pittogrammi di esempio in
docs/esempi/: di amuseapp, licenza CC BY 4.0. Formula di attribuzione: “Pittogrammi: amuseapp (amuseapp.art), CC BY 4.0”. Dettagli indocs/esempi/LICENSE.md. - ARASAAC è citata solo come fonte di convenzioni compositive. Nessun disegno ARASAAC è incluso nel repository o usato per addestrare modelli; con
--arasaac-hintil generatore riceve solo una frase di testo. - Il set completo di pittogrammi amuseapp sarà pubblicato a parte, in un repository dedicato, sempre con CC BY 4.0.
- Le immagini che generi tu con questo software restano soggette ai termini d’uso del servizio Google che le produce.





