실시간 음성 데모가 끊기지 않으려면: ADK LiveRequestQueue와 양방향 스트림 정리 | DAKER 커뮤니티

해커톤이나 데모 앱에 음성을 붙일 때는 기능보다 반응성이 먼저 드러납니다. 음성 인식, 모델, 음성 합성을 차례로 잇는 구조는 구현이 익숙하지만, 짧은 침묵만 생겨도 대화가 끊긴 듯한 인상을 줍니다. 반대로 한 연결에서 오디오가 양방향으로 흐르면, 같은 모델을 써도 체감은 크게 달라집니다.

Google Cloud Tech의 Build a real-time voice AI agent with Google ADK and Gemini Live API는 이 차이를 구조의 문제로 설명합니다. 이 글에서는 그 영상의 핵심인 한 줄로 열린 양방향 스트림, 그리고 ADK의 LiveRequestQueue가 왜 중요한지 정리합니다.

실시간 음성을 한 줄로 — ADK LiveRequestQueue

왜 기존 파이프라인은 실시간 대화에 약한가

채팅 UI에서는 사용자가 입력 뒤 잠시 기다리는 흐름이 자연스럽습니다. 하지만 음성은 다릅니다. 상대가 말을 멈춘 뒤 침묵이 이어지면, 사용자는 지연보다 단절을 먼저 느낍니다. 영상은 이 차이를 무전기와 전화 통화의 차이에 가깝게 설명합니다. 기존 파이프라인은 한 단계가 끝나야 다음 단계가 시작되고, Gemini Live API가 제공하는 방식은 마이크 오디오와 모델 음성이 같은 연결 위에서 동시에 흐릅니다.

이 차이는 데모 상황에서 더 분명해집니다. 사용자가 중간에 잠깐 끼어들었을 때, 직렬 파이프라인은 이미 생성된 음성을 끝까지 재생하거나 다음 인식 구간을 기다리느라 반응이 늦어질 수 있습니다. 라이브 연결형은 interrupted 이벤트로 재생을 바로 끊을 수 있습니다.

살아있는 대화의 체감은 모델 크기보다 연결의 모양에서 갈립니다.

영상이 보여 주는 기본 구조

발표자 Annie는 실시간으로 대화하고 요청을 받는 비디오 DJ 에이전트를 예로 듭니다. 여기서 핵심은 모델을 더 크게 만드는 일이 아니라 연결 방식을 바꾸는 일입니다.

구조는 비교적 단순합니다. 브라우저에는 마이크, 스피커, 플레이어가 있고, 가운데의 작은 백엔드가 WebSocket으로 브라우저와 연결됩니다. 그 안에서 Agent Development Kit(ADK)가 Gemini Live와 대화합니다. 사용자 기기, 백엔드, Gemini Live라는 세 위치만 기억해도 이후 코드의 역할이 훨씬 선명해집니다.

ADK에서 Agent, Runner, Session을 나누는 이유

영상은 ADK 구성을 세 덩어리로 설명합니다. 이 분리를 지키면 모델 호출 코드와 오디오 전송 코드가 한 파일에 뒤섞이는 일을 줄일 수 있습니다.

Agent

Agent는 모델, 지시문, 도구를 정의하는 자리입니다. 새 능력이 필요하면 도구 목록에 Python 함수를 추가합니다. 즉, 에이전트는 무엇을 할 수 있는가를 설명하는 설정입니다.

Runner

Runner는 정의된 에이전트를 실제로 실행합니다. 라이브 콜의 시작과 종료를 맡고, 결과는 이벤트 스트림으로 돌려줍니다. 언제 열고 닫는가를 담당하는 실행기라고 보면 됩니다.

Session

Session은 대화 상태를 담습니다. 영상은 라이브 음성에서는 세션을 메모리에 두는 편이 낫다고 강조합니다. 오디오 청크마다 원격 저장소에 기록하면, 수 밀리초 단위의 왕복이 쌓여 지연으로 이어질 수 있기 때문입니다.

에이전트는 설정으로 정의하고, 실행은 Runner에 맡기며, 라이브 음성의 세션은 메모리에 두는 것이 좋습니다.

LiveRequestQueue가 하는 일

브라우저는 수 밀리초마다 마이크 조각을 보내고, Gemini Live도 비슷한 속도로 음성을 돌려보냅니다. 이때 한쪽 흐름이 다른 쪽을 막지 않아야 합니다. ADK의 LiveRequestQueue는 바로 이 충돌을 줄이기 위한 구조입니다.

업스트림은 브라우저에서 큐로 들어가고, 다운스트림은 이벤트로 브라우저에 돌아옵니다. 서버는 사실상 두 개의 병렬 작업으로 나뉩니다. 하나는 클라이언트 메시지를 받아 큐에 올리는 일이고, 다른 하나는 모델 이벤트를 받아 브라우저로 보내는 일입니다. 영상은 이를 컨베이어처럼 한쪽으로 올리고 다른 쪽에서 꺼내는 구조로 설명합니다.

