Pular para conteúdo

Projeto de referência: CLINFETO

Clínica Clinfeto — clínica de ultrassonografia / medicina fetal. Chatbot focado em triagem de exames e agendamento.

  • Repositório: /home/DOCKER/CLINFETO/
  • Domínio OTIMUS: https://clinfeto.otimusclinic.com:9100
  • LLM: gpt-4.1-mini
  • Particularidade: tools chamam endpoints HTTP do próprio serviço (loopback) em vez de funções Python in-process.

Visão rápida

Componente Caminho
Webhook FastAPI server.py
Workflow LangGraph workflow.py
Agente agent.py (949 linhas)
Cliente OTIMUS agendamento.py (853 linhas)
Cliente WTS wts_client.py
Processamento mídia processors.py
DB db.py + init.sql
Telemetria tracking.py
Guia para agentes CLAUDE.md

Médicos (5)

# agendamento.py:54-70
_DOCTOR_KEYWORDS = [
    ("RENNAN",   59),
    ("PRISCILA", 54),
    ("RAFAELLA", 55),
    ("SUELEM",   57),
    ("JOANA",    58),
]

_AGENDA_FIRST_NAME = {
    59: "RENNAN",
    54: "PRISCILA",
    55: "RAFAELLA",
    57: "SUELEM",
    58: "JOANA",
}

Dra. Joana = ultrassom geral Dr. Rennan = ultrassom geral + doppler Dras. Suelem / Rafaella / Priscila = obstetrícia

Convênios (12)

# agendamento.py:38-51
CONVENIOS = {
    212: "PARTICULAR",
    213: "DIGNA",
    214: "BRADESCO SAUDE",
    215: "UNIMED - CAMPINA GRANDE",
    217: "APSEF SAUDE",
    218: "A VIAGEM",
    219: "PLAFAM",
    220: "SAFVIDA",
    221: "UNIMED - INTERCAMBIO",
    224: "FUSEX",
    230: "GEAP",
    231: "SAUDE CAIXA",
}

Procedimentos (~100+)

Múltiplos dicionários por médico:

# agendamento.py:73-202
PROCEDIMENTOS_JOANA = {
    167: (58, 129, "US ABDOME INFERIOR MASCULINO"),
    170: (58, 129, "US ABDOME TOTAL"),
    # ... ~100 procedimentos
}
PROCEDIMENTOS_RENNAN = { ... }
PROCEDIMENTOS_OB = { ... }  # obstetrícia compartilhada

PROCEDIMENTOS_POR_AGENDA = {
    58: PROCEDIMENTOS_JOANA,
    57: PROCEDIMENTOS_SUELEM,
    55: PROCEDIMENTOS_RAFAELLA,
    54: PROCEDIMENTOS_PRISCILA,
    59: PROCEDIMENTOS_RENNAN,
}

Aliases de procedimento

# agendamento.py:205-292
ALIASES = {
    "OBSTETRICA INICIAL ENDOVAGINAL": "US OBSTETRICA (ENDOVAGINAL)",
    "MORFOLOGICA 1 TRIMESTRE":        "US OBSTETRICA MORFOLOGICA 1 TRIMESTRE",
    "TRANSVAGINAL":                   "US TRANSVAGINAL (UTERO OVARIO ANEXOS E VAGINA)",
    "MAMA":                           "US MAMA / AXILA",
    # ... ~100 aliases
}

Aliases são corretivos — cada um foi adicionado após o LLM errar com alguma variação.

Guardian validator

Único projeto com guardião anti-inversão:

# agendamento.py:356-367
def guardian_validate(nome_convenio: str, nome_procedimento: str):
    conv_parece_proc = any(kw in _normalize(nome_convenio) for kw in _PROCEDURE_KEYWORDS)
    proc_parece_conv = _normalize(nome_procedimento) in _CONVENIO_NAMES
    if conv_parece_proc and proc_parece_conv:
        return nome_procedimento, nome_convenio
    return nome_convenio, nome_procedimento

