为支持长时间交互,您可以使用压缩来缩减上下文大小,同时保留后续轮次所需的状态。
随着对话不断增长,压缩可以帮助您平衡质量、成本和延迟。
您可以在 Responses 创建请求
(POST /responses 或 client.responses.create)中设置
context_management,并在其中配置 compact_threshold,以启用服务端压缩。
- 当渲染后的 Token 数量超过配置的阈值时,服务器会执行服务端压缩。
- 在此模式下,无需单独调用
/responses/compact。
- 响应流中包含加密的压缩项。
- ZDR 说明:当您在 Responses 创建请求中设置
store=false 时,
服务端压缩可兼容 ZDR。
返回的压缩项以更少的 Token 将先前的关键状态和推理传递到下一次运行。其内容是不透明的,并非供人阅读理解。
对于通过输入数组串联的无状态请求,照常追加输出项即可。
如果您使用 previous_response_id,则每轮只需传入新的用户消息。
在这两种情况下,压缩项都会携带下一个窗口所需的上下文。
延迟优化建议:将输出项追加到之前的输入项之后,您可以
删除最近一个压缩项之前的所有项,以缩小请求体积,
降低长尾延迟。最新的压缩项包含
继续对话所需的上下文。如果您使用
previous_response_id 串联请求,请勿手动删减。
- 照常调用
/responses,同时在请求中添加 context_management,并在其中配置
compact_threshold,以启用服务端压缩。
- 在响应流式传输过程中,如果上下文大小超过阈值,服务器会触发一次压缩,在同一响应流中输出一个压缩项,并在继续推断前删减上下文。
- 选择一种方式继续循环:通过输入数组串联无状态请求(将输出
追加到下一个输入数组中,包括压缩项),或
使用
previous_response_id 串联请求(每轮只传入新的用户消息,
并将该 ID 传递到后续请求中)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22import OpenAI from "openai";
import { toResponseInputItems } from "openai/lib/responses/ResponseInputItems";
const client = new OpenAI();
const conversation = [
{
type: "message",
role: "user",
content: "Let's begin a long coding task.",
},
];
const response = await client.responses.create({
model: "gpt-5.3-codex",
input: conversation,
store: false,
context_management: [{ type: "compaction", compact_threshold: 200_000 }],
});
conversation.push(...toResponseInputItems(response.output));
console.log(response.output_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
25conversation = [
{
"type": "message",
"role": "user",
"content": "Let's begin a long coding task.",
}
]
while keep_going:
response = client.responses.create(
model="gpt-5.3-codex",
input=conversation,
store=False,
context_management=[{"type": "compaction", "compact_threshold": 200000}],
)
conversation.extend(response.output)
conversation.append(
{
"type": "message",
"role": "user",
"content": get_next_user_input(),
}
)
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
53
54
55
56package main
import (
"bufio"
"context"
"encoding/json"
"fmt"
"os"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
conversation := []responses.ResponseInputItemUnionParam{
responses.ResponseInputItemParamOfMessage("Let's begin a long coding task.", responses.EasyInputMessageRoleUser),
}
scanner := bufio.NewScanner(os.Stdin)
for {
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-5.3-codex",
Store: openai.Bool(false),
Input: responses.ResponseNewParamsInputUnion{OfInputItemList: conversation},
ContextManagement: []responses.ResponseNewParamsContextManagement{{
Type: "compaction", CompactThreshold: openai.Int(200000),
}},
})
if err != nil {
panic(err)
}
conversation = append(conversation, outputAsInput(response.Output)...)
fmt.Println(response.OutputText())
if !scanner.Scan() {
break
}
conversation = append(conversation,
responses.ResponseInputItemParamOfMessage(scanner.Text(), responses.EasyInputMessageRoleUser),
)
}
if err := scanner.Err(); err != nil {
panic(err)
}
}
func outputAsInput(output []responses.ResponseOutputItemUnion) []responses.ResponseInputItemUnionParam {
input := make([]responses.ResponseInputItemUnionParam, 0, len(output))
for _, item := range output {
var converted responses.ResponseInputItemUnion
if err := json.Unmarshal([]byte(item.RawJSON()), &converted); err != nil {
panic(err)
}
input = append(input, converted.ToParam())
}
return input
}
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
48import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.JsonValue;
import com.openai.models.responses.EasyInputMessage;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.ResponseInputItem;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
var conversation = new ArrayList<ResponseInputItem>();
conversation.add(
ResponseInputItem.ofEasyInputMessage(
EasyInputMessage.builder()
.role(EasyInputMessage.Role.USER)
.content("Let's begin a long coding task.")
.build()));
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-5.3-codex")
.inputOfResponse(conversation)
.store(false)
.putAdditionalBodyProperty(
"context_management",
JsonValue.from(List.of(Map.of("type", "compaction", "compact_threshold", 200000))))
.build();
var response = client.responses().create(params);
response.output().stream()
.map(item -> JsonValue.from(item).convert(ResponseInputItem.class))
.forEach(conversation::add);
conversation.add(
ResponseInputItem.ofEasyInputMessage(
EasyInputMessage.builder()
.role(EasyInputMessage.Role.USER)
.content("Now implement the next step.")
.build()));
client
.responses()
.create(params.toBuilder().inputOfResponse(conversation).build())
.output()
.stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.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
37
38
39
40require "openai"
client = OpenAI::Client.new
conversation = [
{
type: :message,
role: :user,
content: "Let's begin a long coding task."
}
]
response = client.responses.create(
model: "gpt-5.3-codex",
input: conversation,
store: false,
context_management: [
{
type: :compaction,
compact_threshold: 200_000
}
]
)
conversation.concat(response.output)
conversation << {
type: :message,
role: :user,
content: "Now implement the next step."
}
next_response = client.responses.create(
model: "gpt-5.3-codex",
input: conversation,
store: false,
context_management: [
{
type: :compaction,
compact_threshold: 200_000
}
]
)
puts(next_response.output_text)
如果需要显式控制压缩,请使用
独立压缩端点,
在长时间运行的工作流中执行无状态压缩。
此端点完全无状态,且可兼容 ZDR。
您发送完整的上下文窗口(消息、工具及其他项),
端点便会返回一个新的压缩后上下文窗口,您可以将其传入下一次
/responses 调用。
返回的压缩后窗口包含一个加密的压缩项,以更少的 Token 传递先前的关键状态和推理。该压缩项的内容是不透明的,并非供人阅读理解。
注意:压缩后的窗口通常不止包含压缩项,还可能包含从上一个窗口中保留下来的项。
输出处理:请勿删减 /responses/compact 的输出。
返回的窗口就是下一次应使用的标准上下文窗口,因此请将其原样传入下一次 /responses
调用。
- 正常使用
/responses,发送的输入项包括用户消息、
助手输出和工具交互。
- 当上下文窗口变大时,调用
/responses/compact,
生成一个新的压缩后上下文窗口。您发送到 /responses/compact 的窗口
仍必须处于模型的上下文窗口容量限制之内。
- 在后续的
/responses 调用中,将返回的压缩后窗口
(包括压缩项)作为输入传入,替代完整的对话记录。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23import OpenAI from "openai";
const client = new OpenAI();
const conversation = [{ role: "user", content: "Plan a trip to Kyoto." }];
const compacted = await client.responses.compact({
model: "gpt-6-astra",
input: conversation,
});
const nextInput = [
...compacted.output.map((item) => item),
{ role: "user", content: "Add two more days to the itinerary." },
];
const response = await client.responses.create({
model: "gpt-6-astra",
input: nextInput,
store: false,
});
console.log(response.output_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# Full window collected from prior turns
long_input_items_array = [{"role": "user", "content": "Plan a trip to Kyoto."}]
# 1) Compact the current window
compacted = client.responses.compact(
model="gpt-6-astra",
input=long_input_items_array,
)
# 2) Start the next turn by appending a new user message
next_input = [
*compacted.output, # Use compact output as-is
{
"type": "message",
"role": "user",
"content": user_input_message(),
},
]
next_response = client.responses.create(
model="gpt-6-astra",
input=next_input,
store=False, # Keep the flow ZDR-friendly
)
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
53
54package main
import (
"bufio"
"context"
"encoding/json"
"fmt"
"os"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
longInputItems := []responses.ResponseInputItemUnionParam{
responses.ResponseInputItemParamOfMessage("Plan a trip to Kyoto.", responses.EasyInputMessageRoleUser),
}
compacted, err := client.Responses.Compact(context.Background(), responses.ResponseCompactParams{
Model: "gpt-6-astra",
Input: responses.ResponseCompactParamsInputUnion{OfResponseInputItemArray: longInputItems},
})
if err != nil {
panic(err)
}
scanner := bufio.NewScanner(os.Stdin)
if !scanner.Scan() {
return
}
nextInput := append(outputAsInput(compacted.Output),
responses.ResponseInputItemParamOfMessage(scanner.Text(), responses.EasyInputMessageRoleUser),
)
nextResponse, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Store: openai.Bool(false),
Input: responses.ResponseNewParamsInputUnion{OfInputItemList: nextInput},
})
if err != nil {
panic(err)
}
fmt.Println(nextResponse.OutputText())
}
func outputAsInput(output []responses.ResponseOutputItemUnion) []responses.ResponseInputItemUnionParam {
input := make([]responses.ResponseInputItemUnionParam, 0, len(output))
for _, item := range output {
var converted responses.ResponseInputItemUnion
if err := json.Unmarshal([]byte(item.RawJSON()), &converted); err != nil {
panic(err)
}
input = append(input, converted.ToParam())
}
return input
}
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
52import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.responses.EasyInputMessage;
import com.openai.models.responses.ResponseCompactParams;
import com.openai.models.responses.ResponseCompactionItemParam;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.ResponseInputItem;
import java.util.ArrayList;
var compacted =
client
.responses()
.compact(
ResponseCompactParams.builder()
.model("gpt-6-astra")
.input("Plan a trip to Kyoto.")
.build());
var input = new ArrayList<ResponseInputItem>();
for (var item : compacted.output()) {
item.message().map(ResponseInputItem::ofResponseOutputMessage).ifPresent(input::add);
item.reasoning().map(ResponseInputItem::ofReasoning).ifPresent(input::add);
item.compaction()
.map(
value ->
ResponseInputItem.ofCompaction(
ResponseCompactionItemParam.builder()
.id(value.id())
.encryptedContent(value.encryptedContent())
.build()))
.ifPresent(input::add);
}
input.add(
ResponseInputItem.ofEasyInputMessage(
EasyInputMessage.builder()
.role(EasyInputMessage.Role.USER)
.content("Add restaurant recommendations.")
.build()));
client
.responses()
.create(
ResponseCreateParams.builder()
.model("gpt-6-astra")
.inputOfResponse(input)
.store(false)
.build())
.output()
.stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.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
27require "openai"
client = OpenAI::Client.new
long_input = [
{
role: :user,
content: "Plan a trip to Kyoto."
}
]
compaction = client.responses.compact(
model: "gpt-6-astra",
input: long_input
)
next_input = [
*compaction.output,
{
type: :message,
role: :user,
content: "Add restaurant recommendations."
}
]
response = client.responses.create(
model: "gpt-6-astra",
input: next_input,
store: false
)
puts(response.output_text)