본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 24. 06:38

Pipecat 또는 LiveKit 없이 음성 에이전트 구축하기

요약

Pipecat나 LiveKit 같은 오케스트레이션 프레임워크 없이 음성 에이전트를 구축하는 아키텍처와 그 필요성을 분석합니다. 프레임워크가 제공하는 역할과 비용, 의존성 문제를 검토하며 프레임워크 없이도 효율적인 파이프라인을 구성할 수 있는 관점을 제시합니다.

핵심 포인트

  • 음성 에이전트 프레임워크의 핵심 역할은 멀티 벤더 파이프라인의 오케스트레이션임
  • 프레임워크 사용은 추가적인 의존성과 관리 복잡성을 초래할 수 있음
  • 단일 벤더를 사용할 경우 프레임워크 없이도 구축이 가능함
  • 전송 계층(SIP vs WebSocket)과 오디오 파이프라인의 본질적 이해가 중요함

지난 1년 사이 음성 에이전트(voice agent) 구축을 시작했다면, 다음과 같은 질문에 빠르게 직면했을 것입니다: Pipecat나 LiveKit이 꼭 필요한가?

인터넷의 답변은 '그렇다'입니다. 모든 튜토리얼은 에이전트 로직을 한 줄이라도 작성하기 전에 오케스트레이션 프레임워크(orchestration framework)를 찾습니다. 그럴 만한 이유가 있습니다. 해당 프레임워크들은 진정으로 훌륭하며, AssemblyAI는 두 프레임워크 모두를 위한 드롭인 플러그인(drop-in plugins)을 제공하기 때문입니다. 이미 하나를 사용 중이라면, 그대로 계속 사용하십시오.

하지만 "프레임워크가 필요한가"는 잘못된 첫 번째 질문입니다. 진짜 질문은 이것입니다: 무엇이 오디오를 이동시키고, 무엇이 파이프라인(pipeline)을 연결하는가? 이 두 가지에 답하면 프레임워크에 대한 질문은 저절로 해결됩니다. 많은 경우, 솔직한 답변은 다음과 같습니다: 프레임워크는 필요하지 않습니다.

이 포스트는 프레임워크를 사용하지 않는 경로에 대한 아키텍처(architecture) 개요입니다. 우리는 사람들이 Pipecat나 LiveKit 없이 가고자 할 때 실제로 의미하는 네 가지 사항을 다룰 것입니다:

  • 음성 파이프라인 프레임워크가 실제로 해주는 역할(그리고 그 대가).
  • 모두가 혼동하는 전송(transport) 문제 — SIP vs WebSocket.
  • Twilio를 사용하여 전화번호 연결하기.
  • 프레임워크를 사용하지 않는 아키텍처가 엔터프라이즈 규모(enterprise scale)에서도 유지될 수 있는지 여부.

먼저 무엇을 제거하게 되는지부터 시작하겠습니다.

프레임워크가 실제로 해주는 역할

음성 에이전트는 몇 백 밀리초마다 긴밀한 루프(loop) 내에서 몇 가지 작업을 수행해야 합니다: 음성을 텍스트로 변환(speech-to-text), 무엇을 말할지 결정, 이를 다시 음성으로 변환(text-to-speech), 그리고 사용자가 봇의 말을 끊고 말할 때의 중단(interruptions)을 처리하면서 오디오를 입출력하는 것입니다.

역사적으로 이 각각의 작업은 서로 다른 벤더(vendor)가 담당했습니다. 한 제공업체의 음성-텍스트 변환(Speech-to-text), 다른 제공업체의 LLM, 세 번째 제공업체의 텍스트-음성 변환(text-to-speech). 중간에서 지휘를 하는 무언가가 있어야 합니다: 부분적인 전사(partial transcripts)를 LLM으로 라우팅하고, LLM의 토큰(tokens)을 TTS 엔진으로 스트리밍하며, 발화 순서(turn-taking)를 관리하고, 바지인(barge-in, 끼어들기)을 처리하는 것입니다. 그 지휘자가 바로 Pipecat와 LiveKit Agents가 제공하는 것이며, 여기에 사용자에게 오디오를 전달하고 가져오기 위한 전송 계층(transport layer)이 추가됩니다.

