No description
  • Python 76.2%
  • HTML 22.7%
  • Shell 1.1%
Find a file
Massimiliano Santolo e5addce409 La specifica tecnica prende il posto dello stato nell'interfaccia
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>
2026-10-09 11:28:33 +02:00
dati Documento di stato rifatto sul metro nuovo, e tre profili di categoria da provare 2026-10-08 17:24:38 +02:00
servizi Due stadi: prima la sezione, poi la sottosezione dentro a quella 2026-10-08 13:08:29 +02:00
strumenti La specifica tecnica prende il posto dello stato nell'interfaccia 2026-10-09 11:28:33 +02:00
web La specifica tecnica prende il posto dello stato nell'interfaccia 2026-10-09 11:28:33 +02:00
.gitignore Il banco dei nomi certi non entra in git 2026-10-08 13:31:32 +02:00
ALGORITMO.md Registrato dove si cerca: il profilo normativo non parla la lingua dei documenti 2026-10-08 15:47:30 +02:00
DESIGN.md Documentazione e interfaccia dicono l'algoritmo che gira davvero 2026-10-08 13:13:22 +02:00
offerta_axios.pdf Classificazione documenti scolastici per Amministrazione Trasparente 2026-10-07 17:00:04 +02:00
README.md Documentazione e interfaccia dicono l'algoritmo che gira davvero 2026-10-08 13:13:22 +02:00
specifica_tecnica.html La specifica tecnica prende il posto dello stato nell'interfaccia 2026-10-09 11:28:33 +02:00
specifica_tecnica.pdf La specifica tecnica prende il posto dello stato nell'interfaccia 2026-10-09 11:28:33 +02:00
stato_lavori.html Documento di stato rifatto sul metro nuovo, e tre profili di categoria da provare 2026-10-08 17:24:38 +02:00
stato_lavori.pdf Documento di stato rifatto sul metro nuovo, e tre profili di categoria da provare 2026-10-08 17:24:38 +02:00
STIMA.md Documentazione e interfaccia dicono l'algoritmo che gira davvero 2026-10-08 13:13:22 +02:00

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:

  1. formato elettronico — che file è, e si può pubblicare (strumenti/format_check.py);
  2. dati personali — che cosa contiene (strumenti/personal_data.py);
  3. 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.py ha 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.csv in 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. Un 2>/dev/null l'ha tenuto nascosto per un giorno intero — gli errori vanno nel log.
  • Due istituti possono essere la stessa scuola. ist02 e ist13 erano 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 --severo lo 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_id sbagliato 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.
  • no in YAML è un booleano. YAML 1.1 legge no e yes come False/True: i valori di applicabile_scuole vanno virgolettati, altrimenti sette voci su sedici spariscono dai conteggi. check_taxonomy.py lo intercetta.
  • omw-it senza omw-en non 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