Avancé 22 minSanté

Transcription médicale et LLM local : conformité données patient

Dicter une consultation, obtenir une transcription propre, puis un compte-rendu SOAP prêt à coller dans le dossier patient — sans qu'aucune seconde d'audio ne quitte le cabinet. C'est la promesse que ce guide rend opérationnelle : un pipeline transcription médicale LLM local HDS bâti sur Whisper-large-v3 et un modèle 14B+ (Llama 4 ou Qwen3), conçu pour rester en conformité avec le secret médical et le cadre HDS.

Par Léa B.·Màj 2026-06-15·Testé sur Windows, macOS, Linux

#Cadre HDS et secret médical

Le secret médical (article L.1110-4 du Code de la santé publique) s'applique à tout document contenant une information identifiante de santé, transcription audio comprise. Dès qu'une donnée patient est traitée hors de l'enceinte du cabinet ou de l'hôpital, l'hébergeur doit être certifié HDS (référentiel ASIP/ANS). Concrètement, envoyer une consultation à une API cloud non HDS — Whisper d'OpenAI, Claude, ou n'importe quelle plateforme grand public — vous met hors-cadre, même pour un test.

La parade : ne jamais sortir l'audio ni la transcription du périmètre que vous maîtrisez. C'est précisément ce que permet un pipeline transcription médicale LLM local : la machine d'inférence est dans le cabinet, le disque est chiffré, le réseau est isolé. Pas d'hébergeur, pas d'agrément à demander.

!
Cloud non-HDS = sortie de cadre
Beaucoup de praticiens utilisent ChatGPT ou un Whisper hébergé pour gagner du temps. C'est une violation du secret professionnel passible de poursuites disciplinaires (Article 4 du Code de déontologie médicale) et pénales (Article 226-13 du Code pénal). Un audit ARS le détecte facilement via les flux sortants.

#Architecture 100% locale

Le pipeline tient en quatre briques, toutes locales : capture audio → Whisper-large-v3 → LLM avec template SOAP → export vers le logiciel de cabinet (LGC) ou le DMP.

Capture
Micro-cravate ou micro USB cardioïde posé entre praticien et patient. Pas de Bluetooth (latence et compression). Enregistrement en WAV 16 kHz mono.
Transcription
Whisper-large-v3 (OpenAI, poids open, licence MIT) exécuté via faster-whisper sur GPU local. Modèle chargé en mémoire, audio détruit après transcription.
Structuration
Llama 4 Scout (17B-A2B, MoE) ou Qwen3-14B servi par Ollama. Le LLM consomme la transcription et produit un compte-rendu SOAP (Subjective, Objective, Assessment, Plan).
Export
Le compte-rendu est inséré dans le LGC (Weda, Medistory, AxiSanté, Doctolib Pro, Maiia) par presse-papier, raccourci HL7 CDA ou API si disponible.
i
Pourquoi Whisper-large-v3 et pas v3-turbo
Whisper-large-v3-turbo est plus rapide mais perd 1 à 2 points de WER (Word Error Rate) sur le français médical, et surtout dégrade les noms propres et la posologie. En contexte clinique, la précision prime sur la vitesse — surtout que la consultation est déjà passée quand on lance le pipeline.

#Prérequis matériel et logiciel

GPU
RTX 4070 12 Go minimum, RTX 4080 16 Go ou RTX 4090 24 Go confortables. Whisper-large-v3 occupe ~3 Go de VRAM, le LLM 14B en Q4_K_M tourne autour de 9 Go.
Alternative Mac
Mac mini M4 Pro 48 Go de RAM unifiée fait tourner la stack complète. Idéal pour un cabinet libéral : silencieux, fanless en idle, faible consommation.
Stockage
SSD NVMe chiffré LUKS (Linux), BitLocker (Windows Pro) ou FileVault (macOS). 100 Go suffisent : modèles ~25 Go, le reste pour les transcriptions temporaires.
Réseau
Le poste d'inférence est sur un VLAN séparé du Wi-Fi patient. Pas d'accès Internet sortant en production — uniquement pendant les mises à jour, supervisées.
Logiciel
Ollama (≥ 0.5) pour servir le LLM sur http://localhost:11434, faster-whisper (Python) pour la transcription, ffmpeg pour la conversion audio.
Air-gap progressif
Démarrez avec une simple règle pare-feu bloquant tout sortant sauf vers les domaines de mise à jour (ollama.com, huggingface.co, depots distrib). Désactivez la règle uniquement pendant les fenêtres de maintenance, validées et tracées.

#1. Whisper-large-v3 sur consultations FR

faster-whisper est un fork CTranslate2 4 à 5× plus rapide que l'implémentation Python de référence, à qualité identique. Il accepte les mêmes modèles et expose une API simple.

Installation
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')"

Le code de transcription en lui-même tient en quelques lignes. Notez la consigne language='fr' qui désactive la détection automatique et évite des dérives en début de consultation.

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}')
Le rôle de l'initial_prompt
Whisper conditionne son décodage sur ce prompt. Lister vocabulaire métier, molécules courantes et acronymes biologiques améliore significativement la reconnaissance — c'est la version low-tech d'un fine-tuning. Adaptez la liste à votre spécialité (cardio, pédiatrie, gynéco).

