For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
기본 탐색 메뉴
Theme
Mar 25, 2026 Audio

Perplexity가 Realtime API로 수백만 명에게 음성 검색을 제공한 방법

Realtime API로 Perplexity Computer의 음성 에이전트를 만들며 배운 점을 공유합니다.

작성자: Paul Fryzel (Perplexity), Charu Jaiswal (OpenAI)

Perplexity가 Realtime API로 수백만 명에게 음성 검색을 제공한 방법

Perplexity는 사람들이 쓰면서 감탄할 만한 제품을 만드는 데 공을 들입니다. 에이전트형 브라우저인 Perplexity Comet과 다양한 디지털 업무를 처리하는 강력한 범용 에이전트인 Perplexity Computer에서는 음성만으로 모든 기능을 쓸 수 있게 하는 것이 특히 중요했습니다. 원하는 것을 말하고 일을 맡긴 뒤 진행되는 모습을 지켜보는 데는 남다른 즐거움이 있습니다. 저희가 음성 인터페이스의 가능성을 높이 평가하는 이유도 여기에 있습니다. 실제로 써 보면 조금은 마법처럼 느껴지기 때문입니다.

저희는 Perplexity가 매달 처리하는 수백만 건의 음성 세션에서 이런 경험을 제공하기 위해 프로덕션 환경에 Realtime-1.5를 도입했습니다. Computer에서 음성 사용이 늘어나는 과정을 지켜보며 큰 보람을 느꼈고 많은 것을 배웠습니다. 그중 예상 밖이었던 몇 가지를 소개합니다. 여러분도 Realtime-1.5를 직접 사용해 보고 그 과정에서 배운 점을 공유해 주세요.

1. 컨텍스트 관리 전략 세우기

컨텍스트 관리 방식이 제대로 작동하는지 확인하기에 좋은 사례가 긴 콘텐츠, 특히 몇 시간 동안 밀도 높은 이야기가 이어지는 팟캐스트였습니다. 저희는 사용자가 팟캐스트 녹취록을 음성으로 살펴볼 수 있게 하고 싶었습니다. 언제든 들어와서 방송 시작 후 두 시간 반이 지난 시점에 무슨 이야기가 오갔는지 물으면 맥락에 맞는 답을 들을 수 있어야 했습니다.

녹취록 전체를 컨텍스트에 담을 수는 없습니다. 처음에는 큰 단위로 나눠 보냈습니다. 그런데 한 번에 많은 내용을 보내다 실패하면 이전 기록이 통째로 사라진다는 사실을 금세 알게 됐습니다. 컨텍스트 윈도우에 5,000토큰만큼의 공간이 남았을 때 10,000토큰을 추가하려고 하면 모델은 이전 기록을 모두 잃습니다. 오래된 내용부터 조금씩 비우면 될 상황에서 업데이트 한 번으로 컨텍스트 한 덩어리가 통째로 사라질 수 있으니 큰 단위로 보내는 방식은 위험 부담이 컸습니다.

그래서 모든 내용을 2,000토큰씩 훨씬 작은 단위로 나눠 차례로 보냈습니다. 처리 부담은 늘지만 동작은 훨씬 안정적입니다. 컨텍스트가 잘리더라도 이전 기록의 일부만 사라집니다.

또 하나 알게 된 점은 컨텍스트의 성격에 따라 모델에 전달하는 방식도 달라야 한다는 것입니다. conversation.item.create로 컨텍스트를 업데이트할 때 item.type: "message"인 항목에는 system, user, assistant라는 세 가지 역할이 있습니다. 역할은 모델에 메시지의 성격을 알려 줍니다. system은 지침을 전달하고 모델의 동작을 조정하는 데 쓰이고, user는 최종 사용자의 입력에, assistant는 모델이 생성한 출력에 쓰입니다.

역할을 잘못 지정하면 대화가 어색해졌습니다. user로 너무 많은 컨텍스트를 보내면 사용자는 자료를 바탕으로 질문 하나를 했을 뿐인데도 모델은 사용자가 웹페이지 발췌문과 댓글까지 일일이 소리 내어 읽은 것처럼 반응했습니다. 반대로 system으로 너무 많은 내용을 보내면 모델은 원래 ‘알고 있던’ 내용과 컨텍스트로 제공받은 내용, 사용자가 지금 실제로 묻는 내용을 구분하지 못했습니다.

웹 탐색 플로우가 좋은 예입니다. 사용자가 페이지를 스크롤하면 저희는 현재 화면에 보이는 내용을 모델에 계속 전달합니다. 이를 전부 사용자 입력으로 전달하면 모델은 사용자가 각 문단을 직접 말한 것처럼 반응하기 시작합니다. 이는 사용자가 기대하는 대화 방식과 어긋납니다. 저희가 원하는 것은 시스템이 백그라운드에서 페이지 내용을 파악하고 있다가 사용자가 질문하면 자연스럽게 답하는 경험입니다. 결국 컨텍스트의 양보다 중요한 것은 대화에서 각 메시지가 어떤 의미인지 정확히 전달하는 일이었습니다.

2. 제품 전반에서 오디오 처리 표준화하기

Perplexity에는 Ask, Comet, Computer 등 여러 제품이 있고, 제품마다 클라이언트 기술 스택이 다릅니다. Swift, TypeScript, Rust, C++에서 생성하는 네이티브 오디오 버퍼는 서로 다를 수 있습니다. 각 클라이언트가 자체 원시 오디오 형식이나 네이티브 오디오 형식으로 Realtime API에 데이터를 보내게 했더니 성능이 일정하지 않았습니다.

