Decisions API가 정식 출시되었습니다. 공개 베타 이후 새로 추가된 기능을 확인하세요.
Decisions API는 텍스트, 이미지, JSON을 평가하고 질문 유형에 맞는 답을 Responses API보다 약 10배 빠르게 반환합니다. 조건이 참일 확률, 정해진 선택지 중 하나, 또는 평가 기준에 따른 점수를 얻을 수 있습니다. 애플리케이션에서 이 답을 활용해 콘텐츠를 분류하고, 요청을 라우팅하고, 작업의 우선순위를 정하세요.
코드를 작성하기 전에 Playground에서 Decisions API에 여러 질문과 입력을 넣어 보세요.
POST /v1/decisions로 요청을 보내세요. 현재 지원되는 모델은 gpt-6-luna뿐입니다.
공개 베타 출시 이후 Decisions API에 다음 기능이 추가되었습니다.
- 프롬프트 캐싱. 여러 요청에서 공통 입력을 재사용하고 할인된 캐시 요금을 적용받을 수 있습니다.
- 질문 지침에 이미지 포함. 질문마다 별도의 이미지를 제공해 공통 자료와 비교할 수 있습니다.
- JSON 입력.
input_json 콘텐츠 파트로 구조화된 데이터를 직접 전달할 수 있습니다.
- 요청당 질문 수 확대. 요청당 질문 수가 공개 베타의 최대 200개에서 최대 400개로 늘었습니다.
- 호스팅된 이미지 입력. 인라인 base64 데이터 URL과 공개적으로 접근 가능한 HTTP 또는 HTTPS 이미지 URL을 전달할 수 있습니다.
- 대화 기록. 메시지와 함께 함수 호출, 사용자 정의 도구 호출 및 그 출력을 평가할 수 있습니다.
- 조직 사용량 보고. 토큰 사용량과 요청 수를 추적할 수 있습니다.
- GPT-Live 기본 통합. Live가 Decisions를 직접 호출하게 하거나, 입력 또는 출력 음성의 전사 텍스트가 도착할 때 판단을 요청할 수 있습니다.
- Private Link. 프라이빗 연결을 통해 Decisions 요청을 보낼 수 있습니다.
요청은 세 부분으로 구성됩니다.
| 필드 |
용도 |
model |
요청을 평가할 모델입니다. 현재는 gpt-6-luna만 지원합니다. |
input |
모든 질문의 판단 근거가 되는 공통 자료입니다. 텍스트 문자열이나 메시지, 도구 호출, 도구 출력을 순서대로 담은 배열을 전달합니다. |
questions |
평가할 내용을 지정합니다. 각 질문의 유형과 지침, 허용되는 선택지나 점수 단계를 포함합니다. |
응답에는 answers 배열이 포함됩니다. 질문마다 고유한 name을 지정하세요. API가 응답에 같은 이름을 반환하므로 어느 질문에 대한 답인지 식별할 수 있습니다.
| 유형 |
용도 |
주요 결과 |
predicate |
눈에 보이는 손상이 있는지, 지문이 질문과 관련 있는지 등 조건을 확인합니다. |
probability: 조건이 참일 확률을 0에서 1 사이로 추정한 값입니다. |
choice |
부서나 콘텐츠 카테고리 등 선택지 중 하나를 고릅니다. |
choice: 제공한 값 중 하나입니다. |
score |
문제의 심각도처럼 순서가 있는 단계에 따라 입력을 평가합니다. |
score: 단계별 인덱스의 확률 가중 평균입니다. |
choice와 score는 모두 개별 선택지의 확률을 반환합니다. 부서처럼 순서가 없는 범주에는 choice를 사용하세요. 심각도처럼 순서가 있는 단계에는 score를 사용하세요. 각 단계의 숫자 인덱스에 해당 확률을 가중치로 적용해 평균을 구하므로 두 단계 사이의 점수도 나올 수 있습니다.
애플리케이션에 이런 유형의 답이 필요하면 Decisions를 사용하세요. 추출한 필드나 서술형 설명을 직접 정의한 JSON 스키마에 맞춰 객체로 생성하려면 Responses API의 구조화된 출력값을 사용하세요. 모델이 인수를 지정해 도구 호출을 요청하도록 하려면 함수 호출을 사용하세요.
대화 맥락을 고려해 요청을 평가하려면 input에 대화 기록을 전달하세요. 메시지와 함수 또는 사용자 정의 도구의 호출 및 출력을 순서대로 포함하세요. call_id로 각 도구 출력을 해당 호출과 연결하세요.
지원되는 콘텐츠는 메시지 역할이나 항목 유형에 따라 다릅니다.
| 입력 항목 |
지원되는 콘텐츠 |
| 사용자 메시지 |
텍스트, 이미지, JSON |
| 개발자 또는 시스템 메시지 |
입력 텍스트만 지원합니다. 시스템 메시지는 개발자 메시지로 처리합니다. |
| 어시스턴트 메시지 |
출력 텍스트 또는 응답 거부 |
| 함수 또는 사용자 정의 도구 출력 |
해당 도구 호출에 연결된 텍스트와 이미지 |
도구 호출과 출력은 질문에 답할 때 판단 근거로 쓰입니다. Decisions는 질문 유형에 맞는 답을 반환하며, 그 답에 따라 선택된 작업은 애플리케이션이 실행합니다. 파일, 오디오, 항목 참조는 지원하지 않습니다. 허용되는 입력 필드는 Decisions 생성 API 레퍼런스를 참고하세요.
input_json 콘텐츠 파트를 사용하면 구조화된 자료를 문자열로 변환하지 않고 전달할 수 있습니다. 사용자 메시지에 텍스트나 이미지 파트와 함께 이 파트를 넣고 data에 JSON 객체 또는 배열을 지정하세요. 메시지 콘텐츠에서 input_json은 user 역할에만 지원됩니다.
예를 들어 다음 JSON 요청 본문을 POST /v1/decisions로 보내면 주문 기록을 근거로 고객 지원 티켓을 평가할 수 있습니다.
12345678910111213141516171819202122232425262728{
"model": "gpt-6-luna",
"input": [
{
"role": "user",
"content": [
{
"type": "input_text",
"text": "Evaluate the ticket using the order evidence."
},
{
"type": "input_json",
"data": {
"ticket": { "body": "Please refund the duplicate charge." },
"order": { "charges": [49, 49] }
}
}
]
}
],
"questions": [
{
"type": "predicate",
"name": "duplicate_charge",
"instructions": "Does the ticket describe a duplicate charge supported by the order?"
}
]
}
role이나 image_url 같은 JSON 키가 data 안에 있으면 일반 데이터로 취급합니다. 이 키로 메시지를 만들거나 이미지를 가져오지는 않습니다. 답은 선택한 질문 유형을 따르며, JSON 입력을 사용해도 응답 형식은 동일합니다.
predicate 질문으로 제품 사진에서 눈에 보이는 손상이 있는지 확인하세요. 다음 요청은 이미지와 함께 균열, 찢어짐, 찌그러짐을 확인하라는 지침을 전달합니다.
Please cancel my subscription before the next renewal.
Can I switch to annual billing and keep my current plan?
I don’t need this anymore. How do I stop future charges?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22IMAGE_BASE64="$(base64 < product.png | tr -d '\r\n')"
curl https://api.openai.com/v1/decisions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @- <<JSON
{
"model": "gpt-6-luna",
"input": [{
"role": "user",
"content": [
{"type": "input_text", "text": "Inspect the product in this photo."},
{"type": "input_image", "image_url": "data:image/png;base64,$IMAGE_BASE64"}
]
}],
"questions": [{
"type": "predicate",
"name": "visible_damage",
"instructions": "Does the product have visible damage, such as a crack, tear, or dent? Ignore shadows and damage to the packaging."
}]
}
JSON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35import { readFile } from "node:fs/promises";
import OpenAI from "openai";
const client = new OpenAI();
const imageBase64 = (await readFile("product.png")).toString("base64");
const decision = await client.decisions.create({
model: "gpt-6-luna",
input: [
{
role: "user",
content: [
{ type: "input_text", text: "Inspect the product in this photo." },
{
type: "input_image",
image_url: `data:image/png;base64,${imageBase64}`,
},
],
},
],
questions: [
{
type: "predicate",
name: "visible_damage",
instructions:
"Does the product have visible damage, such as a crack, tear, or dent? Ignore shadows and damage to the packaging.",
},
],
});
const answer = decision.answers[0];
if (answer.type === "refusal") {
console.log(`Refused: ${answer.name}`);
} else if (answer.type === "predicate") {
console.log(`Visible damage probability: ${answer.probability}`);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39import base64
from pathlib import Path
from openai import OpenAI
client = OpenAI()
image_base64 = base64.b64encode(Path("product.png").read_bytes()).decode("ascii")
decision = client.decisions.create(
model="gpt-6-luna",
input=[
{
"role": "user",
"content": [
{"type": "input_text", "text": "Inspect the product in this photo."},
{
"type": "input_image",
"image_url": f"data:image/png;base64,{image_base64}",
},
],
}
],
questions=[
{
"type": "predicate",
"name": "visible_damage",
"instructions": (
"Does the product have visible damage, such as a crack, tear, or dent? "
"Ignore shadows and damage to the packaging."
),
}
],
)
answer = decision.answers[0]
if answer.type == "refusal":
print(f"Refused: {answer.name}")
elif answer.type == "predicate":
print(f"Visible damage probability: {answer.probability}")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52package main
import (
"context"
"encoding/base64"
"fmt"
"os"
"github.com/openai/openai-go/v3"
)
func main() {
image, err := os.ReadFile("product.png")
if err != nil {
panic(err)
}
client := openai.NewClient()
decision, err := client.Decisions.New(context.Background(), openai.DecisionNewParams{
Model: "gpt-6-luna",
Input: openai.DecisionNewParamsInputUnion{
OfDecisionInputMessageArray: []openai.DecisionInputMessageParam{{
Content: openai.DecisionInputMessageContentUnionParam{
OfParts: []openai.DecisionInputPartUnionParam{
{OfInputText: &openai.DecisionInputTextParam{Text: "Inspect the product in this photo."}},
{OfInputImage: &openai.DecisionInputImageParam{
ImageURL: "data:image/png;base64," + base64.StdEncoding.EncodeToString(image),
}},
},
},
}},
},
Questions: []openai.DecisionNewParamsQuestionUnion{{
OfPredicate: &openai.DecisionNewParamsQuestionPredicate{
Name: openai.String("visible_damage"),
Instructions: openai.DecisionNewParamsQuestionPredicateInstructionsUnion{
OfString: openai.String("Does the product have visible damage, such as a crack, tear, or dent? Ignore shadows and damage to the packaging."),
},
},
}},
})
if err != nil {
panic(err)
}
switch answer := decision.Answers[0].AsAny().(type) {
case openai.DecisionAnswerPredicate:
fmt.Println(answer.Probability)
case openai.DecisionAnswerRefusal:
fmt.Printf("Decision refused for %s\n", answer.Name)
default:
panic("unexpected answer type")
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51import com.openai.models.decisions.DecisionCreateParams;
import com.openai.models.decisions.DecisionInputImage;
import com.openai.models.decisions.DecisionInputItem;
import com.openai.models.decisions.DecisionInputMessage;
import com.openai.models.decisions.DecisionInputPart;
import com.openai.models.decisions.DecisionInputText;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
import java.util.List;
String imageBase64 =
Base64.getEncoder().encodeToString(Files.readAllBytes(Path.of("product.png")));
var message =
DecisionInputMessage.User.builder()
.contentOfParts(
List.of(
DecisionInputPart.ofInputText(
DecisionInputText.builder()
.text("Inspect the product in this photo.")
.build()),
DecisionInputPart.ofInputImage(
DecisionInputImage.builder()
.imageUrl("data:image/png;base64," + imageBase64)
.build())))
.build();
var decision =
client
.decisions()
.create(
DecisionCreateParams.builder()
.model("gpt-6-luna")
.inputOfDecisionInputItems(
List.of(
DecisionInputItem.ofDecisionInputMessage(
DecisionInputMessage.ofUser(message))))
.addQuestion(
DecisionCreateParams.Question.Predicate.builder()
.name("visible_damage")
.instructions(
"Does the product have visible damage, such as a crack, tear, or"
+ " dent? Ignore shadows and damage to the packaging.")
.build())
.build());
var answer = decision.answers().get(0);
if (answer.isRefusal()) {
System.out.println("Refused: " + answer.asRefusal().name().orElse("visible_damage"));
} else {
System.out.println(answer.asPredicate().probability());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41require "base64"
require "openai"
image_base64 = Base64.strict_encode64(File.binread("product.png"))
client = OpenAI::Client.new
decision = client.decisions.create(
model: "gpt-6-luna",
input: [
{
role: :user,
content: [
{
type: :input_text,
text: "Inspect the product in this photo."
},
{
type: :input_image,
image_url: "data:image/png;base64,#{image_base64}"
}
]
}
],
questions: [
{
type: :predicate,
name: "visible_damage",
instructions: "Does the product have visible damage, such as a crack, tear, or dent? Ignore shadows and damage to the packaging."
}
]
)
answer = decision.answers.fetch(0)
case answer
when OpenAI::Models::Decision::Answer::Predicate
puts(answer.probability)
when OpenAI::Models::Decision::Answer::Refusal
warn("Decision refused for #{answer.name}")
else
raise("Unexpected answer type: #{answer.type}")
end
응답 예시의 일부입니다.
123456789{
"answers": [
{
"type": "predicate",
"name": "visible_damage",
"probability": 0.92
}
]
}
probability는 조건이 참일 확률을 모델이 추정한 값입니다. 임계값을 정한 뒤 이 확률을 기준으로 검토가 필요한 사진을 표시하세요.
이미지는 인라인 base64 데이터 URL이나 공개적으로 접근 가능한 HTTP 또는 HTTPS URL로 제공하세요. 이 엔드포인트는 file_id 입력을 지원하지 않습니다. 사용자 메시지에 input_text와 input_image 파트를 함께 넣으면 지침이나 다른 맥락을 고려해 이미지를 평가할 수 있습니다.
모든 질문에 공통으로 쓰이는 자료는 input에 넣으세요. 질문별로 다른 이미지를 제공하려면 각 질문의 instructions를 input_text와 input_image 파트로 구성된 비어 있지 않은 배열로 설정하세요. 조건 판별형, 선택형, 점수형 질문 모두 이 방식을 지원합니다. 텍스트만 있는 지침은 문자열로 전달해도 됩니다.
예를 들어 다음 JSON 요청 본문을 POST /v1/decisions로 보내면 후보 이미지를 공통 참조 이미지와 비교할 수 있습니다. 예시의 이미지 URL 두 개를 공개적으로 접근 가능한 이미지 URL로 바꾸거나 base64 이미지 데이터 URL을 사용하세요.
12345678910111213141516171819202122232425262728293031323334{
"model": "gpt-6-luna",
"input": [
{
"role": "user",
"content": [
{
"type": "input_text",
"text": "The shared image is reference image A."
},
{
"type": "input_image",
"image_url": "https://example.com/reference.jpg"
}
]
}
],
"questions": [
{
"type": "predicate",
"name": "candidate_matches_reference",
"instructions": [
{
"type": "input_text",
"text": "Is candidate image B a derivative or variant of reference image A, including recompression or resizing?"
},
{
"type": "input_image",
"image_url": "https://example.com/candidate.jpg"
}
]
}
]
}
질문 지침에 이미지를 추가할 때는 다음 사항을 참고하세요.
- 후보 이미지마다 질문을 하나씩 추가하세요. 각 질문은 해당 후보 이미지를 동일한 참조 이미지와 비교합니다.
- 콘텐츠 파트는
instructions에 직접 넣으세요. 메시지로 감싸거나 role을 지정하지 마세요.
- 요청당 이미지 한도를 계산할 때는 질문에 포함된 이미지와
input의 이미지를 합산합니다.
choice 질문은 제공된 선택지에서 값 하나를 고릅니다. 선택지마다 서로 다른 값을 지정하고, 어떤 경우에 해당 선택지를 골라야 하는지 설명하세요.
I was charged twice for this month. Can you refund the extra payment?
Every CSV export fails with an error, even after I refresh the page.
I lost my phone and can’t get past two-step verification.
NORTHLINE STUDIO № 1042
Billed to Cedar & Co.
Design services$1,200.00
Amount due$1,200.00
NORTHLINE STUDIO № 6853
Thank you for your purchase.
Design services$1,200.00
Total paid$1,200.00
NORTHLINE STUDIO 01 / 03
Between Northline Studio and Cedar & Co.
The provider agrees to deliver design services for a term of twelve months.
Provider signatureClient signature
다음 요청은 고객 불만을 처리할 부서를 선택합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18curl https://api.openai.com/v1/decisions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-6-luna",
"input": "I was charged twice for my order.",
"questions": [{
"type": "choice",
"name": "department",
"instructions": "Which department should handle this complaint?",
"choices": [
{"value": "billing", "description": "Payments, invoices, and refunds."},
{"value": "technical", "description": "Problems using the product."},
{"value": "shipping", "description": "Delivery and tracking."},
{"value": "other", "description": "Requests outside these categories."}
]
}]
}'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29import OpenAI from "openai";
const client = new OpenAI();
const decision = await client.decisions.create({
model: "gpt-6-luna",
input: "I was charged twice for my order.",
questions: [
{
type: "choice",
name: "department",
instructions: "Which department should handle this complaint?",
choices: [
{ value: "billing", description: "Payments, invoices, and refunds." },
{ value: "technical", description: "Problems using the product." },
{ value: "shipping", description: "Delivery and tracking." },
{ value: "other", description: "Requests outside these categories." },
],
},
],
});
const answer = decision.answers[0];
if (answer.type === "refusal") {
console.log(`Refused: ${answer.name}`);
} else if (answer.type === "choice") {
console.log(
`Department: ${answer.choice} (confidence: ${answer.confidence})`
);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26from openai import OpenAI
client = OpenAI()
decision = client.decisions.create(
model="gpt-6-luna",
input="I was charged twice for my order.",
questions=[
{
"type": "choice",
"name": "department",
"instructions": "Which department should handle this complaint?",
"choices": [
{"value": "billing", "description": "Payments, invoices, and refunds."},
{"value": "technical", "description": "Problems using the product."},
{"value": "shipping", "description": "Delivery and tracking."},
{"value": "other", "description": "Requests outside these categories."},
],
}
],
)
answer = decision.answers[0]
if answer.type == "refusal":
print(f"Refused: {answer.name}")
elif answer.type == "choice":
print(f"Department: {answer.choice} (confidence: {answer.confidence})")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
)
func main() {
client := openai.NewClient()
decision, err := client.Decisions.New(context.Background(), openai.DecisionNewParams{
Model: "gpt-6-luna",
Input: openai.DecisionNewParamsInputUnion{OfString: openai.String("I was charged twice for my order.")},
Questions: []openai.DecisionNewParamsQuestionUnion{{
OfChoice: &openai.DecisionNewParamsQuestionChoice{
Name: openai.String("department"),
Instructions: openai.DecisionNewParamsQuestionChoiceInstructionsUnion{
OfString: openai.String("Which department should handle this complaint?"),
},
Choices: []openai.DecisionNewParamsQuestionChoiceChoice{
{
Value: openai.DecisionNewParamsQuestionChoiceChoiceValueUnion{OfString: openai.String("billing")},
Description: openai.String("Payments, invoices, and refunds."),
},
{
Value: openai.DecisionNewParamsQuestionChoiceChoiceValueUnion{OfString: openai.String("technical")},
Description: openai.String("Problems using the product."),
},
{
Value: openai.DecisionNewParamsQuestionChoiceChoiceValueUnion{OfString: openai.String("shipping")},
Description: openai.String("Delivery and tracking."),
},
{
Value: openai.DecisionNewParamsQuestionChoiceChoiceValueUnion{OfString: openai.String("other")},
Description: openai.String("Requests outside these categories."),
},
},
},
}},
})
if err != nil {
panic(err)
}
switch answer := decision.Answers[0].AsAny().(type) {
case openai.DecisionAnswerChoice:
fmt.Println(answer.Choice.AsString(), answer.Confidence, answer.Probabilities)
case openai.DecisionAnswerRefusal:
fmt.Printf("Decision refused for %s\n", answer.Name)
default:
panic("unexpected answer type")
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47import com.openai.models.decisions.DecisionChoiceOption;
import com.openai.models.decisions.DecisionCreateParams;
import com.openai.models.decisions.DecisionCreateParams.Question.Choice;
var decision =
client
.decisions()
.create(
DecisionCreateParams.builder()
.model("gpt-6-luna")
.input("I was charged twice for my order.")
.addQuestion(
Choice.builder()
.name("department")
.instructions("Which department should handle this complaint?")
.addChoice(
DecisionChoiceOption.builder()
.value("billing")
.description("Payments, invoices, and refunds.")
.build())
.addChoice(
DecisionChoiceOption.builder()
.value("technical")
.description("Problems using the product.")
.build())
.addChoice(
DecisionChoiceOption.builder()
.value("shipping")
.description("Delivery and tracking.")
.build())
.addChoice(
DecisionChoiceOption.builder()
.value("other")
.description("Requests outside these categories.")
.build())
.build())
.build());
var answer = decision.answers().get(0);
if (answer.isRefusal()) {
System.out.println("Refused: " + answer.asRefusal().name().orElse("department"));
} else {
var choice = answer.asChoice();
System.out.println(choice.choice().asString());
System.out.println(choice.probabilities());
System.out.println(choice.confidence());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42require "openai"
client = OpenAI::Client.new
decision = client.decisions.create(
model: "gpt-6-luna",
input: "I was charged twice for my order.",
questions: [
{
type: :choice,
name: "department",
instructions: "Which department should handle this complaint?",
choices: [
{
value: "billing",
description: "Payments, invoices, and refunds."
},
{
value: "technical",
description: "Problems using the product."
},
{
value: "shipping",
description: "Delivery and tracking."
},
{
value: "other",
description: "Requests outside these categories."
}
]
}
]
)
answer = decision.answers.fetch(0)
case answer
when OpenAI::Models::Decision::Answer::Choice
puts(answer.choice, answer.confidence, answer.probabilities)
when OpenAI::Models::Decision::Answer::Refusal
warn("Decision refused for #{answer.name}")
else
raise("Unexpected answer type: #{answer.type}")
end
응답 예시의 일부입니다.
12345678910111213141516{
"answers": [
{
"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
}
]
}
답의 choice 필드에는 제공한 값 중 하나가 들어갑니다. 이 예시에서는 "billing"입니다. 선택지별 확률을 담은 probabilities 배열과 confidence 필드도 반환합니다. 임계값을 정하는 방법은 답 해석하기를 참고하세요.
범주가 가능한 모든 입력을 포괄하지 못한다면 "other"처럼 어느 범주에도 해당하지 않을 때 사용할 선택지를 포함하세요. 애플리케이션은 이 결과를 일반 검토 대기열로 보낼 수 있습니다.
score 질문은 순서가 있는 2~10개의 단계(levels)를 기준으로 입력을 평가합니다. 단계별 기준을 정의하고 낮은 단계부터 높은 단계 순으로 배치하세요.
0.00/ 2
손상 없음1.00
경미한 손상0.00
심각한 손상0.00
Could you add a dark mode? It would be helpful when I work at night.
CSV exports keep failing for one teammate. The rest of our team can still export.
Checkout is failing for every customer. We haven’t processed an order in 20 minutes.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17curl https://api.openai.com/v1/decisions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-6-luna",
"input": "Export fails in Safari but works in Chrome.",
"questions": [{
"type": "score",
"name": "severity",
"instructions": "How severe is this issue?",
"levels": [
{"label": "Cosmetic", "description": "Appearance only; no lost functionality."},
{"label": "Workaround available", "description": "A task fails, but another way works."},
{"label": "Fully blocked", "description": "A task fails with no workaround."}
]
}]
}'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35import OpenAI from "openai";
const client = new OpenAI();
const decision = await client.decisions.create({
model: "gpt-6-luna",
input: "Export fails in Safari but works in Chrome.",
questions: [
{
type: "score",
name: "severity",
instructions: "How severe is this issue?",
levels: [
{
label: "Cosmetic",
description: "Appearance only; no lost functionality.",
},
{
label: "Workaround available",
description: "A task fails, but another way works.",
},
{
label: "Fully blocked",
description: "A task fails with no workaround.",
},
],
},
],
});
const answer = decision.answers[0];
if (answer.type === "refusal") {
console.log(`Refused: ${answer.name}`);
} else if (answer.type === "score") {
console.log(`Severity: ${answer.score} (confidence: ${answer.confidence})`);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34from openai import OpenAI
client = OpenAI()
decision = client.decisions.create(
model="gpt-6-luna",
input="Export fails in Safari but works in Chrome.",
questions=[
{
"type": "score",
"name": "severity",
"instructions": "How severe is this issue?",
"levels": [
{
"label": "Cosmetic",
"description": "Appearance only; no lost functionality.",
},
{
"label": "Workaround available",
"description": "A task fails, but another way works.",
},
{
"label": "Fully blocked",
"description": "A task fails with no workaround.",
},
],
}
],
)
answer = decision.answers[0]
if answer.type == "refusal":
print(f"Refused: {answer.name}")
elif answer.type == "score":
print(f"Severity: {answer.score} (confidence: {answer.confidence})")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
)
func main() {
client := openai.NewClient()
decision, err := client.Decisions.New(context.Background(), openai.DecisionNewParams{
Model: "gpt-6-luna",
Input: openai.DecisionNewParamsInputUnion{OfString: openai.String("Export fails in Safari but works in Chrome.")},
Questions: []openai.DecisionNewParamsQuestionUnion{{
OfScore: &openai.DecisionNewParamsQuestionScore{
Name: openai.String("severity"),
Instructions: openai.DecisionNewParamsQuestionScoreInstructionsUnion{
OfString: openai.String("How severe is this issue?"),
},
Levels: []openai.DecisionNewParamsQuestionScoreLevel{
{Label: "Cosmetic", Description: openai.String("Appearance only; no lost functionality.")},
{Label: "Workaround available", Description: openai.String("A task fails, but another way works.")},
{Label: "Fully blocked", Description: openai.String("A task fails with no workaround.")},
},
},
}},
})
if err != nil {
panic(err)
}
switch answer := decision.Answers[0].AsAny().(type) {
case openai.DecisionAnswerScore:
fmt.Println(answer.Score, answer.Confidence, answer.Probabilities)
case openai.DecisionAnswerRefusal:
fmt.Printf("Decision refused for %s\n", answer.Name)
default:
panic("unexpected answer type")
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41import com.openai.models.decisions.DecisionCreateParams;
import com.openai.models.decisions.DecisionCreateParams.Question.Score;
var decision =
client
.decisions()
.create(
DecisionCreateParams.builder()
.model("gpt-6-luna")
.input("Export fails in Safari but works in Chrome.")
.addQuestion(
Score.builder()
.name("severity")
.instructions("How severe is this issue?")
.addLevel(
Score.Level.builder()
.label("Cosmetic")
.description("Appearance only; no lost functionality.")
.build())
.addLevel(
Score.Level.builder()
.label("Workaround available")
.description("A task fails, but another way works.")
.build())
.addLevel(
Score.Level.builder()
.label("Fully blocked")
.description("A task fails with no workaround.")
.build())
.build())
.build());
var answer = decision.answers().get(0);
if (answer.isRefusal()) {
System.out.println("Refused: " + answer.asRefusal().name().orElse("severity"));
} else {
var score = answer.asScore();
System.out.println(score.score());
System.out.println(score.probabilities());
System.out.println(score.confidence());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38require "openai"
client = OpenAI::Client.new
decision = client.decisions.create(
model: "gpt-6-luna",
input: "Export fails in Safari but works in Chrome.",
questions: [
{
type: :score,
name: "severity",
instructions: "How severe is this issue?",
levels: [
{
label: "Cosmetic",
description: "Appearance only; no lost functionality."
},
{
label: "Workaround available",
description: "A task fails, but another way works."
},
{
label: "Fully blocked",
description: "A task fails with no workaround."
}
]
}
]
)
answer = decision.answers.fetch(0)
case answer
when OpenAI::Models::Decision::Answer::Score
puts(answer.score, answer.confidence, answer.probabilities)
when OpenAI::Models::Decision::Answer::Refusal
warn("Decision refused for #{answer.name}")
else
raise("Unexpected answer type: #{answer.type}")
end
응답 예시의 일부입니다.
123456789101112131415{
"answers": [
{
"type": "score",
"name": "severity",
"score": 1.1,
"probabilities": [
{ "value": 0, "label": "Cosmetic", "probability": 0.1 },
{ "value": 1, "label": "Workaround available", "probability": 0.7 },
{ "value": 2, "label": "Fully blocked", "probability": 0.2 }
],
"confidence": 0.55
}
]
}
단계 인덱스는 0부터 시작합니다. 여기서 0은 외관상의 문제, 1은 우회 방법이 있는 문제, 2는 작업을 전혀 진행할 수 없는 문제를 뜻합니다. 반환되는 score는 확률 가중 평균이므로 두 단계 사이의 값이 될 수 있습니다. 이 예시에서는 각 단계의 확률이 0.1, 0.7, 0.2이므로 점수는 1.1입니다.
답에는 confidence와 단계별 확률을 담은 probabilities도 포함됩니다. 점수는 여러 단계에 걸친 분포를 하나의 값으로 요약합니다. 단일 범주를 선택하려면 choice를 사용하세요.
요청 하나에 질문을 1~400개 포함할 수 있습니다. 같은 입력을 평가하는 독립적인 질문은 하나의 questions 배열에 넣으세요. 예를 들어 제품 사진 한 장으로 손상 여부 확인과 제품 카테고리 분류를 한 번에 요청할 수 있습니다. 질문마다 다른 유형을 사용해도 됩니다.
앞선 답에 따라 다음 판단이 달라진다면 요청을 나눠 보내세요. 예를 들어 먼저 손상 여부를 확인한 다음, 결과에 따라 수리 유형 분류를 요청할지 결정하세요.
관찰할 수 있는 기준을 중심으로 질문을 작성하세요. 평가할 사항이 서로 다르면 질문을 나누고, 선택지별 의미를 명확히 구분하세요. 점수 단계는 인접한 단계의 기준을 서로 구별할 수 있도록 정의하세요.
조건 판별형 질문은 조건이 참일 확률의 추정값을 반환합니다. 선택형과 점수형 질문은 확률 분포와 별도의 confidence 필드를 반환합니다.
애플리케이션에서 수집한 레이블이 있는 예시를 사용해 라우팅, 필터링 또는 검토에 적용할 임계값을 정하세요. 거짓 양성과 거짓 음성이 각각 초래하는 비용을 고려해 임계값을 선택하세요.
프롬프트 캐싱을 사용하면 반복되는 요청에서 공통 input의 앞부분이 일치할 때 해당 부분을 재사용하고 할인된 캐시 읽기 요금을 적용받을 수 있습니다. 예를 들어 input에 참조 문서나 이미지를 그대로 두고 요청마다 질문만 바꾸세요. 질문별 이미지를 비롯한 questions의 콘텐츠는 재사용 가능한 접두부에 포함되지 않습니다.
기본적으로 Decisions는 공통 input의 끝에 암시적 캐시 경계점을 설정합니다. 기존 요청도 본문을 수정하지 않고 캐싱 혜택을 받을 수 있습니다. 재사용할 접두부는 최소 1,024토큰이어야 하며, 질문 콘텐츠는 이 토큰 수에 포함되지 않습니다. 재사용하려면 요청 간에 접두부를 동일하게 유지하세요.
prompt_cache_key를 사용하면 호출자가 지정한 키별로 캐시 재사용 범위를 나눌 수 있습니다. 경계점을 직접 지정하려면 prompt_cache_breakpoint: {"mode": "explicit"}를 공통 input의 텍스트 또는 이미지 콘텐츠 파트에 추가하세요. 표시한 경계점만 사용하려면 prompt_cache_options.mode를 "explicit"으로 설정하세요. 명시적 모드에서는 경계점을 표시하지 않은 요청에 프롬프트 캐싱이 적용되지 않습니다. 질문 지침에는 경계점 설정을 지원하지 않습니다.
캐시에서 읽은 토큰 수는 usage.input_tokens_details.cached_tokens에서, 캐시에 쓴 토큰 수는 usage.input_tokens_details.cache_write_tokens에서 확인하세요. 두 값 모두 usage.input_tokens에 포함되므로 합계에 다시 더하지 마세요. 캐싱 개념은 프롬프트 캐싱 가이드를, 지원되는 요청 설정은 Decisions 레퍼런스를 참고하세요.
GET /v1/organization/usage/decisions로 조직의 Decisions 토큰 사용량과 요청 수를 조회하세요. 인증에는 관리자 API 키와 api.usage.read 권한이 필요합니다.
조회 기간과 집계 간격을 선택한 다음 프로젝트, API 키 또는 모델별로 결과를 필터링하거나 그룹화하세요. num_model_requests 필드는 요청 단위로 집계하므로 질문이 여러 개인 요청도 한 건으로 계산합니다. 필터와 페이지네이션은 Decisions 사용량 레퍼런스를 참고하세요.
gpt-6-luna에서 캐시되지 않은 입력 요금은 100만 토큰당 $0.10입니다. 프롬프트 캐싱 적중 시 할인된 입력 요금이 적용되며, 캐시 쓰기에는 요금이 부과됩니다. 캐시 읽기 및 쓰기 요금은 표준 gpt-6-luna 프롬프트 캐싱 요금과 동일합니다. 출력 토큰은 무료입니다.
지역별 처리 할증 요금과 긴 컨텍스트 입력에 대한 요금 배수가 적용됩니다. 이 요금은 /v1/decisions에 적용됩니다. gpt-6-luna를 사용하는 다른 요청에는 해당 모델 및 처리 등급별 요금이 적용됩니다.
Decisions API는 자격 요건을 충족하는 고객에게 데이터 비보관(ZDR)과 HIPAA에 따른 사용을 지원합니다. 데이터 레지던시는 지원되는 모든 API 리전에서 사용할 수 있습니다. 지역 내 처리는 미국과 유럽(유럽경제지역 및 스위스)에서 지원합니다. 자격 요건, 필요한 계약, 제한 사항은 데이터 제어를 참고하세요.
Decisions에 음성을 연결하면 음성 요청을 바탕으로 작업을 선택하거나 사용자 또는 GPT-Live가 말하는 도중에 반응할 수 있습니다.