Avanzato 22 minSalute

Trascrizione medica e LLM locale: conformità dei dati patient

Dettare una visita medica, ottenere una trascrizione pulita, poi un resoconto SOAP pronto da incollare nella cartella del paziente — senza che neppure un secondo di audio esca dallo studio. È la promessa che questa guida rende operativa: una pipeline HDS di trascrizione medica con un LLM locale, basata su Whisper-large-v3 e un modello 14B+ (Llama 4 o Qwen3), progettata per rispettare il segreto medico e il quadro HDS.

Di Léa B.·Agg. 2026-06-15·Testato su Windows, macOS e Linux

#Quadro HDS e segreto medico

Il segreto medico (articolo L.1110-4 del Codice della sanità pubblica) si applica a qualsiasi documento contenente informazioni sanitarie che permettano di identificare una persona, comprese le trascrizioni audio. Non appena un dato di un paziente viene trattato al di fuori dello studio o dell'ospedale, il fornitore di hosting deve essere certificato HDS (standard ASIP/ANS). In concreto, inviare una consultazione a un'API cloud non certificata HDS — Whisper di OpenAI, Claude o qualsiasi piattaforma destinata al grande pubblico — ti pone al di fuori del quadro previsto, anche per un test.

La soluzione: non far mai uscire né l'audio né la trascrizione dal perimetro che controlli. È proprio ciò che permette una pipeline di trascrizione medica con un LLM locale: la macchina di inferenza si trova nello studio, il disco è crittografato e la rete è isolata. Nessun fornitore di hosting, nessun accreditamento da richiedere.

!
Cloud non-HDS = fuori dal quadro normativo
Molti professionisti sanitari usano ChatGPT o un'istanza di Whisper ospitata per risparmiare tempo. È una violazione del segreto professionale che può comportare procedimenti disciplinari (articolo 4 del Codice di deontologia medica) e penali (articolo 226-13 del Codice penale). Un audit dell'ARS la rileva facilmente attraverso i flussi di dati in uscita.

#Architettura 100% locale

Il kit IA Locale in Azienda

Implementare un'IA locale sul lavoro: GDPR, AI Act, architettura multiutente, costi, nota per la direzione.

  • Spazio online a vita
  • PDF + file
  • Aggiornamenti a vita

La pipeline è composta da quattro blocchi, tutti locali: acquisizione audio → Whisper-large-v3 → LLM con template SOAP → esportazione verso il software dello studio (LGC) o il DMP.

Registrazione
Microfono lavalier o microfono USB cardioide posizionato tra il professionista sanitario e il paziente. Niente Bluetooth (latenza e compressione). Registrazione in WAV a 16 kHz mono.
Trascrizione
Whisper-large-v3 (OpenAI, pesi aperti, licenza MIT) eseguito tramite faster-whisper su GPU locale. Modello caricato in memoria, audio distrutto dopo la trascrizione.
Strutturazione
Llama 4 Scout (17B-A2B, MoE) o Qwen3-14B servito da Ollama. Il modello LLM elabora la trascrizione e produce un resoconto SOAP (Subjective, Objective, Assessment, Plan).
Esportazione
Il resoconto viene inserito nel LGC (Weda, Medistory, AxiSanté, Doctolib Pro, Maiia) tramite gli appunti, una scorciatoia HL7 CDA o un'API, se disponibile.
i
Perché Whisper-large-v3 e non v3-turbo
Whisper-large-v3-turbo è più veloce ma perde da 1 a 2 punti di WER (Word Error Rate) sul francese medico, e soprattutto peggiora la trascrizione dei nomi propri e della posologia. In ambito clinico, la precisione conta più della velocità, soprattutto perché la visita è già terminata quando si avvia la pipeline.

#Requisiti hardware e software

