Código Inicial - Lab 08 (Parte 2 — Deploy FastAPI + Docker)

Data de Publicação

09/09/2026

Data de Modificação

09/09/2026

🚀 Ponto de Partida

Neste laboratório, você transformará seu modelo serializado em um microsserviço. Copie os blocos abaixo e siga o Guia de Experimentos (PDF) para completar os TODOs.


1. O Código Principal da API

Crie um arquivo chamado src/api.py e cole o código abaixo.
Ele contém o esqueleto FastAPI com TODOs guiados — complete as seções marcadas conforme o Guia.

"""
Laboratório 08 (Parte 2): Deploy de IA com FastAPI e Docker
Arquivo Starter com TODOs guiados

Instruções:
1. Verifique se os artefatos modelo_fraude.keras e scaler_fraude.joblib existem.
2. Complete os TODOs marcados abaixo seguindo o Guia de Experimentos.
3. Execute com: uvicorn src.api:app --host 0.0.0.0 --port 8000 --reload
4. Teste em http://localhost:8000/docs
5. Containerize com Docker e audite com curl.
"""
import os
import joblib
import numpy as np
from fastapi import FastAPI
from pydantic import BaseModel, Field
from tensorflow.keras.models import load_model

# ==============================================================================
# 1. Instanciação da Aplicação
# ==============================================================================
app = FastAPI(
    title="NeoBank - Sentinela de Fraudes API",
    description="Microsserviço de inferência para detecção de fraudes",
    version="1.0.0"
)

MODEL_PATH = "modelo_fraude.keras"
SCALER_PATH = "scaler_fraude.joblib"

modelo = None
scaler = None


# ==============================================================================
# 2. Inicialização dos Artefatos (Evento de Startup)
# ==============================================================================
@app.on_event("startup")
def carregar_artefatos():
    global modelo, scaler
    if not os.path.exists(MODEL_PATH) or not os.path.exists(SCALER_PATH):
        raise RuntimeError(
            f"Artefatos não encontrados: {MODEL_PATH} e {SCALER_PATH}. "
            "Execute o treinamento da Parte 1 primeiro."
        )
    modelo = load_model(MODEL_PATH)
    scaler = joblib.load(SCALER_PATH)
    print("✅ Artefatos de inferência carregados com sucesso.")


# ==============================================================================
# 3. Schemas Pydantic (Contratos de Dados)
# ==============================================================================
class TransacaoInput(BaseModel):
    """
    Schema de entrada validado estritamente pelo Pydantic.
    Exige EXATAMENTE 15 características numéricas da transação.
    """
    features: list[float] = Field(
        ...,
        min_items=15,
        max_items=15,
        description="Vetor com exatamente 15 características da transação",
        example=[1250.0, 2.5, 0.8, -1.2, 0.05, 3.1, -0.4, 1.8,
                 0.9, -0.2, 0.1, 0.4, -0.05, 0.12, -0.3]
    )


# TODO 1: Completar o schema de saída
class PredicaoOutput(BaseModel):
    """Schema de resposta da API."""
    probabilidade: float   # Score entre 0 e 1
    # TODO: Adicione os campos: classe (int), alerta_fraude (bool), status (str)


# ==============================================================================
# 4. Endpoints da API
# ==============================================================================
@app.get("/health", tags=["Monitoramento"])
def health_check():
    """Endpoint de telemetria — verifica se o serviço e o modelo estão vivos."""
    return {
        "status": "healthy",
        "modelo_carregado": modelo is not None,
        "scaler_carregado": scaler is not None
    }


@app.post("/predict", response_model=PredicaoOutput, tags=["Inferência"])
def predict(transacao: TransacaoInput):
    """
    Endpoint de predição. Recebe 15 features, normaliza com o scaler
    de treino e executa inferência na rede neural.
    """
    # TODO 2: Implementar a lógica de inferência completa
    # Dica: X_raw = np.array(...).reshape(1, -1)
    #       X_norm = scaler.transform(X_raw)
    #       score = float(modelo.predict(X_norm, verbose=0)[0][0])
    #       Retornar PredicaoOutput com os 4 campos preenchidos

    # Placeholder — substitua pelo código real
    return PredicaoOutput(
        probabilidade=0.0,
        classe=0,
        alerta_fraude=False,
        status="APROVADA"
    )

2. O Dockerfile

Crie um arquivo chamado Dockerfile na raiz do projeto (não dentro de src/):

FROM python:3.12-slim

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY src/ ./src/
COPY modelo_fraude.keras .
COPY scaler_fraude.joblib .

EXPOSE 8000
CMD ["uvicorn", "src.api:app", "--host", "0.0.0.0", "--port", "8000"]

3. O .dockerignore

Crie na raiz do projeto:

__pycache__/
*.pyc
.git
.venv
tests/
.pytest_cache
*.png

4. O requirements.txt

Crie na raiz do projeto (adicione ao existente se já tiver um):

fastapi>=0.110.0
uvicorn>=0.28.0
pydantic>=2.6.0
tensorflow-cpu>=2.16.0
scikit-learn>=1.4.0
joblib>=1.3.0
numpy>=1.26.0

5. Comandos de Referência Rápida

# Desenvolvimento local (com hot-reload)
uvicorn src.api:app --host 0.0.0.0 --port 8000 --reload

# Swagger UI no navegador
open http://localhost:8000/docs

# Build da imagem Docker
docker build -t neobank-fraude:v1 .

# Executar container em background
docker run -d -p 8000:8000 --name sentinela neobank-fraude:v1

# Verificar logs
docker logs sentinela

# Auditar inferência via terminal
curl -X POST "http://localhost:8000/predict" \
     -H "Content-Type: application/json" \
     -d '{"features": [1250.0, 2.5, 0.8, -1.2, 0.05, 3.1, -0.4, 1.8,
                       0.9, -0.2, 0.1, 0.4, -0.05, 0.12, -0.3]}'

# Teste de robustez (14 features → esperado HTTP 422)
curl -X POST "http://localhost:8000/predict" \
     -H "Content-Type: application/json" \
     -d '{"features": [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0]}'

# Parar e limpar container
docker stop sentinela && docker rm sentinela

# Submeter no GitLab
git add src/api.py Dockerfile .dockerignore requirements.txt
git commit -m "Lab 08 (Parte 2): API FastAPI + Docker — IA em produção"
git push origin main
De volta ao topo