그것은 정말 힘든 작업이며, 프레임워크들은 이를 잘 수행합니다. 문제는, 당신의 파이프라인(pipeline)이 실제로 멀티 벤더(multi-vendor) 파이프라인일 때만 그 작업이 '필요'하다는 점입니다.

여기 비용 측면의 문제가 있습니다. 퀵스타트(quickstart)를 따라가다 보면 놓치기 쉽기 때문입니다. 프레임워크는 당신의 스택(stack)에 또 다른 의존성(dependency)이 됩니다. 즉, 배포하고, 버전을 관리하고, 모니터링해야 하며, 새벽 2시에 무언가 잘못되었을 때 원인을 파악해야 할 대상이 하나 더 늘어나는 것입니다. 당신은 여전히 세 개의 모델 벤더를 서로 연결하고 있으므로, 여전히 세 세트의 API 키, 세 개의 결제 관계, 세 가지 실패 모드(failure modes), 그리고 중첩되는 세 가지 지연 시간 예산(latency budgets)을 직접 관리해야 합니다. 프레임워크는 그 복잡성을 숨겨줄 뿐, 제거해주지는 않습니다.

따라서 질문은 다음과 같습니다. 만약 파이프라인이 멀티 벤더가 아니라면 어떻게 될까요?

프레임워크 없는 아키텍처 (architecture)

여기 패러다임의 전환이 있습니다. AssemblyAI의 Voice Agent API는 음성-텍스트 변환(speech-to-text), LLM, 그리고 텍스트-음성 변환(text-to-speech)을 단일 웹소켓(WebSocket) 연결로 통합합니다. 오디오를 스트리밍하여 입력하면, 오디오가 출력됩니다. 발화 감지(turn detection), 중단 처리(interruption handling), 그리고 도구 호출(tool calling)이 그 하나의 연결 내부에서 일어납니다.

전체 파이프라인이 하나의 API 뒤에 존재할 때, 오케스트레이션(orchestrate)할 것은 아무것도 남지 않습니다. 당신의 "지휘자(conductor)"는 단일 오픈 소켓(open socket)이 됩니다.
두 가지 토폴로지(topologies)를 비교해 보십시오.

프레임워크를 사용할 경우 (계층적, 멀티 벤더):

    ┌──────────── 당신의 오케스트레이션 프레임워크 ────────────┐
...

Voice Agent API는 음성 계층(speech layer)을 위해 Universal-3.5 Pro Realtime에서 실행됩니다. 이 모델은 단어 오류율(Word Error Rate, 6.99%)과 개체 정확도(Entity Accuracy) 모두에서 Pipecat의 에이전트 대화 벤치마크를 선도하며, 짧은 발화("yes", "no", "mmhmm") 처리에 탁월합니다. 이 모델은 에이전트의 질문을 문맥(Context)으로 가져오고, 대화의 롤링 메모리(Rolling Memory)를 유지하며, 주변 소음으로부터 화자를 분리하고, 18개 언어에서 네이티브 코드 스위칭(Code-switching)과 함께 작동합니다. 이는 의도적으로 보이지 않는 인프라(Invisible Infrastructure)로 설계되었습니다. 즉, 하나의 연결, 하나의 청구서, 하나의 로그 세트만 있으면 됩니다. 엔드 투 엔드(End-to-end)로 약 1초 내에 실행되며, 채택해야 할 별도의 SDK가 없기 때문에 Claude Code와 같은 코딩 에이전트와 즉시 연동됩니다.

오케스트레이션 계층(Orchestration Layer) 대신 여러분이 구축하게 되는 것은 얇은 릴레이(Thin Relay)입니다. 사용자가 어디에 있든 오디오를 가져와 WebSocket으로 전달하고, 거기서 나오는 것을 재생하기만 하면 됩니다. 그게 전부입니다. 이제 연결해 보겠습니다. 먼저 코드로, 그다음은 전화로 구현해 보죠.

자체 서버에서 연결하기

서버 측 및 네이티브 클라이언트는 Authorization 헤더에 API 키를 넣어 직접 연결합니다. 단 한 번의 REST 호출로 에이전트를 생성한 다음, agent_id를 통해 연결하세요:

