⚠️ Problema crítico: "primeiro nome do doutor"¶
Esta página existe por pedido explícito — é a armadilha mais perigosa do padrão OTIMUS atual. Leia antes de estender Medcenter ou iniciar projeto novo.
O que é¶
Para resolver o nome do médico (em linguagem natural do paciente) em um
agenda_id numérico, ambos os projetos usam lookup por string pelo
primeiro nome. O MEDCENTER ainda adiciona sobrenomes únicos como
fallback.
# scheduling.py:157-188 (MEDCENTER)
def _validar_agenda_medico(dias: list[dict], medico_esperado: str) -> bool:
medico_norm = _normalize(medico_esperado)
if "JOAO PAULO" in medico_norm:
check_token = "JOAO PAULO"
else:
check_token = medico_norm.split()[0] # <-- primeiro nome
...
# scheduling.py:88-98 (MEDCENTER) — validação no boot
for _p in PROCEDIMENTOS:
_nome = _normalize(_p["medico"]).split()[0]
if _nome in _primeiros_nomes:
raise RuntimeError(
f"ERRO CRITICO: primeiro nome duplicado '{_nome}' ..."
)
_primeiros_nomes.append(_nome)
A premissa é: "todos os médicos têm primeiros nomes únicos".
Por que é frágil¶
Cenário 1 — dois médicos com mesmo primeiro nome¶
Clínica tem Dr. João Paulo Almeida (angiologista) e contrata um Dr. João
Pedro Silva (clínico). Primeiro nome comum: JOÃO.
- Validação no boot trava:
raise RuntimeError. Sistema não sobe. - Desenvolvedor precisa editar
_DOCTOR_KEYWORDSmanualmente, inventar um novo padrão (ex:"JOAO PAULO"+"JOAO PEDRO"). - Se alguém não lembra dessa regra, adiciona o médico no
PROCEDIMENTOSe esquece de atualizar_DOCTOR_KEYWORDS→ container não sobe em produção → incidente.
Cenário 2 — sobrenomes compartilhados¶
No Medcenter:
CUNHAaparece em Regina e Sabrina → não pode estar em_DOCTOR_KEYWORDS.DALCOLaparece em Juriadyson e Sabrina → não pode estar.
Se alguém adiciona CUNHA ou DALCOL como keyword (por engano), a primeira
ocorrência na lista sempre ganha — paciente vai ser agendado com o médico
errado, silenciosamente.
Cenário 3 — paciente cita sobrenome ambíguo¶
Paciente: "Quero agendar com a Dra. Cunha".
- LLM passa
nome_medico="Dra. Cunha"para a tool. _DOCTOR_KEYWORDSnão temCUNHA(porque não é único).- Nenhuma keyword bate → retorna
FALHA_CONSULTA. - Agente transfere para humano. Funciona, mas UX ruim.
Cenário 4 — OTIMUS retorna agenda errada¶
Mesmo com keyword correta, o OTIMUS às vezes retorna a agenda de outro
médico (bug deles). A validação _validar_agenda_medico tenta detectar
checando agendaNome, mas:
- Se
agendaNomevier vazio, a validação aceita (só confia noagenda_idda request). - Se
agendaNomevier presente mas sem o primeiro nome esperado, rejeita. - Em clínicas grandes com agendas mal nomeadas (
agendaNome="Sala 2"), o primeiro nome nunca bate e tudo vira FALHA_CONSULTA.
Impacto em números¶
No Medcenter, hoje, com 7 médicos e primeiros nomes manualmente curados para serem únicos: zero incidentes. Mas:
- Adicionar o 8º médico é risco de incidente.
- Adicionar clínica nova (com mais médicos) herda a fragilidade.
- A trava-no-boot protege contra duplicatas óbvias, mas não contra sobrenomes ambíguos ou casos como "Ana C./Ana M.".
Mitigações já em vigor¶
_validate_unique_first_names()no boot (Medcenter): trava se alguém adiciona médico com primeiro nome duplicado.- Multi-palavra na frente (
"JOAO PAULO"antes de"JOAO"): permite dois médicos comJoãose um deles tiver nome composto. - Sobrenomes únicos como fallback (
"BRITO","ALMEIDA","SIQUEIRA"): cobrem caso em que o paciente só cita sobrenome. - Validação de
agendaNomeno response: última linha de defesa contra OTIMUS retornar agenda errada.
Solução recomendada (evolução)¶
Opção A — forçar escolha numerada no menu¶
No system prompt, em vez de aceitar "Dra. Joana" como input livre, forçar o paciente a escolher de uma lista numerada:
Temos os seguintes medicos disponiveis para agendamento:
1) Dra. Joana Silva (ultrassom geral)
2) Dr. Rennan Santos (doppler)
3) Dra. Suelem Oliveira (obstetricia)
Digite o NUMERO do medico desejado.
A tool passa a receber medico_id (int) em vez de nome. Lookup vira
int → dict direto — impossível ambiguidade.
Trade-off: menu numerado é menos natural na conversa.
Opção B — id canônico interno¶
Manter input livre, mas expor ao LLM IDs canônicos:
MEDICOS = {
"joana": {"agenda_id": 58, "nome_display": "Dra. Joana Silva"},
"rennan": {"agenda_id": 59, "nome_display": "Dr. Rennan Santos"},
"suelem": {"agenda_id": 57, "nome_display": "Dra. Suelem Oliveira"},
}
No prompt, instruir o LLM a usar medico_slug="joana" na chamada da tool.
lookup = MEDICOS[slug].
Vantagem: sem parsing. Se o LLM errar e passar string qualquer, KeyError
claro → FALHA_CONSULTA com motivo específico.
Opção C — Levenshtein com threshold e rejeição em caso de ambiguidade¶
Usar fuzzy matching com recusa explícita quando há empate:
from rapidfuzz import process
def resolver_medico(nome: str) -> dict | None:
candidatos = [(p["medico"], p) for p in PROCEDIMENTOS]
matches = process.extract(nome, [c[0] for c in candidatos], limit=3)
best, score, _ = matches[0]
second, score2, _ = matches[1] if len(matches) > 1 else ("", 0, 0)
if score < 85:
return None # nenhum suficientemente próximo
if score2 >= 85 and (score - score2) < 5:
return None # ambiguidade — rejeita
return dict(candidatos)[best]
Trade-off: mais robusto mas requer dependência adicional e threshold de score empírico.
O que NÃO fazer¶
- ❌ Baixar o threshold do match para "ser mais tolerante".
- ❌ Silenciar a validação de
agendaNome. - ❌ Remover a trava-no-boot.
- ❌ Acumular sobrenomes genéricos (
SILVA,SOUZA,SANTOS) em_DOCTOR_KEYWORDSachando que vai melhorar o match.
Ação recomendada agora¶
- No Medcenter: manter como está, mas antes de adicionar novo médico,
revisar
_DOCTOR_KEYWORDSe validar os nomes únicos. - Em projetos novos: adotar Opção A (menu numerado) ou Opção B (slug canônico). Nunca repetir o padrão "primeiro nome único por convenção".
- No futuro: migrar Medcenter gradualmente para Opção B durante uma janela de manutenção tranquila.
Relato histórico da decisão¶
Segundo os logs e comentários do código, o padrão foi escolhido por:
- Simplicidade — uma lista de keywords.
- Acreditar que "poucos médicos, nomes conhecidos, vai ser ok".
- Não havia biblioteca fuzzy instalada.
Na prática, foi adequado para 7 médicos numa clínica. Não escala.