OpenAI Decisions API를 사용하려면, https://api.openai.com/v1/decisions로 POST 요청을 보내십시오. 이때 "model": "gpt-6-luna", input (텍스트, 이미지 또는 둘 다), 그리고 각 질문이 predicate, choice, score 중 하나인 questions 배열을 포함해야 합니다. 파싱할 텍스트 대신 확률을 포함한 유형화된 답변을 받게 되며, 출력, 캐시 읽기 또는 캐시 쓰기 요금 없이 1백만 입력 토큰당 0.10달러를 지불합니다. 이 엔드포인트는 2026년 10월 6일부로 공개 베타 상태입니다.
이 가이드에서는 키 발급, curl, Python 및 JavaScript를 사용한 첫 호출, 각 답변 유형 읽기, 하나의 지원 티켓에 대한 세 가지 질문, 이미지 입력, 임계값, 그리고 Apidog에서의 테스트 설정에 대해 다룹니다. 이 엔드포인트를 언제 선택해야 하는지에 대한 내용은 OpenAI Decisions API란 무엇인가에서 시작하세요.
Decisions API 요청 한눈에 보기
| 필드 | 설명 |
|---|---|
model |
gpt-6-luna (베타 버전에서 사용 가능한 유일한 모델) |
input |
문자열, 또는 content가 문자열이거나 input_text 및 input_image 유형의 부분으로 구성된 user 메시지 배열 |
questions[].type |
predicate, choice, 또는 score |
questions[].instructions |
필수; 일반 언어로 된 질문 |
questions[].name |
선택 사항; 답변에서 에코백됨 (생략 시 null) |
questions[].choices |
choice 전용; 2개에서 255개의 고유한 {value, description} 객체, value는 문자열 또는 부울 |
questions[].levels |
score 전용; 정렬된 {label, description} 객체, 가장 낮은 값부터 시작하며 인덱스는 0부터 |
safety_identifier |
선택 사항인 불투명한 최종 사용자 ID, 최대 128자 |
출처: Decisions API 참조. 이 엔드포인트에는 temperature, stream, tools 또는 text.format이 없습니다.
키 발급 및 첫 호출
OpenAI 대시보드에서 키를 생성하고 (OpenAI API 키 가이드 참조), OPENAI_API_KEY로 내보내고, 절대로 코드에 붙여넣지 마십시오. 그런 다음 예/아니오 질문을 하나 던지십시오:
curl https://api.openai.com/v1/decisions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-6-luna",
"input": "The box arrived crushed and the screen is cracked.",
"questions": [
{"type": "predicate", "name": "damaged",
"instructions": "Does the customer report a damaged item?"}
]
}'
응답은 세 가지 최상위 필드인 model, answers, usage를 포함합니다. 이는 OpenAI 참조의 형식이며, 엔드포인트가 출력을 청구하지 않으므로 output_tokens는 0입니다:
{
"model": "gpt-6-luna",
"answers": [
{"type": "predicate", "name": "damaged", "probability": 0.95}
],
"usage": {
"input_tokens": 42,
"input_tokens_details": {"cached_tokens": 0, "cache_write_tokens": 0},
"output_tokens": 0,
"output_tokens_details": {"reasoning_tokens": 0},
"total_tokens": 42
}
}
Python에서의 동일한 호출 (SDK 3.26.0 이상):
from openai import OpenAI
client = OpenAI() # reads OPENAI_API_KEY from the environment
decision = client.decisions.create(
model="gpt-6-luna",
input="The box arrived crushed and the screen is cracked.",
questions=[
{"type": "predicate", "name": "damaged",
"instructions": "Does the customer report a damaged item?"}
],
)
print(decision.answers[0].probability)
그리고 JavaScript에서 (SDK 7.30.0 이상):
import OpenAI from "openai";
const client = new OpenAI();
const decision = await client.decisions.create({
model: "gpt-6-luna",
input: "The box arrived crushed and the screen is cracked.",
questions: [
{ type: "predicate", name: "damaged",
instructions: "Does the customer report a damaged item?" },
],
});
console.log(decision.answers[0].probability);
유형별 답변 읽기
답변은 요청한 순서대로 돌아오며, 각 답변에는 type이 있습니다. 모든 질문이 refusal로 돌아올 수 있으므로, 유형에 따라 처리하십시오.
predicate는 조건이 참일 확률을 0에서 1 사이의 값으로 추정하여probability를 반환합니다.choice는choice(선택된value),{value, probability}객체 배열 형태의probabilities, 그리고confidence를 반환합니다.score는score,{value, label, probability}객체 배열 형태의probabilities(여기서value는 0부터 시작하는 레벨 인덱스), 그리고confidence를 반환합니다. 스코어는 레벨 인덱스의 확률 가중 평균이므로, 레벨 사이에 있을 수 있습니다: 1.1은 "레벨 1과 레벨 2 사이, 레벨 1에 가까움"을 의미합니다.refusal은type과name만 반환합니다. 모델이 해당 질문을 거부한 경우이며, 동일 요청 내 다른 질문들은 여전히 답변을 받을 수 있습니다.
for a in decision.answers:
if a.type == "refusal":
send_to_review(a.name)
elif a.type == "predicate":
flag = a.probability > 0.9
elif a.type == "choice":
route = a.choice if a.confidence > 0.8 else "review"
elif a.type == "score":
priority = round(a.score)
OpenAI 가이드에서는 다음과 같이 구분합니다: 부서와 같이 순서가 없는 카테고리에는 choice를, 심각도와 같이 순서가 있는 레벨에는 score를 사용합니다.
하나의 지원 티켓에 대한 세 가지 질문
독립적인 질문들은 하나의 요청과 하나의 input을 공유하며, 각 질문은 다른 유형을 사용할 수 있습니다. 다음은 단일 티켓에 대한 predicate, choice, score의 예시입니다:
{
"model": "gpt-6-luna",
"input": "I was charged twice for my order.",
"questions": [
{"type": "predicate", "name": "refund_requested",
"instructions": "Is the customer asking for money back?"},
{"type": "choice", "name": "department",
"instructions": "Which team should handle this ticket?",
"choices": [
{"value": "billing", "description": "Charges, refunds, invoices"},
{"value": "technical", "description": "Bugs and errors in the product"},
{"value": "shipping", "description": "Delivery and tracking"},
{"value": "other", "description": "Anything else"}
]},
{"type": "score", "name": "urgency",
"instructions": "How urgent is this ticket?",
"levels": [
{"label": "low", "description": "No time pressure"},
{"label": "medium", "description": "Needs a reply this week"},
{"label": "high", "description": "Customer is blocked or losing money"}
]}
]
}
answers 배열은 요청한 순서와 동일하게 돌아옵니다. 아래의 choice 값은 이 정확한 입력에 대한 OpenAI의 가이드 값이며, predicate 및 score 값은 예시입니다:
"answers": [
{"type": "predicate", "name": "refund_requested", "probability": 0.88},
{"type": "choice", "name": "department", "choice": "billing",
"probabilities": [
{"value": "billing", "probability": 0.95},
{"value": "technical", "probability": 0.02},
{"value": "shipping", "probability": 0.01},
{"value": "other", "probability": 0.02}
],
"confidence": 0.93},
{"type": "score", "name": "urgency", "score": 1.6,
"probabilities": [
{"value": 0, "label": "low", "probability": 0.05},
{"value": 1, "label": "medium", "probability": 0.30},
{"value": 2, "label": "high", "probability": 0.65}
],
"confidence": 0.65}
]
가이드의 두 가지 규칙: 카테고리가 모든 입력을 다루지 못할 경우 other와 같은 대체 항목을 포함하고, 인접한 점수 레벨이 서로 다른 의미를 가지도록 관찰 가능한 기준을 중심으로 질문을 작성하십시오. 두 번째 결정이 첫 번째 답변에 의존하는 경우, 별도의 요청을 보내야 합니다.
이미지 입력
user 메시지 내부의 콘텐츠 부분으로 이미지를 전달하십시오. 가이드에서는 인라인 base64 데이터 URL을 설명합니다:
{
"model": "gpt-6-luna",
"input": [{
"role": "user",
"content": [
{"type": "input_text", "text": "Photo attached to a return request."},
{"type": "input_image", "image_url": "data:image/jpeg;base64,/9j/4AAQ..."}
]
}],
"questions": [
{"type": "predicate", "name": "visible_damage",
"instructions": "Is the product visibly damaged?"}
]
}
API 참조는 또한 공개적으로 접근 가능한 HTTP(S) URL, 하나의 요청에서 모든 메시지에 걸쳐 최대 128개의 이미지, 그리고 선택적인 detail 필드 (low, high, auto, original)를 나열하므로, 호스팅된 URL을 사용하기 전에 자신의 계정으로 테스트하십시오. file_id 입력은 어느 페이지에서도 지원되지 않습니다.
레이블이 지정된 예시에서 임계값 선택
OpenAI는 이 엔드포인트에 대한 정확도나 보정 수치를 공개하지 않습니다. 가이드라인은 오탐지(false positive) 비용 대비 미탐지(false negative) 비용을 기반으로 라우팅, 필터링 또는 검토를 위한 임계값을 설정하기 위해 자체 애플리케이션의 레이블이 지정된 예시를 사용하도록 권장합니다. 실제로는 사람이 선택한 부서와 함께 실제 티켓의 작은 CSV를 동일한 요청을 통해 실행하여, confidence가 깔끔한 경로와 사람의 개입이 필요한 경로를 어떻게 구분하는지 확인할 수 있습니다. 다음 섹션에서 이 루프를 구축합니다.
Apidog에서 Decisions API 테스트
저장된 요청을 통해 임계값 조정 및 회귀 검사를 반복할 수 있습니다. Apidog에서의 설정은 다음과 같습니다:
- 키를 환경 변수로 저장합니다. 환경을 생성하고,
OPENAI_API_KEY를 비밀 변수로 추가한 다음 (Apidog 환경 및 비밀 변수에서 설정 방법을 보여줍니다),Authorization헤더를Bearer {{OPENAI_API_KEY}}로 설정합니다. 키는 공유 요청 본문에 절대 포함되지 않습니다. - 질문 유형별로 요청을 하나씩 저장합니다.
https://api.openai.com/v1/decisions로Content-Type: application/json을 사용하여 POST 요청을 생성하고, 위 티켓 예시의choice질문을 별도로 붙여넣어 저장합니다. predicate 및 score 버전으로 복제하십시오. - JSONPath 어설션을 추가합니다. choice 요청에서: 상태는 200,
$.answers[0].type은choice와 같고,$.answers[0].choice는billing과 같고,$.answers[0].confidence는 0.8보다 크고,$.usage.output_tokens는 0과 같다고 어설션합니다. 손상 predicate의 경우,$.answers[?(@.name=='damaged')].probability가 0.9보다 크다고 어설션합니다. 지침의 문구 변경 또는 모델 동작 변경은 이제 티켓을 잘못 라우팅하는 대신 테스트를 실패하게 만듭니다. - 레이블이 지정된 티켓에 대해 실행합니다. 저장된 요청에서 테스트 시나리오를 구축하고,
ticket_text및expected_department두 열을 포함하는 작은 CSV 파일을 첨부합니다.{{ticket_text}}를input으로 매핑하고$.answers[0].choice가{{expected_department}}와 같다고 어설션합니다. 실행 보고서는 각 행에 대한confidence를 보여주며, 이는 OpenAI가 임계값을 설정하도록 알려주는 데이터입니다. 모든 잘못된 경로가 위치하는 지점 아래가 코드에서 "자동 라우팅" 임계값이 됩니다. - 프론트엔드를 위해
answers배열을 모의합니다. 라우터 또는 UI를 동일한 엔드포인트의 모의(mock)로 지정하여, 임계값 위아래의confidence를 가진choice답변과refusal을 반환하도록 합니다. 이렇게 하면 입력 토큰을 소비하기 전에 검토 대기열 경로가 구축됩니다. Apidog의 조건부 모의 응답은 요청 내용에 따라 모의를 전환하는 방법을 다룹니다. - CI에서 시나리오를 실행합니다. 액세스 토큰을 내보내고, 파이프라인에 단계를 추가합니다:
apidog run --access-token "$APIDOG_ACCESS_TOKEN" \
-t "$SCENARIO_ID" -e "$ENV_ID" -r cli,junit
어설션 실패는 빌드 실패로 이어지므로, confidence의 조용한 하락은 지원 대기열에서 발견되기보다 배포 전에 감지됩니다. 더 광범위한 패턴에 대해서는 LLM 애플리케이션 테스트를 참조하십시오.
오류 및 예외 처리
- 429 요청률 제한. Decisions API에 특정한 제한은 게시되지 않았습니다. 자신의 숫자를 확인하려면 설정(Settings) > 조직(Organization) > 제한(Limits)을 확인하십시오. 지수적으로 백오프하고,
Retry-After헤더가 있으면 이를 준수하십시오. 요청률 초과 가이드에는 재시도 래퍼가 있습니다. - 거부 답변.
type: "refusal"은 예외가 아닌 라우팅 결과로 처리하십시오. 해당 티켓은 사람에게 보내고, 동일 요청의 다른 답변은 유지하십시오. - 종속적인 결정. 모든 질문은 공유 입력에 대해 독립적으로 평가됩니다. 이전 답변에 의존하는 모든 것은 별도의 요청이 필요합니다.
자주 묻는 질문
- Decisions API 비용은 얼마입니까?
gpt-6-luna에서 1백만 입력 토큰당 0.10달러이며, 출력, 캐시 읽기 또는 캐시 쓰기 비용은 없습니다. 세 가지 질문이 포함된 500토큰 티켓은 500 / 1,000,000 x $0.10 = $0.00005이므로, 백만 개의 이러한 티켓은 50달러입니다. 272K 토큰을 초과하는 장문 컨텍스트 입력은 2배이며, 지역 처리는 10% 추가됩니다. - Decisions API는 무료입니까? 아니요. Decisions API의 무료 티어는 없습니다. 비용 지불 없이 GPT-6 Luna를 사용해보고 싶다면, GPT-6 Luna 무료 경로 게시물에 존재하는 목록이 있습니다.
- 얼마나 빠릅니까? OpenAI는 Responses API보다 약 10배 빠르다고 말하며, 절대적인 지연 시간 수치는 공개하지 않습니다. OpenAI 포럼의 한 개발자는 이미지 결정이 약 0.8초만에 이루어졌다고 보고했습니다.
- Decisions API는 어떤 모델과 작동합니까? 현재는
gpt-6-luna만 지원됩니다. 이것은 별도의 모델이 아니라 Luna의 엔드포인트입니다. 모델 자체에 대한 내용은 GPT-6 Luna란 무엇인가를 참조하십시오. - 언제 대신 Structured Outputs를 사용해야 합니까? 추출된 필드 또는 서면 설명과 같이 자체 JSON 스키마에 객체가 필요하거나, 모델이 인수를 사용하여 도구를 요청해야 하는 함수 호출 시에 사용해야 합니다. Decisions API 대 Responses API 게시물에서는 동일한 티켓을 두 가지 방식으로 처리하는 방법을 보여줍니다.
- Jev와 비교하면 어떻습니까? 둘 다 확률을 포함한 유형화된 답변을 반환하고 입력에 대해서만 비용을 청구합니다. Jev는 1백만 토큰당 0.042달러로 텍스트 전용입니다. Decisions API 대 Jev 비교에 전체 표가 있습니다.
다음 단계
이 가이드의 세 가지 질문 티켓 요청을 보낸 다음, 자체 레이블이 지정된 20개의 티켓에 대해 실행하여 confidence가 올바른 경로와 잘못된 경로를 어떻게 구분하는지 확인하십시오. 그런 다음 요청, CSV 시나리오 및 어설션을 함께 유지할 수 있도록 Apidog를 다운로드하여 오늘 선택한 임계값이 배포할 때마다 다시 확인되도록 하십시오.
