Pacchetto di design completo (nessun codice applicato, nessuna migrazione eseguita): - DESIGN aggiornato con 5 review agenti + 3 decisioni utente + pilastro AI consulente (sez. 12-14) - docs/supplier-portal/template-nis2-base.questions.json: 26 domande GV.SC (Allegato 2 ACN) con nis2_ref/vuln_flag e fonti certe verbatim - docs/supplier-portal/AI_CONSULENTE_NORMATIVO.md: corpus normativo aggiornato + persona consulente (modello TRPG) - docs/supplier-portal/UX_MINI_SPEC.md: mini-spec portale fornitore (stati/copy/autosave/mobile/a11y/editor no-code) - docs/sql/032-035: migrazioni idempotenti proposte (modulo, suppliers, portale auth, migrazione 027->campaigns) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
92 lines
7.9 KiB
Markdown
92 lines
7.9 KiB
Markdown
# Pilastro AI "vero consulente NIS2" — corpus normativo incluso/aggiornato + persona consulente
|
|
|
|
> Deliverable agente design 2026-05-31 (richiesta utente: "come TRPG, contesto normativo incluso e aggiornato + AI istruita, un vero consulente"). Sola analisi; nessun codice applicato.
|
|
> Riferimento incrociato: [[DESIGN_MODULO_QUESTIONARI_FORNITORI]] §13.
|
|
|
|
## 0. Stato attuale (già esistente in produzione)
|
|
La pipeline RAG c'è già e funziona — NON si riparte da zero:
|
|
- `EmbedService` (Voyage `voyage-3-lite`, 512-dim), `VectorService` (Qdrant `nis2_kb`, Cosine, filtro authz 3 livelli), `RagService` (embed→search→format).
|
|
- `AIService::askWithRag()` (top-5, minScore 0.28, fallback graceful) + `authoritativeSourcesBlock()` (anti-allucinazione).
|
|
- `AiController` → `POST /api/ai/ask` (rate-limited, audit). Persona **ARIA_SUPPORT_NIS2** (id=4, operativa).
|
|
- Registry fonti `application/config/nis2_sources.php` (7 fonti). Ingest `scripts/ingest-nis2-sources.php` — **eseguito 2026-05-29: 287 chunk** (NIS2 171 + CER 77 + Det.333017 25 + Det.164179 9 + Ambiti 5).
|
|
- Feed `normative_updates` + ACK (mig.009) — ma **scollegato dalla KB**.
|
|
- Catalogo requisiti ACN granulari: mig.031 + `docs/nis2/allegati_acn/acn_requirements.json` (87 imp + 116 ess) — in DB/JSON ma **non in KB**.
|
|
|
|
## 1. Gap del corpus (da colmare — priorità)
|
|
1. **D.Lgs. 138/2024 ASSENTE** (`file => null` nel registry) — fonte primaria obblighi italiani. **Priorità massima.**
|
|
2. **203 requisiti ACN granulari** (Allegati 1-2) non in KB → ingerire come **chunk-per-requisito** con payload ricco (`subcategory=GV.SC-04`, `function`, `entity=essenziale/importante`, `source`). È il salto da "conosce la direttiva" a "conosce i requisiti puntuali da audit".
|
|
3. Assenti dal registro/KB: **Reg. UE 2024/2690**, **ENISA Good Practices Supply Chain**, **NIST SP 800-161r1 / CSF 2.0**, **DORA (Reg. UE 2022/2554)**.
|
|
4. Fix banale: commento `EmbedService` dice "1024 dim" ma il codice forza 512 (coerente con la collection) → allineare per evitare ricreazioni errate.
|
|
> Copyright: ISO 27001 e materiali ENISA/NIST → ingerire solo sintesi/mappature proprie o testi a licenza aperta, non riproduzioni integrali protette.
|
|
|
|
## 2. Aggiornamento del corpus (chiudere il ciclo)
|
|
Oggi `normative_updates` (feed UI/ACK) e `nis2_kb` (Qdrant) sono disgiunti. Design:
|
|
- **Versioning fonte** in `nis2_sources.php`: `version`, `published_at`, `supersedes`, `status (in_force|superseded|draft)`, `content_sha`.
|
|
- **Idempotenza** ingest: `doc_uuid` deterministico per-fonte (`uuid5(ns,key)`) + `content_sha` nel payload → skip re-embed se invariato (risparmio Voyage).
|
|
- **Feed → KB**: estendere `NormativeController::create()` per ingerire opzionalmente in `nis2_kb` (scope SYSTEM) quando un super_admin pubblica un update con testo. Aggiungere `normative_updates.kb_indexed`, `kb_doc_uuid`. Così l'ACK audit e la conoscenza AI nascono dallo stesso atto.
|
|
- **Cron** settimanale `ingest-nis2-sources.php` (cron registry AgileHub, TZ Europe/Rome, log `/var/log/nis2/`), `--dry-run` confronta `content_sha`, re-ingerisce solo i delta.
|
|
- **Provenienza nei payload**: `version`, `published_at`, `url` → `formatContext()` espone la data ("secondo la Determina ACN 164179 del 14/04/2025…").
|
|
- **Cruscotto** `GET /api/knowledgebase/corpus-status` (super_admin): fonti, versione ingerita, ultimo ingest, drift vs registry.
|
|
> Caveat: `kb_uploaded_documents` (mig.013) potrebbe non essere applicata su prod → applicare migrazioni KB su Hetzner (`mysql -h localhost`) prima di basarvi il cruscotto.
|
|
|
|
## 3. Persona consulente — rafforzare i system prompt
|
|
Debolezze attuali di `askWithRag()`: prompt generico ("cita quando rilevante" → non obbligatorio), nessun tono/scope/persona, nessun comportamento esplicito quando la KB non copre, nessun disclaimer consulenza legale. Inoltre `suggestRisks`/`generatePolicy` **non** iniettano le fonti certe (incoerenza).
|
|
|
|
### Bozza system prompt (da iniettare in askWithRag, conforme a persona-conversational-rules v2.0)
|
|
```
|
|
# IDENTITÀ
|
|
Sei ARIA, consulente esperto di cybersecurity e compliance NIS2 della piattaforma
|
|
"NIS2 Agile". Operi come un consulente NIS2 reale: rigoroso, concreto, orientato
|
|
all'azione. Rispondi sempre in italiano professionale. Se ti viene chiesto
|
|
esplicitamente, dichiari di essere un assistente AI.
|
|
|
|
# AMBITO
|
|
Direttiva (UE) 2022/2555 (NIS2), (UE) 2022/2557 (CER), D.Lgs. 138/2024,
|
|
Determinazioni ACN (specifiche di base, classificazione/notifica incidenti),
|
|
Framework Nazionale (GV/ID/PR/DE/RS/RC) e l'uso operativo dei moduli della piattaforma.
|
|
|
|
# REGOLE DI CITAZIONE (vincolanti)
|
|
1. OGNI affermazione normativa DEVE citare la fonte: articolo/comma, determina+allegato,
|
|
o il riferimento [n] del contesto documentale.
|
|
2. Col contesto presente, cita i riferimenti [1],[2]… dei documenti forniti.
|
|
3. NON inventare numeri di articolo, determine, allegati, date o requisiti. Se incerto,
|
|
dichiaralo e rimanda alla fonte ufficiale.
|
|
4. Preferisci D.Lgs.138/2024 + Determine ACN per gli OBBLIGHI OPERATIVI; la Direttiva UE
|
|
per i PRINCIPI.
|
|
|
|
# QUANDO MANCA IL CONTESTO
|
|
Dichiaralo ("Non ho trovato un riscontro nelle fonti indicizzate") e rispondi con
|
|
conoscenza generale chiaramente etichettata come tale, senza citare fonti inventate.
|
|
|
|
# FUORI AMBITO (rifiuto cortese, niente eco di parole problematiche)
|
|
"Mi occupo di compliance NIS2 e cybersecurity. Per [tema] ti consiglio di rivolgerti
|
|
alla funzione competente."
|
|
|
|
# LIMITI (no deception / GDPR)
|
|
Orientamento di compliance, NON consulenza legale vincolante: per decisioni formali
|
|
rimanda a un legale o all'ACN. Non includere mai dati identificativi dell'organizzazione.
|
|
|
|
# STILE
|
|
Conciso e strutturato: (1) risposta diretta, (2) base normativa citata, (3) azione
|
|
consigliata nella piattaforma. Niente preamboli.
|
|
```
|
|
Sotto si concatenano `authoritativeSourcesBlock()` + contesto documentale numerato.
|
|
|
|
### Centralizzazione
|
|
Estrarre i blocchi prompt in `application/config/ai_persona.php` (single source of truth) e iniettarli in tutti i metodi `AIService` (oggi solo classifyIncident/askWithRag sono grounded).
|
|
|
|
## 4. Integrazione coi questionari fornitori (primo caso d'uso del consulente in-context)
|
|
- **(a) "Perché te lo chiediamo?"** (lato azienda): pulsante per domanda → `POST /api/ai/ask` precompilata "Spiega in 2 frasi il requisito {nis2_ref} e perché è rilevante per la sicurezza della catena di fornitura". Grounded sul chunk-per-requisito.
|
|
- **(b) Guida compilazione** (lato fornitore, no-auth): endpoint dedicato **read-only, rate-limited** `POST /api/supplier-portal/explain` che accetta solo il `nis2_ref` e risponde da cache pre-generata / RAG scope SYSTEM (no esposizione di askWithRag completo a non autenticati, niente PII).
|
|
- **(c) `AIService::suggestSupplierRemediation(negativeAnswers, supplierContext)`** grounded: per ogni vuln_flag/"no" propone rimedio citando GV.SC/PR + Art.21.2(d) → alimenta piano trattamento fornitore o NCR/CAPA. Anonimizzare (no nome/P.IVA nel prompt).
|
|
|
|
## 5. Piano incrementale
|
|
- **Fase 0 (igiene, ~0.5g)**: allineare commento EmbedService 512; grounding anche in `suggestRisks`/`generatePolicy`.
|
|
- **Fase 1 (corpus, alto valore)**: procurare D.Lgs.138/2024 (Gazzetta Ufficiale), promuovere Allegati 1-2 a fonti KB, ingerire i 203 requisiti chunk-per-requisito, eseguire `seed_acn_requirements.php`. Su Hetzner (Qdrant+Voyage), non in devenv.
|
|
- **Fase 2 (persona, ~basso rischio)**: `ai_persona.php` + nuovo prompt askWithRag; aggiornare help.js + KB ARIA; smoke 10-15 domande note + 3 fuori scope + 1 senza copertura.
|
|
- **Fase 3 (ciclo aggiornamento)**: versioning registry + doc_uuid/content_sha + feed→KB + cron + cruscotto corpus-status.
|
|
- **Fase 4 (AI nei questionari)**: dipende dal modulo questionari — "perché te lo chiediamo", `supplier-portal/explain`, `suggestSupplierRemediation`.
|
|
|
|
## 6. Rischi trasversali
|
|
Qdrant IP drift (assegnare ipv4 statico, conferma utente) · PHP-FPM Alpine env/DNS (mantenere multi-source lookup + IP letterale) · DB topology = host MySQL · disciplina commit (USR2→smoke→commit) · copyright ISO/ENISA/NIST.
|