//Receita

Extrair valores que a regex já encontrou

O modelo não digita o valor: ele aponta qual dos trechos achados é o certo. Essa inversão elimina uma classe inteira de erro.

Chega um e-mail com quatro endereços no cabeçalho e uma frase no corpo: “não usem o endereço de faturamento para este, manda o recibo no meu pessoal”. Qual endereço vai no cadastro?

Ou uma fatura com quatro valores: subtotal, imposto, total e um crédito de cortesia. Qual deles é o que o cliente precisa pagar, e qual precisa entrar com sinal invertido?

O dado está no texto. O problema é escolher.

Por que a solução ingênua falha

Regex sozinha não decide. Ela acha os quatro e-mails com precisão e não tem como saber qual é o do recibo, porque isso depende de uma frase em português no corpo da mensagem. Regra fixa do tipo “use o Reply-To” acerta neste e-mail e erra no próximo.

Modelo generativo decide e digita. E digitar é o problema. Pedir “extraia o total da fatura” a um modelo que gera texto significa aceitar um valor reescrito — e um dígito trocado numa fatura é incidente contábil, não bug de formatação. Pior: o erro é invisível, porque o valor sai plausível.

Essa fronteira é declarada. A documentação lista geração entre os modos de falha do jev-1.13 e recomenda exatamente a inversão desta receita: quando o espaço de resposta é limitado, transforme extração em escolha entre opções, em vez de pedir o valor.

O desenho das perguntas

Três passos, e o papel de cada peça fica nítido:

  1. A regex acha os candidatos, calibrada para achar com folga (melhor achar demais que de menos).
  2. Uma pergunta Choice escolhe qual candidato responde à pergunta. As opções são os trechos achados, mais um escape.
  3. O código copia o trecho escolhido e normaliza.
import re
from typesafe_sdk import Choice, TypeSafeClient

EMAIL_RE = re.compile(r"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}")
NENHUM = "nenhum"

def achar(padrao, texto):
    vistos, saida = set(), []
    for trecho in padrao.findall(texto):
        trecho = trecho.strip()
        if trecho and trecho not in vistos:
            vistos.add(trecho)
            saida.append(trecho)
    return saida

def escolher(documento, candidatos, pergunta):
    criteria = {c: None for c in candidatos} | {NENHUM: "Nenhum destes é o valor pedido."}
    with TypeSafeClient() as client:
        resposta = client.system_one(
            state=documento,
            questions={"escolha": Choice(instructions=pergunta, criteria=criteria)},
        )
    escolha = resposta.answers["escolha"]
    return {"valor": escolha.choice, "confianca": escolha.confidence}

candidatos = achar(EMAIL_RE, documento)
recibo = escolher(documento, candidatos, "Para qual endereço o remetente quer que o recibo seja enviado?")

A propriedade que isso compra: a resposta é um dos trechos, copiado sem alteração. Não existe caminho pelo qual o modelo devolva um e-mail que não estava no documento, ou um valor com dígito diferente. Ele aponta; o código copia.

Depois da escolha, perguntas auxiliares completam o que o código precisa para normalizar — e vão na mesma leva, cada uma com o tipo certo. A moeda é um Choice sobre um conjunto fechado; “isto é crédito ou cobrança?” é um Noul.

O que o experimento oficial mediu

O cookbook mostra três casos. Os números são da TypeSafe, com o jev-1.12:

CasoCandidatos achadosEscolhaConfiança
Endereço para o recibo4 e-mailso endereço pessoal, contrariando o campo de faturamento0,98
Endereço do remetenteos mesmos 4o endereço da linha “De”1,00
Celular entre três telefones3 númeroso celular direto1,00
País do escritóriolista fechadaEstados Unidos0,90
Total a pagar na fatura4 valoresUS$ 1.315,50, classificado como cobrançaprobabilidade de crédito 0,01
Crédito de cortesiaos mesmos 4US$ 50,00, classificado como créditoprobabilidade de crédito 0,99

