⚡ CULTIVA IA · IA Engineering

Azure OpenAI Service — Integración Enterprise

Guía de producción para MediSync · Python + TypeScript · Managed Identity · AKS West Europe
GPT-4o Whisper Embeddings DALL-E 3 Managed Identity HIPAA · GDPR AKS · West Europe

Certificaciones

HIPAA BAA
GDPR Art.28
SOC 2 Type II
ISO 27001
Región: West Europe (Amsterdam) · Data residency garantizada
1 Azure OpenAI vs OpenAI Direct — Por Qué Elegimos Azure
Característica OpenAI Direct Azure OpenAI (MediSync)
Autenticación Solo API Key ✔ Managed Identity (sin secretos)
Residencia de datos US primariamente ✔ West Europe (Amsterdam)
Cumplimiento HIPAA ✗ Sin BAA ✔ BAA firmado con Microsoft
GDPR Art. 28 DPA ✗ Limitado ✔ Data Processing Agreement completo
Aislamiento VNET ✗ No ✔ Private endpoints en VNET MediSync
Filtrado de contenido ✗ No ✔ Configurable por deployment
Control de deployments Compartido ✔ Recursos propios con quota dedicada
Integración AKS Manual ✔ Workload Identity nativa
Monitoreo / alertas Dashboard básico ✔ Azure Monitor + Budget Alerts
2 Deployments Configurados — Recurso: medisync-oai-prod
gpt-4o-medisync
Modelo: gpt-4o (2024-11-20) · api_version: 2024-10-21
Resúmenes de notas clínicas · Informes de alta · Anamnesis estructurada
150,000 TPM · Filtrado: Medio · Prompt injection: ON
gpt-4o-mini-fast
Modelo: gpt-4o-mini (2024-07-18) · api_version: 2024-10-21
Clasificación de urgencia · Triaje de síntomas · Respuestas rápidas
300,000 TPM · Filtrado: Alto · Latencia objetivo: <800ms
whisper-transcribe
Modelo: whisper-1 · api_version: 2024-06-01
Transcripción de grabaciones de consulta · ES/EN/CA · Hasta 25MB
100 req/min · Formatos: mp3, mp4, wav, m4a
embed-clinico
Modelo: text-embedding-3-large · api_version: 2024-10-21
Búsqueda semántica de pacientes · RAG sobre historial · Similitud de síntomas
1,000,000 TPM · 3072 dims · Azure AI Search integrado
3 Arquitectura de Autenticación — Managed Identity en AKS

Sin API keys en código. El pod de AKS obtiene un token AAD via Workload Identity y lo intercambia por acceso a Cognitive Services.

🐳
FastAPI Pod
AKS · Workload Identity
🔐
Azure AD
DefaultAzureCredential
☁️
Azure OpenAI
medisync-oai-prod
🔒
VNET Privada
Private Endpoint
Python medisync/ai/client.py
# ✅ PRODUCCIÓN: Managed Identity — sin API keys en código ni variables de entorno
from openai import AzureOpenAI
from azure.identity import DefaultAzureCredential, get_bearer_token_provider
from functools import lru_cache

AZURE_ENDPOINT = "https://medisync-oai-prod.openai.azure.com/"
API_VERSION    = "2024-10-21"

@lru_cache(maxsize=1)
def get_azure_client() -> AzureOpenAI:
    """Singleton con Managed Identity. Funciona en AKS (Workload Identity)
    y localmente con `az login`. Nunca usa API keys."""
    credential = DefaultAzureCredential()
    token_provider = get_bearer_token_provider(
        credential,
        "https://cognitiveservices.azure.com/.default"
    )
    return AzureOpenAI(
        azure_endpoint=AZURE_ENDPOINT,
        azure_ad_token_provider=token_provider,
        api_version=API_VERSION,
    )
4 Casos de Uso — MediSync Production
🩺
Resumen de Notas Clínicas
gpt-4o-medisync · streaming=True
Python ai/summarizer.py
async def resumir_nota_clinica(
    nota_raw: str,
    especialidad: str = "medicina general"
) -> AsyncIterator[str]:
    client = get_azure_client()
    system = f"""Eres un asistente médico experto en {especialidad}.
Extrae del texto clínico: motivo de consulta,
antecedentes relevantes, exploración, diagnóstico
y plan terapéutico. Formato SOAP estructurado."""

    stream = client.chat.completions.create(
        model="gpt-4o-medisync",
        messages=[
            {"role": "system", "content": system},
            {"role": "user", "content": nota_raw}
        ],
        max_tokens=800,
        temperature=0.1,  # Bajo: precisión clínica
        stream=True,
    )
    for chunk in stream:
        if chunk.choices[0].delta.content:
            yield chunk.choices[0].delta.content
🎙️
Transcripción de Consulta (Whisper)
whisper-transcribe · api_version: 2024-06-01
Python ai/transcriber.py
from openai import AzureOpenAI
from azure.identity import DefaultAzureCredential
import tempfile, pathlib

def _whisper_client():
    # Whisper usa api_version diferente
    credential = DefaultAzureCredential()
    token_prov = get_bearer_token_provider(
        credential,
        "https://cognitiveservices.azure.com/.default")
    return AzureOpenAI(
        azure_endpoint=AZURE_ENDPOINT,
        azure_ad_token_provider=token_prov,
        api_version="2024-06-01",
    )

def transcribir_consulta(
    audio_bytes: bytes,
    idioma: str = "es"
) -> str:
    client = _whisper_client()
    with tempfile.NamedTemporaryFile(suffix=".mp3") as tmp:
        tmp.write(audio_bytes); tmp.flush()
        with open(tmp.name, "rb") as f:
            result = client.audio.transcriptions.create(
                model="whisper-transcribe",
                file=f,
                language=idioma,
                response_format="verbose_json",
                prompt="Transcripción médica en español"
            )
    return result.text
