tutorial

IA na saúde brasileira: como anonimizar prontuário eletrônico antes de mandar pra LLM (LGPD + CFM)

Hospital de SP usa pipeline de anonimização + GPT-4o-mini via Tokia pra resumir evolução médica. Código real de regex + NER, custo por consulta e o que CFM Resolução 2.227/2018 exige.

Caso real (sob NDA): clínica multi-especialidade SP, 22 médicos, ~4.000 consultas/mês. Antes: médico digitava evolução de 8-15min depois de cada consulta (tempo "perdido" entre paciente sair e próximo entrar). Depois: gravação áudio → Whisper → anonimização → GPT-4o-mini resume em SOAP estruturado. Médico ganhou 12min/consulta de volta. 4.000 consultas × 12min = 800h/mês de produtividade médica recuperada.

Esse post mostra como fazer certo — porque o erro mais comum (mandar prontuário cru pra OpenAI) viola LGPD, CFM e expõe a clínica a R$ 50M de multa potencial.

O problema regulatório (que 90% dos devs ignoram)

3 normas BR que se aplicam antes de qualquer prompt:

  1. LGPD Art. 11: dados de saúde são sensíveis. Tratamento exige base legal específica (consentimento ou tutela da saúde).
  2. CFM Resolução 2.227/2018: prontuário eletrônico precisa garantir confidencialidade, integridade e disponibilidade. Mandar pra cloud gringa sem cuidado é falta ética do médico responsável.
  3. Marco Civil + LGPD Art. 33: transferência internacional de dados pessoais exige garantias contratuais. OpenAI/Anthropic têm DPA padrão mas leitor de DPO precisa avaliar.

Conclusão: você precisa garantir que o prompt que chega na LLM não contém PII identificável. Anonimização é pré-processamento obrigatório, não opcional.

A arquitetura segura

Áudio consulta → Whisper (Tokia)
                    ↓
                Texto cru com PII (paciente, médico, CPF, telefone)
                    ↓
                Pipeline anonimização (regex + NER local)
                    ↓
                Texto com PLACEHOLDERS ([PACIENTE_001], [CPF])
                    ↓
                GPT-4o-mini (Tokia) → SOAP estruturado
                    ↓
                Reverse-mapping placeholders → texto final pro prontuário

Princípio: LLM nunca vê dado real. Só placeholders + contexto clínico.

O pipeline de anonimização (Python + spaCy)

import re
import spacy
from typing import Dict, Tuple

# spaCy modelo PT-BR pra Named Entity Recognition local (não vai pra cloud)
nlp = spacy.load("pt_core_news_lg")

PATTERNS = {
    "CPF": re.compile(r"\b\d{3}\.?\d{3}\.?\d{3}-?\d{2}\b"),
    "CNS": re.compile(r"\b\d{15}\b"),  # cartão SUS
    "TELEFONE": re.compile(r"\b(?:\+?55\s?)?(?:\(?\d{2}\)?\s?)?\d{4,5}-?\d{4}\b"),
    "EMAIL": re.compile(r"\b[\w.-]+@[\w.-]+\.\w+\b"),
    "DATA_NASC": re.compile(r"\b\d{2}/\d{2}/(?:19|20)\d{2}\b"),
    "CEP": re.compile(r"\b\d{5}-?\d{3}\b"),
}

def anonymize(text: str) -> Tuple[str, Dict[str, str]]:
    """
    Retorna (texto_anonimizado, mapping_pra_reverter).
    Mapping NUNCA vai pra LLM — fica em memória local pra reconstruir
    o output depois.
    """
    mapping: Dict[str, str] = {}
    counter = {"PACIENTE": 0, "MEDICO": 0, "LUGAR": 0}

    # 1. Regex pra padrões estruturados
    for label, pat in PATTERNS.items():
        for match in pat.finditer(text):
            original = match.group()
            placeholder = f"[{label}]"
            mapping[placeholder] = original
            text = text.replace(original, placeholder)

    # 2. NER local pra nomes + lugares
    doc = nlp(text)
    for ent in doc.ents:
        if ent.label_ == "PER":  # pessoa
            counter["PACIENTE"] += 1
            placeholder = f"[PACIENTE_{counter['PACIENTE']:03d}]"
            mapping[placeholder] = ent.text
            text = text.replace(ent.text, placeholder)
        elif ent.label_ == "LOC":  # local
            counter["LUGAR"] += 1
            placeholder = f"[LUGAR_{counter['LUGAR']:03d}]"
            mapping[placeholder] = ent.text
            text = text.replace(ent.text, placeholder)

    return text, mapping


def restore(text: str, mapping: Dict[str, str]) -> str:
    """Reverte placeholders → originais após a LLM processar."""
    for placeholder, original in mapping.items():
        text = text.replace(placeholder, original)
    return text

O prompt SOAP (Subjective, Objective, Assessment, Plan)

