clinical-triage-rag-api

Clinical Triage RAG API

Sistema de suporte a triagem hospitalar: classificador ML (Heart Disease UCI) + assistente RAG sobre protocolos clínicos fictícios + API REST (FastAPI).


Como rodar

Pré-requisitos

Setup e execução (tudo em um comando)

bash run.sh

O script executa sequencialmente:

  1. Verifica dependências (Python, pip, Docker, Docker Compose)
  2. Remove runs antigos do MLflow (mlruns/)
  3. Instala dependências Python (requirements.txt)
  4. Treina os modelos (ml/train.py)
  5. Promove o melhor modelo para produção (ml/promote.py)
  6. Gera o índice FAISS a partir dos protocolos (rag/ingest.py)
  7. Sobe os containers com docker-compose up --build

Nota: o script exporta MLFLOW_TRACKING_URI=file:///mlruns antes do treino para que os artefatos sejam gravados com o caminho do container (/mlruns) em vez do caminho absoluto do host, evitando incompatibilidade ao montar o volume no Docker.

Serviços disponíveis

Serviço URL
API REST http://localhost:8000
Swagger (docs interativos) http://localhost:8000/docs
MLflow UI http://localhost:5000

Rotas da API

Método Rota Descrição
GET /health Healthcheck (modelo carregado? índice pronto?)
POST /triage/predict Classe de urgência + importância local por feature
POST /rag/query Resposta assistida + chunks fonte com scores

Como executar testes

PYTHONPATH=. pytest --cov

Os testes usam TestClient do FastAPI com mocks para modelo ML e answer_query, validando contratos HTTP e mascaramento de PII.


Como acessar o MLflow UI

Após docker compose up, abra http://localhost:5000.


Estrutura do repositório

clinical-triage-rag-api/
├── app/                        # Módulo 3 — API REST (FastAPI)
│   ├── main.py                 #   Aplicação, lifespan, middleware PII
│   ├── routers/                #   Rotas: health, triage, rag
│   ├── schemas/                #   Contratos Pydantic (request/response)
│   ├── services/               #   Lógica: predição + PII
│   ├── config.py               #   Constantes (MLflow URI, nomes)
│   └── deps.py                 #   Dependências FastAPI (pipeline singleton)
│
├── ml/                         # Módulo 1 — Classificador de Urgência
│   ├── train.py                #   Treino (LR + XGBoost), CV, SHAP, MLflow
│   ├── promote.py              #   Promoção objetiva do melhor modelo
│   ├── notebooks/eda.ipynb     #   EDA (classes, missing, correlações)
│   ├── artifacts/              #   Gráficos gerados (SHAP, EDA)
│   └── requirements.txt        #   Deps específicas do ML
│
├── rag/                        # Módulo 2 — Assistente RAG
│   ├── protocols/              #   5 protocolos clínicos fictícios (.md)
│   ├── ingest.py               #   Chunking, embeddings, índice FAISS
│   ├── retriever.py            #   Busca vetorial + threshold + log
│   ├── guardrails.py           #   Detecção/bloqueio de CPF
│   ├── mock_llm.py             #   Template de resposta (sem LLM real)
│   ├── index/                  #   Artefatos gerados (não versionados)
│   └── requirements.txt        #   Deps específicas do RAG
│
├── tests/                      # Testes pytest (API)
├── ARCHITECTURE.md             # Módulo 4 — Documentação de arquitetura
├── Dockerfile
├── docker-compose.yml          # API + MLflow UI
├── requirements.txt            # Deps consolidadas (dev + Docker)
└── spec/                       # Enunciado do desafio (referência)

O que mudaria com mais tempo

  1. Testes de integração end-to-end — rodar predição e RAG com artefatos reais (modelo MLflow + índice FAISS), não apenas mocks; validar que docker compose up + requests produzem respostas corretas.

  2. CI/CD com GitHub Actions — lint (ruff), testes, build da imagem Docker e push para registry a cada PR.

  3. Retreino periódico com avaliação de drift — pipeline agendado que compara métricas do modelo atual com dados novos; só promove se não houver degradação.

  4. Substituir template mockado por LLM real — chamada a API de LLM (OpenAI, Bedrock) com guardrails de prompt, rate limiting e fallback para template se a API estiver fora.

  5. Chunking sentence-aware — em vez de janela fixa de caracteres, dividir por sentenças completas (ex.: nltk.sent_tokenize ou regex) para evitar cortes no meio de frases.

  6. Cache de embeddings — queries frequentes não precisam ser re-codificadas; um cache LRU ou Redis reduz latência.