파일 전사

파일 전사 (File transcription)

녹음이 이미 완료됐거나, 한정된 길이의 오디오 요청이라면 파일 전사(file transcription) 방식을 사용해요. 오디오를 업로드하면 최종 전사 결과를 받거나, 모델이 파일을 처리하는 동안 텍스트를 스트리밍으로 받을 수 있어요.

시작은 gpt-transcribe 모델로 하는 걸 추천해요. 녹음된 음성을 원래 언어로 전사하는 데 권장되는 모델이에요. 화자 라벨, 단어 타임스탬프, 자막 형식, 영어 번역이 필요할 때만 특수 모델을 쓰면 됩니다.

파일은 최대 25 MB까지 올릴 수 있어요. 지원되는 입력 형식은 mp3, mp4, mpeg, mpga, m4a, wav, webm이에요.

마이크, 통화, 미디어 스트림에서 아직 들어오고 있는 오디오라면 Realtime 전사를 사용해야 해요.

출처: 공식문서

빠른 시작 (Quickstart)

전사 (Transcriptions)

오디오 파일을 gpt-transcribe 모델과 함께 /v1/audio/transcriptions로 보내면 돼요.

오디오 전사하기

import fs from "fs";
import OpenAI from "openai";

const openai = new OpenAI();

const transcription = await openai.audio.transcriptions.create({
  file: fs.createReadStream("fixtures/audio.wav"),
  model: "gpt-transcribe",
});

console.log(transcription.text);
from openai import OpenAI

client = OpenAI()
audio_file = open("audio.wav", "rb")

transcription = client.audio.transcriptions.create(
    model="gpt-transcribe", file=audio_file
)

print(transcription.text)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/openai/openai-go/v3"
)