Sur une RTX 4080, une consultation de 15 minutes se transcrit en environ 90 secondes. Le fichier audio source est supprimé immédiatement après — la transcription est suffisante, l'audio pose un risque inutile.

#2. LLM et template SOAP

Le compte-rendu SOAP est le standard de fait : Subjective (motif, plaintes du patient), Objective (examen clinique, constantes, examens complémentaires), Assessment (diagnostic ou hypothèses), Plan (prescriptions, examens à venir, suivi). On demande au LLM de remplir ce template strictement, sans inventer.

Choix du modèle
# 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

Le system prompt impose un cadre strict : pas d'invention, citation littérale en cas d'incertitude, langue française médicale.

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.
!
Posologie : zéro tolérance à l'invention
Une erreur de posologie générée par un LLM peut tuer. La règle de citation textuelle entre guillemets est non négociable. Si le modèle reformule une dose, vous avez un problème — changez de modèle ou durcissez le prompt.

#3. Pipeline bout-en-bout

On enchaîne transcription puis structuration via l'API REST locale d'Ollama. Le script reste compact et auditable — environ 40 lignes, ce qui est volontaire : moins de code, moins de surface d'attaque.

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}')
Température basse, contexte généreux
Température 0.1 verrouille la créativité — souhaitable ici. num_ctx 8192 couvre une consultation longue sans perte. Si vous gérez des cas complexes (psychiatrie, neurologie avec anamnèse étendue), montez à 16384 et choisissez un modèle dont la fenêtre native le permet (Llama 4 Scout est confortable jusqu'à 256k).

Une consultation de 15 minutes complète le pipeline en moins de 3 minutes sur RTX 4080. Le compte-rendu .soap.md est prêt à être collé dans le LGC.

#4. Intégration logiciel cabinet

Trois niveaux d'intégration selon votre LGC :

  1. 01
    Niveau 1 — Presse-papier
    Le script copie automatiquement le compte-rendu (pyperclip). Le praticien colle dans le LGC en deux clics. Compatible avec 100% des LGC, zéro intégration côté éditeur. C'est le point de départ recommandé.
  2. 02
    Niveau 2 — Raccourci HL7 CDA
    Le compte-rendu Markdown est converti en CDA R2 (Clinical Document Architecture) par un script pandoc + template XML, puis importé via la fonction d'import documentaire du LGC. Compatible avec Weda, Medistory, AxiSanté qui acceptent CDA.
  3. 03
    Niveau 3 — API native du LGC
    Certains éditeurs (Doctolib Pro, Maiia) exposent une API d'insertion d'observation. Le pipeline pousse directement le compte-rendu dans le dossier patient identifié par INS. C'est le plus confortable mais demande un accord éditeur et une authentification CPS.
i
Identifiant National de Santé (INS)
Si vous automatisez le rattachement au bon patient, utilisez l'INS qualifié et non le numéro de dossier interne. C'est la seule clé qui survit au changement de LGC et qui est légalement opposable. La récupération de l'INS se fait via téléservice INSi.

#Conformité et journalisation d'audit

L'usage d'un outil d'aide à la rédaction médicale doit être documenté. Trois éléments minimums dans le dossier patient :

Mention IA d'aide à la rédaction
Une ligne en pied de compte-rendu : "Compte-rendu structuré assisté par un modèle d'IA local (Qwen3-14B, version 2026-04-12). Validé par le Dr [...] le [date]." Cette mention vaut traçabilité.
Journal des accès
Chaque exécution du pipeline est loguée localement (qui, quand, quel audio, quel modèle, hash du compte-rendu). Le log est append-only, signé, conservé 10 ans (durée de conservation du dossier patient).
DPIA RGPD
Faites une analyse d'impact (article 35 RGPD) pour le traitement "transcription assistée par IA". Le caractère 100% local simplifie l'analyse : pas de transfert hors-UE, pas de sous-traitant, finalité unique.
Information du patient
L'affiche en salle d'attente et la mention dans le formulaire d'accueil doivent indiquer l'usage d'un outil d'aide à la transcription. Le patient peut refuser — prévoyez un workflow alternatif.
Chiffrement au repos
Le disque entier est chiffré. Les comptes-rendus sont stockés dans le dossier patient du LGC, pas sur le système de fichiers du poste d'inférence. Purgez les .soap.md temporaires en fin de journée.
Mises à jour supervisées
Toute mise à jour de modèle (Whisper, LLM) déclenche une revalidation sur un jeu de 10 consultations-tests anonymisées. Bug de régression connu = non-déploiement.
!
Suppression du fichier ≠ effacement sécurisé
Un unlink() supprime l'inode mais laisse les blocs récupérables sur SSD non chiffré. Le chiffrement intégral du disque (LUKS/FileVault/BitLocker) est ce qui rend la suppression irréversible en pratique. Ne faites pas l'impasse.

#Pour aller plus loin

Ce pipeline couvre l'usage quotidien d'un praticien libéral ou d'un service hospitalier de taille modeste. Trois extensions naturelles : industrialiser le déploiement multi-postes avec Docker et un reverse proxy d'authentification CPS, ajouter un RAG local sur les recommandations HAS pour assister l'Assessment, et durcir la sécurité réseau jusqu'à l'air-gap complet.

Ce guide vous a aidé ?

Un retour, une erreur, une précision ? Faites-nous signe, ça améliore le guide pour tout le monde.