O caso do e-mail é o que melhor mostra o valor da receita: o endereço certo não é o que uma regra fixa escolheria, é o que a frase do corpo pede — e o modelo leu a frase. O caso do telefone mostra a divisão de trabalho completa: o modelo escolhe o número e lê o país no texto, e uma biblioteca de telefone junta os dois para produzir o formato internacional. Nada nos dígitos diz qual número é o celular; as palavras em volta dizem.

No caso do dinheiro, as duas perguntas auxiliares fazem o código saber o sinal de cada valor: 0,01 de probabilidade de crédito no total e 0,99 no crédito.

O cookbook registra dois limites com clareza. O primeiro é o teto de 255 opções de um Choice — acima disso, escolha em duas etapas. O segundo é honesto sobre onde está o trabalho: achar os candidatos é a parte difícil. E-mail, telefone e valor têm expressão regular que os cobre; nome de pessoa não tem, e aí os candidatos precisam vir de um cadastro, de um reconhecedor de entidades ou de um modelo generativo.

O que muda em português

Este é um dos casos em que a adaptação brasileira muda código, não só texto.

Separador decimal. O cookbook avisa que a conversão dele assume vírgula de milhar e ponto decimal, e que em “€1.315,50” é o contrário. No Brasil é sempre o contrário: “R$ 1.315,50”. O próprio cookbook sugere a solução elegante — perguntar com um Noul qual convenção o documento usa e ramificar no código.

Documentos nossos. CPF, CNPJ, chave PIX, número de nota fiscal e código de barras de boleto são todos conjuntos com formato conhecido: regex acha, e a pergunta escolhe qual dos achados é o pedido. É a receita inteira, aplicada ao documento brasileiro mais comum.

Telefone. Nosso celular tem nono dígito e DDD, e a distinção entre fixo e celular está no formato — mas qual deles é “o do financeiro” está nas palavras em volta. Mesma divisão: formato para a regex, papel para o modelo.

Como a TypeSafe declara que o inglês é a língua principal de treino, teste a escolha com os seus documentos antes de confiar em confiança alta.

Onde isso quebra

  • Candidato ausente. Se a regex não achou, o modelo não pode escolher. Calibre para achar demais e deixe o escape nenhum fazer a limpeza.
  • Muitos candidatos. Acima de 255 opções, duas etapas.
  • Leitura literal. “Qual valor o cliente deve pagar?” e “qual é o total da fatura?” podem apontar trechos diferentes quando existe crédito aplicado. Escreva a condição exata, como discutido em critérios que separam.
  • Conta. Somar subtotal com imposto e conferir se fecha com o total é aritmética, e aritmética é código — está entre os limites declarados.

Para datas, que têm partes em conjuntos fechados e a mesma divisão de trabalho, veja extrair datas. O índice está em receitas.

Perguntas frequentes

Por que não pedir o valor direto ao modelo?

Porque o jev-1.13 não é treinado para gerar texto, e a documentação recomenda o contrário: levantar os candidatos com expressão regular ou outro modelo e deixar o Jev escolher o correto entre eles. Assim o valor devolvido é um trecho copiado, não digitado.

Isso impede erro de dígito?

Impede essa classe de erro. Como as opções da pergunta são os próprios trechos achados no documento, a resposta é um daqueles trechos sem alteração. O modelo não tem como inventar um valor nem transpor um número.

E quando nenhum candidato serve?

A receita inclui uma opção de escape, 'nenhum', descrita como 'nenhum destes é o valor pedido'. Sem esse escape, o modelo é forçado a apontar um trecho errado.

Quantos candidatos cabem?

Até 255, que é o limite de opções de uma pergunta Choice. Acima disso, o cookbook sugere duas etapas: escolher a seção primeiro e o trecho dentro dela depois.

Serve para nome de pessoa?

Só se você tiver de onde tirar os candidatos. O cookbook é explícito: e-mail, telefone e valor têm expressão regular que os cobre; nome não tem, então os candidatos precisam vir de um cadastro que você já tem, de um reconhecedor de entidades ou de um modelo generativo.

Quer dominar decisões com IA em português? O curso da comunidade está em pré-venda.

Garantir pré-venda por R$ 499,00

Pré-venda: R$ 499,00 · Após o lançamento: R$ 799,00