func main() {
	file, err := os.Open("fixtures/audio.wav")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	client := openai.NewClient()
	transcription, err := client.Audio.Transcriptions.New(context.Background(), openai.AudioTranscriptionNewParams{
		File:  file,
		Model: "gpt-transcribe",
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(transcription.Text)
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.audio.transcriptions.TranscriptionCreateParams;
import java.nio.file.Path;

var result =
    client
        .audio()
        .transcriptions()
        .create(
            TranscriptionCreateParams.builder()
                .file(Path.of(System.getenv("OPENAI_EXAMPLE_AUDIO_PATH")))
                .model("gpt-transcribe")
                .build());

System.out.println(result.asTranscription().text());
using OpenAI.Audio;

string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
string model = "gpt-transcribe";
AudioClient client = new(model, key);

await using FileStream audio = File.OpenRead("audio.wav");
AudioTranscription transcription = await client.TranscribeAudioAsync(
    audio,
    "audio.wav"
);

Console.WriteLine(transcription.Text);
require "openai"
require "pathname"

client = OpenAI::Client.new
audio = Pathname("audio.wav")
transcript = client.audio.transcriptions.create(
  file: audio,
  model: "gpt-transcribe"
)
puts(transcript.text)
openai audio:transcriptions create \
  --model gpt-transcribe \
  --file /path/to/file/audio.mp3 \
  --raw-output \
  --transform text
curl --request POST \
  --url https://api.openai.com/v1/audio/transcriptions \
  --header "Authorization: Bearer ***" \
  --header 'Content-Type: multipart/form-data' \
  --form file=@/path/to/file/audio.mp3 \
  --form model=gpt-transcribe

모델은 전사 결과와 감지된 언어를 JSON으로 반환해요.

{
  "text": "Bonjour, pouvez-vous m'entendre ?",
  "languages": [{ "code": "fr" }]
}

모델이 신뢰할 수 있는 언어 예측을 하지 못하면 "languages": []를 반환해요. 전체 요청·응답 필드는 Audio API 참조를 확인하세요.

전사 컨텍스트 추가하기

gpt-transcribe와 함께 prompt, keywords, languages를 사용하면 도메인 용어와 다국어 오디오의 전사 품질을 높일 수 있어요.

컨텍스트와 언어 힌트 추가하기

import fs from "fs";
import OpenAI from "openai";

const openai = new OpenAI();

const request = {
  model: "gpt-transcribe",
  file: fs.createReadStream("fixtures/audio.wav"),
  prompt: "A customer support call about a premium plan and account AC-42.",
};

const transcription = await openai.audio.transcriptions.create(request, {
  body: {
    ...request,
    keywords: ["premium plan", "AC-42", "billing"],
    languages: ["en", "fr"],
  },
});

console.log(transcription.text);
from openai import OpenAI

client = OpenAI()

with open("meeting.wav", "rb") as audio_file:
    transcription = client.audio.transcriptions.create(
        model="gpt-transcribe",
        file=audio_file,
        prompt="A customer support call about a premium plan and account AC-42.",
        extra_body={
            "keywords": ["premium plan", "AC-42", "billing"],
            "languages": ["en", "fr"],
        },
    )

print(transcription.text)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/openai/openai-go/v3"
)

func main() {
	file, err := os.Open("fixtures/audio.wav")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	parameters := openai.AudioTranscriptionNewParams{
		File:   file,
		Model:  "gpt-transcribe",
		Prompt: openai.String("A customer support call about a premium plan and account AC-42."),
	}
	parameters.SetExtraFields(map[string]any{
		"keywords":  []string{"premium plan", "AC-42", "billing"},
		"languages": []string{"en", "fr"},
	})
	client := openai.NewClient()
	transcription, err := client.Audio.Transcriptions.New(context.Background(), parameters)
	if err != nil {
		panic(err)
	}
	fmt.Println(transcription.Text)
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.JsonValue;
import com.openai.models.audio.transcriptions.TranscriptionCreateParams;
import java.nio.file.Path;
import java.util.List;

var result =
    client
        .audio()
        .transcriptions()
        .create(
            TranscriptionCreateParams.builder()
                .file(Path.of(System.getenv("OPENAI_EXAMPLE_AUDIO_PATH")))
                .model("gpt-transcribe")
                .prompt("A customer support call about a premium plan and account AC-42.")
                .putAdditionalBodyProperty(
                    "keywords", JsonValue.from(List.of("premium plan", "AC-42", "billing")))
                .putAdditionalBodyProperty("languages", JsonValue.from(List.of("en", "fr")))
                .build());

System.out.println(result.asTranscription().text());
require "openai"
require "pathname"

client = OpenAI::Client.new
audio = Pathname("audio.wav")
transcript = client.audio.transcriptions.create(
  file: audio,
  model: "gpt-transcribe",
  keywords: ["OpenAI", "Responses API", "Codex"]
)
puts(transcript.text)
curl https://api.openai.com/v1/audio/transcriptions \
  -H "Authorization: Bearer ***" \
  -H "Content-Type: multipart/form-data" \
  -F model="gpt-transcribe" \
  -F file="@/path/to/file/meeting.wav" \
  -F 'prompt=A customer support call about a premium plan and account AC-42.' \
  -F 'keywords[]=premium plan' \
  -F 'keywords[]=AC-42' \
  -F 'keywords[]=billing' \
  -F 'languages[]=en' \
  -F 'languages[]=fr'
  • prompt는 녹음에 대한 비구조화된 컨텍스트에 사용해요.
  • keywords는 들릴 것으로 예상하는 리터럴 용어에 사용해요.
  • languages는 예상 입력 언어에 사용해요.

keywords는 힌트이지 필수 출력이 아니에요. 관련 있는 용어만 넣고, 실제로 말하지 않은 용어가 나타나지 않으면서 정확도가 좋아지는지 평가해 보세요.

gpt-transcribe에서는 languages가 단일 language 필드를 대체해요. 두 필드를 함께 보내면 안 됩니다. 각 keyword는 한 줄에 유지하고 <, >, 캐리지 리턴, 라인 피드를 포함하면 안 돼요. 이 중 하나가 있거나 prompt가 모델 길이 제한을 초과하면 API가 전체 요청을 거부해요.

화자 분리 (Speaker diarization)

녹음의 여러 부분에서 누가 말했는지 식별해야 할 때만 gpt-4o-transcribe-diarize를 사용해요. 이 특수 화자 라벨 모델은 일반적인 파일 전사에 권장되는 모델이 아니에요.

diarized_json 응답 형식을 요청하면 speaker, start, end 메타데이터가 있는 세그먼트를 받을 수 있어요. 30초보다 긴 오디오라면 chunking_strategy"auto" 또는 음성 활동 감지 설정으로 지정해야 합니다.

선택적으로 known_speaker_names[]known_speaker_references[]로 최대 네 개의 짧은 오디오 참조를 제공해 세그먼트를 알려진 화자에 매핑할 수 있어요. 참조 클립은 메인 오디오 업로드가 지원하는 아무 입력 형식으로 2~10초 길이로 제공하고, 멀티파트 폼 데이터를 쓸 때는 data URL로 인코딩하세요.

회의 녹음 화자 분리하기

import fs from "fs";
import OpenAI from "openai";

const openai = new OpenAI();

const agentRef = fs.readFileSync("fixtures/agent.wav").toString("base64");

const transcript = /** @type {OpenAI.Audio.TranscriptionDiarized} */ (
  await openai.audio.transcriptions.create({
    file: fs.createReadStream("fixtures/meeting.wav"),
    model: "gpt-4o-transcribe-diarize",
    response_format: "diarized_json",
    chunking_strategy: "auto",
    known_speaker_names: ["agent"],
    known_speaker_references: ["data:audio/wav;base64," + agentRef],
  })
);

for (const segment of transcript.segments) {
  if (!("speaker" in segment)) continue;

  console.log(
    `${segment.speaker}: ${segment.text}`,
    segment.start,
    segment.end
  );
}
import base64
from openai import OpenAI

client = OpenAI()


def to_data_url(path: str) -> str:
    with open(path, "rb") as fh:
        return "data:audio/wav;base64," + base64.b64encode(fh.read()).decode("utf-8")


with open("meeting.wav", "rb") as audio_file:
    transcript = client.audio.transcriptions.create(
        model="gpt-4o-transcribe-diarize",
        file=audio_file,
        response_format="diarized_json",
        chunking_strategy="auto",
        extra_body={
            "known_speaker_names": ["agent"],
            "known_speaker_references": [to_data_url("agent.wav")],
        },
    )

for segment in transcript.segments:
    print(segment.speaker, segment.text, segment.start, segment.end)
package main

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"os"

	"github.com/openai/openai-go/v3"
	"github.com/openai/openai-go/v3/shared/constant"
)

type diarizedTranscript struct {
	Segments []struct {
		Speaker string  `json:"speaker"`
		Text    string  `json:"text"`
		Start   float64 `json:"start"`
		End     float64 `json:"end"`
	} `json:"segments"`
}

func main() {
	agentAudio, err := os.ReadFile("fixtures/agent.wav")
	if err != nil {
		panic(err)
	}
	meeting, err := os.Open("fixtures/meeting.wav")
	if err != nil {
		panic(err)
	}
	defer meeting.Close()

	client := openai.NewClient()
	transcription, err := client.Audio.Transcriptions.New(context.Background(), openai.AudioTranscriptionNewParams{
		File:           meeting,
		Model:          "gpt-4o-transcribe-diarize",
		ResponseFormat: openai.AudioResponseFormatDiarizedJSON,
		ChunkingStrategy: openai.AudioTranscriptionNewParamsChunkingStrategyUnion{
			OfAuto: constant.ValueOf[constant.Auto](),
		},
		KnownSpeakerNames:      []string{"agent"},
		KnownSpeakerReferences: []string{"data:audio/wav;base64," + base64.StdEncoding.EncodeToString(agentAudio)},
	})
	if err != nil {
		panic(err)
	}
	var result diarizedTranscript
	if err := json.Unmarshal([]byte(transcription.RawJSON()), &result); err != nil {
		panic(err)
	}
	for _, segment := range result.Segments {
		fmt.Println(segment.Speaker+":", segment.Text, segment.Start, segment.End)
	}
}
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.audio.AudioResponseFormat;
import com.openai.models.audio.transcriptions.TranscriptionCreateParams;
import com.openai.models.audio.transcriptions.TranscriptionDiarized;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;

Path audio = Path.of(System.getenv("OPENAI_EXAMPLE_AUDIO_PATH"));
Path speakerAudio = Path.of(System.getenv("OPENAI_EXAMPLE_SPEAKER_AUDIO_PATH"));
String speakerReference =
    "data:audio/wav;base64,"
        + Base64.getEncoder().encodeToString(Files.readAllBytes(speakerAudio));

var result =
    client
        .audio()
        .transcriptions()
        .create(
            TranscriptionCreateParams.builder()
                .file(audio)
                .model("gpt-4o-transcribe-diarize")
                .responseFormat(AudioResponseFormat.DIARIZED_JSON)
                .chunkingStrategyAuto()
                .addKnownSpeakerName("agent")
                .addKnownSpeakerReference(speakerReference)
                .build());

TranscriptionDiarized diarized =
    result.isDiarized()
        ? result.asDiarized()
        : new JsonMapper()
            .readValue(result.asTranscription().text(), TranscriptionDiarized.class);
for (var segment : diarized.segments()) {
  System.out.println(
      segment.speaker()
          + ": "
          + segment.text()
          + " ("
          + segment.start()
          + "-"
          + segment.end()
          + ")");
}
require "base64"
require "openai"
require "pathname"

client = OpenAI::Client.new
audio = Pathname("meeting.wav")
speaker_reference = Base64.strict_encode64(File.binread("agent.wav"))
transcript = client.audio.transcriptions.create(
  file: audio,
  model: "gpt-4o-transcribe-diarize",
  response_format: :diarized_json,
  chunking_strategy: :auto,
  known_speaker_names: ["agent"],
  known_speaker_references: ["data:audio/wav;base64,#{speaker_reference}"]
)
segments = Array(
  transcript.to_h.fetch(:segments) do
    raise "The transcription did not include speaker segments"
  end
)
segments.each do |segment|
  segment = Hash.try_convert(segment) or raise "Invalid speaker segment"
  puts(
    "#{segment.fetch(:speaker)}: #{segment.fetch(:text)} " \
      "(#{segment.fetch(:start)}-#{segment.fetch(:end)})"
  )
end
curl --request POST \
  --url https://api.openai.com/v1/audio/transcriptions \
  --header "Authorization: Bearer ***" \
  --header 'Content-Type: multipart/form-data' \
  --form file=@/path/to/file/meeting.wav \
  --form model=gpt-4o-transcribe-diarize \
  --form response_format=diarized_json \
  --form chunking_strategy=auto \
  --form 'known_speaker_names[]=agent' \
  --form 'known_speaker_references[]=data:audio/wav;base64,AAA...'

stream=true일 때 화자 라벨이 있는 응답은 세그먼트가 완료될 때마다 transcript.text.segment 이벤트를 내보내요. transcript.text.delta 이벤트는 segment_id 필드를 포함하지만, 델타에는 부분 화자 할당이 포함되지 않아요. 모델은 세그먼트를 확정할 때만 화자를 할당해요.

화자 라벨링은 /v1/audio/transcriptions에서 사용할 수 있어요. Realtime 전사 세션에서는 지원되지 않습니다.

번역 (Translations)

완료된 오디오 녹음을 영어로 번역하려면 whisper-1과 함께 /v1/audio/translations를 사용해요. 녹음의 원래 언어를 보존하는 전사와 달리, 이 엔드포인트는 영어 텍스트를 반환해요.

오디오 번역하기

import fs from "fs";
import OpenAI from "openai";

const openai = new OpenAI();

const translation = await openai.audio.translations.create({
  file: fs.createReadStream("fixtures/german.wav"),
  model: "whisper-1",
});

console.log(translation.text);
from openai import OpenAI

client = OpenAI()
audio_file = open("german.wav", "rb")

translation = client.audio.translations.create(
    model="whisper-1",
    file=audio_file,
)

print(translation.text)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/openai/openai-go/v3"
)

func main() {
	file, err := os.Open("fixtures/german.wav")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	client := openai.NewClient()
	translation, err := client.Audio.Translations.New(context.Background(), openai.AudioTranslationNewParams{
		File:  file,
		Model: openai.AudioModelWhisper1,
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(translation.Text)
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.audio.translations.TranslationCreateParams;
import java.nio.file.Path;

var result =
    client
        .audio()
        .translations()
        .create(
            TranslationCreateParams.builder()
                .file(Path.of(System.getenv("OPENAI_EXAMPLE_AUDIO_PATH")))
                .model("whisper-1")
                .build());

System.out.println(result.asTranslation().text());
using OpenAI.Audio;

string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
AudioClient client = new("whisper-1", key);

await using FileStream audio = File.OpenRead("german.wav");
AudioTranslation translation = await client.TranslateAudioAsync(
    audio,
    "german.wav"
);

Console.WriteLine(translation.Text);
require "openai"
require "pathname"

client = OpenAI::Client.new
audio = Pathname("german.wav")
translation = client.audio.translations.create(file: audio, model: "whisper-1")
puts(translation.text)
curl --request POST \
  --url https://api.openai.com/v1/audio/translations \
  --header "Authorization: Bearer ***" \
  --header 'Content-Type: multipart/form-data' \
  --form file=@/path/to/file/german.mp3 \
  --form model=whisper-1 \

다른 언어의 오디오 녹음이라면 응답에 영어 번역이 들어있어요.

Hello, my name is Wolfgang and I come from Germany. Where are you heading today?

이 엔드포인트는 영어로의 번역만 지원해요.

지원되는 언어

어떤 입력 언어가 올지 알고 있다면 gpt-transcribe와 함께 languages를 사용해요. 지원되는 언어 코드 형식은 다음과 같아요.

  • ISO 639-1 코드: en, es, fr 등.
  • 일부 ISO 639-3 코드: eng, spa, yue, cmn 등.
  • 지역 zh 로케일 코드: zh-cn, zh-tw, zh-hk 등.

API는 지원되지 않거나 형식이 잘못된 언어 코드를 거부해요. 응답은 모델이 신뢰할 수 있게 감지한 언어들도 식별해줘요.

whisper-1은 Whisper 언어 목록을 참고하세요. Whisper는 98개 언어를 지원하지만 언어마다 정확도가 달라요. 언어 힌트 하나만 받는 기존 모델들은 languages 대신 language를 사용해요.

타임스탬프 (Timestamps)

단어나 세그먼트 타임스탬프가 필요하다면 whisper-1을 사용해요. timestamp_granularities[] 파라미터는 자막과 비디오 편집을 위한 구조화된 타임스탬프 데이터를 반환해요.

타임스탬프 옵션

import fs from "fs";
import OpenAI from "openai";

const openai = new OpenAI();

const transcription = await openai.audio.transcriptions.create({
  file: fs.createReadStream("fixtures/audio.wav"),
  model: "whisper-1",
  response_format: "verbose_json",
  timestamp_granularities: ["word"],
});

console.log(transcription.words);
from openai import OpenAI

client = OpenAI()
audio_file = open("speech.wav", "rb")

transcription = client.audio.transcriptions.create(
    file=audio_file,
    model="whisper-1",
    response_format="verbose_json",
    timestamp_granularities=["word"],
)

print(transcription.words)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/openai/openai-go/v3"
)

func main() {
	file, err := os.Open("fixtures/audio.wav")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	client := openai.NewClient()
	transcription, err := client.Audio.Transcriptions.New(context.Background(), openai.AudioTranscriptionNewParams{
		File:                   file,
		Model:                  openai.AudioModelWhisper1,
		ResponseFormat:         openai.AudioResponseFormatVerboseJSON,
		TimestampGranularities: []string{"word"},
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(transcription.Words)
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.audio.AudioResponseFormat;
import com.openai.models.audio.transcriptions.TranscriptionCreateParams;
import java.nio.file.Path;

var result =
    client
        .audio()
        .transcriptions()
        .create(
            TranscriptionCreateParams.builder()
                .file(Path.of(System.getenv("OPENAI_EXAMPLE_AUDIO_PATH")))
                .model("whisper-1")
                .responseFormat(AudioResponseFormat.VERBOSE_JSON)
                .addTimestampGranularity(TranscriptionCreateParams.TimestampGranularity.WORD)
                .build());

result
    .asVerbose()
    .words()
    .orElseThrow()
    .forEach(
        word -> System.out.println(word.word() + ": " + word.start() + " - " + word.end()));
using OpenAI.Audio;

string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
string model = "whisper-1";
AudioClient client = new(model, key);

await using FileStream audio = File.OpenRead("speech.wav");
AudioTranscriptionOptions options = new()
{
    ResponseFormat = AudioTranscriptionFormat.Verbose,
    TimestampGranularities = AudioTimestampGranularities.Word,
};
AudioTranscription transcription = await client.TranscribeAudioAsync(
    audio,
    "speech.wav",
    options
);

foreach (TranscribedWord word in transcription.Words)
{
    Console.WriteLine(
        $"{word.Word}: {word.StartTime.TotalSeconds:0.00}s - {word.EndTime.TotalSeconds:0.00}s"
    );
}
require "openai"
require "pathname"
require "pp"

client = OpenAI::Client.new
audio = Pathname("audio.wav")
transcript = client.audio.transcriptions.create(
  file: audio,
  model: "whisper-1",
  response_format: :verbose_json,
  timestamp_granularities: [:word]
)
pp(transcript[:words])
curl https://api.openai.com/v1/audio/transcriptions \
  -H "Authorization: Bearer ***" \
  -H "Content-Type: multipart/form-data" \
  -F file="@/path/to/file/audio.mp3" \
  -F "timestamp_granularities[]=word" \
  -F model="whisper-1" \
  -F response_format="verbose_json"

timestamp_granularities[] 파라미터는 whisper-1에서만 지원돼요.

더 긴 입력 (Longer inputs)

Transcriptions API는 최대 25 MB 파일을 받아요. 더 큰 녹음이라면 압축 오디오 형식을 사용하거나 파일을 25 MB 이하 덩어리로 나누세요. 문장 중간에서 나누면 컨텍스트가 사라져 정확도가 떨어질 수 있으니 피해야 합니다.

한 가지 방법은 PyDub 오픈소스 파이썬 패키지로 오디오를 나누는 거예요.

from pydub import AudioSegment

song = AudioSegment.from_wav("good_morning.wav")

# PyDub handles time in milliseconds
ten_minutes = 10 * 60 * 1000

first_10_minutes = song[:ten_minutes]

first_10_minutes.export("good_morning_10.wav", format="wav")

OpenAI는 PyDub 같은 서드파티 소프트웨어의 유용성이나 보안을 보장하지 않아요.

프롬프팅 (Prompting)

prompt를 사용하면 이름, 약어, 형식, 녹음 고유 어휘의 인식을 개선할 수 있어요. gpt-transcribe에서는 프롬프트를 전사 컨텍스트 추가keywordslanguages와 함께 써요.

기존 gpt-4o-transcribegpt-4o-mini-transcribe 통합도 프롬프팅을 지원해요. gpt-4o-transcribe-diarize는 프롬프트를 지원하지 않아요.

유용한 프롬프팅 시나리오는 다음과 같아요.

  • 제품 이름, 기술 용어, 약어를 올바르게 전사하기.
  • 더 긴 녹음의 이전 덩어리에서 컨텍스트 가져오기.
  • 구두점, 대문자, 필러 단어 보존하기.
  • 언어에 대해 선호하는 문자 체계 선택하기.

whisper-1은 프롬프트에 224토큰 제한이 있고, 권장 전사 모델보다 제어력이 떨어져요. 워크플로가 Whisper를 필요로 한다면 신뢰성 개선 섹션을 참고하세요.

스트리밍 전사

파일 전사는 모델이 완료된 녹음을 처리하는 동안 부분 텍스트를 스트리밍할 수 있어요. Realtime 세션이 필요하지 않아요.

완료된 오디오 녹음의 전사 스트리밍

gpt-transcribe와 함께 stream=true를 설정해요. Transcriptions API는 모델이 녹음의 각 부분을 전사할 때마다 transcript 이벤트를 반환해요.

전사 스트리밍

import fs from "fs";
import OpenAI from "openai";

const openai = new OpenAI();

const stream = await openai.audio.transcriptions.create({
  file: fs.createReadStream("fixtures/speech.wav"),
  model: "gpt-transcribe",
  // highlight-start
  stream: true,
  // highlight-end
});

// highlight-start
for await (const event of stream) {
  console.log(event);
}
// highlight-end
from openai import OpenAI

client = OpenAI()
audio_file = open("speech.wav", "rb")

stream = client.audio.transcriptions.create(
    model="gpt-transcribe",
    file=audio_file,
    # highlight-start
    stream=True,
    # highlight-end
)

# highlight-start
for event in stream:
    print(event)
# highlight-end
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/openai/openai-go/v3"
)

func main() {
	file, err := os.Open("fixtures/speech.wav")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	client := openai.NewClient()
	stream := client.Audio.Transcriptions.NewStreaming(context.Background(), openai.AudioTranscriptionNewParams{
		File:  file,
		Model: "gpt-transcribe",
	})
	for stream.Next() {
		fmt.Println(stream.Current().Type)
	}
	if err := stream.Err(); err != nil {
		panic(err)
	}
}
require "openai"
require "pathname"

client = OpenAI::Client.new
audio = Pathname("speech.wav")
stream = client.audio.transcriptions.create_streaming(
  file: audio,
  model: "gpt-transcribe"
)

stream.each { |event| puts(event.type) }
curl --request POST \
  --url https://api.openai.com/v1/audio/transcriptions \
  --header "Authorization: Bearer ***" \
  --header 'Content-Type: multipart/form-data' \
  --form [email protected] \
  --form model=gpt-transcribe \
  # highlight-start
  --form stream=true

모델은 오디오를 전사하면서 transcript.text.delta 이벤트를 내보내고, 마지막 transcript.text.done 이벤트에서 전체 전사 결과를 반환해요. response_format="diarized_json"으로 화자 라벨이 있는 전사를 할 때는, 분리 모델이 세그먼트를 확정할 때마다 transcript.text.segment 이벤트도 내보내요.

gpt-transcribe의 경우 마지막 이벤트에는 감지된 언어도 포함돼요.

{
  "type": "transcript.text.done",
  "text": "Bonjour, pouvez-vous m'entendre ?",
  "languages": [{ "code": "fr" }]
}

기존 gpt-4o-transcribe, gpt-4o-mini-transcribe, gpt-4o-transcribe-diarize 통합도 파일 스트리밍을 지원해요. whisper-1은 지원하지 않아요.

진행 중인 오디오 녹음의 전사 스트리밍

마이크, 통화, 미디어 스트림에서 오는 라이브 오디오라면 위의 파일 기반 스트리밍 대신 Realtime 전사 가이드를 사용하세요. 현재 전사 세션 흐름과 gpt-live-transcribe를 사용한 권장 실시간 경로를 다룹니다.

신뢰성 개선

타임스탬프, 자막, 번역 때문에 whisper-1을 쓴다면, 이 기법들이 흔하지 않은 단어와 약어의 인식을 개선할 수 있어요. 새 범용 전사에는 gpt-transcribe로 시작하고 전사 컨텍스트를 대신 사용하세요.

prompt 파라미터 사용하기

첫 번째 방법은 선택적 prompt 파라미터로 올바른 철자의 사전을 전달하는 거예요.

Whisper는 범용 텍스트 모델처럼 지시를 따라주진 않고, 최대 224토큰까지의 프롬프트를 받아요.

prompt 파라미터

import fs from "fs";
import OpenAI from "openai";

const openai = new OpenAI();

const transcription = await openai.audio.transcriptions.create({
  file: fs.createReadStream("fixtures/speech.wav"),
  model: "whisper-1",
  response_format: "text",
  prompt:
    "ZyntriQix, Digique Plus, CynapseFive, VortiQore V8, EchoNix Array, OrbitalLink Seven, DigiFractal Matrix, PULSE, RAPT, B.R.I.C.K., Q.U.A.R.T.Z., F.L.I.N.T.",
});

console.log(transcription);
from openai import OpenAI

client = OpenAI()
audio_file = open("speech.wav", "rb")

transcription = client.audio.transcriptions.create(
    model="whisper-1",
    file=audio_file,
    response_format="text",
    prompt="ZyntriQix, Digique Plus, CynapseFive, VortiQore V8, EchoNix Array, OrbitalLink Seven, DigiFractal Matrix, PULSE, RAPT, B.R.I.C.K., Q.U.A.R.T.Z., F.L.I.N.T.",
)

print(transcription.text)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/openai/openai-go/v3"
)

func main() {
	file, err := os.Open("fixtures/speech.wav")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	client := openai.NewClient()
	var transcription []byte
	err = client.Post(context.Background(), "audio/transcriptions", openai.AudioTranscriptionNewParams{
		File:           file,
		Model:          openai.AudioModelWhisper1,
		ResponseFormat: openai.AudioResponseFormatText,
		Prompt:         openai.String("ZyntriQix, Digique Plus, CynapseFive, VortiQore V8, EchoNix Array, OrbitalLink Seven, DigiFractal Matrix, PULSE, RAPT, B.R.I.C.K., Q.U.A.R.T.Z., F.L.I.N.T."),
	}, &transcription)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(transcription))
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.http.HttpResponse;
import com.openai.models.audio.AudioResponseFormat;
import com.openai.models.audio.transcriptions.TranscriptionCreateParams;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;

try (HttpResponse result =
    client
        .audio()
        .transcriptions()
        .withRawResponse()
        .create(
            TranscriptionCreateParams.builder()
                .file(Path.of(System.getenv("OPENAI_EXAMPLE_AUDIO_PATH")))
                .model("whisper-1")
                .responseFormat(AudioResponseFormat.TEXT)
                .prompt(
                    "ZyntriQix, Digique Plus, CynapseFive, VortiQore V8, EchoNix Array, "
                        + "OrbitalLink Seven, DigiFractal Matrix, PULSE, RAPT, B.R.I.C.K., "
                        + "Q.U.A.R.T.Z., F.L.I.N.T.")
                .build())) {
  System.out.println(new String(result.body().readAllBytes(), StandardCharsets.UTF_8));
}
using OpenAI.Audio;

string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
string model = "whisper-1";
AudioClient client = new(model, key);

await using FileStream audio = File.OpenRead("speech.wav");
AudioTranscriptionOptions options = new()
{
    ResponseFormat = AudioTranscriptionFormat.Text,
    Prompt = "ZyntriQix, Digique Plus, CynapseFive, VortiQore V8, EchoNix Array, OrbitalLink Seven, DigiFractal Matrix, PULSE, RAPT, B.R.I.C.K., Q.U.A.R.T.Z., F.L.I.N.T.",
};
AudioTranscription transcription = await client.TranscribeAudioAsync(
    audio,
    "speech.wav",
    options
);

Console.WriteLine(transcription.Text);
require "openai"
require "pathname"

client = OpenAI::Client.new
audio = Pathname("speech.wav")
transcript = client.audio.transcriptions.create(
  file: audio,
  model: "whisper-1",
  prompt: "The speaker says OpenAI and Responses API"
)
puts(transcript.text)
curl --request POST \
  --url https://api.openai.com/v1/audio/transcriptions \
  --header "Authorization: Bearer ***" \
  --header 'Content-Type: multipart/form-data' \
  --form file=@/path/to/file/speech.mp3 \
  --form model=whisper-1 \
  --form prompt="ZyntriQix, Digique Plus, CynapseFive, VortiQore V8, EchoNix Array, OrbitalLink Seven, DigiFractal Matrix, PULSE, RAPT, B.R.I.C.K., Q.U.A.R.T.Z., F.L.I.N.T."

신뢰성은 높여주지만 이 기법은 224토큰으로 제한돼요. 그래서 SKU 목록이 상대적으로 작아야 확장 가능한 해법이 돼요.

텍스트 모델로 후처리하기

두 번째 방법은 텍스트 모델로 전사 결과를 후처리하는 거예요.

system_prompt 변수로 지시를 제공해요. 전사 프롬프트와 마찬가지로 회사명과 제품명을 포함할 수 있어요.

후처리

const systemPrompt = `
You are a helpful assistant for the company ZyntriQix. Your task is
to correct any spelling discrepancies in the transcribed text. Make
sure that the names of the following products are spelled correctly:
ZyntriQix, Digique Plus, CynapseFive, VortiQore V8, EchoNix Array,
OrbitalLink Seven, DigiFractal Matrix, PULSE, RAPT, B.R.I.C.K.,
Q.U.A.R.T.Z., F.L.I.N.T. Only add necessary punctuation such as
periods, commas, and capitalization, and use only the context provided.
`;

const transcript = await transcribe(audioFile);
const completion = await openai.chat.completions.create({
  model: "gpt-4.1",
  temperature: temperature,
  messages: [
    {
      role: "system",
      content: systemPrompt,
    },
    {
      role: "user",
      content: transcript,
    },
  ],
  store: true,
});

console.log(completion.choices[0].message.content);
system_prompt = """
You are a helpful assistant for the company ZyntriQix. Your task is to correct
any spelling discrepancies in the transcribed text. Make sure that the names of
the following products are spelled correctly: ZyntriQix, Digique Plus,
CynapseFive, VortiQore V8, EchoNix Array, OrbitalLink Seven, DigiFractal
Matrix, PULSE, RAPT, B.R.I.C.K., Q.U.A.R.T.Z., F.L.I.N.T. Only add necessary
punctuation such as periods, commas, and capitalization, and use only the
context provided.
"""


def generate_corrected_transcript(temperature, system_prompt, audio_file):
    response = client.chat.completions.create(
        model="gpt-4.1",
        temperature=temperature,
        messages=[
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": transcribe(audio_file, "")},
        ],
    )
    return response.choices[0].message.content


corrected_text = generate_corrected_transcript(0, system_prompt, fake_company_filepath)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/openai/openai-go/v3"
)

const systemPrompt = `
You are a helpful assistant for the company ZyntriQix. Your task is
to correct any spelling discrepancies in the transcribed text. Make
sure that the names of the following products are spelled correctly:
ZyntriQix, Digique Plus, CynapseFive, VortiQore V8, EchoNix Array,
OrbitalLink Seven, DigiFractal Matrix, PULSE, RAPT, B.R.I.C.K.,
Q.U.A.R.T.Z., F.L.I.N.T. Only add necessary punctuation such as
periods, commas, and capitalization, and use only the context provided.
`

func main() {
	file, err := os.Open("fixtures/speech.wav")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	client := openai.NewClient()
	transcription, err := client.Audio.Transcriptions.New(context.Background(), openai.AudioTranscriptionNewParams{
		File:  file,
		Model: openai.AudioModelGPT4oTranscribe,
	})
	if err != nil {
		panic(err)
	}
	completion, err := client.Chat.Completions.New(context.Background(), openai.ChatCompletionNewParams{
		Model:       "gpt-4.1",
		Temperature: openai.Float(0),
		Messages: []openai.ChatCompletionMessageParamUnion{
			openai.SystemMessage(systemPrompt),
			openai.UserMessage(transcription.Text),
		},
		Store: openai.Bool(true),
	})
	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.audio.transcriptions.TranscriptionCreateParams;
import com.openai.models.chat.completions.ChatCompletionCreateParams;
import java.nio.file.Path;

String systemPrompt =
    """
    You are a helpful assistant for the company ZyntriQix. Your task is to
    correct any spelling discrepancies in the transcribed text. Make sure that
    the names of the following products are spelled correctly: ZyntriQix,
    Digique Plus, CynapseFive, VortiQore V8, EchoNix Array, OrbitalLink Seven,
    DigiFractal Matrix, PULSE, RAPT, B.R.I.C.K., Q.U.A.R.T.Z., F.L.I.N.T.
    Only add necessary punctuation such as periods, commas, and capitalization,
    and use only the context provided.
    """;

var result =
    client
        .audio()
        .transcriptions()
        .create(
            TranscriptionCreateParams.builder()
                .file(Path.of(System.getenv("OPENAI_EXAMPLE_AUDIO_PATH")))
                .model("gpt-4o-transcribe")
                .build());

var completion =
    client
        .chat()
        .completions()
        .create(
            ChatCompletionCreateParams.builder()
                .model("gpt-4.1")
                .temperature(0.0)
                .store(true)
                .addSystemMessage(systemPrompt)
                .addUserMessage(result.asTranscription().text())
                .build());
completion.choices().stream()
    .flatMap(choice -> choice.message().content().stream())
    .forEach(System.out::println);
using OpenAI.Audio;
using OpenAI.Chat;

string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
string model = "gpt-4.1";
ChatClient client = new(model, key);

string transcriptionModel = "gpt-4o-transcribe";
AudioClient audio = new(transcriptionModel, key);

await using FileStream source = File.OpenRead("speech.wav");
AudioTranscription transcription = await audio.TranscribeAudioAsync(source, "speech.wav");

string systemPrompt =
    """
    You are a helpful assistant for the company ZyntriQix. Correct any
    spelling discrepancies in the transcribed text. Make sure the names
    of these products are spelled correctly: ZyntriQix, Digique Plus,
    CynapseFive, VortiQore V8, EchoNix Array, OrbitalLink Seven,
    DigiFractal Matrix, PULSE, RAPT, B.R.I.C.K., Q.U.A.R.T.Z., F.L.I.N.T.
    Only add necessary punctuation such as periods, commas, and
    capitalization, and use only the context provided.
    """;
ChatCompletionOptions correctionOptions = new() { Temperature = 0 };
ChatCompletion completion = await client.CompleteChatAsync(
    [
        new SystemChatMessage(systemPrompt),
        new UserChatMessage(transcription.Text),
    ],
    correctionOptions
);
Console.WriteLine(completion.Content[0].Text);
require "openai"
require "pathname"

client = OpenAI::Client.new
audio = Pathname("speech.wav")
transcript = client.audio.transcriptions.create(
  file: audio,
  model: "gpt-4o-mini-transcribe"
)

response = client.responses.create(
  model: "gpt-4.1",
  input: "Add punctuation and paragraph breaks without changing the words:\n#{transcript.text}"
)
puts(response.output_text)

텍스트 모델은 오타를 고치고 Whisper의 224토큰 프롬프트 창보다 더 긴 용어 목록을 처리할 수 있어요. 화자가 말한 내용을 바꾸지 않도록 원본 오디오에 대해 수정 결과를 평가하는 것이 좋습니다.

더 알아보기 (Learn more)

  • Realtime 전사 — 아직 도착 중인 라이브 오디오를 실시간으로 전사해 보세요.
  • Realtime 및 오디오 개요 — 음성 에이전트, 번역, 전사, 음성 생성을 위한 올바른 경로를 선택해 보세요.