Choice
Um Choice é um tipo de pergunta do System One para selecionar uma opção de um conjunto definido. A resposta inclui a opção escolhida, uma probabilidade para cada opção e a confiança.
Use um Choice quando a resposta é uma de um conjunto fixo de opções. Por exemplo, qual equipe cuida de um ticket, a qual categoria um produto pertence ou em qual linguagem um trecho de código está escrito. Se a resposta é uma posição num espectro, use um Score. Se for sim ou não, use um Noul. Escolha um tipo de pergunta compara os três.
Uma resposta de Choice é a opção escolhida em choice. O modelo também retorna uma probabilidade para cada opção em probabilities, e um valor de confidence para a opção escolhida.
Perguntas de exemplo:
"What programming language is this code written in"
→ options: python, javascript, typescript, go, rust, other
"What type of meeting is this based on the title and description"
→ options: standup, planning, retrospective, one on one, brainstorm, none of the above
"Which product category does this item belong to"
→ options: electronics, clothing, home garden, food and beverage
Estrutura da requisição
O corpo da requisição POST para a API da TypeSafe tem uma estrutura específica. O nível superior tem três campos: state, o conteúdo a avaliar; model; e questions, um mapa dos ids de pergunta que você escolhe para objetos de pergunta. Cada pergunta Choice tem os seguintes campos:
type: Sempre"choice".instructions: A pergunta que o modelo responde.criteria: As opções de resposta, como um mapa. Cada chave é um nome de opção e cada valor é uma descrição dessa opção.
Abaixo está uma requisição em que o estado é um ticket de suporte de uma loja de calçados online e a pergunta é qual equipe deve cuidar dele:
{
"state": "My running shoes arrived in the wrong size. Can I swap them for a size 10?",
"questions": {
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"returns": "Exchanges, wrong or damaged items",
"shipping": "Delivery status, delays, lost packages",
"billing": "Charges, invoices, payment problems"
}
}
}
}Você escolhe o id da pergunta, department neste caso. A resposta é retornada sob o mesmo id. O modelo nunca vê o id da pergunta. Os nomes das opções e suas descrições são ambos enviados ao modelo, então escreva descrições que separem as opções entre si.
Nossos SDKs de cliente fornecem perguntas tipadas. Em Python, a mesma pergunta é um Choice:
from typesafe_sdk import Choice, TypeSafeClient
with TypeSafeClient() as client:
response = client.system_one(
state="My running shoes arrived in the wrong size. Can I swap them for a size 10?",
questions={
"department": Choice(
instructions="Which team should handle this?",
criteria={
"returns": "Exchanges, wrong or damaged items",
"shipping": "Delivery status, delays, lost packages",
"billing": "Charges, invoices, payment problems",
},
),
},
)
print(response.answers["department"].choice)
Use o método system_one ou o endpoint https://api.typesafe.ai/v1/systemone para chamar um modelo System One. O campo model seleciona qual modelo cuida da requisição. Como construir com TypeSafe cobre onde chamá-lo no seu código.
Use um dos nossos SDKs de cliente ou chame a API HTTP diretamente. Se um agente de código estiver escrevendo a integração para você, 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. Esta é a resposta para a requisição de exemplo acima:
{
"model": "jev-1.13.0",
"answers": {
"department": {
"type": "choice",
"choice": "returns",
"confidence": 1.0,
"probabilities": {
"shipping": 0.0,
"returns": 1.0,
"billing": 0.0
}
}
},
"usage": {
"input_tokens": 328,
"output_tokens": 34
}
}
Além de type, cada resposta de Choice tem três valores:
choice: A opção com a maior probabilidade.probabilities: A distribuição de probabilidade completa em todas as opções. A soma de todos os valores é 1.confidence: Um número de 0 a 1 calculado a partir de comoprobabilitiesestá distribuída. Uma forma achatada, com a probabilidade espalhada por várias opções, significa confiança baixa. Um único pico numa opção significa confiança alta.
Este ticket é fácil, então toda a probabilidade está em returns e a confiança é 1.0. Um ticket que mencionasse um tamanho errado e um reembolso ausente dividiria a probabilidade entre returns e billing, e a confiança cairia.
Boa prática: faça mais de uma pergunta por chamada
Faça todas as perguntas Choice que seu código possa precisar numa única requisição, em vez de uma requisição por pergunta. As perguntas são avaliadas em paralelo. Adicionar perguntas quase não muda o tempo de resposta, e o código pode ignorar as respostas de que não precisa. Perguntas extras ainda custam tokens. Faça várias perguntas juntas explica isso por completo; a próxima seção mostra cinco perguntas Choice numa única chamada.
A mesma lógica se aplica às opções dentro de uma única pergunta Choice. Uma pergunta Choice aceita até 255 opções, e adicionar opções custa alguns tokens cada, então dê ao modelo a lista completa de equipes, categorias ou produtos, em vez de uma lista reduzida. Adicione uma opção other ou none of the above quando a lista puder não cobrir todas as entradas, para que o modelo possa dizer que nenhuma das outras serve.
Para classificar documentos numa hierarquia profunda ou taxonomia grande, encadeie perguntas Choice nível por nível. O cookbook de classificação hierárquica mostra como executar uma busca em feixe sobre as probabilidades de Choice, mantendo os melhores K caminhos candidatos em cada nível, em vez de se comprometer com um único caminho guloso.
Um exemplo mais complexo
O exemplo básico acima encaminha um ticket para uma equipe. Um sistema de suporte maior pode precisar também do motivo da devolução, do problema de entrega, do que o cliente quer e do tom do cliente.
A requisição abaixo faz cinco perguntas Choice sobre um ticket mais ambíguo que o primeiro: ele envolve três equipes e não diz o que o cliente quer.
{
"state": "Shoes arrived two weeks late and in the wrong size. Also I see two charges of $120 on my card. What are you going to do about this?",
"questions": {
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"returns": "Exchanges, wrong or damaged items",
"shipping": "Delivery status, delays, lost packages",
"billing": "Charges, invoices, payment problems"
}
},
"return_reason": {
"type": "choice",
"instructions": "If the customer wants to return something, why?",
"criteria": {
"wrong_size": "The item doesn't fit",
"wrong_item": "A different product was delivered",
"damaged": "The item arrived broken or faulty",
"changed_mind": "The item is fine, the customer no longer wants it",
"other": "A return reason that fits none of the above"
}
},
"shipping_issue": {
"type": "choice",
"instructions": "If this is a shipping problem, which kind is it?",
"criteria": {
"not_delivered": "The package never arrived",
"delayed": "The package is late but still on its way",
"wrong_address": "The package went to the wrong place",
"damaged_in_transit": "The package arrived damaged",
"other": "A shipping problem that fits none of the above"
}
},
"requested_resolution": {
"type": "choice",
"instructions": "What does the customer want to happen?",
"criteria": {
"exchange": "Swap the item for a different one",
"refund": "Money back",
"replacement": "The same item sent again",
"information": "Just an answer, no action needed"
}
},
"tone": {
"type": "choice",
"instructions": "What is the customer's tone?",
"criteria": {
"calm": null,
"frustrated": null,
"angry": null
}
}
}
}Duas dessas perguntas Choice são especulativas: return_reason só importa se o department for returns, e shipping_issue só importa se for shipping. A pergunta tone usa descrições null porque os nomes das opções já são claros por si sós.
A resposta da TypeSafe:
{
"model": "jev-1.13.0",
"answers": {
"department": {
"type": "choice",
"choice": "returns",
"confidence": 0.42,
"probabilities": {
"shipping": 0.04,
"billing": 0.35,
"returns": 0.61
}
},
"return_reason": {
"type": "choice",
"choice": "wrong_size",
"confidence": 1.0,
"probabilities": {
"other": 0.0,
"wrong_size": 1.0,
"changed_mind": 0.0,
"damaged": 0.0,
"wrong_item": 0.0
}
},
"shipping_issue": {
"type": "choice",
"choice": "delayed",
"confidence": 0.67,
"probabilities": {
"wrong_address": 0.0,
"other": 0.26,
"not_delivered": 0.0,
"damaged_in_transit": 0.0,
"delayed": 0.74
}
},
"requested_resolution": {
"type": "choice",
"choice": "refund",
"confidence": 0.2,
"probabilities": {
"replacement": 0.34,
"refund": 0.4,
"information": 0.02,
"exchange": 0.24
}
},
"tone": {
"type": "choice",
"choice": "frustrated",
"confidence": 0.76,
"probabilities": {
"frustrated": 0.84,
"angry": 0.16,
"calm": 0.0
}
}
},
"usage": {
"input_tokens": 589,
"output_tokens": 212
}
}
Cada pergunta é respondida por conta própria em relação ao ticket:
- A resposta de
departmentéreturnscom probabilidade de 0.61, masbillingtem 0.35 por causa da cobrança dupla. O ticket pertence a duas equipes, e a confiança dividida de 0.42 reflete isso. - O
return_reasonéwrong_sizecom confiança de 1.0, o que é esperado porque o ticket diz isso claramente. - A resposta de
shipping_issueestá dividida entredelayedeother. É uma pergunta especulativa e odepartmentnão voltou como shipping, então o código pode ignorá-la, como mostra o trecho de código de exemplo abaixo. - A resposta de
requested_resolutionpende pararefund, com 0.40, enquantoreplacementeexchangedividem a maior parte do resto, e a confiança é 0.20. A cobrança dupla sugere dinheiro de volta, o tamanho errado sugere uma troca, e o cliente nunca diz qual dos dois quer. - A resposta de
toneéfrustrated, com probabilidade de 0.84 e confiança de 0.76.
O código de exemplo abaixo lê as respostas de que precisa, ignora o resto e trata uma resposta de confiança baixa como motivo para perguntar, em vez de agir:
from typesafe_sdk import Choice, TypeSafeClient
TRIAGE_QUESTIONS = {
"department": Choice(
instructions="Which team should handle this?",
criteria={
"returns": "Exchanges, wrong or damaged items",
"shipping": "Delivery status, delays, lost packages",
"billing": "Charges, invoices, payment problems",
},
),
"return_reason": Choice(
instructions="If the customer wants to return something, why?",
criteria={
"wrong_size": "The item doesn't fit",
"wrong_item": "A different product was delivered",
"damaged": "The item arrived broken or faulty",
"changed_mind": "The item is fine, the customer no longer wants it",
"other": "A return reason that fits none of the above",
},
),
"shipping_issue": Choice(
instructions="If this is a shipping problem, which kind is it?",
criteria={
"not_delivered": "The package never arrived",
"delayed": "The package is late but still on its way",
"wrong_address": "The package went to the wrong place",
"damaged_in_transit": "The package arrived damaged",
"other": "A shipping problem that fits none of the above",
},
),
"requested_resolution": Choice(
instructions="What does the customer want to happen?",
criteria={
"exchange": "Swap the item for a different one",
"refund": "Money back",
"replacement": "The same item sent again",
"information": "Just an answer, no action needed",
},
),
"tone": Choice(
instructions="What is the customer's tone?",
criteria={"calm": None, "frustrated": None, "angry": None},
),
}
def triage(ticket: str) -> None:
with TypeSafeClient() as client:
response = client.system_one(
state=ticket,
questions=TRIAGE_QUESTIONS,
)
answers = response.answers
department = answers["department"]
if department.confidence < 0.3:
# Not clear which team to send to. Let a person decide.
send_to_manual_triage(ticket)
return
if department.choice == "returns":
# return_reason answer is only used here
assign(ticket, team="returns", issue=answers["return_reason"].choice)
elif department.choice == "shipping":
# shipping_issue answer is only used here
assign(ticket, team="shipping", issue=answers["shipping_issue"].choice)
else:
assign(ticket, team="billing")
# A second team with a real share of the probability gets a copy
for team, probability in department.probabilities.items():
if team != department.choice and probability > 0.25:
notify(ticket, team=team)
resolution = answers["requested_resolution"]
if resolution.confidence < 0.5:
# The customer hasn't said what they want. Ask, don't guess.
ask_customer_what_they_want(ticket)
elif resolution.choice == "refund":
flag_for_refund_approval(ticket)
if answers["tone"].choice == "angry":
flag_for_senior_agent(ticket)
Para o ticket acima, isso atribui o ticket à equipe de devoluções com o problema wrong_size, envia uma cópia à equipe de cobrança porque sua fatia de 0.35 está acima do limiar de 0.25, e pergunta ao cliente o que ele quer porque a confiança da resolução de 0.20 está abaixo de 0.5. O código não usa a resposta de shipping_issue.
Uma requisição, cinco respostas, e a lógica de roteamento são comandos if comuns. Se mais tarde você precisar saber o idioma do cliente, ou sobre qual produto é o ticket, adicione outra pergunta Choice a TRIAGE_QUESTIONS; a contagem de requisições continua em uma.
A demo do assistente de casa inteligente avalia cada solicitação do usuário em relação a uma longa lista de perguntas Choice numa única chamada: a categoria da solicitação, o ambiente, o dispositivo e a ação. A maioria dessas perguntas é irrelevante para qualquer solicitação e o código as ignora.
Instruções e criteria estruturados
Comece com uma descrição de uma linha por opção. Quando duas opções são parecidas e o modelo continua confundindo-as, descreva cada uma com um objeto em vez de uma string. Dê a ele campos para o que a opção cobre, o que pertence a uma opção vizinha em vez dela, e alguns exemplos de entrada.
As duas opções de resposta abaixo, return_policy e return_status, são fáceis de confundir. Um ticket sobre qualquer uma delas pode mencionar devoluções e reembolsos, então cada opção diz para que não serve.
{
"state": "I sent the shoes back a week ago. When do I get my money?",
"questions": {
"return_topic": {
"type": "choice",
"instructions": {
"question": "Which returns topic is the customer asking about?",
"focus": "Classify the information the customer wants."
},
"criteria": {
"return_policy": {
"what": "Whether and how an item can be returned",
"not_for": "Progress of a return already sent",
"examples": [
"Can I return shoes I've worn once?",
"How long do I have to return an order?"
]
},
"return_status": {
"what": "Progress of a return already sent",
"not_for": "Whether and how an item can be returned",
"examples": [
"Has my return arrived yet?",
"When will my refund be paid?"
]
}
}
}
}
}A resposta é return_status com confiança 1.0:
{
"model": "jev-1.13.0",
"answers": {
"return_topic": {
"type": "choice",
"choice": "return_status",
"confidence": 1.0,
"probabilities": {
"return_policy": 0.0,
"return_status": 1.0
}
}
},
"usage": {
"input_tokens": 407,
"output_tokens": 32
}
}
Os nomes de campo question, focus, what, not_for e examples não fazem parte da API e nenhum é reservado. Você os escolhe, do mesmo modo que escolhe os nomes das opções. O modelo vê os nomes junto com os valores, então use nomes curtos que rotulem o que vem a seguir.