Execute tarefas de longa duração de forma assíncrona em segundo plano.
Agentes como o Codex e a pesquisa aprofundada mostram que modelos de raciocínio podem levar vários minutos para resolver problemas complexos. O modo em segundo plano permite executar tarefas de longa duração de forma confiável com modelos como GPT-5.2 e GPT-5.2 Pro, sem precisar se preocupar com limites de tempo ou outros problemas de conexão.
O modo em segundo plano inicia essas tarefas de forma assíncrona. Os desenvolvedores podem consultar os objetos de resposta periodicamente para acompanhar o status das tarefas. Para começar a gerar uma resposta em segundo plano, faça uma requisição à API com background definido como true.
As requisições em segundo plano de projetos com zero retenção de dados (ZDR) são executadas com
store=false. Os dados da resposta ficam armazenados temporariamente em disco por cerca de 10
minutos para permitir a execução assíncrona e as consultas periódicas.
Em projetos que usam o monitoramento modificado de abusos, inclusive em sua versão aprimorada, as requisições em primeiro plano seguem a política de retenção padrão quando você omite store ou o define como true. As respostas em segundo plano só ficam armazenadas após o período de consulta quando você informa store=true explicitamente. Se você omitir store ou o definir como false em uma requisição em segundo plano, a resposta será excluída após cerca de 10 minutos.
Gerar uma resposta em segundo plano
Python
1
2
3
4
5
6
7
8curl https://api.openai.com/v1/responses \-H "Content-Type: application/json" \-H "Authorization: Bearer $OPENAI_API_KEY" \-d '{ "model": "gpt-6-astra", "input": "Write a very long novel about otters in space.", "background": true}'
1
2
3
4
5
6
7
8
9
10import OpenAI from "openai";const client = new OpenAI();const resp = await client.responses.create({ model: "gpt-6-astra", input: "Write a very long novel about otters in space.", background: true,});console.log(resp.status);
1
2
3
4
5
6
7
8
9
10
11from openai import OpenAIclient = OpenAI()resp = client.responses.create(model="gpt-6-astra",input="Write a very long novel about otters in space.",background=True,)print(resp.status)
Consultar periodicamente as respostas em segundo plano
Para verificar o status das requisições em segundo plano, use o endpoint GET da API Responses. Repita a consulta enquanto a requisição estiver no estado queued ou in_progress. Quando sair desses estados, ela terá atingido um estado final (terminal).
Recuperar uma resposta em execução em segundo plano
Cancelar a mesma resposta duas vezes é uma operação idempotente. As chamadas seguintes apenas retornam o objeto Response em seu estado final.
Transmitir uma resposta em segundo plano
Você pode criar um objeto Response em segundo plano e começar a receber seus eventos por streaming imediatamente. Isso pode ser útil se você prevê que o cliente interromperá a transmissão e quer poder retomá-la depois. Para isso, crie um objeto Response com background e stream definidos como true. Atualize o “cursor” com o valor de sequence_number recebido em cada evento da transmissão.
Atualmente, o primeiro token de uma resposta em segundo plano demora mais para chegar do que o de uma resposta síncrona. Estamos trabalhando para reduzir essa diferença de latência nas próximas semanas.
Gerar e transmitir uma resposta em segundo plano
curl
1
2
3
4
5
6
7
8
9
10
11
12
13
14curlhttps://api.openai.com/v1/responses\-H "Content-Type: application/json"\-H "Authorization: Bearer $OPENAI_API_KEY"\-d '{ "model": "gpt-6-astra", "input": "Write a very long novel about otters in space.", "background": true, "stream": true}'//Toresume:curl"https://api.openai.com/v1/responses/resp_123?stream=true&starting_after=42"\-H "Content-Type: application/json"\-H "Authorization: Bearer $OPENAI_API_KEY"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25import OpenAI from "openai";const client = new OpenAI();const stream = await client.responses.create({ model: "gpt-6-astra", input: "Write a very long novel about otters in space.", background: true, stream: true,});let cursor = null;let responseId = null;for await (const event of stream) { console.log(event); // Keepalives use connection counters, not stored-event resume positions. if (String(event.type) !== "keepalive") cursor = event.sequence_number; if ("response" in event && event.response?.id) responseId = event.response.id;}// If the connection drops, resume streaming from the last stored-event cursor:// const resumedStream = await client.responses.retrieve(responseId, {// stream: true,// starting_after: cursor,// });// for await (const event of resumedStream) { ... }
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
28from openai import OpenAIclient = OpenAI()# Fire off an async response but also start streaming immediatelystream = client.responses.create( model="gpt-6-astra", input="Write a very long novel about otters in space.", background=True, stream=True,)cursor = Noneresponse_id = Nonefor event in stream: print(event) if event.type == "response.created": response_id = event.response.id # Keepalives use connection counters, not stored-event resume positions. if event.type != "keepalive": cursor = event.sequence_number # If your connection drops, the response continues running and you can reconnect: # with client.responses.stream(response_id=response_id, starting_after=cursor) as resumed: # for event in resumed: # print(event) if event.type == "response.created": response_id = event.response.id
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
49package mainimport ( "context" "fmt" "github.com/openai/openai-go/v3" "github.com/openai/openai-go/v3/responses")func main() { client := openai.NewClient() stream := client.Responses.NewStreaming(context.Background(), responses.ResponseNewParams{ Model: "gpt-6-astra", Background: openai.Bool(true), Input: responses.ResponseNewParamsInputUnion{ OfString: openai.String("Write a very long novel about otters in space."), }, }) defer stream.Close() var cursor int64 var responseID string for stream.Next() { event := stream.Current() fmt.Println(event.Type) // Keepalives use connection counters, not stored-event resume positions. if event.Type != "keepalive" { cursor = event.SequenceNumber } if event.Response.ID != "" { responseID = event.Response.ID } } if err := stream.Err(); err != nil { panic(err) } fmt.Printf("response %s last cursor %d\n", responseID, cursor) // If the connection drops, resume streaming from the last cursor: // resumed := client.Responses.GetStreaming( // context.Background(), // responseID, // responses.ResponseGetParams{StartingAfter: openai.Int(cursor)}, // ) // for resumed.Next() { // fmt.Println(resumed.Current().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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106import com.fasterxml.jackson.databind.json.JsonMapper;import com.openai.client.OpenAIClient;import com.openai.client.okhttp.OpenAIOkHttpClient;import com.openai.core.RequestOptions;import com.openai.core.http.StreamResponse;import com.openai.errors.OpenAIIoException;import com.openai.models.responses.ResponseCreateParams;import com.openai.models.responses.ResponseRetrieveParams;import com.openai.models.responses.ResponseStreamEvent;import java.time.Duration;import java.util.concurrent.atomic.AtomicBoolean;import java.util.concurrent.atomic.AtomicLong;import java.util.concurrent.atomic.AtomicReference;ResponseCreateParams params = ResponseCreateParams.builder() .model("gpt-6-astra") .input("Write a very long novel about otters in space.") .background(true) .build();AtomicLong lastSequenceNumber = new AtomicLong(-1);AtomicReference<String> responseId = new AtomicReference<>("");AtomicBoolean streamCompleted = new AtomicBoolean(false);JsonMapper json = new JsonMapper();// This long-running response can exceed the SDK's default request deadline.RequestOptions streamOptions = RequestOptions.builder().timeout(Duration.ofMinutes(30)).build();try (StreamResponse<ResponseStreamEvent> stream = client.responses().createStreaming(params, streamOptions)) { stream.stream() .forEach( event -> { if (event.failed().isPresent() || event.incomplete().isPresent() || event.error().isPresent()) { throw new IllegalStateException("Background response did not complete"); } var data = json.valueToTree(event); // Keepalives count connection traffic, not stored response events. if (data.path("type").asText().startsWith("response.") && data.path("sequence_number").isIntegralNumber()) { lastSequenceNumber.set(data.path("sequence_number").asLong()); } event .created() .ifPresent( created -> { responseId.set(created.response().id()); System.out.println("response.created"); }); event .outputTextDelta() .ifPresent( delta -> { System.out.println("response.output_text.delta"); }); event .completed() .ifPresent( completed -> { streamCompleted.set(true); System.out.println("response.completed"); }); });} catch (OpenAIIoException error) { // Resume an interrupted stream once, from its stored-event checkpoint. if (Thread.currentThread().isInterrupted() || streamCompleted.get() || responseId.get().isBlank() || lastSequenceNumber.get() < 0) { throw error; }}System.out.println( "Response " + responseId.get() + "; last sequence number " + lastSequenceNumber.get());if (!streamCompleted.get()) { if (Thread.currentThread().isInterrupted() || responseId.get().isBlank() || lastSequenceNumber.get() < 0) { throw new IllegalStateException("Stream ended without a resumable checkpoint"); } try (StreamResponse<ResponseStreamEvent> resumed = client .responses() .retrieveStreaming( ResponseRetrieveParams.builder() .responseId(responseId.get()) .startingAfter(lastSequenceNumber.get()) .build(), streamOptions)) { resumed.stream() .forEach( event -> { if (event.failed().isPresent() || event.incomplete().isPresent() || event.error().isPresent()) { throw new IllegalStateException("Resumed response did not complete"); } event.outputTextDelta().ifPresent(delta -> System.out.println(delta.delta())); event.completed().ifPresent(completed -> streamCompleted.set(true)); }); } if (!streamCompleted.get()) { throw new IllegalStateException("Resumed response did not complete"); }}
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82using OpenAI.Responses;#pragma warning disable OPENAI001string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;ResponsesClient client = new(key);CreateResponseOptions options = new(){ Model = "gpt-6-astra", BackgroundModeEnabled = true, StreamingEnabled = true,};options.InputItems.Add( ResponseItem.CreateUserMessageItem("Write a very long novel about otters in space."));string? responseId = null;int lastSequenceNumber = -1;bool completed = false;void HandleUpdate(StreamingResponseUpdate update){ lastSequenceNumber = update.SequenceNumber; switch (update) { case StreamingResponseCreatedUpdate created: responseId = created.Response.Id; break; case StreamingResponseOutputTextDeltaUpdate text: Console.Write(text.Delta); break; case StreamingResponseCompletedUpdate: completed = true; break; case StreamingResponseFailedUpdate: throw new InvalidOperationException("The background response failed."); case StreamingResponseIncompleteUpdate: throw new InvalidOperationException("The background response was incomplete."); case StreamingResponseErrorUpdate error: throw new InvalidOperationException($"The response stream failed: {error.Message}"); }}try{ await foreach ( StreamingResponseUpdate update in client.CreateResponseStreamingAsync(options) ) { HandleUpdate(update); }}catch (Exception error) when (error is HttpRequestException or IOException && responseId is not null){ // The background response continues after its streaming connection is interrupted.}if (!completed){ if (responseId is null) { throw new InvalidOperationException("The response stream ended before providing its ID."); } GetResponseOptions resumeOptions = new(responseId) { StartingAfter = lastSequenceNumber, StreamingEnabled = true, }; await foreach (StreamingResponseUpdate update in client.GetResponseStreamingAsync(resumeOptions)) { HandleUpdate(update); } if (!completed) { throw new InvalidOperationException( "The resumed response stream ended before the background response completed." ); }}
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
31require "openai"# An uncapped background novel can run beyond the SDK's default ten minutes.client = OpenAI::Client.new(timeout: 30 * 60)stream = client.responses.stream( model: "gpt-6-astra", input: "Write a very long novel about otters in space.", background: true)last_sequence_number = -1response_id = ""begin stream.each do |event| puts(event.type) # The SDK filters transport keepalives before yielding stored events. last_sequence_number = event.sequence_number || last_sequence_number if event.is_a?(OpenAI::Models::Responses::ResponseCreatedEvent) response_id = event.response.id end endensure stream.closeendputs("response #{response_id} last cursor #{last_sequence_number}")# If the connection drops, resume from the last sequence number:# client.responses.stream(response_id: response_id, starting_after: last_sequence_number).each do |event|# puts(event.type)# end
Limites
Requisições em segundo plano podem usar store=false, mas os dados da resposta ficam armazenados temporariamente para permitir a execução assíncrona e as consultas periódicas.
Para cancelar uma resposta síncrona, encerre a conexão.
Você só pode iniciar uma nova transmissão a partir de uma resposta em segundo plano se tiver criado essa resposta com stream=true.