Noul
Uma pergunta Noul pede ao modelo da TypeSafe que avalie uma pergunta de sim/não e retorne a probabilidade de a resposta ser sim.
Use um Noul quando a resposta é sim ou não. Por exemplo, se esta mensagem pede um reembolso, se este currículo menciona sistemas distribuídos, se este comentário contém dados pessoais. Se a resposta é uma entre várias opções, use um Choice. Se for uma posição num espectro, use um Score. Escolha um tipo de pergunta compara os três.
Uma resposta de Noul é um único número que representa a probabilidade de a resposta ser sim, em que 0 significa não e 1 significa sim.
Estrutura da requisição
O corpo da requisição POST para a API da TypeSafe tem os mesmos três campos de nível superior que qualquer outro tipo de pergunta: state, que é o conteúdo a avaliar; model; e questions. Cada pergunta Noul tem os seguintes campos:
type: Sempre"noul".instructions: A pergunta de sim/não que o modelo responde, ou uma afirmação para ele julgar.criteria: Opcional. Um objeto com descriçõestrueefalsedo que um sim e um não significam.
Abaixo está uma requisição em que o estado é uma mensagem de suporte e as duas perguntas são se o cliente quer uma pessoa e se ele já contatou o suporte antes:
{
"state": "I have asked three times now. Can I please just talk to a real person?",
"questions": {
"is_human_escalation": {
"type": "noul",
"instructions": "Is the customer asking for a human agent?"
},
"is_repeat_contact": {
"type": "noul",
"instructions": "Has the customer contacted support about this before?",
"criteria": {
"true": "Mentions a prior attempt, ticket, or that they have asked before",
"false": "No sign of any previous contact"
}
}
}
}Você escolhe os ids das perguntas, is_human_escalation e is_repeat_contact aqui. Os ids não são enviados ao modelo. Cada resposta é retornada sob o mesmo id. A primeira pergunta depende só de instructions. A segunda acrescenta criteria para dizer o que conta como sim e o que conta como não.
Com o SDK de Python, as mesmas perguntas são objetos Noul:
from typesafe_sdk import Noul, NoulCriteria, TypeSafeClient
with TypeSafeClient() as client:
response = client.system_one(
model="jev-latest",
state="I have asked three times now. Can I please just talk to a real person?",
questions={
"is_human_escalation": Noul(
instructions="Is the customer asking for a human agent?",
),
"is_repeat_contact": Noul(
instructions="Has the customer contacted support about this before?",
criteria=NoulCriteria(
true="Mentions a prior attempt, ticket, or that they have asked before",
false="No sign of any previous contact",
),
),
},
)
print(response.answers["is_human_escalation"].noul)
print(response.answers["is_repeat_contact"].noul)
O método system_one e o endpoint https://api.typesafe.ai/v1/systemone têm ambos o nome de System One, o modelo de IA da TypeSafe. Como construir com TypeSafe cobre onde usá-lo no seu código.
Se você estiver usando um agente de código, instale antes a skill de agente da TypeSafe para que ele conheça os formatos de requisição e resposta.
Estrutura da resposta
A resposta tem uma entrada em answers por pergunta, sob os ids da requisição:
{
"model": "jev-1.13.0",
"answers": {
"is_human_escalation": {
"type": "noul",
"noul": 0.99
},
"is_repeat_contact": {
"type": "noul",
"noul": 0.93
}
},
"usage": {
"input_tokens": 360,
"output_tokens": 39
}
}
Ambas as respostas aqui estão próximas de 1. O cliente diz “talk to a real person”, então is_human_escalation é 0.99. “I have asked three times now” corresponde à descrição true de is_repeat_contact, então é 0.93.
Ler um Noul
O número é a resposta e a certeza num só. Um valor perto de 1 é um sim forte. Um valor perto de 0 é um não forte. Um valor perto de 0.5 significa que o modelo dá probabilidades parecidas a sim e a não.
A tabela abaixo mostra respostas registradas do jev-1.13.0 para a pergunta is_human_escalation em diferentes mensagens de clientes:
| Estado | noul |
|---|---|
| Obrigado, isso resolveu! | 0.02 |
| Como faço para redefinir minha senha? | 0.07 |
| Preciso que isso seja resolvido hoje, custe o que custar. | 0.26 |
| Você é um bot? | 0.40 |
| Existe alguma forma de falar com alguém sobre minha fatura? | 0.84 |
| Já pedi três vezes. Posso falar com uma pessoa de verdade, por favor? | 0.99 |
As duas primeiras e as duas últimas estão claras. “Preciso que isso seja resolvido hoje” é urgente mas nunca pede uma pessoa, e recebe 0.26. “Você é um bot?” sugere que se quer um humano sem pedir um, e o modelo se divide quase meio a meio, em 0.40. Ambas são o tipo de mensagem em que a decisão precisa ser tomada com base num limiar no seu código.
Não existe um valor de confidence separado para um Noul, ao contrário de um Choice ou um Score. A distribuição de probabilidade de um Noul tem só dois resultados, sim e não, então o único valor noul a descreve por completo. Um Choice ou um Score espalha a probabilidade por várias opções ou níveis, e confidence resume essa distribuição.
Na maioria das vezes, seu código converte noul num booleano por meio de um limiar:
wants_human = response.answers["is_human_escalation"].noul > 0.9
if wants_human:
route_to_agent(ticket)
else:
route_to_bot(ticket)
Onde definir o limiar depende do custo de errar. Use 0.5 quando agir sobre um sim ou sobre um não for igualmente fácil. Aumente-o quando agir sobre um falso sim for caro, como acionar alguém ou emitir um reembolso. Diminua-o quando deixar passar um sim verdadeiro for caro, como não sinalizar um problema de segurança. Valores intermediários podem ir para uma pessoa em vez de para qualquer um dos caminhos de código. É a mesma divisão em três caminhos que a página de Confiança descreve para respostas de Choice e Score.
Um valor de Noul vai de 0 a 1, mas não é uma escala da coisa sobre a qual você perguntou. É a probabilidade de a resposta ser sim. Se a pergunta trata na verdade de um grau, o valor não mede o grau. Abaixo, “O candidato é forte em Python?” é perguntado sobre quatro candidatos, ao lado de um Score com quatro níveis: sem experiência, alguma familiaridade, uso regular no trabalho, experiência profunda.
| Candidato | Noul: “O candidato é forte em Python?” | Score: “Quanta experiência o candidato tem com Python?” |
|---|---|---|
| Minha experiência é em Java e Go. Não usei Python. | 0.03 | 0.0 (Sem experiência) |
| Usei Python ocasionalmente para pequenos scripts junto ao meu trabalho principal em Java. | 0.14 | 1.0 (Alguma familiaridade) |
| Usei Python todos os dias por dois anos no meu último emprego, principalmente pipelines de dados. | 0.81 | 2.05 (Uso regular no trabalho) |
| Escrevi Python diariamente por oito anos, incluindo a manutenção de uma grande base de código Django. | 0.92 | 2.89 (Experiência profunda) |
O Noul julga uma única proposição, “forte”, e os valores são a probabilidade de isso ser verdade. Você poderia criar níveis no intervalo de 0 a 1 no seu código, como 0.3 a 0.7 para “alguma experiência”, mas o modelo não os verá, então nada na resposta foi julgado em relação a eles. Um valor intermediário pode significar experiência média ou um caso pouco claro, e o espaçamento entre candidatos não é algo que você escolheu. O Score julga cada descrição de nível por si só, então cada candidato caiu num nível que você escreveu ou perto dele, e as probabilidades retornadas mostram como o modelo dividiu seu julgamento entre os níveis. Se você discordar, reformule um nível e execute de novo. Escolha um tipo de pergunta explica a distinção.
Escrever uma pergunta Noul
Faça uma pergunta de sim/não por Noul. Se uma pergunta tem duas condições, como “O cliente está irritado e pedindo um reembolso?”, o modelo precisa julgar as duas ao mesmo tempo e o valor significa menos. Faça dois Nouls e combine-os no código.
Formule a pergunta de modo que um valor alto signifique sim. “A mensagem contém dados pessoais?” é claro. “A mensagem está livre de dados pessoais?” inverte o significado, e o código que a ler depois vai entendê-la ao contrário.
Uma afirmação funciona tão bem quanto uma pergunta. Para “O cliente está pedindo um reembolso”, um valor perto de 1 significa que a afirmação é verdadeira. Teste as duas formulações com seus próprios dados para ver qual funciona melhor.
Torne o limite entre sim e não inequívoco. “Este candidato tem alguma experiência com Python?” funciona bem porque “alguma” não deixa meio-termo. Quando o limite for sutil, adicione criteria com descrições true e false, como faz a pergunta is_repeat_contact acima. A instrução basta para a maioria dos Nouls, então teste suas perguntas com e sem criteria e fique com a que der melhores respostas nos seus documentos.
Boa prática: faça mais de uma pergunta por chamada
Para uma lista de verificação de condições, faça muitas perguntas Noul numa única requisição: uma pergunta por condição, e o código decide o que a combinação significa. As perguntas são avaliadas em paralelo, então adicionar Nouls quase não muda o tempo de resposta. Faça várias perguntas juntas explica isso com mais detalhe.
Processar várias respostas de Noul no código
A requisição de duas perguntas acima dá ao código o suficiente para encaminhar a mensagem. O exemplo abaixo escala para uma pessoa quando o cliente pede uma, e aumenta a prioridade quando ele já entrou em contato antes. Um valor intermediário em qualquer uma das duas perguntas vai para um revisor, em vez de para um caminho de código:
from typesafe_sdk import Noul, NoulCriteria, TypeSafeClient
SUPPORT_QUESTIONS = {
"is_human_escalation": Noul(
instructions="Is the customer asking for a human agent?",
),
"is_repeat_contact": Noul(
instructions="Has the customer contacted support about this before?",
criteria=NoulCriteria(
true="Mentions a prior attempt, ticket, or that they have asked before",
false="No sign of any previous contact",
),
),
}
YES = 0.8
NO = 0.2
def route(message: str) -> None:
with TypeSafeClient() as client:
response = client.system_one(
model="jev-latest",
state=message,
questions=SUPPORT_QUESTIONS,
)
answers = response.answers
wants_human = answers["is_human_escalation"].noul
repeat = answers["is_repeat_contact"].noul
if NO < wants_human < YES or NO < repeat < YES:
# The model isn't sure either way. Let a person decide.
send_to_review(message)
return
priority = "high" if repeat > YES else "normal"
if wants_human > YES:
route_to_agent(message, priority=priority)
else:
route_to_bot(message, priority=priority)
Para a mensagem acima, o valor da resposta noul de is_human_escalation é 0.99 e o de is_repeat_contact é 0.93, então o código a encaminha para um agente com prioridade alta. A mensagem “Como faço para redefinir minha senha?” dá 0.07 nas duas perguntas e é encaminhada para o bot.
Os limiares ficam no seu código. Se os revisores virem mensagens demais, estreite a diferença entre NO e YES. Se passarem rotas erradas demais, amplie-a. Se mais tarde você precisar saber se a mensagem menciona um pagamento, ou se contém dados pessoais, adicione outro Noul a SUPPORT_QUESTIONS. A contagem de requisições continua em uma.
Instruções estruturadas
As instruções podem ser um objeto em vez de uma string, com a pergunta num campo e dados complementares nos outros. Use estrutura nas perguntas cobre quando isso ajuda. Aqui ela é usada para uma pergunta construída com código: um currículo que acabou de chegar é comparado com registros de um banco de dados de candidatos que podem ser a mesma pessoa. Cada registro vai para um campo potential_duplicate como está, a question é a mesma para todos os registros, e todos os registros são verificados numa única requisição. As chaves de pergunta geradas por código contêm o ID de banco de dados de cada registro:
{
"state": {
"resume": {
"name": "John Smith",
"location": "Oakland, CA",
"summary": "Backend engineer with eight years of Python and Go experience.",
"experience": [
{
"employer": "Google",
"title": "Senior Backend Engineer",
"years": "2021-2025"
},
{
"employer": "Microsoft",
"title": "Software Engineer",
"years": "2017-2021"
}
]
}
},
"questions": {
"same_as_record_18": {
"type": "noul",
"instructions": {
"potential_duplicate": {
"name": "Jon Smith",
"location": "Oakland, CA",
"last_employer": "Google"
},
"question": "Is the resume for the same person as `potential_duplicate`?"
}
},
"same_as_record_42": {
"type": "noul",
"instructions": {
"potential_duplicate": {
"name": "John Smith",
"location": "Austin, TX",
"last_employer": "Lone Star Freight"
},
"question": "Is the resume for the same person as `potential_duplicate`?"
}
},
"same_as_record_77": {
"type": "noul",
"instructions": {
"potential_duplicate": {
"name": "John Smithers",
"location": "Oakland, CA",
"last_employer": "Bay Health Clinic"
},
"question": "Is the resume for the same person as `potential_duplicate`?"
}
}
}
}The response:
{
"model": "jev-1.13.0",
"answers": {
"same_as_record_18": {
"type": "noul",
"noul": 0.74
},
"same_as_record_42": {
"type": "noul",
"noul": 0.09
},
"same_as_record_77": {
"type": "noul",
"noul": 0.08
}
},
"usage": {
"input_tokens": 535,
"output_tokens": 58
}
}
Cada resposta é a probabilidade de o currículo ser da pessoa daquele registro. O registro 18 escreve o nome de outra forma, mas coincide em localização e empregador, e recebe 0.74. O registro 42 tem o mesmo nome em outra cidade com outro empregador, e recebe 0.09. O registro 77 é um nome parecido na mesma localização com outro empregador, e recebe 0.08. Aplique um limiar a cada valor no seu código, como em Processar várias respostas de Noul no código, e envie os valores intermediários para uma pessoa.
Com o SDK de Python, as perguntas são construídas a partir dos registros de candidatos. O texto da pergunta é fixo e o registro muda:
from typesafe_sdk import Noul, TypeSafeClient
SAME_PERSON = "Is the resume for the same person as `potential_duplicate`?"
def duplicate_questions(candidates: list[dict]) -> dict[str, Noul]:
"""One Noul per candidate record, all asking the same question."""
return {
f"same_as_record_{candidate['id']}": Noul(
instructions={
"potential_duplicate": {
"name": candidate["name"],
"location": candidate["location"],
"last_employer": candidate["last_employer"],
},
"question": SAME_PERSON,
},
)
for candidate in candidates
}
def find_duplicates(resume: dict, candidates: list[dict]) -> list[str]:
with TypeSafeClient() as client:
response = client.system_one(
model="jev-latest",
state={"resume": resume},
questions=duplicate_questions(candidates),
)
return [
question_id
for question_id, answer in response.answers.items()
if answer.noul > 0.7
]
O cookbook de cascata de extração de dados estruturados usa instruções estruturadas para verificar um registro extraído. Cada campo recebe o mesmo conjunto de perguntas. O objeto instructions de cada pergunta tem o texto da pergunta na propriedade main_question. Também há as propriedades field_spec e extracted_field, que mudam para cada campo.
Noul nos cookbooks
Dê uma olhada nos nossos cookbooks para ver aplicações que usam perguntas Noul:
- Perguntas em paralelo executa uma lista de verificação regulatória de 13 perguntas sobre um artigo numa única requisição.
- Autoconsistência: nouls pontua uma reclamação de seguro segundo uma rubrica de 15 perguntas e mede quão estáveis os valores são entre execuções.
- Reordenação usa a própria probabilidade, não um limiar: um Noul por par consulta-candidato, e depois ordena os candidatos pelo valor.
- Busca linha por linha combina um Choice que encontra a linha correspondente com um Noul que verifica se o documento contém alguma resposta.
- Recuperação de estrutura faz um Noul por par de linhas, perguntando se uma quebra de linha dividiu uma frase, para reconstruir parágrafos a partir de texto simples.