GPU
Come minimo una RTX 4070 da 12 GB; una RTX 4080 da 16 GB o una RTX 4090 da 24 GB offrono più margine. Whisper-large-v3 occupa ~3 GB di VRAM, il LLM da 14B in Q4_K_M richiede circa 9 GB.
Alternativa Mac
Il Mac mini M4 Pro con 48 GB di RAM unificata fa girare l'intero stack. Ideale per uno studio sanitario libero-professionale: silenzioso, con le ventole ferme quando è inattivo, a basso consumo.
Archiviazione
SSD NVMe cifrato con LUKS (Linux), BitLocker (Windows Pro) o FileVault (macOS). 100 GB sono sufficienti: circa 25 GB per i modelli, il resto per le trascrizioni temporanee.
Rete
La postazione di inferenza si trova su una VLAN separata dalla rete Wi-Fi dei pazienti. Nessun accesso a Internet in uscita in produzione — consentito soltanto durante gli aggiornamenti, sotto supervisione.
Software
Ollama (≥ 0.5) per servire il LLM su http://localhost:11434, faster-whisper (Python) per la trascrizione, ffmpeg per la conversione audio.
→
Air-gap progressivo
Inizia con una semplice regola del firewall che blocchi tutto il traffico in uscita tranne quello diretto ai domini di aggiornamento (ollama.com, huggingface.co, repository della distribuzione). Disattiva la regola solo durante le finestre di manutenzione approvate e documentate.

#1. Whisper-large-v3 su consulti in francese

faster-whisper è un fork basato su CTranslate2, da 4 a 5 volte più veloce dell'implementazione Python di riferimento, a parità di qualità. Accetta gli stessi modelli ed espone un'API semplice.

Installazione
python -m venv ~/venv-medtranscript
source ~/venv-medtranscript/bin/activate
pip install faster-whisper==1.1.0 ffmpeg-python

# Téléchargement du modèle (téléchargé une fois, ~3 Go)
python -c "from faster_whisper import WhisperModel; WhisperModel('large-v3', device='cuda', compute_type='float16')"

Il codice di trascrizione in sé occupa poche righe. Nota l'impostazione language='fr', che disattiva il rilevamento automatico della lingua ed evita derive linguistiche all'inizio del consulto.

transcribe.py
from faster_whisper import WhisperModel
import sys, pathlib

AUDIO = pathlib.Path(sys.argv[1])
model = WhisperModel('large-v3', device='cuda', compute_type='float16')

segments, info = model.transcribe(
    str(AUDIO),
    language='fr',
    vad_filter=True,             # coupe les silences
    vad_parameters={'min_silence_duration_ms': 500},
    beam_size=5,
    initial_prompt=(
        'Consultation médicale en français. Vocabulaire clinique, '
        'noms de molécules, posologies. Termes courants : doliprane, '
        'amoxicilline, lévothyrox, ECG, IRM, NFS, CRP, HbA1c.'
    ),
)

transcript = '\n'.join(seg.text.strip() for seg in segments)
OUT = AUDIO.with_suffix('.txt')
OUT.write_text(transcript, encoding='utf-8')
print(f'Transcription : {OUT}')
→
Il ruolo dell'initial_prompt
Whisper condiziona la decodifica in base a questo prompt. Elencare il lessico specialistico, le molecole comuni e gli acronimi biologici migliora significativamente il riconoscimento: è la versione low-tech di un fine-tuning. Adatta l'elenco alla tua specialità (cardiologia, pediatria, ginecologia).

Su una RTX 4080, una visita di 15 minuti viene trascritta in circa 90 secondi. Il file audio originale viene eliminato immediatamente dopo — la trascrizione è sufficiente, l'audio rappresenta un rischio inutile.

#2. LLM e template SOAP

Il resoconto SOAP è lo standard di fatto: Subjective (motivo della visita, disturbi riferiti dal paziente), Objective (esame clinico, parametri vitali, esami complementari), Assessment (diagnosi o ipotesi), Plan (prescrizioni, esami da effettuare, follow-up). Si chiede al LLM di compilare questo modello attenendosi rigorosamente alla struttura, senza inventare.

Scelta del modello
# Option 1 — Llama 4 Scout (multimodal, 17B MoE, ~10 Go en Q4)
ollama pull llama4:scout

# Option 2 — Qwen3 14B (excellent en français)
ollama pull qwen3:14b

# Option 3 — Mistral Magistral 24B si VRAM ≥ 16 Go
ollama pull magistral:24b

Il prompt di sistema impone un quadro rigoroso: nessuna invenzione, citazione letterale in caso di incertezza, linguaggio medico francese.