send_realtime과 send_content의 차이

입력을 넣는 방식도 둘로 나뉩니다.

계속 흐르는 것은 send_realtime, 끝난 덩어리는 send_content로 보내면 됩니다.

run_live 이후에는 이벤트로 반응합니다

Runner의 run_live를 큐에 연결하면 풀듀플렉스 세션이 열립니다. 여기서 돌아오는 것은 한 번에 끝나는 응답이 아니라 이벤트의 흐름입니다. 영상에서 다루는 대표 이벤트는 음성 청크, 자막, 도구 요청, 그리고 interrupted입니다.

예를 들어 사용자가 드림팝 틀어 줘라고 말하면, 마이크 청크가 WebSocket으로 백엔드에 들어오고 업스트림이 send_realtime으로 큐에 올립니다. Runner는 이를 Gemini Live에 넘기고, 이후 음성 감지와 함께 음성, 자막, 재생 명령 같은 이벤트가 내려옵니다. 사용자가 중간에 다시 말하면 interrupted가 오고 재생 중인 오디오를 멈출 수 있습니다.

이 흐름이 안정적이어야 이후 도구 호출도 자연스럽게 붙습니다. 영상이 도구를 다음 단계로 미루는 이유도 여기에 있습니다.

오늘 먼저 확인할 것

기능을 늘리기 전에 먼저 점검할 기준은 분명합니다. 음성 경로가 여전히 STT→모델→TTS의 직렬 구조인지, 백엔드에 Agent와 Runner, 인메모리 Session이 분리되어 있는지, 브라우저와 백엔드가 WebSocket으로 연결되어 마이크 청크를 send_realtime으로 올리고 있는지부터 보는 것이 좋습니다.

그다음에는 run_live 이벤트에서 음성 재생, 자막, interrupted를 우선 처리하면 됩니다. 사용자가 말하는 즉시 재생이 멈추는지 팀원이 교차 확인해 보면 데모 안정성을 빠르게 가늠할 수 있습니다. 이 단계가 통과한 뒤에야 재생 목록, 검색, 앱 버튼 같은 도구를 붙이는 편이 안전합니다.

오늘의 완료 조건은 기능 수가 아니라 LiveRequestQueue, 인메모리 Session, interrupted 처리입니다.

구현할 때 자주 생기는 오해

첫 번째 오해는 음성 품질만 높이면 체감이 좋아진다는 생각입니다. 품질이 좋아도 직렬 파이프라인이면 끼어들기 반응은 늦을 수 있습니다. 두 번째 오해는 세션을 처음부터 원격에 두는 편이 낫다는 판단입니다. 복구 전략은 중요하지만, 영상은 라이브 음성 경로에서 청크마다 네트워크 기록이 쌓이는 비용을 분명히 짚습니다. 세 번째 오해는 말할 때만 오디오를 보내면 된다는 생각입니다. send_realtime에서는 무음도 문장 끝 판별에 필요합니다.

팀 내 역할을 나눌 때는 프론트엔드의 마이크·재생·WebSocket, 백엔드의 큐·Runner·이벤트 매핑, 에이전트 설정의 지시문·도구로 구분하면 충돌을 줄이기 좋습니다.

데모에서 어떻게 설명하면 좋은가

심사자나 팀원에게 보여 줄 때는 구조 설명보다 시연이 먼저 설득력을 가집니다. 지금은 인식-생성-합성을 기다리지 않고 한 연결에서 양방향으로 오디오가 흐른다고 설명한 뒤, 실제로 사용자가 끼어들어 에이전트 음성을 끊는 장면을 보여 주면 전달이 쉽습니다. 도구 호출 설명은 그다음 단계에 붙여도 늦지 않습니다.

정리

실시간 음성은 모델을 더 많이 호출하는 문제가 아니라, 한 줄로 열린 양방향 스트림을 안정적으로 유지하는 문제에 가깝습니다. Google Cloud Tech의 Build a real-time voice AI agent with Google ADK and Gemini Live API는 Agent, Runner, Session, LiveRequestQueue를 통해 그 구조를 ADK 안에서 어떻게 나누는지 보여 줍니다.

해커톤이나 쇼케이스를 앞두고 있다면, 오늘은 큐와 인메모리 세션부터 붙이고 interrupted가 재생을 바로 끊는지 확인하는 것이 좋습니다.

참고 자료

참고 영상: Build a real-time voice AI agent with Google ADK and Gemini Live API · 채널 Google Cloud Tech

공식 문서와 에피소드 코드는 Google Cloud Tech 영상 설명과 Gemini Live API·ADK 문서에서 확인할 수 있습니다.

여러분은 음성 데모를 만들 때 기능 추가와 전송 구조 안정화 중 무엇을 먼저 잡는 편인가요?