Ver Guardian validator.

Loopback HTTP (diferença importante)

Tools chamam endpoints do próprio serviço, não funções Python:

# agent.py
CONSULTAR_HORARIOS_URL = os.getenv(
    "CONSULTAR_HORARIOS_URL",
    "http://clinfeto-chatbot:8000/webhook/consultar",
)
AGENDAR_CONSULTA_URL = os.getenv(
    "AGENDAR_CONSULTA_URL",
    "http://clinfeto-chatbot:8000/webhook/agendar",
)

@tool
def consultar_horarios(...):
    resp = requests.post(CONSULTAR_HORARIOS_URL, json={...}, timeout=60)
    ...

E o server expõe:

# server.py
@app.post("/webhook/consultar")
async def webhook_consultar(request: Request):
    body = await request.json()
    result = consultar_horarios(body)     # chama agendamento.py
    return JSONResponse({"result": result})

Historicamente via n8n

A URL default (http://clinfeto-chatbot:8000) aponta para o próprio container. Antigamente apontava para um workflow n8n externo (webhook.agendaexame.com). O env var permite switch, mas o código embute agora tudo no mesmo serviço.

Para projetos novos: prefira in-process (como Medcenter).

Fluxo de obstetrícia

Clinfeto tem regras especiais para idade gestacional. Exames como:

  • US morfológica 1º trimestre (11-13 sem)
  • US morfológica 2º trimestre (20-24 sem)
  • Ecocardio fetal (24-28 sem)

São transferidos para humano se a IG fora da janela (o prompt carrega essa lógica).

Transferência automática

Procedimentos sempre transferidos:

  • Ecocardiograma fetal
  • Endovaginal 3D
  • Amniocentese
  • HYCOSY
  • Histerossonografia
  • Pessário
  • NIPT
  • Core biopsy
  • PAAF
  • Qualquer Doppler vascular

Implementação: o prompt instrui o agente a chamar transferir_atendimento quando detectar esses termos (não há lista em código).

Range de datas e filtro

# agendamento.py:682-683, 502-550
data_fim = (hoje + timedelta(days=90)).strftime("%Y-%m-%d")
# Filtra 5 dias x 2 manhã + 2 tarde

Tabelas do DB

n8n_fila_mensagens_clinfeto
dados_cliente_clinfeto
dashboard_sessions
dashboard_token_usage
dashboard_errors
dashboard_tags
checkpoint* (LangGraph)

CLAUDE.md — guia para outros agentes

O Clinfeto tem um CLAUDE.md próprio com:

  • Stack resumida
  • Fluxo do request passo-a-passo
  • Env vars obrigatórias
  • URLs dos webhooks
  • Critical patterns (PostgresSaver, transferência v2, split por \n\n)

Ótimo exemplo

Use esse CLAUDE.md como template para o seu CLAUDE.md de projeto novo — apenas troque o que for específico da clínica e aponte para OTIDOC para o padrão genérico.

Débitos técnicos notórios

  1. Token OTIMUS default hardcoded em config.py:55.
  2. id: 123 no cadastro (igual a Medcenter).
  3. Loopback HTTP desnecessário — adiciona latência sem valor.
  4. Aliases gigantescos — cada um precisa de manutenção.
  5. Sem validação de IG em código — só no prompt; se o LLM falhar, agenda errado.

Checklist Clinfeto-específico

Ao estender o Clinfeto:

  • [ ] Adicionou médico novo? Atualizar _DOCTOR_KEYWORDS, _AGENDA_FIRST_NAME, PROCEDIMENTOS_POR_AGENDA + prompt.
  • [ ] Procedimento novo? Adicionar no dict do médico + lista no prompt (senão LLM não sugere).
  • [ ] Convênio novo? Adicionar em CONVENIOS + lista no prompt.
  • [ ] Alias novo? Em ALIASES — incluir comentário do caso que motivou.