system_prompt_soap.txt
Tu es un assistant médical chargé de structurer une transcription
de consultation au format SOAP. Tu écris en français médical clair.

RÈGLES IMPÉRATIVES :
1. N'INVENTE JAMAIS de symptôme, diagnostic, traitement ou posologie
   qui ne figure pas explicitement dans la transcription.
2. En cas d'information ambiguë, écris littéralement :
   "À préciser par le praticien : [extrait textuel de la transcription]".
3. Conserve les unités exactes (mg, mL, fois/jour). Ne convertis rien.
4. Pour toute posologie, cite mot pour mot la phrase d'origine
   entre guillemets dans la section Plan.
5. Ne formule pas de diagnostic définitif si le praticien ne l'a pas
   posé. Utilise "hypothèse diagnostique" ou "à confirmer par...".
6. Garde uniquement ce qui relève de la consultation. Ignore les
   éléments hors propos (conversations annexes, interruptions).

STRUCTURE DE SORTIE (Markdown, exactement ces sections) :

## Subjective
- Motif de consultation :
- Histoire de la maladie actuelle :
- Antécédents pertinents évoqués :
- Traitements en cours évoqués :

## Objective
- Examen clinique :
- Constantes mentionnées :
- Examens complémentaires cités :

## Assessment
- Hypothèse(s) diagnostique(s) :
- Diagnostics différentiels évoqués :

## Plan
- Prescriptions (citer textuellement) :
- Examens complémentaires demandés :
- Conseils donnés au patient :
- Suivi prévu :

À la fin, ajoute une section :

## Points à vérifier par le praticien
Liste des éléments douteux, manquants ou nécessitant validation.
!
Posologia: tolleranza zero per le informazioni inventate
Un errore di dosaggio generato da un LLM può uccidere. La regola della citazione testuale tra virgolette non è negoziabile. Se il modello riformula una dose, hai un problema: cambia modello o rendi il prompt più rigoroso.

#3. Pipeline completo

Si esegue prima la trascrizione, poi la strutturazione tramite l'API REST locale di Ollama. Lo script resta compatto e verificabile — circa 40 righe, una scelta voluta: meno codice, meno superficie di attacco.

pipeline_soap.py
import sys, json, pathlib, requests, hashlib, datetime
from faster_whisper import WhisperModel

AUDIO = pathlib.Path(sys.argv[1])
MODEL_LLM = 'qwen3:14b'
SYS_PROMPT = pathlib.Path('system_prompt_soap.txt').read_text('utf-8')

# --- 1. Transcription locale
whisper = WhisperModel('large-v3', device='cuda', compute_type='float16')
segments, _ = whisper.transcribe(str(AUDIO), language='fr', vad_filter=True)
transcript = '\n'.join(s.text.strip() for s in segments)

# --- 2. Structuration SOAP via Ollama (localhost uniquement)
resp = requests.post(
    'http://localhost:11434/api/chat',
    json={
        'model': MODEL_LLM,
        'messages': [
            {'role': 'system', 'content': SYS_PROMPT},
            {'role': 'user', 'content': transcript},
        ],
        'options': {'temperature': 0.1, 'num_ctx': 8192},
        'stream': False,
    },
    timeout=600,
)
soap = resp.json()['message']['content']

# --- 3. Sortie + hash d'intégrité pour le journal d'audit
ts = datetime.datetime.now().isoformat(timespec='seconds')
sha = hashlib.sha256(soap.encode('utf-8')).hexdigest()[:16]
out = AUDIO.with_suffix('.soap.md')
out.write_text(
    f'<!-- généré le {ts} — modèle {MODEL_LLM} — sha256:{sha} -->\n\n{soap}',
    encoding='utf-8',
)

# --- 4. Effacement immédiat de l'audio et de la transcription brute
AUDIO.unlink()
print(f'Compte-rendu : {out}')
→
Temperatura bassa, contesto generoso
Una temperatura di 0.1 blocca la creatività — un effetto desiderabile qui. num_ctx 8192 copre un consulto lungo senza perdite. Se gestisci casi complessi (psichiatria, neurologia con anamnesi estesa), aumenta a 16384 e scegli un modello la cui finestra nativa lo permetta (Llama 4 Scout gestisce agevolmente fino a 256k).

