Predicted Outputs
Predicted Outputs
Predicted Outputs를 사용하면 출력 토큰 중 많은 부분이 미리 알려져 있을 때 Chat Completions의 API 응답을 빠르게 만들 수 있습니다. 이는 텍스트 또는 코드 파일을 약간 수정해 다시 생성할 때 가장 흔히 발생합니다. Chat Completions의 prediction 요청 매개변수를 사용해 예측값을 제공할 수 있습니다.
Predicted Outputs는 최신 gpt-4o, gpt-4o-mini, gpt-4.1, gpt-4.1-mini, gpt-4.1-nano 모델에서 오늘 사용할 수 있습니다. 애플리케이션에서 Predicted Outputs를 사용해 지연 시간을 줄이는 방법을 알아보세요.
출처: 문서
본문
코드 리팩터링 예시
Predicted Outputs는 작은 수정을 가한 텍스트 문서와 코드 파일을 다시 생성할 때 특히 유용합니다. GPT-4o 모델이 JavaScript 코드 조각을 리팩터링해 User 클래스의 username 속성을 email로 바꾸도록 하려 한다고 가정해 보세요:
class User {
firstName = "";
lastName = "";
username = "";
}
export default User;
파일의 대부분은 위 4번째 줄을 제외하면 변경되지 않습니다. 현재 코드 파일 텍스트를 예측값으로 사용하면 전체 파일을 더 낮은 지연 시간으로 다시 생성할 수 있습니다. 이러한 시간 절약은 파일이 클수록 빠르게 누적됩니다.
아래는 SDK에서 prediction 매개변수를 사용해 모델의 최종 출력이 예측 텍스트로 사용하는 원래 코드 파일과 매우 유사할 것임을 예측하는 예시입니다.
Predicted Output로 JavaScript 클래스 리팩터링하기
import OpenAI from "openai";
const code = `
class User {
firstName = "";
lastName = "";
username = "";
}
export default User;
`.trim();
const openai = new OpenAI();
const refactorPrompt = `
Replace the "username" property with an "email" property. Respond only
with code, and with no markdown formatting.
`;
const completion = await openai.chat.completions.create({
model: "gpt-4.1",
messages: [
{
role: "user",
content: refactorPrompt,
},
{
role: "user",
content: code,
},
],
store: true,
prediction: {
type: "content",
content: code,
},
});
// Inspect returned data
console.log(completion);
console.log(completion.choices[0].message.content);
from openai import OpenAI
code = """
class User {
firstName = "";
lastName = "";
username = "";
}
export default User;
""".strip()
refactor_prompt = """
Replace the "username" property with an "email" property. Respond only
with code, and with no markdown formatting.
"""
client = OpenAI()
completion = client.chat.completions.create(
model="gpt-4.1",
messages=[
{"role": "user", "content": refactor_prompt},
{"role": "user", "content": code},
],
prediction={"type": "content", "content": code},
)
print(completion)
print(completion.choices[0].message.content)
package main
import (
"context"
"fmt"
"strings"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/shared"
)
func main() {
client := openai.NewClient()
code := strings.TrimSpace(`
class User {
firstName = "";
lastName = "";
username = "";
}
export default User;
`)
refactorPrompt := strings.TrimSpace(`
Replace the "username" property with an "email" property. Respond only
with code, and with no markdown formatting.
`)
completion, err := client.Chat.Completions.New(context.Background(), openai.ChatCompletionNewParams{
Model: shared.ChatModelGPT4_1,
Messages: []openai.ChatCompletionMessageParamUnion{
openai.UserMessage(refactorPrompt),
openai.UserMessage(code),
},
Store: openai.Bool(true),
Prediction: openai.ChatCompletionPredictionContentParam{
Content: openai.ChatCompletionPredictionContentContentUnionParam{OfString: openai.String(code)},
},
})
if err != nil {
panic(err)
}
fmt.Println(completion.Choices[0].Message.Content)
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.chat.completions.ChatCompletionCreateParams;
import com.openai.models.chat.completions.ChatCompletionPredictionContent;
String code =
"""
class User {
firstName: string = "";
lastName: string = "";
username: string = "";
}
export default User;
""";
String refactorPrompt =
"Replace the \"username\" property with an \"email\" property. "
+ "Respond only with code, and with no markdown formatting.";
ChatCompletionCreateParams params =
ChatCompletionCreateParams.builder()
.model("gpt-4.1")
.addUserMessage(refactorPrompt)
.addUserMessage(code)
.prediction(ChatCompletionPredictionContent.builder().content(code).build())
.store(true)
.build();
client.chat().completions().create(params).choices().stream()
.flatMap(choice -> choice.message().content().stream())
.forEach(System.out::println);
using OpenAI.Chat;
#pragma warning disable OPENAI001
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
string model = "gpt-4.1";
ChatClient client = new(model, key);
string code =
"""
class User {
firstName = "";
lastName = "";
username = "";
}
export default User;
""";
ChatCompletionOptions options = new()
{
OutputPrediction = ChatOutputPrediction.CreateStaticContentPrediction(code),
};
ChatCompletion completion = await client.CompleteChatAsync(
[
new UserChatMessage(
"Replace the username property with an email property. Respond only with code, and with no markdown formatting."
),
new UserChatMessage(code),
],
options
);
Console.WriteLine(completion.Content[0].Text);
require "openai"
client = OpenAI::Client.new
code = <<~CODE
class User {
firstName: string = "";
lastName: string = "";
username: string = "";
}
export default User;
CODE
refactor_prompt = <<~PROMPT
Replace the "username" property with an "email" property. Respond only
with code, and with no markdown formatting.
PROMPT
completion = client.chat.completions.create(
model: "gpt-4.1",
messages: [
{
role: :user,
content: refactor_prompt
},
{
role: :user,
content: code
}
],
prediction: {
type: :content,
content: code
},
store: true
)
puts(completion.choices.fetch(0).message.content)
curl https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ***" \
-d '{
"model": "gpt-4.1",
"messages": [
{
"role": "user",
"content": "Replace the username property with an email property. Respond only with code, and with no markdown formatting."
},
{
"role": "user",
"content": "$CODE_CONTENT_HERE"
}
],
"prediction": {
"type": "content",
"content": "$CODE_CONTENT_HERE"
}
}'
리팩터링된 코드 외에도 choices 필드가 없는 축약된 모델 응답에는 다음과 같은 사용량(usage) 데이터가 포함됩니다:
{
"id": "chatcmpl-xxx",
"object": "chat.completion",
"created": 1786652188,
"model": "gpt-4.1-2025-04-14",
"usage": {
"prompt_tokens": 59,
"completion_tokens": 24,
"total_tokens": 83,
"prompt_tokens_details": { "cached_tokens": 0, "audio_tokens": 0 },
"completion_tokens_details": {
"reasoning_tokens": 0,
"audio_tokens": 0,
"accepted_prediction_tokens": 14,
"rejected_prediction_tokens": 2
}
},
"system_fingerprint": "fp_6ddb4f7408"
}
usage 객체의 accepted_prediction_tokens와 rejected_prediction_tokens를 모두 주목하세요. 이 예시에서는 예측에서 14개 토큰이 응답을 빠르게 하는 데 사용되었고, 2개는 거부되었습니다.
거부된 토큰은 API가 생성한 다른 completion 토큰과 마찬가지로 여전히 청구되므로, Predicted Outputs는 요청 비용을 높일 수 있습니다.
스트리밍 예시
Predicted Outputs의 지연 시간 이득은 API 응답에 스트리밍을 사용할 때 훨씬 더 큽니다. 아래는 같은 코드 리팩터링 사용 사례를 OpenAI SDK에서 스트리밍으로 대신 사용한 예시입니다.
Predicted Outputs와 스트리밍
import OpenAI from "openai";
const code = `
class User {
firstName = "";
lastName = "";
username = "";
}
export default User;
`.trim();
const openai = new OpenAI();
const refactorPrompt = `
Replace the "username" property with an "email" property. Respond only
with code, and with no markdown formatting.
`;
const completion = await openai.chat.completions.create({
model: "gpt-4.1",
messages: [
{
role: "user",
content: refactorPrompt,
},
{
role: "user",
content: code,
},
],
store: true,
prediction: {
type: "content",
content: code,
},
stream: true,
});
// Inspect returned data
for await (const chunk of completion) {
process.stdout.write(chunk.choices[0]?.delta?.content || "");
}
from openai import OpenAI
code = """
class User {
firstName = "";
lastName = "";
username = "";
}
export default User;
""".strip()
refactor_prompt = """
Replace the "username" property with an "email" property. Respond only
with code, and with no markdown formatting.
"""
client = OpenAI()
stream = client.chat.completions.create(
model="gpt-4.1",
messages=[
{"role": "user", "content": refactor_prompt},
{"role": "user", "content": code},
],
prediction={"type": "content", "content": code},
stream=True,
)
for chunk in stream:
if chunk.choices[0].delta.content is not None:
print(chunk.choices[0].delta.content, end="")
package main
import (
"context"
"fmt"
"strings"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/shared"
)
func main() {
client := openai.NewClient()
code := strings.TrimSpace(`
class User {
firstName = "";
lastName = "";
username = "";
}
export default User;
`)
refactorPrompt := strings.TrimSpace(`
Replace the "username" property with an "email" property. Respond only
with code, and with no markdown formatting.
`)
stream := client.Chat.Completions.NewStreaming(context.Background(), openai.ChatCompletionNewParams{
Model: shared.ChatModelGPT4_1,
Messages: []openai.ChatCompletionMessageParamUnion{
openai.UserMessage(refactorPrompt),
openai.UserMessage(code),
},
Store: openai.Bool(true),
Prediction: openai.ChatCompletionPredictionContentParam{
Content: openai.ChatCompletionPredictionContentContentUnionParam{OfString: openai.String(code)},
},
})
for stream.Next() {
if len(stream.Current().Choices) > 0 {
fmt.Print(stream.Current().Choices[0].Delta.Content)
}
}
if err := stream.Err(); err != nil {
panic(err)
}
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.http.StreamResponse;
import com.openai.models.chat.completions.ChatCompletionChunk;
import com.openai.models.chat.completions.ChatCompletionCreateParams;
import com.openai.models.chat.completions.ChatCompletionPredictionContent;
String code =
"""
class User {
firstName: string = "";
lastName: string = "";
username: string = "";
}
export default User;
""";
String refactorPrompt =
"Replace the \"username\" property with an \"email\" property. "
+ "Respond only with code, and with no markdown formatting.";
ChatCompletionCreateParams params =
ChatCompletionCreateParams.builder()
.model("gpt-4.1")
.addUserMessage(refactorPrompt)
.addUserMessage(code)
.prediction(ChatCompletionPredictionContent.builder().content(code).build())
.store(true)
.build();
try (StreamResponse<ChatCompletionChunk> stream =
client.chat().completions().createStreaming(params)) {
stream.stream()
.flatMap(chunk -> chunk.choices().stream())
.flatMap(choice -> choice.delta().content().stream())
.forEach(System.out::print);
}
using OpenAI.Chat;
#pragma warning disable OPENAI001
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
string model = "gpt-4.1";
ChatClient client = new(model, key);
string code =
"""
class User {
firstName = "";
lastName = "";
username = "";
}
export default User;
""";
ChatCompletionOptions options = new()
{
OutputPrediction = ChatOutputPrediction.CreateStaticContentPrediction(code),
};
await foreach (
StreamingChatCompletionUpdate update in client.CompleteChatStreamingAsync(
[
new UserChatMessage(
"Replace the username property with an email property. Respond only with code, and with no markdown formatting."
),
new UserChatMessage(code),
],
options
)
)
{
foreach (ChatMessageContentPart part in update.ContentUpdate)
{
Console.Write(part.Text);
}
}
require "openai"
client = OpenAI::Client.new
code = <<~CODE
class User {
firstName: string = "";
lastName: string = "";
username: string = "";
}
export default User;
CODE
refactor_prompt = <<~PROMPT
Replace the "username" property with an "email" property. Respond only
with code, and with no markdown formatting.
PROMPT
stream = client.chat.completions.stream(
model: "gpt-4.1",
messages: [
{
role: :user,
content: refactor_prompt
},
{
role: :user,
content: code
}
],
prediction: {
type: :content,
content: code
},
store: true
)
stream.text.each { |text| print(text) }
응답에서 예측 텍스트의 위치
예측 텍스트를 제공할 때 예측값은 생성된 응답의 어느 위치에나 나타날 수 있으며, 여전히 응답의 지연 시간을 줄여줍니다. 예측 텍스트가 아래에 표시된 간단한 Hono 서버라고 가정해 보세요:
import { serve } from "@hono/node-server";
import { serveStatic } from "@hono/node-server/serve-static";
import { Hono } from "hono";
const app = new Hono();
app.get("/api", (c) => {
return c.text("Hello Hono!");
});
// You will need to build the client code first: `pnpm run ui:build`.
app.use(
"/*",
serveStatic({
rewriteRequestPath: (path) => `./dist${path}`,
})
);
const port = 3000;
console.log(`Server is running on port ${port}`);
serve({
fetch: app.fetch,
port,
});
다음과 같은 프롬프트로 모델에 파일 재생성을 요청할 수 있습니다:
Add a get route to this application that responds with
the text "hello world". Generate the entire application
file again with this route added, and with no other
markdown formatting.
프롬프트에 대한 응답은 이렇게 보일 수 있습니다:
import { serve } from "@hono/node-server";
import { serveStatic } from "@hono/node-server/serve-static";
import { Hono } from "hono";
const app = new Hono();
app.get("/api", (c) => {
return c.text("Hello Hono!");
});
app.get("/hello", (c) => {
return c.text("hello world");
});
// You will need to build the client code first: `pnpm run ui:build`.
app.use(
"/*",
serveStatic({
rewriteRequestPath: (path) => `./dist${path}`,
})
);
const port = 3000;
console.log(`Server is running on port ${port}`);
serve({
fetch: app.fetch,
port,
});
choices 필드가 없는 축약된 모델 응답은, 예측 텍스트가 응답에 추가된 새 내용 앞뒤로 모두 나타났더라도 여전히 수용된 예측 토큰(accepted prediction tokens)을 보여줍니다:
{
"id": "chatcmpl-xxx",
"object": "chat.completion",
"created": 1731014771,
"model": "gpt-4o-2024-08-06",
"usage": {
"prompt_tokens": 203,
"completion_tokens": 159,
"total_tokens": 362,
"prompt_tokens_details": { "cached_tokens": 0, "audio_tokens": 0 },
"completion_tokens_details": {
"reasoning_tokens": 0,
"audio_tokens": 0,
"accepted_prediction_tokens": 60,
"rejected_prediction_tokens": 0
}
},
"system_fingerprint": "fp_9ee9e968ea"
}
이번에는 예측한 파일의 전체 내용이 최종 응답에 사용되었으므로 거부된 예측 토큰이 없습니다. 좋네요! 🔥
제한 사항
Predicted Outputs를 사용할 때 다음 요소와 제한 사항을 고려해야 합니다.
- Predicted Outputs는 GPT-4o, GPT-4o-mini, GPT-4.1, GPT-4.1-mini, GPT-4.1-nano 모델 계열에서만 지원됩니다.
- 예측을 제공할 때 최종 completion에 포함되지 않는 토큰은 여전히 completion 토큰 요율로 청구됩니다. 최종 응답에 사용되지 않은 토큰 수는
usage객체의rejected_prediction_tokens속성을 참고하세요. - Predicted Outputs를 사용할 때 다음 API 매개변수는 지원되지 않습니다:
n: 1보다 큰 값은 지원되지 않습니다.logprobs: 지원되지 않습니다.presence_penalty: 0보다 큰 값은 지원되지 않습니다.frequency_penalty: 0보다 큰 값은 지원되지 않습니다.audio: Predicted Outputs는 오디오 입력 및 출력과 호환되지 않습니다.modalities:text모달리티만 지원됩니다.max_completion_tokens: 지원되지 않습니다.tools: Predicted Outputs에서는 현재 함수 호출(Function calling)이 지원되지 않습니다.