Documentação

Confiança

Como a TypeSafe comunica a certeza, em que difere da probabilidade e como a usar para controlar o comportamento do sistema.

Todas as respostas de Score e Choice da TypeSafe incluem uma propriedade probabilities que representa a distribuição de probabilidade pelas opções (no caso do Choice) ou pelos níveis (no caso do Score). A forma dessa distribuição é o que te diz quão certo o modelo está: concentrada num só resultado significa uma resposta confiante; dispersa significa uma resposta incerta.

A propriedade confidence da resposta resume essa forma num único número de 0 a 1, para que possas aplicar um limiar sem fazer as contas. Vale 1 quando toda a probabilidade está num único resultado e 0 quando a probabilidade está distribuída por igual. (As respostas Noul não têm uma; vê mais abaixo Noul.)

A confiança deriva das probabilidades

confidence é uma estatística calculada a partir da distribuição de probabilidade que a resposta já te dá. A TypeSafe calcula-a por ti e devolve-a em todas as respostas Choice e Score, por isso o caso comum não exige trabalho extra da tua parte.

Exemplo: pergunta choice com três opções

Veja como a distribuição de probabilidade muda a confiança

Confiança

0.85

Arraste um controlo para mudar a probabilidade de uma opção. As restantes ajustam-se automaticamente para manter o total em 100%.

Selecionado: Opção A

Como esta demonstração calcula a confiança

O TypeSafe calcula a confiança a partir de como a probabilidade se distribui entre as opções. Tudo numa só opção dá 1.0; quanto mais uniforme, menor a confiança. Esta demonstração usa (3 × probabilidade máxima − 1) / 2 para aproximar a confiança com três opções.

Para um Choice, a distribuição são as probabilities pelas tuas opções. Para um Score, é a distribuição pelos teus níveis. Em ambos os casos, uma distribuição mais plana significa menor confiança: baixa confiança num Choice significa muitas vezes que nenhuma das opções é claramente melhor que as outras, e baixa confiança num Score significa muitas vezes que os níveis são ambíguos, multidimensionais ou que o estado não contém informação suficiente.

A fórmula exata de cada tipo de pergunta está em Como a confiança é calculada, para que vejas como qualquer valor de confidence decorre das probabilities da própria resposta.

«Não sei» é um sinal útil

Se um sistema inteligente, humano ou máquina, não consegue expressar incerteza honesta, não se pode confiar nele.

A confiança dá-te um mecanismo incorporado para o modelo dizer «não tenho a certeza quanto a isto». Isto permite ao teu código implementar comportamentos diferentes para níveis diferentes de certeza, que é a base para construir sistemas em que podes realmente confiar.

Três caminhos para usar a confiança no teu código

Um bom padrão para começar é dividir a confiança em três intervalos, cada um com um comportamento de sistema diferente:

Confiança alta: Age automaticamente. O modelo tem uma leitura clara e podes avançar sem intervenção humana.

Confiança média: Avança com cautela. O modelo tem uma resposta razoável, mas não está certo. Dependendo do contexto, podes pedir confirmação ao utilizador, assinalar para revisão ou reunir mais informação antes de agir.

Confiança baixa: Não ajas. Encaminha para uma pessoa, pede esclarecimentos ou recorre a um sistema diferente. O modelo está a dizer-te que não tem informação suficiente ou que a pergunta não é adequada.

Onde traças essas fronteiras depende do que está em jogo.

Os limiares escalam com o risco

Um limiar de confiança não é um único número. Ações diferentes dentro do mesmo sistema devem ser condicionadas em níveis diferentes, consoante as consequências de errar.

response = client.system_one(
    state=user_message,
    questions={
        "action": Choice(
            instructions="What is the user trying to do?",
            criteria={
                "check_balance": "View account balance",
                "approve_transfer": "Approve the pending withdrawal request",
                "support": "Get help with an issue",
            },
        ),
    },
)

action = response.answers["action"]
confidence = action.confidence

if confidence < 0.5:
    # Model is genuinely unsure. Don't guess.
    route_to_human(user_message)

elif action.choice == "check_balance":
    # Low stakes. Showing the wrong screen is recoverable.
    show_balance(account_id)

elif action.choice == "approve_transfer":
    if confidence > 0.9:
        # High stakes, high confidence. Proceed with confirmation.
        confirm_then_execute(account_id)
    else:
        # High stakes, moderate confidence. Verify first.
        ask_user_to_confirm(account_id)

O piso de confiança de 0.5 apanha tudo o que o modelo reporta como genuinamente incerto. Acima disso, o limiar para agir sem confirmação é mais alto para uma operação destrutiva do que para uma apenas de leitura. O teu código codifica a tolerância ao risco.

Como a confiança é calculada

Cada tipo de pergunta resume a sua distribuição de forma um pouco diferente. Um Noul tem dois resultados, um Choice tem qualquer número de opções em ordem alguma, e os níveis de um Score são ordenados, por isso cada fórmula abaixo assenta na anterior.

A confidence da TypeSafe é uma forma razoável de resumir uma distribuição, não a única. Devolvemos uma medida fixa para que cada resposta venha com um padrão sensato: podes condicionar por confidence desde a primeira chamada, na mesma escala de 0 a 1 para qualquer pergunta, sem primeiro escolher e validar uma estatística tua. Como as fórmulas abaixo são exatas e cada resposta inclui as suas probabilities completas, podes calcular em vez dela a medida que mais importa para a tua aplicação.