L'elaborazione di una consultazione medica di 15 minuti attraverso l'intera pipeline richiede meno di 3 minuti su RTX 4080. Il resoconto .soap.md è pronto per essere incollato nel LGC.

#4. Integrazione con il software dello studio medico

Tre livelli di integrazione in base al tuo LGC:

  1. 01
    Livello 1 — Appunti
    Lo script copia automaticamente il resoconto (pyperclip). Il professionista sanitario lo incolla nel LGC con due clic. Compatibile con il 100% dei LGC, nessuna integrazione da parte del fornitore del software. È il punto di partenza consigliato.
  2. 02
    Livello 2 — Scorciatoia HL7 CDA
    Il reporto Markdown viene convertito in CDA R2 (Clinical Document Architecture) mediante un script pandoc + template XML, poi importato tramite la funzione di importazione documentale del LGC. Compatibile con Weda, Medistory, AxiSanté che accettano CDA.
  3. 03
    Livello 3 — API nativa del LGC
    Alcuni fornitori di software (Doctolib Pro, Maiia) mettono a disposizione un'API per l'inserimento di osservazioni. La pipeline inserisce direttamente il referto nella cartella del paziente identificato tramite INS. È la soluzione più comoda, ma richiede un accordo con il fornitore e un'autenticazione CPS.
i
Identificativo nazionale sanitario (INS)
Se automatizzi l'associazione al paziente corretto, usa l'INS qualificato e non il numero interno della cartella. È l'unica chiave che rimane valida al cambio di LGC e che è giuridicamente opponibile. Il recupero dell'INS avviene tramite il servizio telematico INSi.

#Conformità e registrazione dei log di audit

L'uso di uno strumento di supporto alla redazione medica deve essere documentato. Almeno tre elementi nella cartella del paziente:

Dicitura sull'uso dell'IA come supporto alla redazione
Una riga in calce al resoconto: "Resoconto strutturato con l'assistenza di un modello di IA locale (Qwen3-14B, versione 2026-04-12). Validato dal Dott. [...] il [data]." Questa dicitura documenta la tracciabilità.
Log degli accessi
Ogni esecuzione della pipeline è registrata localmente (chi, quando, quale audio, quale modello, hash del resoconto). Il log è append-only, firmato e conservato per 10 anni (durata di conservazione della cartella del paziente).
DPIA GDPR
Esegui una valutazione d'impatto (articolo 35 RGPD) per il trattamento «trascrizione assistita dall'IA». Il funzionamento al 100% in locale semplifica la valutazione: nessun trasferimento fuori dall'UE, nessun responsabile del trattamento, un'unica finalità.
Informazione al paziente
L'avviso affisso in sala d'attesa e la menzione nel modulo di accoglienza devono indicare l'uso di uno strumento di supporto alla trascrizione. Il paziente può rifiutare — prevedi un flusso di lavoro alternativo.
Crittografia a riposo
Tutto il disco è crittografato. I report vengono salvati nella cartella paziente del LGC, non sul sistema di file del computer di inferenza. Elimina i file .soap.md temporanei alla fine della giornata.
Aggiornamenti supervisionati
Ogni aggiornamento di un modello (Whisper, LLM) comporta una nuova validazione su un insieme di 10 consultazioni di prova anonimizzate. Bug di regressione noto = nessun deployment.
!
Cancellazione del file ≠ eliminazione sicura
Un unlink() elimina l'inode ma lascia i blocchi recuperabili su un SSD non crittografato. La crittografia completa del disco (LUKS/FileVault/BitLocker) è ciò che rende l'eliminazione irreversibile in pratica. Non farne a meno.

#Per approfondire

Questa pipeline copre l'uso quotidiano di un professionista sanitario che esercita la libera professione o di un servizio ospedaliero di dimensioni modeste. Tre estensioni naturali: industrializzare il deployment su più postazioni con Docker e un reverse proxy per l'autenticazione CPS, aggiungere un RAG locale basato sulle raccomandazioni HAS per supportare l'Assessment e rafforzare la sicurezza di rete fino al completo isolamento air-gap.

Questa guida ti è stata utile?

Un feedback, un errore, una precisazione? Facci sapere, così la guida migliora per tutti.