Tools expostas ao agente¶
Três tools (exatamente) em todo projeto OTIMUS:
transferir_atendimento(motivo)consultar_horarios(nome_medico, nome_procedimento, nome_convenio, data_interesse="")agendar_consulta(nome_completo, cpf, data_nascimento, nome_medico, data_exame, hora_exame, nome_procedimento, nome_convenio)
Mais tools adicionam complexidade sem valor — se surgir necessidade, pense duas vezes.
Decorator @tool¶
from langchain_core.tools import tool
@tool
def consultar_horarios(nome_medico: str, nome_procedimento: str, nome_convenio: str) -> str:
"""Use essa ferramenta para verificar disponibilidade de horarios.
Parametros:
- nome_medico: nome do médico (ou "" se sem preferência)
- nome_procedimento: nome exato do procedimento
- nome_convenio: PARTICULAR ou convênio aceito
Se a resposta iniciar com FALHA_CONSULTA, transfira para equipe humana
e NAO invente horarios.
"""
...
A docstring é o prompt da tool — o LLM usa isso para decidir quando e como chamar. Trate-a como parte do system prompt.
1. transferir_atendimento(motivo: str)¶
Quando chamar (conforme prompt)¶
- Ao final do agendamento, depois de confirmar dados.
- Solicitações que a clínica não atende automaticamente (exames especiais).
- Quando o paciente pede "falar com humano" / "atendente".
- Quando há loop sem evolução na conversa.
- Cancelamento ou reagendamento (fora do escopo do agente).
- Mensagens ofensivas, vídeos, spam.
- Urgências.
- Edad gestacional incompatível com exame (Clinfeto).
Implementação¶
# agent.py (Clinfeto loopback HTTP — padrão legacy)
@tool
def transferir_atendimento(motivo: str) -> str:
"""..."""
session_id = _current_session_id.get()
phone = _current_phone_number.get()
try:
resp = requests.post(
TRANSFERIR_ATENDIMENTO_URL,
json={"id session": session_id},
timeout=30,
)
resp.raise_for_status()
log_transfer(session_id)
return "Atendimento transferido com sucesso."
except Exception as e:
log_error("agent_error", f"Erro ao transferir: {e}", session_id, phone)
return f"Erro ao transferir: {e}"
# VERSÃO RECOMENDADA (in-process)
@tool
def transferir_atendimento(motivo: str) -> str:
"""..."""
session_id = _current_session_id.get()
try:
transfer_session(session_id) # wts_client
log_transfer(session_id)
return "Atendimento transferido com sucesso."
except Exception as e:
log_error("agent_error", f"Erro transferir: {e}", session_id)
return f"Erro ao transferir: {e}"
O que o agente faz depois¶
Após chamar transferir_atendimento, o agente geralmente ainda gera uma
mensagem final para o paciente (ex: "Transferi você para um atendente,
que entrará em contato em breve"). O LLM faz isso sozinho se bem
prompted.
2. consultar_horarios(...)¶
Docstring recomendada¶
@tool
def consultar_horarios(
nome_medico: str,
nome_procedimento: str,
nome_convenio: str,
data_interesse: str = "",
) -> str:
"""Verifica disponibilidade de horarios na agenda do medico.
Parametros:
- nome_medico: nome do medico (ex: "Dr. Rennan", "Dra. Joana").
Se o paciente nao tem preferencia, passe string vazia "" ou
"sem preferencia".
- nome_procedimento: nome EXATO do procedimento conforme a lista da
clinica (no system prompt).
- nome_convenio: nome do convenio (ex: PARTICULAR, UNIMED, BRADESCO SAUDE).
- data_interesse: opcional, formato AAAA-MM-DD.
ATENCAO:
- Verifique a lista de procedimentos antes de chamar.
- Se a resposta iniciar com FALHA_CONSULTA, transfira para humano e
NAO invente horarios.
- NUNCA chame essa ferramenta mais de 2 vezes no mesmo turno.
"""
Implementações possíveis¶
@tool
def consultar_horarios(nome_medico, nome_procedimento, nome_convenio, data_interesse=""):
"""..."""
try:
resp = requests.post(
CONSULTAR_HORARIOS_URL,
json={
"nome_medico": nome_medico,
"nome_procedimento": nome_procedimento,
"nome_convenio": nome_convenio,
"data_interesse": data_interesse,
},
timeout=60,
)
resp.raise_for_status()
payload = resp.json()
return payload.get("result", "") or "FALHA_CONSULTA: resposta vazia"
except Exception as e:
return f"FALHA_CONSULTA: erro ao consultar: {e}"
3. agendar_consulta(...)¶
Docstring recomendada¶
@tool
def agendar_consulta(
nome_completo: str,
cpf: str,
data_nascimento: str,
nome_medico: str,
data_exame: str,
hora_exame: str,
nome_procedimento: str,
nome_convenio: str,
) -> str:
"""Cria o agendamento apos confirmacao do paciente.
Parametros:
- nome_completo: nome completo do paciente
- cpf: somente numeros, 11 digitos
- data_nascimento: formato AAAA-MM-DD
- nome_medico: nome do medico (mesmo usado em consultar_horarios)
- data_exame: formato AAAA-MM-DD
- hora_exame: formato HH:MM (24h)
- nome_procedimento: nome EXATO do procedimento
- nome_convenio: nome do convenio por extenso (NUNCA ID)
O telefone e injetado automaticamente (nao passe como parametro).
Se a resposta iniciar com FALHA_CONSULTA, transfira para humano e
NAO tente novamente.
"""
Injeção de telefone¶
Nunca deixar o LLM passar o telefone como parâmetro. Injeção via contextvar:
Razão: o telefone vem do webhook WTS e é imutável para a sessão. Se o LLM tivesse que passar, teria como errar.
Design das tools — princípios¶
- Docstring é prompt. Seja explícito sobre formato, limites, e o que fazer em erro.
- Só strings. Use retornos
str(facilita parsing pelo LLM). Se retornar JSON, embrulhe em string. - Nunca lance exceção. Tools que levantam exception abortam o
agente. Capture e retorne
FALHA_CONSULTA: .... - Não passe session_id, phone, contact_name como parâmetro. Use contextvars. Ver Contextvars.
- Idempotência. Chamar
consultar_horariosduas vezes seguidas deve dar o mesmo resultado (barra tempo passar). Não mantenha estado na tool.