SYSTEM = """Você é assistente de transcrição médica para clínica brasileira.
Dado a transcrição da consulta (texto bruto), estruture em formato SOAP:

S (Subjetivo): queixa principal e histórico contado pelo paciente
O (Objetivo): exame físico, sinais vitais, achados
A (Avaliação): hipóteses diagnósticas, raciocínio clínico
P (Plano): conduta, prescrições, retornos, exames

REGRAS:
- Use APENAS informação presente na transcrição. Não invente sintomas.
- Mantenha PLACEHOLDERS literais (ex: [PACIENTE_001], [CPF]).
- Linguagem clínica formal PT-BR.
- Retorne JSON: {"subjetivo": "", "objetivo": "", "avaliacao": "", "plano": ""}
"""

def gerar_soap(transcricao_anonimizada: str) -> dict:
    completion = client.chat.completions.create(
        model="gpt-4o-mini",
        response_format={"type": "json_object"},
        temperature=0.2,
        messages=[
            {"role": "system", "content": SYSTEM},
            {"role": "user", "content": transcricao_anonimizada},
        ],
    )
    return json.loads(completion.choices[0].message.content)

Custo real por consulta

| Item | Valor | Detalhe | |---|---|---| | Whisper áudio 15min | $0.09 | $0.006/min × 15 | | Anonimização local | $0.00 | spaCy roda no servidor da clínica | | GPT-4o-mini SOAP | $0.003 | ~2k input + 800 output tokens | | Total USD | $0.093 | | | Total BRL | R$ 0,79 | fx 5.10 + markup Tokia 1.8x | | 4.000 consultas/mês | R$ 3.160 | |

Comparação: 1 escriba médico CLT custa R$ 5.500/mês com encargos. Bot Tokia economiza R$ 2.340/mês e opera 24/7.

Por que GPT-4o-mini e não DeepSeek?

DeepSeek é mais barato em LLM puro, mas:

  1. Terminologia médica PT-BR: GPT-4o-mini foi treinado com mais texto médico BR (artigos PubMed traduzidos). DeepSeek erra termos como "azitromicina" vs "azitromicina-hidrato".
  2. Compliance HIPAA: OpenAI tem DPA padrão. DeepSeek é empresa chinesa — DPO conservador rejeita.
  3. Latência consistente: GPT-4o-mini responde em menos de 2s p95. DeepSeek varia 1-8s dependendo da hora (horário pico China).

Para casos não-clínicos (resumo de exame laboratorial sem PII por exemplo), DeepSeek serve. Use Tokia pra orquestrar ambos.

Os 5 erros que vimos em outras clínicas

1. Anonimizar só com regex, sem NER

Regex pega CPF/CNS/email, mas perde "o Sr. João da Silva veio com queixa…". NER local resolve. Use spaCy pt_core_news_lg (gratuito) ou Stanza.

2. Logging do prompt cru

Quem usa print(prompt) ou Sentry com payload completo pra debug vira o problema. Antes de mandar pra LLM, logue só hash + length. Tokia não loga conteúdo por padrão.

3. Não validar o reverse-mapping

Se LLM gerar texto NOVO que coincide com um placeholder ("paciente_002" em texto natural), o restore vai errar. Mitigação: use placeholders bem distintos ([[PACIENTE_001]] com duplo bracket).

4. Mandar áudio direto pra Whisper sem consent

CFM exige autorização explícita do paciente pra gravação. Modelo de termo: "Autorizo a gravação desta consulta exclusivamente para fins de elaboração do prontuário eletrônico, ciente que o áudio será processado por sistema de IA e descartado em até 24h." — papel ou checkbox no sistema antes de iniciar.

5. Não ter human-in-the-loop

Médico precisa revisar e assinar o SOAP antes de virar prontuário oficial. UI deve mostrar diff visual com áudio reproduzível, não "aceitar tudo".

Checklist compliance antes de subir em produção

  • [ ] DPO da clínica revisou pipeline e aprovou
  • [ ] Termo de consentimento atualizado (paciente assina antes da gravação)
  • [ ] Áudio descartado em menos de 24h (não armazenar em S3 indefinidamente)
  • [ ] Anonimização testada com 100 prontuários reais (manual review)
  • [ ] Médico assina cada SOAP (audit trail em prontuário)
  • [ ] Backup do mapping placeholders → original em servidor LOCAL da clínica
  • [ ] Plano de contingência se Tokia/OpenAI cair (downgrade pra escriba humano)

Quando NÃO usar isso

  • Psiquiatria/psicologia: sigilo aumentado (Resolução CFP 11/2018). Risco de exposição inaceitável mesmo com anonimização.
  • Pediatria com menores: precisa autorização dos pais por escrito, ECA Art. 17. Mais barato fazer manual.
  • Casos forenses/perícia: prontuário pode virar prova judicial. Qualquer transformação automatizada vira questionamento.

Próximo passo

Cria sua key gratuita no /dashboard, testa o prompt no /playground com transcrição fake e mede latência + qualidade do JSON. Se viável, próximo passo é discutir DPA específico com DPO da clínica — contato@usetokia.com.

Links

#saude#prontuario#anonimizacao#lgpd#cfm#pii

Quer testar Tokia com R$ 10 via PIX?

Criar conta grátis →