當許多輸出 Token 已預先得知時,預測輸出 可加快 Chat Completions 的 API 回應速度。最常見的情境是對文字或程式碼檔案稍作修改,再重新生成整份檔案。你可以透過 Chat Completions 中的 prediction 請求參數 提供預測內容。
目前,最新的 gpt-4o、gpt-4o-mini、gpt-4.1、gpt-4.1-mini 和 gpt-4.1-nano 模型均可使用預測輸出。請繼續閱讀,瞭解如何使用預測輸出降低應用程式的延遲。
預測輸出特別適合用於稍作修改後重新生成文字文件與程式碼檔案。假設你想讓 GPT-4o 模型 重構一段 JavaScript 程式碼,將 User 類別的 username 屬性改為 email:
1
2
3
4
5
6
7 class User {
firstName = "" ;
lastName = "" ;
username = "" ;
}
export default User;
除了上方第 4 行之外,檔案的大部分內容都會保持不變。如果使用程式碼檔案的現有文字作為預測內容,就能以較低的延遲重新生成整份檔案。檔案越大,累積節省的時間就越可觀。
以下範例示範如何在我們的 SDK 中使用 prediction 參數。我們預期模型的最終輸出會與原始程式碼檔案非常相似,因此使用該檔案作為預測文字。
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 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); 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 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) 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 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)
} 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 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); 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 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); 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 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) 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 curl https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-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 欄位後的精簡回應如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 {
"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 個 Token 用於加快回應速度,另有 2 個遭到拒絕。
請注意,遭到拒絕的 Token 仍會比照 API 生成的其他補全 Token 計費,因此預測輸出可能會增加請求費用。
當 API 回應使用串流時,預測輸出降低延遲的效果會更顯著。以下範例沿用相同的程式碼重構情境,但改用 OpenAI SDK 的串流功能。
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 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 || "" );
} 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 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="") 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 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)
}
} 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 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);
} 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 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);
}
} 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 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 伺服器程式碼:
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 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.
模型對該提示詞的回應可能如下:
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 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 欄位後的精簡模型回應仍會顯示已接受的預測 Token:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 {
"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"
}
這次沒有任何預測 Token 遭到拒絕,因為我們提供作為預測的整份檔案內容都用於最終回應。太棒了!🔥
使用預測輸出時,應考量以下因素與限制。
只有 GPT-4o、GPT-4o-mini、GPT-4.1、GPT-4.1-mini 和 GPT-4.1-nano 系列模型支援預測輸出。
提供預測內容時,其中未納入最終補全內容的 Token 仍會按補全 Token 費率計費。請查看 usage 物件的 rejected_prediction_tokens 屬性 ,瞭解有多少 Token 未用於最終回應。
使用預測輸出時,不支援以下 API 參數 :
n:不支援大於 1 的值
logprobs:不支援
presence_penalty:不支援大於 0 的值
frequency_penalty:不支援大於 0 的值
audio:預測輸出無法與音訊輸入及輸出 搭配使用
modalities:僅支援 text 模態
max_completion_tokens:不支援
tools:預測輸出目前不支援函式呼叫