curl -X POST https://agents.assemblyai.com/v1/agents \
  -H "Authorization: $ASSEMBLYAI_API_KEY" \
  -H "Content-Type: application/json" \
...

그러면 id가 반환됩니다. 이제 WebSocket을 열고 여기에 바인딩(Bind)하세요. 에이전트에 저장된 프롬프트(Prompt), 음성(Voice), 도구(Tools)가 자동으로 로드되므로 다시 보낼 필요가 없습니다:

import asyncio, json, base64, websockets

URL = "wss://agents.assemblyai.com/v1/ws"
...

파이프라인 그래프(pipeline graph), 서비스 레지스트리(service registry), 또는 구성해야 할 턴 감지(turn-detection) 플러그인이 없다는 점에 주목하세요. session.ready, transcript.agent, reply.audio, 그리고 끼어들기(barge-in)를 위한 input.speech.started와 같은 이벤트들이 전체 프로토콜이며, 이는 모든 전송 방식(transport)에서 동일합니다. 브라우저의 경우, 서버 측에서 수명이 짧은 토큰을 생성하고 wss://agents.assemblyai.com/v1/ws?token=을 열어 API 키가 클라이언트로 전송되지 않도록 할 수 있습니다. 동일한 session.update와 동일한 이벤트가 적용됩니다. 브라우저를 통한 전체 워크스루는 Voice Agent API quickstart에서 확인할 수 있습니다.

실시간 Voice AI 작동 모습 확인하기

통화 오디오를 입력하면 Universal-3.5 Pro Realtime이 이름, 숫자, 그리고 대화 순서(turn-taking)를 실시간으로 처리하는 것을 코딩 없이 확인할 수 있습니다.

Playground 체험하기

전송 방식의 문제: SIP vs. WebSocket

대부분의 혼란이 발생하는 지점이므로, 이 부분을 정리해 보겠습니다.
오디오는 사용자(user)와 에이전트(agent) 사이에서 물리적으로 이동해야 합니다. 오디오를 이동시키는 세 가지 일반적인 방법이 있습니다:

  • WebRTC — 브라우저 및 앱의 실시간 표준입니다. NAT 트래버설(NAT traversal), 지터 버퍼링(jitter buffering), 에코 캔슬레이션(echo cancellation)을 처리합니다. 이는 LiveKit과 Daily의 주력 분야이며, 다자간 룸(multi-party rooms), 비디오, 그리고 풍부한 클라이언트 SDK를 위한 적합한 도구입니다.
  • SIP — 전화망의 신호 프로토콜(signaling protocol)입니다. 가공되지 않은 PSTN 전화를 종료하거나 자체 전화 인프라를 운영하는 경우, SIP 영역에 해당합니다.
  • WebSocket — 단순한 양방향 소켓(bidirectional socket)입니다. 미디어 서버(media server), SFU, 신호 주고받기(signaling dance)가 필요 없습니다. 오디오 프레임을 보내고, 오디오 프레임을 받습니다.

AssemblyAI는 WebSocket을 통해 오디오를 전달합니다. 이는 STT 레이어와 Voice Agent API 모두에 적용되는 전체 전송 방식의 핵심입니다.

"하지만 제 사용자들은 전화기를 사용 중입니다. SIP가 필요하지 않나요?" 이 부분에서 사람들이 흔히 오해하곤 합니다. 여러분이 직접 SIP를 다룰 필요도 없고, WebRTC 미디어 서버를 구축할 필요도 없습니다. 전화 서비스 제공업체(Telephony provider)가 그 역할을 대신해 주기 때문입니다. Twilio는 PSTN 전화를 종료하고, SIP 측을 처리한 뒤, 여러분에게 통화 오디오를 — 짐작하신 대로 — WebSocket을 통해 전달합니다. 따라서 에이전트는 사용자 측에서는 WebSocket이고, AssemblyAI 측에서도 WebSocket입니다. 처음부터 끝까지 소켓(Socket)으로 연결되는 구조입니다.

이것이 바로 전화 통화를 위해 프레임워크의 전송 계층(Transport layer)을 건너뛸 수 있는 이유입니다. 통신사(Carrier)가 여러분에게 도달하기 전에 이미 어려운 부분을 WebSocket으로 변환해 두었기 때문입니다. 여러분의 역할은 단지 두 개의 소켓을 연결하는 브릿지(Bridge) 역할을 하는 것뿐입니다.

