Pular para conteúdo

Filtragem e seleção de slots

Dado o bruto do OTIMUS (dias com listas enormes de horários), reduzir para algo que caiba em uma mensagem WhatsApp sem poluir a conversa.

Parâmetros de configuração

Ambos os projetos usam os mesmos limites (hardcoded):

Parâmetro Valor Descrição
Dias no futuro 5 dias Primeiros dias com disponibilidade
Slots manhã/dia 2 Horários < 12:00
Slots tarde/dia 2 Horários >= 12:00
Descarte passado Sempre Dias < hoje e horas <= agora

Implementação de referência

# agendamento.py:502-550 (CLINFETO)
from datetime import date, datetime

def _filter_and_select_slots(data_items: list) -> list[dict]:
    """Filtra slots do OTIMUS e devolve uma lista enxuta para o agente.

    Regras:
    - Descarta dias < hoje (America/Sao_Paulo).
    - Se dia == hoje, descarta horas <= agora.
    - Agrupa por dia, depois por período (manhã < 12h, tarde >= 12h).
    - Seleciona os 5 primeiros dias com disponibilidade.
    - Pega 2 slots manhã + 2 tarde por dia.
    """
    hoje = _today_br()
    agora = _now_br().replace(tzinfo=None)
    slots_by_day: dict[date, dict] = {}

    for item in data_items:
        item_date = _parse_date_ddmmyyyy(item.get("data", ""))
        if item_date is None or item_date < hoje:
            continue

        for agenda in item.get("agendas", []):
            for horario in agenda.get("horarios", []):
                hora5 = (horario.get("hora", "") or "").strip()[:5]
                try:
                    hora_h, hora_m = int(hora5.split(":")[0]), int(hora5.split(":")[1])
                except ValueError:
                    continue

                # Se for hoje, descartar horas que já passaram
                if item_date == hoje:
                    h_dt = datetime(item_date.year, item_date.month, item_date.day, hora_h, hora_m)
                    if h_dt <= agora:
                        continue

                if item_date not in slots_by_day:
                    slots_by_day[item_date] = {"manha": [], "tarde": []}

                entry = {
                    "data": item.get("data"),           # DD-MM-YYYY
                    "hora": hora5,                      # HH:MM
                    "dataconsulta": horario.get("dataconsulta", ""),
                }
                if hora_h < 12:
                    slots_by_day[item_date]["manha"].append(entry)
                else:
                    slots_by_day[item_date]["tarde"].append(entry)

    selected: list[dict] = []
    for day in sorted(slots_by_day.keys())[:5]:
        day_slots = slots_by_day[day]
        selected.extend(day_slots["manha"][:2])
        selected.extend(day_slots["tarde"][:2])
    return selected

Por que 2 manhã + 2 tarde

Dois motivos:

  1. Legibilidade no WhatsApp — mensagens com 20 horários viram spam.
  2. Taxa de conversão — paciente escolhe mais rápido com menos opções.

Se a clínica pedir mais opções, aumente mas sem passar de ~6 por dia.

Por que 5 dias

Suficiente para o paciente ver "hoje, amanhã, próxima semana" sem scroll excessivo. Se todos os 5 dias vierem cheios, o paciente pode pedir datas mais à frente e o agente re-chama a tool com data_interesse.

Ordem dos slots

Dentro do dia: manhã primeiro, tarde depois. Mantenha a ordem cronológica interna (ex: 08:00, 08:30 antes de 14:00, 14:30).

Formato final entregue ao agente

Recomendação: JSON list com 3 campos.

[
  {"data": "15-04-2026", "hora": "08:00", "dataconsulta": "2026-04-15 08:00:00"},
  {"data": "15-04-2026", "hora": "08:30", "dataconsulta": "2026-04-15 08:30:00"},
  {"data": "15-04-2026", "hora": "14:00", "dataconsulta": "2026-04-15 14:00:00"},
  {"data": "15-04-2026", "hora": "14:30", "dataconsulta": "2026-04-15 14:30:00"},
  {"data": "16-04-2026", "hora": "08:00", "dataconsulta": "2026-04-16 08:00:00"}
]

O agente então formata em PT-BR para o paciente:

Temos disponibilidade:

Quarta, 15 de abril
 - 08:00 | 08:30 | 14:00 | 14:30

Quinta, 16 de abril
 - 08:00

E se só veio 1 dia com disponibilidade?

Devolve mesmo assim. O agente diz ao paciente a realidade e pergunta se quer horário em dia mais à frente.

E se não veio nada?

Retorna string FALHA_CONSULTA: agenda sem horarios disponiveis. Agente transfere.

_parse_date_ddmmyyyy helper

def _parse_date_ddmmyyyy(s: str) -> date | None:
    if not s:
        return None
    try:
        d, m, y = s.split("-")
        return date(int(y), int(m), int(d))
    except Exception:
        return None