- Python 76.2%
- HTML 22.7%
- Shell 1.1%
A chi arriva sull'interfaccia serve sapere come funziona il sistema, non a che punto e' il progetto. La rotta /stato lascia il posto a /specifica, e accanto c'e' /specifica.pdf per allegarlo a una mail. La pagina e' lo **stesso file** da cui esce il PDF: un generatore solo, due supporti, e nessuna possibilita' che divergano — che era il difetto che la vecchia pagina di stato risolveva passando da un JSON intermedio. A schermo il foglio prende una larghezza di lettura e una barra di servizio; a stampa resta A4 identico, dodici pagine. I link della barra sono relativi (`.`, `./insieme`, `./specifica.pdf`): si risolvono da soli sotto /trasparenza/ senza che il generatore debba sapere dove sara' montato. Azione inversa: ripristinare la funzione `stato()` in web/app.py e i due link nei template. Non e' stato cancellato nulla — `stato.html`, `dati/stato_lavori.json`, `build_status_report.py` ed `export_status_content.py` restano al loro posto e la pagina torna in linea con sei righe. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|---|---|---|
| dati | ||
| servizi | ||
| strumenti | ||
| web | ||
| .gitignore | ||
| ALGORITMO.md | ||
| DESIGN.md | ||
| offerta_axios.pdf | ||
| README.md | ||
| specifica_tecnica.html | ||
| specifica_tecnica.pdf | ||
| stato_lavori.html | ||
| stato_lavori.pdf | ||
| STIMA.md | ||
AxiosTrasparenza
Classificazione dei documenti caricati dalle scuole per la sezione Amministrazione Trasparente, su tre parametri: formato (PDF/A e accessibilità), dati personali non diffondibili, categoria ANAC.
Committente Axios. Progetto aperto il 2026-10-02.
DESIGN.md— l'impianto: ordine dei parametri, asset versionati, pipeline, dove sta l'LLM, come si misura, domande aperte verso il committente.ALGORITMO.md— perché il motore è quello che è, e cosa è stato provato e scartato.STIMA.md— valutazione dei tempi, traguardi, incertezza, leve di riduzione.
Come è fatto oggi (8 ottobre 2026)
Il servizio in esercizio è su https://axios-ai.okone.it/trasparenza/ (basic auth), e risponde a tre domande in quest'ordine:
- formato elettronico — che file è, e si può pubblicare (
strumenti/format_check.py); - dati personali — che cosa contiene (
strumenti/personal_data.py); - categoria — dove va, con le parole che l'hanno determinata (
strumenti/engine.py).
Più un riassunto per punti del documento caricato, generato da un modello locale
(strumenti/summarize.py): arriva dopo la pagina, e se il modello non c'è la pagina
funziona lo stesso.
Il motore, in due passi
La collocazione si cerca prima per sezione, poi per sottosezione dentro quella. Non è una scelta di eleganza, è il risultato di una misura: scegliendo fra tutte e 327 le categorie insieme, la categoria giusta manca dal modello solo nello 0,7% dei casi ma finisce in nona posizione (mediana) — mentre dentro la sezione giusta la sottosezione corretta è nelle prime tre volte su quattro, fra una quindicina di candidate. Il motore non sbagliava la sottosezione: sbagliava la sezione.
- primo passo — regressione logistica sulle 23 sezioni di Amministrazione Trasparente. Sceglie la sezione giusta nel 73,4% dei casi, contro il 57,3% dei centroidi che la descrivevano invece di separarla. Dà probabilità calibrate, che servono per astenersi e per aprire la seconda ipotesi quando le prime due sono vicine;
- secondo passo — mappa di calore categoria × termine (classificatore a centroidi nello spazio dei termini, Rocchio 1971), ispezionabile cella per cella.
La sezione pesa, non filtra: entra nel punteggio con peso 0,1 e riordina i candidati. Filtrare taglierebbe via la categoria giusta tutte le volte che la sezione è sbagliata — un documento su quattro. Misurato: col filtro duro 5.338 documenti ben collocati, col peso 5.487.
Come si misura
Il metro è il documento, non la categoria. La media per categoria tratta una categoria da tre documenti come una da trecento: dice quanto il metodo è uniforme, non quanto lavoro toglie di mano. Si contano i documenti collocati bene su scuole mai viste, lasciando fuori un istituto per volta.
Non entrano nel conto i documenti illeggibili, vuoti o malformati, né quelli che la scuola ha messo in un contenitore di sua invenzione — ma le categorie ANAC rare restano: una da tre documenti vale come una da trecento, perché quei tre vanno collocati lo stesso.
python3 strumenti/check_corpus.py corpus/*/banco.csv --severo # doppioni e istituti magri
python3 strumenti/bench_documents.py corpus/*/banco.csv # quanti documenti colloca bene
python3 strumenti/bench_documents.py corpus/*/banco.csv --taratura # griglia soglie × rosa
python3 strumenti/bench_documents.py corpus/*/banco.csv --diagnosi # perché sbaglia
python3 strumenti/learning_curve.py corpus/*/banco.csv --test corpus/udic848005_2025/banco.csv
python3 strumenti/section_classifier.py corpus/*/banco.csv # i motori del primo passo
python3 strumenti/analyze_ranking.py corpus/*/banco.csv # dove cade la categoria giusta
La prima esecuzione del banco costa un quarto d'ora e salva le somiglianze in
dati/cache_bench/: le tarature successive sono istantanee.
Catena di raccolta
Gli istituti stanno in dati/istituti.csv — sigla, customer_id, codice fiscale,
denominazione — e non si incollano a mano da nessuna parte: è incollandoli che la stessa
scuola è finita due volte nel parco.
python3 strumenti/check_customer_id.py --registry dati/istituti.csv # gli id sono giusti?
python3 strumenti/crawl_institute.py <customer_id> --out dati/inventario_<sigla>.csv
python3 strumenti/download_documents.py <customer_id> --inventory <inv.csv> --year 2025 \
--out corpus/<sigla>_2025 --strategy stratified --skip-existing
python3 strumenti/extract_text.py --corpus corpus/<sigla>_2025
python3 strumenti/train_model.py corpus/*/banco.csv --out modello
python3 strumenti/build_overview.py corpus/*/ --out dati/riepilogo.json
python3 strumenti/build_status_report.py # stato_lavori.pdf
--strategy stratified prende i documenti girando fra i percorsi dell'albero invece di
tagliare i primi N nell'ordine di visita. Su un istituto il taglio in testa aveva portato
a casa 220 PDF tutti dalla stessa manciata di rami, nessuno dei quali utilizzabile.
Stato
Il corpus c'è: 23 istituti in sei regioni, 8.649 documenti, tutto il 2025 di ciascuna scuola — non un campione. Di questi 7.645 sono leggibili e con una collocazione riconoscibile, ed è su quelli che si misura.
Dove siamo, misurato lasciando fuori un istituto per volta:
| documenti | |
|---|---|
| collocati bene (rosa di 5) | 5.487 su 7.645 |
| al primo posto | 2.026 |
| la categoria giusta è nelle prime dieci | 81,5% |
| la categoria giusta non esiste nel modello | 0,7% |
Fatto il 02/10: tassonomia ANAC importata, normalizzata e classificata; obsolescenza dei riferimenti normativi misurata; WordNet italiano escluso con misura; sezione contratti riscritta sul d.lgs. 36/2023; riconciliazione post d.lgs. 97/2016 (41 obblighi vigenti, 22 portatori di documento); offerta commerciale in sei lotti.
Aperti
- Le proposte secche sbagliano il 61,4% delle volte (1.077 su 1.754). È il problema più grave: una proposta singola sbagliata nessuno la controlla, perché il sistema non ha espresso dubbio.
- Uno stadio che riordina i primi dieci candidati: il tetto è l'81,5%, contro il 71,8% di adesso. È lo stadio a modello linguistico del lotto 2, ma su dieci candidati invece che su tre.
- Il rilevatore di nomi di persona riconosce i nomi confrontandoli con un elenco di nomi di battesimo: un nome raro gli sfugge in entrambi gli ordini. La quota di documenti con dati personali che dichiariamo è quindi una stima per difetto.
- 1.359 documenti (17,8%) hanno la categoria giusta oltre la decima posizione: lì il testo non la sostiene, e serve una rappresentazione diversa.
Dati
| file | contenuto |
|---|---|
dati/anac430_allegato2_originale.xls |
originale ANAC, md5 5480750cd5c87c03a2c54d5e1a5cf623, salvato il 20/04/2016 |
dati/anac430_all2.csv |
tabella normalizzata, 114 righe, celle unite riempite |
dati/anac430_classificati.csv |
un record per obbligo + classe D/T/B |
dati/banco_coppie.csv |
banco di accettazione della gerarchia dei concetti |
dati/tassonomia_contratti_264_2023.yaml |
sezione contratti vigente: 16 voci, 8 applicabili alle scuole, 24 tipi documentali |
dati/anac264_allegato1_consolidato.xlsx |
originale ANAC, All. I della 264/2023 consolidato con la 601/2023, md5 e4b53b7f569a61a4a3a783d563279cf2 |
dati/anac264_consolidata.pdf |
testo della delibera 264/2023 come modificata dalla 601/2023 |
dati/anac430_delibera_testo.txt |
testo della delibera (gli allegati sono file separati) |
dati/anac1310_allegato1_testo.txt |
All. 1 della delibera 1310/2016: la tabella post-riforma, usata per la riconciliazione |
Classe: D = portatore di documento · T = solo dati, tabelle, recapiti (il
classificatore non li vede mai) · B = borderline, la norma chiede un dato ma la prassi
pubblica un PDF. Colonna stato: vigente · superato (regime contratti 36/2023) ·
soppresso / dimezzato / base_mutata (d.lgs. 97/2016). Sui soli obblighi vigenti il
bersaglio documentale è 22, più le 8 voci contratti.
Strumenti
python3 strumenti/parse_anac_allegato2.py # rigenera i due CSV + rapporto obsolescenza
# richiede xlrd>=2.0
python3 strumenti/check_taxonomy.py # coerenza della tassonomia contratti
# richiede pyyaml
python3 strumenti/build_offerta.py --tariffa 650 # rigenera offerta_axios.pdf
# richiede Google Chrome
python3 -m venv .venv && .venv/bin/pip install wn
WN_DATA=./.wndata .venv/bin/python strumenti/probe_wordnet_coverage.py
La classificazione D/T/B dei 50 obblighi è manuale e vive nel dizionario
OBLIGATION_CLASS dentro parse_anac_allegato2.py: è lì che si discute, non nel CSV
generato.
Trappole già incontrate
Misurare
- Le soglie della CLI non sono quelle del motore.
heatmap_classifier.pyha 0.25/0.12 di default, il motore in esercizio usa 0.08/0.06. Una curva misurata con le prime e raccontata come il comportamento del secondo misura un'altra cosa. SHORTLIST_SIZEè 5, non 3. Misurare con tre candidati mentre il prodotto ne mostra cinque, su un istituto, faceva la differenza fra il 22% e il 72%.- Concatenare i
banco.csvin un file solo distrugge la provenienza. L'identità dell'istituto è il percorso del file: con un file solo nessun termine risulta condiviso fra istituti, il vocabolario esce vuoto e il passo muore. Un2>/dev/nulll'ha tenuto nascosto per un giorno intero — gli errori vanno nel log. - Due istituti possono essere la stessa scuola.
ist02eist13erano lo stesso parco, scaricato due volte: nel leave-one-out il gemello addestrava mentre l'altro faceva da prova, e facevano entrambi il 100%.check_corpus.py --severolo intercetta. - Pesare le classi ottimizza il metro sbagliato.
class_weight="balanced"fa salire la media per categoria e scendere i documenti collocati bene: 66,9% contro 73,4%.
Raccogliere
- Un
customer_idsbagliato non dà errore: il gestore di download risponde 614 byte di HTML. Si verifica riscaricando un documento e confrontando lo SHA-256 con il manifest (check_customer_id.py --verify). L'id annotato per Monopoli era di un'altra scuola. - Tagliare i primi N documenti nell'ordine di visita dell'albero non è campionare. Su un istituto ha portato a casa 220 PDF tutti di sezioni senza sottosezione: zero documenti utilizzabili e il budget di scarico speso.
Normativa e formati
- Celle unite. Al cambio di sotto-sezione di livello 1 vanno azzerati livello 2 e denominazione. Senza, sei obblighi di Bandi di gara e contratti finiscono sotto la L2 sbagliata.
- La delibera 430/2016 è pre-riforma. È del 13 aprile 2016, il d.lgs. 97/2016 del 25 maggio: il suo Allegato 2 cita ancora il PTTI e il Responsabile della trasparenza separato, aboliti. Va usato come filtro di applicabilità alle scuole, con la tabella di riferimento presa dalla delibera 1310/2016 e la sezione contratti riscritta sulla 264/2023 + 601/2023.
noin YAML è un booleano. YAML 1.1 leggenoeyescomeFalse/True: i valori diapplicabile_scuolevanno virgolettati, altrimenti sette voci su sedici spariscono dai conteggi.check_taxonomy.pylo intercetta.omw-itsenzaomw-ennon ha le relazioni is-a: ogni iperonimo risulta vuoto e la profondità misurata è zero. È un artefatto dell'installazione, non un risultato.
Fonti
- ANAC, delibera 430 del 13/04/2016 e Allegato 2 —
anticorruzione.it - ANAC, delibera 1310 del 28/12/2016, Allegato 1
- ANAC, delibera 264/2023 Allegato 1, come modificata dalla 601/2023
- d.lgs. 33/2013, Allegato A
- Vademecum per la trasparenza delle istituzioni scolastiche, USR Piemonte
- Axios,
QG_BIL_Cig e Amministrazione Trasparente.pdf— esistenza del modulo AT e provenienza dei documenti da Segreteria Digitale