Speech Engine 퀵스타트

ElevenLabs SDK를 사용해 채팅 에이전트에 음성을 추가하세요.

이 가이드에서는 Speech Engine으로 음성 기반 에이전트를 빌드하는 방법을 안내합니다. LLM을 ElevenLabs에 연결하는 서버를 설정한 다음, 사용자가 에이전트와 음성 대화를 나눌 수 있도록 브라우저 클라이언트를 연결합니다.

ElevenLabs Speech Engine 스킬을 사용해 채팅 에이전트에 음성을 추가하세요.

npx skills add elevenlabs/skills --skill speech-engine

Speech Engine 작동 방식

Speech Engine은 LLM을 ElevenLabs에 연결하여 사용자가 에이전트에게 말하고 응답을 들을 수 있게 합니다. ElevenLabs는 음성-텍스트 변환과 텍스트 음성 변환을 처리하며, 서버는 LLM 로직을 제공합니다.

각 WebSocket 연결은 하나의 대화를 나타냅니다. 사용자가 말하면 ElevenLabs가 오디오를 트랜스크립션하고 서버로 전송합니다. 서버는 이를 LLM에 전달한 다음 응답을 다시 스트리밍합니다. ElevenLabs는 텍스트를 음성으로 변환해 브라우저에서 재생합니다. SDK는 연결 관리, 턴 관리 및 인터럽트 감지를 처리합니다.

사전 요구 사항

이 튜토리얼은 LLM에 OpenAI API를 사용합니다. OPENAI_API_KEY 환경 변수에 설정된 OpenAI API 키가 필요합니다.

서버 설정

1

API 키 생성

대시보드에서 API 키를 생성하세요. 이 키로 API에 안전하게 액세스할 수 있습니다.

키는 관리형 시크릿으로 저장하고, .env 파일을 통한 환경 변수 또는 앱 구성에서 직접 SDK에 전달하세요.

.env
ELEVENLABS_API_KEY=<your_api_key_here>
2

종속성 설치

pip install elevenlabs openai python-dotenv
3

서버 공개

Speech Engine에는 공개적으로 접근 가능한 URL이 필요합니다. ngrok을 사용해 로컬 서버를 공개하세요. 서버는 아직 빌드되지 않았지만, 다음 단계에서 사용할 URL이 필요하므로 먼저 ngrok을 실행해야 합니다.

ngrok http 3001

