탐색으로 건너뛰기

화자 분리

이 가이드에서는 Realtime Speech to Text API를 사용하여 실시간 트랜스크립트에서 누가 말하는지 라벨링하는 방법을 알아봅니다.

사용 방법 가이드 · 클라이언트 측 또는 서버 측 스트리밍 가이드를 완료했다고 가정합니다.

개요

화자 분리는 단어에 화자 ID를 할당하여 누가 무엇을 말했는지 구분할 수 있게 합니다. 화자 분리를 활성화하면 부분 트랜스크립트에 임시 화자 라벨이 포함되며, 확정된 각 트랜스크립트 뒤에는 별도의 diarized_transcript 이벤트가 이어집니다. 오디오가 더 수신되면 화자 라벨이 변경될 수 있습니다.

화자 분리 활성화

model_id=scribe_v3_live로 WebSocket 연결을 열 때 불리언 쿼리 파라미터 diarize=true를 설정하세요. 화자 분리는 기본적으로 false입니다.

다음 서버 측 예제에서는 연결을 열고 이벤트를 수신합니다. Python에서는 websockets를, TypeScript에서는 ws와 개발 종속성 @types/ws를 설치하세요. 서버 환경에 ELEVENLABS_API_KEY를 설정하세요. 예제에서는 이를 xi-api-key 헤더로 전송합니다. 이 키는 브라우저 코드가 아닌 서버에 보관하세요.

이 예제에서는 오디오를 전송하지 않습니다. 서버 측 스트리밍 가이드의 오디오 프로듀서와 통합하세요. 이벤트를 수신하는 동안 같은 연결에서 input_audio_chunk 메시지를 전송합니다. 예제에서는 pcm_16000을 선택하므로 일치하는 오디오를 사용하세요. Python에서는 오디오 전송 작업을 수신 루프와 동시에 실행하고, TypeScript에서는 연결이 열린 후 오디오를 전송하세요.

import asyncio
import json
import os
from websockets.asyncio.client import connect
async def main():
url = (
"wss://api.elevenlabs.io/v1/speech-to-text/realtime"
"?model_id=scribe_v3_live&audio_format=pcm_16000&diarize=true"
)
headers = {"xi-api-key": os.environ["ELEVENLABS_API_KEY"]}
async with connect(url, additional_headers=headers) as websocket:
async for message in websocket:
event = json.loads(message)
print(event)
if __name__ == "__main__":
asyncio.run(main())

이벤트 처리

부분 트랜스크립트

화자 분리를 활성화하면 각 partial_transcript에 배치 트랜스크립션과 동일한 단어 형식의 임시 words가 포함됩니다. 화자가 식별되면 각 단어의 speaker_id는 speaker_0, speaker_1 등으로 지정되며, 아직 확인되지 않은 경우에는 null입니다.

이 단어들은 임시 데이터로 처리하세요. 오디오가 더 수신되면 화자 ID와 타임스탬프가 변경될 수 있습니다. 아직 정렬되지 않은 단어에는 타임스탬프가 없습니다(start 및 end가 null이거나 없음).

확정 및 화자 분리 트랜스크립트

화자 분리는 committed_transcript를 지연시키지 않습니다. 화자 분리를 활성화하면 이 이벤트에 0부터 시작하는 정수 segment_index가 추가됩니다.

{
"message_type": "committed_transcript",
"segment_index": 0,
"text": "Hello."
}

각 확정 뒤에는 일치하는 segment_index, text, words를 포함하는 별도의 diarized_transcript 이벤트가 이어집니다.

{
"message_type": "diarized_transcript",
"segment_index": 0,
"text": "Hello.",
"words": [
{
"text": "Hello.",
"speaker_id": "speaker_0"
}
]
}

위 단어에는 텍스트와 화자 라벨만 표시됩니다. 전체 단어 형식은 speaker_id와 함께 배치 트랜스크립션과 동일한 start, end, type, logprob, characters 필드를 사용합니다. 확인되지 않은 화자의 ID는 null입니다.

화자 분리 트랜스크립트는 확정 순서대로 전송됩니다. 텍스트나 다음에 수신한 이벤트가 아니라 segment_index를 사용하여 확정된 트랜스크립트와 연결하세요. 화자 식별에 실패한 경우에도 각 확정에 대해 항상 diarized_transcript가 전송됩니다. 이 경우 확인되지 않은 화자 ID는 null입니다.

화자 병합

speakers_merged 이벤트는 두 화자 ID가 동일인을 가리킨다는 의미입니다.

{
"message_type": "speakers_merged",
"from_speaker_id": "speaker_1",
"to_speaker_id": "speaker_0"
}

부분 및 화자 분리 트랜스크립트 모두에서 이전의 모든 from_speaker_id 사용을 to_speaker_id로 바꾸세요. 화자 ID는 재사용되지 않습니다. 병합은 연속해서 발생할 수 있으므로 수신 순서대로 적용하세요. 예를 들어 speaker_2가 speaker_1로 병합된 후 speaker_1이 speaker_0으로 병합되면, 두 ID에 속했던 이전의 모든 단어는 최종적으로 speaker_0으로 라벨링되어야 합니다.

예를 들어 수신한 각 이벤트에 대해 이 핸들러를 호출하세요. 여기서 transcript_history는 이전 부분 스냅샷을 포함하여 애플리케이션이 유지하는 모든 부분 및 화자 분리 트랜스크립트를 저장합니다. 각 병합은 저장된 모든 단어를 제자리에서 업데이트하므로 연속된 병합도 이전에 병합된 단어를 업데이트합니다. 이 업데이트된 저장소에서 화자 라벨을 렌더링하세요. null 라벨은 null로 유지됩니다.

transcript_history = []
def handle_event(event):
if event["message_type"] in ("partial_transcript", "diarized_transcript"):
transcript_history.append(event)
elif event["message_type"] == "speakers_merged":
for transcript in transcript_history:
for word in transcript.get("words") or []:
if word.get("speaker_id") == event["from_speaker_id"]:
word["speaker_id"] = event["to_speaker_id"]

제한 사항

  • 화자 분리는 scribe_v3_live에서만 지원됩니다. 다른 모델은 "The 'diarize' parameter is only supported by scribe_v3_live." 메시지와 함께 invalid_request 오류를 반환한 후 연결을 닫습니다.
  • 화자 분리는 filter_background_audio와 함께 사용할 수 없습니다. 이 조합은 "cannot be combined with filter_background_audio"를 포함하는 메시지와 함께 invalid_request 오류를 반환한 후 연결을 닫습니다.
  • 화자 라벨은 확인되지 않은 상태(null)로 남을 수 있으며, 이후의 speakers_merged 이벤트가 이전 라벨을 수정할 수 있습니다. 이러한 병합 이벤트는 화자 분리가 활성화된 경우에만 전송됩니다.