🔍
Búsqueda Semántica de Pacientes
embed-clinico · 3072 dims · Azure AI Search
Python ai/semantic_search.py
def vectorizar_sintomas(sintomas: list[str]) -> list[list[float]]:
    """Genera embeddings para búsqueda de pacientes similares."""
    client = get_azure_client()
    response = client.embeddings.create(
        model="embed-clinico",
        input=sintomas,
    )
    return [item.embedding for item in response.data]

# Ejemplo real: buscar pacientes con síntomas similares
def buscar_pacientes_similares(
    query: str,
    top_k: int = 5
) -> list[dict]:
    [query_vec] = vectorizar_sintomas([query])
    # Azure AI Search: vector search con filtro por clínica
    results = search_client.search(
        search_text=None,
        vector_queries=[{
            "kind": "vector",
            "vector": query_vec,
            "fields": "embedding",
            "k": top_k,
        }],
        filter="clinica_id eq 'CL-42'"  # aislamiento por tenant
    )
    return [r for r in results]
📋
Informe de Alta Automatizado
gpt-4o-medisync · function_calling + structured output
Python ai/alta_report.py
TOOLS = [{
    "type": "function",
    "function": {
        "name": "generar_informe_alta",
        "description": "Genera informe alta hospitalaria",
        "parameters": {
            "type": "object",
            "properties": {
                "diagnostico_principal": {"type": "string"},
                "codigo_cie10": {"type": "string"},
                "tratamiento_ambulatorio": {
                    "type": "array",
                    "items": {"type": "string"}},
                "fecha_revision": {"type": "string",
                                   "format": "date"},
            },
            "required": ["diagnostico_principal", "codigo_cie10"]
        }
    }
}]

def generar_alta(historial: str) -> dict:
    client = get_azure_client()
    resp = client.chat.completions.create(
        model="gpt-4o-medisync",
        messages=[
            {"role": "system", "content": "Médico experto. Extrae datos del alta."},
            {"role": "user", "content": historial}
        ],
        tools=TOOLS, tool_choice="required"
    )
    import json
    args = json.loads(
        resp.choices[0].message.tool_calls[0].function.arguments)
    return args  # dict estructurado → BD MediSync
5 Manejo de Filtrado de Contenido — Configuración HIPAA
Python medisync/ai/middleware.py
from openai import BadRequestError
import logging, structlog

log = structlog.get_logger()

async def safe_completion(client, **kwargs) -> str:
    """Wrapper con manejo de content filter + retry + logging para HIPAA audit trail."""
    try:
        response = client.chat.completions.create(**kwargs)
        return response.choices[0].message.content

    except BadRequestError as e:
        if e.code == "content_filter":
            # HIPAA: loguear el rechazo sin los datos del paciente
            log.warning(
                "content_filter_triggered",
                deployment=kwargs.get("model"),
                categories=str(e.error.innererror) if hasattr(e, 'error') else "unknown",
                request_id=e.request_id,
            )
            raise ContentFilterError("Contenido rechazado por política de seguridad")
        raise  # re-raise otros errores

# Ejemplo de uso en endpoint FastAPI:
@app.post("/api/v1/resumir-nota")
async def resumir_nota(request: NotaRequest, user=Depends(verify_jwt)):
    client = get_azure_client()
    texto = await safe_completion(
        client,
        model="gpt-4o-medisync",
        messages=[{"role": "user", "content": request.nota}],
        max_tokens=600,
        temperature=0.1,
    )
    return {"resumen": texto, "tokens_usados": ...}
6 Frontend TypeScript — Streaming en React
TypeScript src/hooks/useAzureStream.ts
// Hook React para streaming de resúmenes clínicos via Azure OpenAI (backend proxy)
import { useState, useCallback } from 'react';

export function useResumenClinico() {
  const [resumen, setResumen] = useState('');
  const [loading, setLoading] = useState(false);

  const resumir = useCallback(async (notaId: string) => {
    setLoading(true); setResumen('');
    // El backend FastAPI hace la llamada a Azure con Managed Identity
    const res = await fetch(`/api/v1/resumir-nota/${notaId}`, {
      headers: { 'Authorization': `Bearer ${getJwt()}` },
    });
    const reader = res.body!.getReader();
    const decoder = new TextDecoder();

    while (true) {
      const { done, value } = await reader.read();
      if (done) break;
      setResumen(prev => prev + decoder.decode(value));
    }
    setLoading(false);
  }, []);

  return { resumen, loading, resumir };
}
7 Checklist de Producción — MediSync HIPAA
🔑
Sin API Keys en Código
Usar exclusivamente Managed Identity / DefaultAzureCredential. Prohibido AZURE_OPENAI_API_KEY en AKS. Rotar cada 90 días en dev.
📍
Pinear api_version
Nunca usar "latest". Fijar 2024-10-21 para chat/embed y 2024-06-01 para Whisper. Testar upgrades en staging antes de producción.
🌐
Private Endpoints
Todo el tráfico pasa por la VNET privada de MediSync. Sin acceso desde Internet público. NSG rules configuradas.
📊
Azure Monitor + Alertas
Alert si tokens/día > 80% quota. Budget alert a 1.800€/mes. Log Analytics Workspace con retención 90 días (HIPAA audit).
⚖️
Content Filtering: Medio
Violencia/odio en "medio". Prompt injection ON. Custom blocklist con términos no médicos. Log de rechazos sin PII.
🔄
Retry + Circuit Breaker
tenacity con backoff exponencial para 429/5xx. Circuit breaker con pybreaker. SLA objetivo: 99.5% en horario clínico.