포워딩 URL(예: https://abc123.ngrok.io)을 복사하세요.

4

Speech Engine 인스턴스 생성

SDK를 사용해 Speech Engine 인스턴스를 생성하고, /ws 경로를 추가한 ngrok URL을 WebSocket URL로 전달하세요.

import asyncio
from dotenv import load_dotenv
from elevenlabs import AsyncElevenLabs
load_dotenv()
elevenlabs = AsyncElevenLabs(
api_key=os.getenv("ELEVENLABS_API_KEY"),
)
async def main():
engine = await elevenlabs.speech_engine.create(
name="My Speech Engine",
speech_engine={
# Note we use the wss protocol instead of https
"ws_url": "wss://abc123.ngrok.io/ws",
},
)
print(f"Speech Engine ID: {engine.engine_id}")
if __name__ == "__main__":
asyncio.run(main())

이 스크립트를 실행하고 다음 단계에서 사용할 Speech Engine ID(예: seng_8k3m9xr4hjnfg983brhmhkd98n6)를 복사하세요.

5

서버 생성

다음 내용으로 server.py 또는 server.mts 파일을 생성하세요. 서버를 설정하고, /ws 경로에 Speech Engine을 연결하며, OpenAI로 응답을 생성합니다.

import asyncio
import os
from dotenv import load_dotenv
from openai import AsyncOpenAI
from elevenlabs import AsyncElevenLabs
load_dotenv()
# Replace with your Speech Engine ID from step 4
SPEECH_ENGINE_ID = "seng_8k3m9xr4hjnfg983brhmhkd98n6"
openai = AsyncOpenAI(
api_key=os.getenv("OPENAI_API_KEY"),
)
elevenlabs = AsyncElevenLabs(
api_key=os.getenv("ELEVENLABS_API_KEY"),
)
def on_init(conversation_id, session):
print(f"Session started: {conversation_id}")
async def on_transcript(transcript, session):
stream = await openai.responses.create(
model="gpt-4o",
instructions="You are a helpful voice assistant. Keep responses concise and conversational.",
input=[
{"role": "assistant" if m.role == "agent" else m.role, "content": m.content}
for m in transcript
],
stream=True,
)
await session.send_response(stream)
def on_close(session):
print(f"Session ended: {session.conversation_id}")
def on_error(err, session):
print(f"Error: {err}")
async def main():
engine = await elevenlabs.speech_engine.get(SPEECH_ENGINE_ID)
await engine.serve(
port=3001,
path="/ws",
debug=True,
on_init=on_init,
on_transcript=on_transcript,
on_close=on_close,
on_error=on_error,
)
if __name__ == "__main__":
asyncio.run(main())

onTranscript / on_transcript 콜백은 전체 대화 기록과 현재 세션을 받습니다. TypeScript SDK는 사용자가 응답 도중 인터럽트하면 실행되는 AbortSignal도 제공합니다. signal을 OpenAI 호출에 전달하면 인터럽트 시 LLM 요청이 자동으로 취소됩니다.

sendResponse() / send_response()는 문자열, 비동기 이터러블 또는 OpenAI, Anthropic, Google Gemini의 스트림을 받습니다. SDK가 텍스트 콘텐츠를 자동으로 추출합니다.

위 예시에서는 사용자의 전체 트랜스크립트를 LLM에 전달합니다. 프로덕션 환경에서는 프롬프트 인젝션이나 조작 시도를 방지하기 위한 가드레일을 추가해야 합니다.

6

서버 시작

python server.py

클라이언트 설정

1

클라이언트 SDK 설치

npm install @elevenlabs/react
2

토큰 엔드포인트 생성

대화 토큰을 생성하는 서버 측 엔드포인트를 추가하세요. 이렇게 하면 API 키가 브라우저에 노출되지 않고 최상의 오디오 품질을 위해 WebRTC를 사용할 수 있습니다.

import os
from dotenv import load_dotenv
from flask import Flask, jsonify
from elevenlabs import ElevenLabs
load_dotenv()
app = Flask(__name__)
elevenlabs = ElevenLabs(
api_key=os.getenv("ELEVENLABS_API_KEY"),
)
@app.route("/api/token")
def get_token():
# Replace with your Speech Engine ID from step 4 of the server setup
speech_engine_id = "seng_8k3m9xr4hjnfg983brhmhkd98n6"
response = elevenlabs.conversational_ai.conversations.get_webrtc_token(
agent_id=speech_engine_id,
)
return jsonify(token=response.token)
if __name__ == "__main__":
app.run(port=3002)
3

대화 UI 빌드

서버에서 대화 토큰을 가져와 세션을 시작하는 데 사용하세요.

App.tsx
import { useConversation } from "@elevenlabs/react";
import { useCallback } from "react";
async function getToken(): Promise<string> {
const response = await fetch("/api/token");
if (!response.ok) {
throw Error("Failed to get conversation token");
}
const data = await response.json();
return data.token;
}
export default function App() {
const conversation = useConversation({
onConnect: () => console.log("Connected"),
onDisconnect: () => console.log("Disconnected"),
onError: (error: Error) => console.error("Error:", error),
});
const startConversation = useCallback(async () => {
await navigator.mediaDevices.getUserMedia({ audio: true });
const token = await getToken();
await conversation.startSession({ conversationToken: token });
}, [conversation]);
const stopConversation = useCallback(async () => {
await conversation.endSession();
}, [conversation]);
return (
<div>
<p>Status: {conversation.status}</p>
<button onClick={startConversation} disabled={conversation.status === "connected"}>
Start conversation
</button>
<button onClick={stopConversation} disabled={conversation.status !== "connected"}>
End conversation
</button>
</div>
);
}
4

테스트

다음 3개 프로세스가 실행 중인지 확인하세요.

  1. ngrok - 포트 3001로 포워딩
  2. Speech Engine 서버 - python server.py 또는 npx tsx server.mts
  3. 토큰 서버 - npx tsx token-server.mts 또는 python token_server.py

브라우저에서 클라이언트 애플리케이션을 열고 대화 시작을 클릭하세요. 메시지가 표시되면 마이크 액세스를 허용한 다음 말해 보세요. 스피커를 통해 에이전트의 응답을 들을 수 있습니다.

서버에서 debug: true를 활성화한 경우, 콘솔에 수신 트랜스크립트와 발신 응답이 기록됩니다.

세션 이벤트

이벤트TypeScript 콜백Python 콜백설명
user_transcriptonTranscripton_transcript사용자 음성이 트랜스크립션되었습니다. 전체 대화 기록과 중단 신호를 포함합니다.
initonIniton_init대화 ID로 세션이 초기화되었습니다.
closeonCloseon_closeElevenLabs에서 정상적으로 연결이 해제되었습니다.
disconnectedonDisconnecton_disconnectWebSocket 연결이 예기치 않게 끊어졌습니다.
erroronErroron_error프로토콜 또는 WebSocket 오류입니다.

첫 에이전트 메시지 구성

기본적으로 에이전트는 사용자가 먼저 말하기를 기다립니다. 대화가 시작될 때 에이전트가 사용자에게 인사하게 하려면 세션 시작 시 클라이언트의 overrides 옵션에 첫 메시지를 설정하세요.

1

에이전트가 먼저 말할 수 있도록 Speech Engine 리소스를 업데이트하여 클라이언트에서 이를 설정할 수 있게 해야 합니다.

engine = await elevenlabs.speech_engine.update(
speech_engine_id="seng_8k3m9xr4hjnfg983brhmhkd98n6",
overrides={
"first_message": True,
},
)
2

다음으로 클라이언트 SDK에서 첫 메시지를 구성합니다.

conversation.startSession({
conversationToken: token,
overrides: {
agent: {
firstMessage: "Hello! How can I help you today?",
},
},
});

첫 메시지는 연결이 설정되는 즉시 에이전트가 말합니다. 이 메시지는 서버의 onTranscript 콜백을 실행하지 않으며, 전적으로 ElevenLabs 측에서 처리됩니다.

다음 단계