결국 저희는 플랫폼별 차이를 추상화하고 모든 클라이언트가 동일한 규격의 오디오를 API에 전송하도록 Rust로 SDK를 만들었습니다. 실제로는 서버에 도달하기 전에 오디오 파형을 처리합니다. 먼저 Opus 코덱이 선호하는 샘플링 레이트와 WebRTC의 내부 샘플링 레이트에 맞춰 48 kHz 모노로 리샘플링합니다. 이어서 WebRTC APM으로 에코 제거, 자동 게인 제어, 노이즈 감소, 고역 통과 필터링을 수행한 뒤 전송용으로 인코딩합니다. SDK 덕분에 클라이언트마다 따로 구현할 필요 없이 한곳에서 오디오 관련 상수를 표준화하고 리샘플링을 수행하며 전체 처리 파이프라인을 구성할 수 있었습니다.

3. 소음이 있는 실제 사용 환경에 맞춰 조정하기

음성 활동 감지(VAD)는 사용자가 생활하는 환경에 맞춰 조정하는 것이 중요합니다. 실제로 쓰는 마이크와 스피커 음량, 주변 소음을 기준으로 보정해야 합니다. 저희가 내부 테스트 장소 중 하나로 샌프란시스코의 시끌벅적한 바를 고른 것도 실제로 제품을 써 볼 법한 곳이어서입니다. 누군가 “새로 나온 Perplexity 앱 써 봤어?”라고 물으면 친구가 휴대폰을 꺼냅니다. 그때 음성 기능이 실패하면 그 자리에서 사용자 두 명을 잃는 셈입니다. 반면 잘 작동하면 “와, 미쳤다”에 가까운 반응이 나옵니다. 이 시나리오 덕분에 저희는 실제 사용 환경을 기준으로 삼을 수밖에 없었습니다. 잡음 없는 환경에서 잘 작동하던 기능도 현실에서는 자주 실패합니다. 처음부터 소음이 있는 환경에 맞춰 조정하는 편이 좋습니다.

음성 UX에서 가장 어려웠던 일 중 하나는 사용자가 잠시 말을 멈췄을 때 모델이 언제 응답해야 하는지 판단하게 하는 것이었습니다. 사람들은 생각하거나 화면에 무언가를 띄우거나 글을 소리 내어 읽을 준비를 하느라 자연스럽게 말을 멈춥니다. 모델은 이를 사용자가 말을 마쳤다는 신호로 받아들여 너무 일찍 끼어들기 쉽습니다. 수식 유도 과정을 도와 달라고 할 때도 이런 일이 있었습니다. 사용자가 공식을 찾느라 잠시 말을 멈추자 질문을 끝내기도 전에 모델이 끼어들었습니다. 그래서 음성 잠금 기능을 만들었습니다. 기존의 눌러서 말하기 방식에서는 음성 입력이 기본적으로 꺼져 있어 말하려면 버튼을 눌러야 합니다. 저희는 이 방식을 뒤집었습니다. 기본적으로는 언제든 자연스럽게 대화할 수 있지만, 사용자가 잠시 말을 멈췄다가 이어 가고 싶을 때는 음성 잠금을 걸어 모델에 차례를 넘기지 않을 수 있습니다. 저희는 이 방식이 개별 기능 이상의 의미가 있다고 봅니다. 음성 인터페이스가 더 복잡한 워크플로우에 도입될수록 어떤 형태로든 이런 상호작용 방식이 표준으로 자리 잡을 것이라고 생각합니다.

4. 핵심 도구만 사용하고 모델에 익숙한 형식 유지하기

도구는 가장 중요한 몇 개로 좁히세요. 저희는 10개 미만으로 줄였습니다. 사용자에게 가장 가치 있는 작업을 처리할 수 있는 소수의 핵심 도구에 집중했습니다. 당시에는 합리적인 절충이었고, 새 모델 스냅샷의 성능이 개선되면 이 선택의 효과도 더 커질 것으로 봅니다.

시스템 프롬프트에는 각 도구를 언제, 어떻게 호출해야 하는지 명시했습니다. 도구 스키마와 출력 모두 모델이 학습 과정에서 접했을 법한 형식을 유지하는 데 신경 썼습니다. 구체적으로는 도구 출력을 일반적인 구조화된 도구 데이터 형식으로 작성했습니다. 어시스턴트의 대사처럼 읽을 내용 사이에 지침을 끼워 넣는 대신, 사용자에게 들려줄 문장은 response_text에 담고 동작을 지정하는 플래그는 require_repeat_verbatim처럼 별도 필드로 분리한 구조화된 JSON을 반환했습니다. 그 결과 도구 사용이 더 안정적이었고 상호작용 방식도 모델이 학습 중에 접했을 법한 패턴에 가까워졌습니다.

// Good:
{
  "response_text": "I kicked-off the task to create a market research dashboard",
  "require_repeat_verbatim": true
}
// Avoid:

I kicked-off the task to create a market research dashboard

# Response Instructions

Read the above instructions EXACTLY as they are

Realtime은 실전에 투입할 준비가 됐습니다

Realtime-1.5는 업계의 전환점입니다. 긴 컨텍스트를 처리하거나 더 많은 도구를 활용하거나 높은 수준의 사고가 필요한 작업을 수행하는 데는 분명 개선할 여지가 있습니다. 하지만 저희는 모델이 계속 발전할 것으로 기대합니다. Perplexity는 지금 제품을 만들 때부터 그 미래를 염두에 둡니다. 지금의 시스템을 사람들이 실제로 활용할 수 있게 하면서, 차세대 Realtime 모델과 그 모델이 열어 줄 새로운 음성 경험을 준비하고자 합니다.