Noul

Uma resposta Noul é uma única probabilidade $p$ de que a resposta seja sim, e a TypeSafe não devolve um confidence separado para ela. A probabilidade já carrega a incerteza: um valor próximo de 0.5 é o modelo a dizer que não tem a certeza, e a página Noul explica como aplicar um limiar diretamente sobre ela.

Se mesmo assim quiseres um número ao estilo da confiança, por exemplo para condicionar Nouls e Choices com o mesmo código, usa a distância até 0.5:

$$ \text{confidence} = |2p - 1| $$

Isto dá 0 em $p = 0.5$ e 1 em $p = 0$ ou $p = 1$. É também a fórmula de Choice abaixo aplicada a um Choice de sim/não, por isso fica na mesma escala da confiança de Choice.

Choice

Um Choice estende a mesma ideia a qualquer número de opções. Para um Choice com $n$ opções, em que $p_{\max}$ é a probabilidade da opção selecionada:

$$ \text{confidence} = \frac{p_{\max} - \frac{1}{n}}{1 - \frac{1}{n}} $$

Isto mede quanto a probabilidade mais alta fica acima de uma divisão igual de $\frac{1}{n}$ por opção, numa escala em que a divisão igual é 0 e a certeza é 1. Só a probabilidade mais alta conta, por isso $(0.6, 0.3, 0.1)$ e $(0.6, 0.2, 0.2)$ têm ambas confiança 0.4.

def choice_confidence(probabilities: list[float]) -> float:
    n = len(probabilities)
    return (max(probabilities) - 1 / n) / (1 - 1 / n)

choice_confidence(list(answer.probabilities.values()))

O explorador no topo desta página usa esta fórmula com três opções.

Outras duas medidas mais simples, calculadas a partir das mesmas probabilities, são muitas vezes muito eficazes na prática e vale a pena experimentá-las a par de confidence:

  • Probabilidade mais alta, $p_{\max}$. Lê-se diretamente como «quão provável é a opção selecionada», o que torna os limiares fáceis de raciocinar. O seu significado depende do número de opções, já que 0.5 é uma resposta fraca entre duas opções e forte entre dez, por isso define o seu limiar por pergunta.
  • Razão entre a mais alta e a segunda, $p_{\max} / p_{\text{second}}$. Mede com que clareza a opção selecionada bate o segundo lugar e ignora como o resto se distribui. Muitas decisões reais resumem-se aos dois candidatos principais, e esta razão visa exatamente isso.

Score

Os níveis de um Score são ordenados, por isso a sua fórmula também conta quão longe a probabilidade está do nível mais provável. Para um Score com $n$ níveis numerados de $0$ a $n - 1$, em que $p_i$ é a probabilidade do nível $i$ e $m$ é o nível mais provável:

$$ \text{confidence} = \max\left(0,\ 1 - \frac{\sum_i p_i , |i - m|}{\text{MAD}{\text{unif}}}\right) \qquad \text{MAD}{\text{unif}} = \frac{1}{n} \sum_i \left| i - \frac{n - 1}{2} \right| $$

A soma no numerador é a distância média ponderada pela probabilidade, em níveis, entre a resposta e o nível mais provável. $\text{MAD}_{\text{unif}}$ é o mesmo tipo de distância média para uma distribuição igual por todos os níveis, medida a partir do nível central. A confiança compara as duas e tem piso 0 quando a resposta está pelo menos tão distribuída como uma divisão igual.

Probabilidade num nível vizinho reduz a confiança menos do que a mesma probabilidade num nível mais distante. Com três níveis, $(0, 0.5, 0.5)$ tem confiança 0.25, porque o modelo está dividido entre dois níveis adjacentes. $(0.5, 0, 0.5)$ tem confiança 0, porque está dividido entre extremos opostos. A fórmula de Choice daria 0.25 às duas distribuições.

Pergunta de pontuação com cinco níveis

Veja como a distância entre níveis altera a confiança

Confiança

0.92

Mova um cursor para alterar a probabilidade de um nível. Os restantes ajustam-se para manter o total em 100%.

Pontuação
2.00
Fórmula do Choice com as mesmas probabilidades
0.88
Como esta demonstração calcula a confiança

Esta demonstração usa a fórmula do Score: calcula a distância média, em níveis, entre a resposta e o pico, ponderada pela probabilidade. Divide esse valor por 1.2, a mesma média para uma distribuição uniforme em cinco níveis medida a partir do nível central, e subtrai o resultado de 1, parando em 0. Probabilidade num nível vizinho do pico reduz menos a confiança do que probabilidade distante dele, algo que a fórmula do Choice não tem em conta.

def score_confidence(probabilities: list[float]) -> float:
    n = len(probabilities)
    m = probabilities.index(max(probabilities))
    spread = sum(p * abs(i - m) for i, p in enumerate(probabilities))
    even_spread = sum(abs(i - (n - 1) / 2) for i in range(n)) / n
    return max(0.0, 1 - spread / even_spread)

levels = sorted(answer.probabilities)
score_confidence([answer.probabilities[level] for level in levels])

A resposta bug_severity no exemplo de resposta de Score tem probabilidades $(0, 0.57, 0.43)$. O nível mais provável é 1, a distância é $0.43$, e o $\text{MAD}_{\text{unif}}$ para três níveis é $\frac{2}{3}$, por isso a confiança é $1 - 0.43 / \frac{2}{3} \approx 0.35$.