nis2-agile/docs/supplier-portal/AI_CONSULENTE_NORMATIVO.md
DevEnv nis2-agile aa2db4c6c2 [DOCS] Design modulo questionari fornitori + portale OTP + AI consulente normativo
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>
2026-05-31 09:53:07 +02:00

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.