OpenAI 上下文压缩

2026-11-06 18:37 更新

概览

上下文压缩(compaction)缩小长期交互的上下文,同时保留后续轮次所需状态,帮助平衡质量、费用与延迟。

服务端压缩

创建Responses请求(POST /responses或client.responses.create)时,在context_management设置compact_threshold:

  • 渲染后的词元数跨过阈值时,服务端执行压缩。
  • 此模式无需单独调用/responses/compact。
  • 响应流包含加密的压缩项。
  • 创建请求设置store=false时,这种服务端压缩适用于ZDR场景。

压缩项以较少词元携带关键历史状态和推理,是不透明数据,不供人直接阅读。

无状态输入数组续接时,照常追加输出项;使用previous_response_id时,每轮只发送新用户消息并传递前一响应ID。两者均由压缩项携带下一窗口需要的上下文。

减少长尾延迟的方法:采用输入数组续接时,追加输出后可丢弃最新压缩项之前的项目,保留最新压缩项及其后内容。使用previous_response_id链时,不要手动裁剪。

使用流程

  1. 正常调用/responses,附上context_management与compact_threshold。
  2. 流式处理中跨过阈值后,服务端压缩,在同一流发出压缩输出项,裁剪上下文后继续推理。
  3. 后续固定采用一种方式:无状态数组追加完整输出(含压缩项),或previous_response_id续接(仅新消息加前一ID)。

使用流程示例

来源示例 1(javascript):

// [中文] 用context_management的compaction和阈值200000进行服务端压缩;保留输出项继续会话。示例开场为开始长期编程任务。 先订阅标准输入避免管道消息丢失,读取非空行逐轮回应并关闭接口。
import OpenAI from "openai";
import { toResponseInputItems } from "openai/lib/responses/ResponseInputItems";
import { createInterface } from "node:readline";

const client = new OpenAI();

const conversation = [
  {
    type: "message",
    role: "user",
    content: "Let's begin a long coding task.",
  },
];

// Subscribe before generation so piped follow-up turns are not lost.
const lines = createInterface({ input: process.stdin, crlfDelay: Infinity });
const turns = lines[Symbol.asyncIterator]();

async function respond() {
  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);
}

try {
  await respond();
  for await (const line of turns) {
    if (!line.trim()) continue;
    conversation.push({ type: "message", role: "user", content: line });
    await respond();
  }
} finally {
  lines.close();
}

来源示例 2(python):

## [中文] 用context_management的compaction和阈值200000进行服务端压缩;保留输出项继续会话。示例开场为开始长期编程任务。 client与keep_going由外围应用提供,片段不是独立完整程序。
conversation = [
    {
        "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)

    try:
        next_input = get_next_user_input()
    except EOFError:
        break
    conversation.append(
        {
            "type": "message",
            "role": "user",
            "content": next_input,
        }
    )

来源示例 3(go):

// [中文] 用context_management的compaction和阈值200000进行服务端压缩;保留输出项继续会话。示例开场为开始长期编程任务。
package 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
}

来源示例 4(java):

// [中文] 用context_management的compaction和阈值200000进行服务端压缩;保留输出项继续会话。示例开场为开始长期编程任务。
import 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()));

来源示例 5(ruby):

## [中文] 用context_management的compaction和阈值200000进行服务端压缩;保留输出项继续会话。示例开场为开始长期编程任务。
require "openai"

client = OpenAI::Client.new
conversation = [
  {
    type: :message,
    role: :user,
    content: "Let's begin a long coding task."
  }
]

loop do
  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)
  puts(response.output_text)
  line = $stdin.gets
  break unless line

  conversation << {
    type: :message,
    role: :user,
    content: line.chomp
  }
end

JavaScript示例在生成前订阅标准输入,读取非空行后继续;Python在EOF时结束循环;Ruby逐轮打印输出并读取下一行,标准输入结束时退出。

独立压缩端点

需要显式控制时,使用独立压缩端点。它完全无状态,适用于ZDR。

发送完整上下文窗口(消息、工具及其他项),端点返回可用于下一次/responses的压缩窗口,其中含携带关键状态与推理的加密项。返回窗口通常不只有压缩项,还可能保留原窗口中的其他项目。

不要裁剪/responses/compact返回的窗口。它是下一次请求应采用的规范上下文,应原样传入。这与服务端自动压缩后对输入数组的可选裁剪不是同一操作。

独立压缩使用流程

  1. 正常调用/responses,输入用户消息、助手输出和工具交互。
  2. 上下文变大时调用/responses/compact;传入窗口仍必须容纳于模型上下文限制内。
  3. 后续调用以返回的完整压缩窗口(含压缩项)替代全部历史。

使用流程示例

来源示例 6(javascript):

// [中文] 先独立compact完整历史,再将全部compacted.output与新消息交给下一请求;任务为规划京都旅行。
import 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);

来源示例 7(python):

## [中文] 先独立compact完整历史,再将全部compacted.output与新消息交给下一请求;任务为规划京都旅行。
## 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
)

来源示例 8(go):

// [中文] 先独立compact完整历史,再将全部compacted.output与新消息交给下一请求;任务为规划京都旅行。
package 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
}

来源示例 9(java):

// [中文] 先独立compact完整历史,再将全部compacted.output与新消息交给下一请求;任务为规划京都旅行。
import 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()));

来源示例 10(ruby):

## [中文] 先独立compact完整历史,再将全部compacted.output与新消息交给下一请求;任务为规划京都旅行。
require "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)
以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号