Choice
Choice는 정의된 집합에서 하나의 선택지를 고르기 위한 System One 질문 유형입니다. 답변에는 선택된 선택지, 선택지별 확률, 그리고 신뢰도가 포함됩니다.
답변이 고정된 선택지 집합 중 하나일 때 Choice를 사용하십시오. 예를 들어 어느 팀이 티켓을 처리하는지, 상품이 어느 범주에 속하는지, 코드 조각이 어느 언어로 작성되었는지 등입니다. 답변이 스펙트럼 위의 위치라면 Score를 사용하십시오. yes 또는 no라면 Noul을 사용하십시오. 질문 유형 선택하기에서 세 가지를 모두 비교합니다.
Choice 답변은 choice에 담긴 선택된 선택지입니다. 모델은 또한 probabilities에 모든 선택지의 확률을, 선택된 선택지에 대한 confidence 값을 반환합니다.
예시 질문:
"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
요청 구조
TypeSafe API로 보내는 POST 요청 본문은 특정한 구조를 가집니다. 최상위에는 세 개의 필드가 있습니다. 평가할 콘텐츠인 state, model, 그리고 여러분이 고른 질문 id에서 질문 객체로 가는 맵인 questions입니다. 각 Choice 질문에는 다음 필드가 있습니다.
type: 항상"choice"입니다.instructions: 모델이 답하는 질문입니다.criteria: 답변 선택지이며 맵으로 주어집니다. 각 키는 선택지 이름이고 각 값은 그 선택지에 대한 설명입니다.
아래는 상태가 온라인 신발 가게의 지원 티켓이고, 질문은 어느 팀이 처리해야 하는지인 요청입니다.
{
"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"
}
}
}
}질문 id는 여러분이 고르며, 여기서는 department입니다. 답변은 같은 id 아래에 반환됩니다. 모델은 질문 id를 보지 못합니다. 선택지 이름과 그 설명이 모두 모델에 전송되므로, 선택지들을 서로 구분해 주는 설명을 쓰십시오.
저희 클라이언트 SDK는 타입이 지정된 질문을 제공합니다. Python에서 같은 질문은 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)
System One 모델을 호출하려면 system_one 메서드 또는 https://api.typesafe.ai/v1/systemone 엔드포인트를 사용하십시오. model 필드가 어느 모델이 요청을 처리할지 선택합니다. 코드의 어디에서 호출할지는 TypeSafe로 구축하는 방법에서 다룹니다.
저희 클라이언트 SDK 중 하나를 사용하거나 HTTP API를 직접 호출하십시오. 코딩 에이전트가 통합을 대신 작성한다면, 요청과 응답 형태를 알 수 있도록 먼저 TypeSafe 에이전트 스킬을 설치하십시오.
응답 구조
응답에는 요청의 id 아래에 질문당 answers 항목이 하나씩 있습니다. 다음은 위 예시 요청에 대한 응답입니다.
{
"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
}
}
type 외에도 각 Choice 답변에는 세 개의 값이 있습니다.
choice: 확률이 가장 높은 선택지입니다.probabilities: 모든 선택지에 걸친 전체 확률 분포입니다. 모든 값의 합은 1입니다.confidence:probabilities가 얼마나 퍼져 있는지로 계산된 0에서 1 사이의 숫자입니다. 여러 선택지에 확률이 퍼진 평평한 모양은 낮은 신뢰도를 뜻합니다. 한 선택지에 단일 봉우리가 있으면 높은 신뢰도를 뜻합니다.
이 티켓은 쉬운 편이라 모든 확률이 returns에 쏠려 있고 신뢰도가 1.0입니다. 잘못된 사이즈와 누락된 환불을 함께 언급한 티켓이라면 확률이 returns와 billing 사이로 갈라지고 신뢰도가 떨어질 것입니다.
모범 사례: 호출당 둘 이상의 질문을 하십시오
코드가 필요로 할 수 있는 모든 Choice 질문을 질문당 하나의 요청이 아니라 하나의 요청에 담아 질문하십시오. 질문은 병렬로 평가됩니다. 질문을 추가해도 응답 시간은 거의 변하지 않고, 코드는 필요 없는 답변을 무시할 수 있습니다. 추가 질문은 여전히 토큰을 소모합니다. 여러 질문을 함께 하기에서 자세히 설명하며, 다음 절에서는 다섯 개의 Choice 질문을 하나의 호출로 보여줍니다.
같은 논리가 하나의 Choice 질문 안의 선택지에도 적용됩니다. Choice 질문은 최대 255개의 선택지를 받으며, 선택지를 추가하면 각각 몇 개의 토큰이 들므로, 짧은 후보 목록이 아니라 팀, 범주 또는 상품의 전체 목록을 모델에 주십시오. 목록이 모든 입력을 포괄하지 못할 수 있으면 other 또는 none of the above 선택지를 추가하여, 모델이 다른 어느 것도 맞지 않는다고 말할 수 있게 하십시오.
깊은 계층이나 큰 분류 체계를 통해 문서를 분류하려면 Choice 질문을 레벨별로 연결하십시오. 계층적 분류 쿡북은 단일 탐욕 경로에 확정하는 대신 각 레벨에서 최선의 K 후보 경로를 유지하며 Choice 확률에 대한 빔 서치를 실행하는 방법을 보여줍니다.
더 복잡한 예시
위의 기본 예시는 티켓을 팀으로 라우팅합니다. 더 큰 지원 시스템은 반품 사유, 배송 문제, 고객이 원하는 것, 고객의 어조도 필요할 수 있습니다.
아래 요청은 첫 번째보다 더 모호한 티켓에 대해 다섯 개의 Choice 질문을 합니다. 세 팀이 관련되어 있고 고객이 무엇을 원하는지 말하지 않습니다.
{
"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
}
}
}
}이 Choice 질문 중 두 개는 투기적입니다. return_reason은 department가 returns일 때만 중요하고, shipping_issue는 shipping일 때만 중요합니다. tone 질문은 선택지 이름만으로 명확하므로 null 설명을 사용합니다.
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
}
}
각 질문은 티켓에 대해 독립적으로 답변됩니다.
department답변은 확률 0.61로returns이지만, 이중 청구 때문에billing이 0.35입니다. 이 티켓은 두 팀에 걸쳐 있고, 갈라진 신뢰도 0.42가 이를 반영합니다.return_reason은 신뢰도 1.0으로wrong_size인데, 티켓에 이것이 명확히 나와 있으므로 예상된 결과입니다.shipping_issue답변은delayed와other로 갈라집니다. 투기적 질문이고department가 shipping으로 돌아오지 않았으므로, 아래 예시 코드 조각처럼 코드에서 무시할 수 있습니다.requested_resolution답변은refund로 0.40 기울며,replacement와exchange가 나머지의 대부분을 나눠 가지고, 신뢰도는 0.20입니다. 이중 청구는 환불을, 잘못된 사이즈는 교환을 시사하는데, 고객은 어느 쪽을 원하는지 말하지 않습니다.tone답변은 확률 0.84, 신뢰도 0.76으로frustrated입니다.
아래 예시 코드는 필요한 답변을 읽고, 나머지는 무시하고, 낮은 신뢰도의 답변을 행동이 아니라 질문을 해야 할 이유로 취급합니다.
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)
위 티켓에 대해 이 코드는 티켓을 wrong_size 이슈로 반품 팀에 배정하고, 0.35 비중이 0.25 임계값을 넘으므로 청구 팀에 사본을 보내고, 해결책 신뢰도 0.20이 0.5 미만이므로 고객에게 원하는 바를 묻습니다. 코드는 shipping_issue 답변을 사용하지 않습니다.
하나의 요청, 다섯 개의 답변, 그리고 라우팅 로직은 평범한 if 문입니다. 나중에 고객의 언어나 티켓이 어느 상품에 관한 것인지 알아야 하면 TRIAGE_QUESTIONS에 Choice 질문을 하나 더 추가하십시오. 요청 횟수는 하나로 유지됩니다.
스마트 홈 어시스턴트 데모는 모든 사용자 요청을 하나의 호출에서 긴 Choice 질문 목록에 대해 평가합니다. 요청 범주, 방, 기기, 동작입니다. 그 질문들 대부분은 어느 한 요청에도 관련이 없고 코드는 이를 무시합니다.
구조화된 instructions와 criteria
선택지당 한 줄 설명으로 시작하십시오. 두 선택지가 비슷하고 모델이 계속 혼동한다면, 각각을 문자열 대신 객체로 설명하십시오. 그 선택지가 무엇을 다루는지, 무엇이 대신 이웃 선택지에 속하는지, 그리고 몇 가지 예시 입력에 대한 필드를 주십시오.
아래 두 답변 선택지 return_policy와 return_status는 혼동하기 쉽습니다. 어느 쪽에 관한 티켓이든 반품과 환불을 언급할 수 있으므로, 각 선택지는 자신이 무엇을 위한 것이 아닌지 말합니다.
{
"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?"
]
}
}
}
}
}응답은 신뢰도 1.0의 return_status입니다.
{
"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
}
}
필드 이름 question, focus, what, not_for, examples는 API의 일부가 아니며, 어느 것도 예약되어 있지 않습니다. 선택지 이름을 고르는 것과 같은 방식으로 여러분이 고릅니다. 모델은 값과 함께 이름도 보므로, 뒤따르는 내용에 이름표를 붙이는 짧은 이름을 사용하십시오.