Twilio를 사용하여 전화번호 연결하기

전화 사례를 구체적으로 살펴보겠습니다. 이 사례는 가장 많은 장치가 필요할 것처럼 들리지만, 실제로는 가장 적은 장치가 필요한 사례이기 때문입니다.
엔드 투 엔드(End-to-end) 경로는 네 단계의 홉(Hop)으로 이루어집니다:

발신자 ↔ Twilio Media Streams ↔ 여러분의 서버 ↔ Voice Agent API

Twilio가 전화 네트워크를 처리합니다. 여러분의 서버는 얇은 브릿지 역할을 합니다. 에이전트는 음성 대 음성(Speech-to-speech)을 처리합니다. 그리고 한 종류의 고통을 완전히 제거해 주는 세부 사항이 있습니다. Twilio의 네이티브 G.711 μ-law 오디오는 Voice Agent API의 audio/pcmu 인코딩과 바이트 호환(Byte-compatible)이 되므로, 여러분의 브릿지는 트랜스코딩(Transcoding)이나 리샘플링(Resampling) 없이 오디오를 있는 그대로 전달합니다.

전화가 들어오면 Twilio는 여러분의 서버에 있는 웹훅(Webhook)을 호출합니다. 여러분은 Media Streams WebSocket을 여는 TwiML을 반환합니다:

