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¶
- Token OTIMUS default hardcoded em
config.py:55. id: 123no cadastro (igual a Medcenter).- Loopback HTTP desnecessário — adiciona latência sem valor.
- Aliases gigantescos — cada um precisa de manutenção.
- 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.