Pular para conteúdo

Guardian validator — anti-inversão de argumentos

LLMs às vezes trocam os argumentos ao chamar a tool. Exemplo real:

{
  "nome_medico": "JOANA",
  "nome_procedimento": "PARTICULAR",
  "nome_convenio": "US ABDOME TOTAL"
}

PARTICULAR é convênio, US ABDOME TOTAL é procedimento. O modelo inverteu. O Clinfeto tem um guardião que detecta e corrige.

Implementação (Clinfeto)

# agendamento.py:294-367 (CLINFETO)
_PROCEDURE_KEYWORDS = [
    "US ", "USG", "OBSTETRICA", "TIREOIDE", "JOELHO", "DOPPLER",
    "TRANSVAGINAL", "ENDOVAGINAL", "MORFOLOGICA", "MAMARIA", "PROSTATA",
    "ABDOME", "MAMA", "TIREOIDE",
]

_CONVENIO_NAMES = {_normalize(v) for v in CONVENIOS.values()}

def guardian_validate(nome_convenio: str, nome_procedimento: str) -> tuple[str, str]:
    """Se LLM inverteu convenio/procedimento, corrige.

    Returns: (nome_convenio_corrigido, nome_procedimento_corrigido)
    """
    conv_norm = _normalize(nome_convenio)
    proc_norm = _normalize(nome_procedimento)

    conv_parece_proc = any(kw in conv_norm for kw in _PROCEDURE_KEYWORDS)
    proc_parece_conv = proc_norm in _CONVENIO_NAMES

    if conv_parece_proc and proc_parece_conv:
        logger.info("Guardian: invertendo convenio<->procedimento")
        return nome_procedimento, nome_convenio

    return nome_convenio, nome_procedimento

Heurística

Dois sinais simultâneos precisam acender:

  1. O campo nome_convenio contém palavra típica de procedimento (ex: "US ").
  2. O campo nome_procedimento é exatamente um dos nomes de convênio conhecidos.

Só aí o guardião inverte. Uma condição sozinha não é suficiente — risco de falso positivo é alto.

Onde plugar

No início de toda função pública exposta ao agente:

def consultar_horarios_workflow(payload: dict) -> str:
    nome_medico       = payload.get("nome_medico", "")
    nome_procedimento = payload.get("nome_procedimento", "")
    nome_convenio     = payload.get("nome_convenio", "")

    nome_convenio, nome_procedimento = guardian_validate(nome_convenio, nome_procedimento)
    # ... resto do fluxo

Quando adicionar um guardian

Só quando você observar o problema em produção. Não crie guardiões preventivos — eles mascaram bugs reais do prompt e criam magia.

Se o LLM ficar invertendo:

  1. Primeiro tente corrigir no prompt (descrição da tool + exemplos).
  2. Se persistir, adicione guardian com logs que deixem claro quando dispara.

Outros guardiões possíveis

Nenhum outro foi implementado hoje, mas padrões observáveis:

  • Data vs hora invertidos: cliente pede data="14:30", hora="2026-04-15". Detecção: regex ISO vs regex hora.
  • CPF no lugar do telefone: ambos são números, mas CPF tem 11 dígitos e telefone internacional 12-13.
  • Nome do médico com "DR" / "DRA" no início: normalização simples removendo prefixos já resolve.

Log é obrigatório

Quando guardian dispara, logue com nível INFO (não ERROR — é um comportamento esperado e tratado):

logger.info("Guardian: invertendo convenio<->procedimento (%r <-> %r)",
            nome_convenio, nome_procedimento)

Ajuda a monitorar se o prompt está ficando ruim / se o modelo novo regrediu.