app.post("/twiml", (req, res) => {
  const callId = newCallId();
  const hostname = process.env.HOSTNAME.replace(/^https?:\/\//, "");
...

Twilio가 해당 Media Streams 소켓을 열면, 여러분의 서버는 Voice Agent API에 병렬 연결을 열고 세션을 인라인(Inline)으로 구성합니다. 즉, Twilio와 맞추기 위해 양방향 모두 μ-law로 말하도록 설정하는 것입니다:

const aaiWs = new WebSocket("wss://agents.assemblyai.com/v1/ws", {
  headers: { Authorization: process.env.ASSEMBLYAI_API_KEY },
});
...

session.ready가 발생하면, 전체 브릿지는 두 개의 전달(Forwarding) 규칙과 중단(Interruptions)을 위한 규칙 하나로 구성됩니다:

// Caller → Agent: 각 Twilio media 이벤트는 input.audio 이벤트가 됩니다.
tw.on("media", (msg) => {
  if (msg.media.track !== "inbound") return;
...

이것이 바로 프로덕션급 전화 에이전트입니다. SIP 스택도, 미디어 서버(Media Server)도, 오케스트레이션 프레임워크(Orchestration Framework)도 필요 없습니다. 오직 웹훅(Webhook)과 소켓 브릿지(Socket Bridge)만 있으면 됩니다. AssemblyAI는 인바운드(Inbound) 및 아웃바운드(Outbound) 호출과 도구 처리(Tool Handling)가 연결된 완전한 Twilio 예제 리포지토리를 유지 관리하고 있으며, 전체 가이드는 Connect to Twilio에서 확인할 수 있습니다. 동일한 패턴이 Twilio, Vonage 및 기타 다른 통신사(Carriers)에도 적용됩니다. 이들은 모두 미디어 웹소켓(Media WebSocket)을 제공합니다.

STT 레이어만 교체하고 싶다면 어떻게 하나요?

모든 사람이 완전히 새로운 것을 구축하는 것은 아닙니다. 이미 좋아하는 LLM과 TTS 음성을 가지고 있고, 오직 더 나은 전사(Transcription) 기능만을 원할 수도 있습니다. 즉, 한 부분만 떼어내어 교체하는(Rip-and-replace) 경로를 원하는 경우입니다. 이 역시 프레임워크 없이 수행할 수 있습니다.

스트리밍 음성-텍스트 변환 (Streaming Speech-to-Text) 웹소켓에 직접 연결하고 나머지 스택은 그대로 유지하면 됩니다:

import json
from urllib.parse import urlencode
import websocket  # pip install websocket-client
...

루프(Loop)를 직접 제어하게 됩니다. 즉, LLM에는 부분 전사(Partial Transcripts)를 전달하고, TTS에는 토큰(Tokens)을 전달하며, 끼어들기(Barge-in)를 트리거하기 위해 SpeechStarted 이벤트를 처리합니다. 이는 올인원(All-in-one) Voice Agent API보다 더 많은 배선(Wiring) 작업이 필요하지만, 여전히 직접적인 소켓을 사용하며 프레임워크를 배포할 필요도 없습니다. 더 긴 빌드 과정을 원하신다면, 저희의 Python을 이용한 실시간 전사 (Real-time transcription in Python) 가이드에서 단계별로 확인하실 수 있습니다.

사람들이 혼동하기 쉬운 몇 가지 세부 사항이 있습니다: 스트리밍(streaming)은 speech_model(단수)을 사용합니다. 이는 폴백 라우팅(fallback routing)을 위해 speech_models 배열을 받는 사전 녹음 API와는 반대되는 방식입니다. 또한, universal-3-5-pro는 문장 부호 기반의 턴 감지(turn detection)를 사용하므로, end_of_turn_confidence_threshold 설정은 이 모델에서 아무런 동작을 하지 않습니다(no-op). 세션이 단일 언어(monolingual)라면 language_code를 전달하여 모델이 하나의 언어에 고정되도록 하세요. 네이티브 코드 스위칭(code-switching)을 유지하려면 이 설정을 비워두면 됩니다.

이것이 엔터프라이즈 규모에서도 유효할까요?

타당한 반론이 있을 수 있습니다: "단일 API는 데모용으로는 괜찮지만, 실제 운영 환경(production)에서도 버틸 수 있을까요?" 바로 이 지점이 파이프라인을 통합하는 것이 해가 되기는커녕 오히려 도움이 되는 부분입니다.

가동 부품이 적을수록 장애 모드(failure modes)도 적어집니다. 세 개의 벤더(vendor)를 계층적으로 연결하면 속도 제한(rate-limit)이 걸릴 수 있는 요소가 3개, 장애가 발생할 수 있는 요소가 3개, 그리고 복합적으로 작용하는 지연 시간(latency) 예산이 3개가 됩니다. 반면 단일 연결은 각각 하나씩만 가집니다. 여러분이 직접 장애 대응(pager)을 책임져야 하는 상황이라면, 이 계산은 그 어떤 벤치마크보다 중요합니다.

함께 확장되는 동시성(Concurrency). 종량제(Pay-as-you-go) 계정은 분당 100개의 새로운 스트림으로 시작하며 동시 세션에 대한 엄격한 제한이 없고, 용량은 자동으로 확장됩니다. 이용률이 70%를 넘어서면 대략 60초마다 약 10%씩 증가합니다. AssemblyAI는 매달 수백만 시간의 오디오와 6억 건 이상의 추론(inference) 호출을 처리하며, 플랫폼 내에서 무제한의 동시성을 제공합니다.

예측 가능한 가격 책정. Voice Agent API는 STT, LLM, TTS를 하나로 묶어 시간당 4.50달러의 고정 요금을 적용합니다. 모델링해야 할 별도의 벤더별 측정 항목이 없으므로, 용량 계획(capacity planning)을 추측 게임이 아닌 스프레드시트로 관리할 수 있습니다. (직접 LLM과 TTS를 가져오는 것을 선호하시나요? 스트리밍 STT 레이어는 별도의 가격 정책에 따라 별도로 청구됩니다. Universal-3.5 Pro Realtime의 경우 컨텍스트 및 키워드 프롬프팅이 포함되어 시간당 0.45달러입니다.)

준수 사항 및 데이터 거주성(residency) 항목. SOC 2 Type 2를 준수하며, streaming.eu.assemblyai.com을 통해 미국과 동일한 가격으로 EU 데이터 거주성(data residency)을 제공합니다. 오디오가 귀하의 VPC(Virtual Private Cloud)를 벗어날 수 없는 경우를 위한 셀프 호스팅(self-hosted) 옵션도 제공됩니다. 보호 대상 건강 정보(PHI)를 취급하는 팀의 경우, AssemblyAI는 HIPAA에 따른 비즈니스 관계자(business associate)이며, 영업 담당자와의 통화 없이도 몇 분 만에 서명할 수 있는 비즈니스 관계자 부속서(BAA, Business Associate Addendum)를 제공합니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0