텍스트로 도는 에이전트를 하나 만들어 두고 앞에 음성 인식을, 뒤에 음성 합성을 붙이면 음성 에이전트가 된다 — 라고 생각하면 첫 시연에서 무너진다. 동작은 한다. 그런데 대화처럼 느껴지지 않는다. 말을 끝냈는데 한참 조용하고, 끼어들면 무시당하고, 끊었는데 로봇이 하던 말을 계속한다.
원인은 모델의 성능이 아니라 시간이다. 텍스트 채팅에서 3초는 "생각하는 중"이지만 통화에서 3초 침묵은 "끊겼나?"다. 음성 에이전트를 만든다는 것은 대체로 시간 예산을 짜고 지키는 일이고, 나머지는 그 예산을 맞추기 위한 선택이다.
두 가지 구성 방식
지금 음성 에이전트를 만드는 길은 크게 둘이다.
이어 붙인 구성은 음성 인식으로 말을 글로 옮기고, LLM이 답을 만들고, 음성 합성으로 다시 소리를 만든다. 세 부품이 분리되어 있어서 각각을 따로 고르고 따로 고칠 수 있다.
음성 대 음성 모델은 소리를 직접 받아 소리를 직접 내놓는다. 중간에 글자가 없다. 멀티모달 LLM이 이미지를 받아들이는 것과 같은 방식으로 오디오를 토큰으로 다룬다.
둘의 가장 큰 차이는 응답까지 걸리는 시간이다.
이어 붙인 구성에서는 단계마다의 시간이 그대로 더해진다. 게다가 각 단계가 앞 단계가 끝나야 시작할 수 있는 구간이 있다. 음성 인식의 최종 결과가 나와야 LLM에 보낼 문장이 확정되고, LLM이 문장 하나를 마쳐야 합성을 시작할 수 있다.
물론 겹칠 수 있는 부분을 최대한 겹치면 예산은 꽤 줄어든다.
- 음성 인식은 중간 결과를 흘려보낸다. 최종 결과를 기다리지 말고 부분 인식 결과로 미리 모델 호출을 준비한다
- LLM 출력은 문장 단위로 잘라 합성에 넘긴다. 전체 답이 끝날 때까지 기다리지 않는다
- 합성은 조각으로 스트리밍한다. 첫 조각이 나오는 대로 재생을 시작한다
이렇게 하면 이어 붙인 구성도 800 ms 안쪽으로 들어갈 수 있다. 그래도 음성 대 음성 쪽이 구조적으로 유리한 것은 사실이다. 대신 잃는 것도 분명하다.
| 이어 붙인 구성 | 음성 대 음성 | |
|---|---|---|
| 응답 시간 | 단계가 더해진다. 최적화 여지가 많다 | 짧다 |
| 억양·감정·웃음 | 글자로 옮기며 사라진다 | 그대로 전달된다 |
| 부품 교체 | 인식·모델·합성을 따로 바꾼다 | 통째로 바꿔야 한다 |
| 무엇이 오갔는지 확인 | 글자로 남아 그대로 읽을 수 있다 | 따로 받아쓰기를 붙여야 한다 |
| 출력 통제 | 텍스트를 검사한 뒤 소리로 바꿀 수 있다 | 소리가 나온 뒤에는 늦다 |
| 목소리 선택 | 합성 엔진이 지원하는 만큼 | 모델이 주는 만큼 |
통제와 확인이 필요한 서비스라면 이어 붙인 구성이 여전히 안전하다. 금융 상담처럼 말한 내용을 기록으로 남기고 금지어를 걸러야 하는 곳에서는, 소리가 나가기 전에 텍스트를 한 번 볼 수 있다는 점이 크다. 반대로 사람처럼 들리는 것이 목적이면 음성 대 음성이 낫다.
섞어 쓰는 구성도 흔하다. 음성 대 음성 모델을 쓰되 받아쓰기를 나란히 돌려 기록만 남기는 방식이다.
발화 끝을 언제로 볼 것인가
이어 붙인 구성의 시간 예산에서 가장 큰 항목이 대체로 여기다. 그리고 여기가 가장 자주 대충 넘어가는 자리이기도 하다.
기본 도구는 음성 활동 감지(VAD)다. 소리가 사람의 말인지 아닌지를 판단해서, 말이 멈추고 일정 시간이 지나면 발화가 끝났다고 본다. 이 "일정 시간"이 문제다.
- 짧게 잡으면(200 ms) 사람이 중간에 숨을 고르거나 생각하느라 멈춘 자리를 끝으로 착각한다. 문장을 반만 듣고 답한다
- 길게 잡으면(1,000 ms) 대화가 굼떠진다. 사용자는 말을 끝냈는데 시스템이 계속 기다린다
고정값 하나로는 잘 안 맞는다. 그래서 문장이 끝난 것처럼 들리는지까지 보는 방식을 쓴다. 부분 인식 결과를 보고 "삼십이만 오천 원을" 처럼 아직 이어질 문장이면 더 기다리고, "네 그렇게 해 주세요" 처럼 끝난 문장이면 짧게 기다린다. 억양이 올라갔는지 내려갔는지를 함께 보기도 한다.
여기서 정해 둘 것이 하나 더 있다. 잘못 잘랐을 때 어떻게 할 것인가. 사용자가 말을 이어 가는 중에 답을 시작했다면, 그것은 끼어들기와 같은 상황으로 처리해야 한다.
끼어들기는 되돌리기 문제다
사람 사이의 대화에서는 상대가 말하는 중에 끼어드는 일이 자연스럽다. 음성 에이전트도 이것을 받아 줘야 하는데, 구현에서 자주 빠뜨리는 부분이 있다. 끼어들기를 처리한다는 것은 재생을 멈추는 것이 아니라 이미 만들어 둔 것을 되감는 일이다.
세 가지 중 두 번째가 가장 눈에 안 띄고 가장 오래 아프다. 모델은 "결제 내역을 확인해 보니 8월분 12만 원이 미납이고 9월분은 정상 처리되었습니다"라는 문장 전체를 만들었지만, 사용자는 "결제 내역을 확인해 보니 8월분"까지만 듣고 끼어들었다고 하자. 이때 대화 기록에 문장 전체를 남기면 모델은 자기가 다 말했다고 믿는다. 사용자가 나중에 "9월은 어떻게 됐냐"고 물으면 "아까 말씀드렸듯이"로 시작하는 답이 나온다.
그래서 재생기가 실제로 어디까지 소리를 냈는지를 알려 줄 수 있어야 하고, 대화 기록은 거기까지만 남긴다. 잘린 자리에 「(도중에 끊김)」 같은 표시를 함께 남겨 두면 모델이 상황을 더 잘 이해한다.
세 번째, 도구 실행 취소도 챙긴다. 사용자가 "아니 잠깐만요"로 끊었는데 이미 시작된 결제 API 호출이 그대로 진행되면 곤란하다. 되돌릴 수 없는 동작은 실행 전에 한 번 확인을 받는 편이 안전하다.
도구를 부르는 동안의 침묵
음성 에이전트가 텍스트 에이전트보다 훨씬 어려워지는 지점이 여기다. 텍스트 채팅에서는 도구를 부르는 동안 "조회 중..."을 띄우면 되지만, 통화에서는 그 자리가 그냥 무음이다. 2초 넘게 조용하면 사용자는 끊겼다고 생각한다.
쓸 수 있는 방법이 몇 가지 있다.
- 먼저 말하고 조회한다. "네, 확인해 볼게요" 를 먼저 내보내고 그 소리가 나가는 동안 API를 부른다. 가장 자연스럽고 가장 흔히 쓰인다
- 채움말을 정해 둔다. 조회가 길어지면 "조금만 기다려 주세요"를 한 번 더 넣는다. 다만 같은 문장이 반복되면 금방 어색해지므로 몇 개를 돌려 쓴다
- 긴 작업은 아예 다른 차례로 넘긴다. "확인해서 문자로 보내 드릴게요" 처럼 통화 안에서 끝내지 않는다
그리고 도구 하나하나에 시간 상한을 둔다. 텍스트 에이전트에서는 10초 걸리는 조회도 참을 만하지만 통화에서는 아니다. 상한을 넘기면 실패로 처리하고 사람에게 넘기거나 다른 경로를 안내한다.
무엇이 유독 자주 깨지는가
음성 에이전트에서 품질 문제가 나는 자리는 대체로 정해져 있다.
고유명사와 숫자가 첫째다. 사람 이름, 주소, 계좌번호, 상품 코드처럼 문맥으로 추측할 수 없는 것들이다. 인식기가 한 글자만 틀려도 조회가 실패한다. 대응은 두 가지다. 그 서비스에서 나올 법한 어휘 목록을 인식기에 힌트로 넘기고, 중요한 값은 다시 읽어 확인한다. "010-1234-5678 맞으실까요"가 번거로워 보여도 잘못된 조회보다 낫다.
언어가 섞이는 구간이 둘째다. 한국어 문장 안에 영어 제품명이나 영문 약어가 들어오는 경우가 실제 대화에서는 아주 흔한데, 인식기가 한 언어로 고정되어 있으면 여기서 무너진다.
입력 품질이 셋째다. 전화망을 지나온 소리는 8 kHz로 대역이 잘려 있고, 스피커폰에는 반향이 있고, 매장에서는 옆 사람 목소리가 함께 들어온다. 개발자 헤드셋으로만 시험한 시스템은 현장에서 다르게 동작한다. 실제 경로로 녹음한 소리를 평가 데이터에 반드시 넣는다.
자기 소리를 자기가 듣는 것이 넷째다. 스피커로 나간 합성음이 마이크로 다시 들어오면 시스템이 그것을 끼어들기로 오해해 스스로 말을 끊는다. 반향 제거가 켜져 있는지, 켜져 있어도 실제로 동작하는지 확인한다.
평가는 텍스트 에이전트와 다르게 짠다
과제 성공률만 재면 음성 에이전트의 실제 품질을 못 본다. 최소한 이 셋을 더 본다.
- 응답 시간의 꼬리. 평균이 아니라 상위 95%·99% 지점을 본다. 평균 600 ms인데 스무 번에 한 번 3초가 걸리면 그 한 번이 통화를 망친다
- 끼어들기 성공률. 사용자가 말을 시작한 시점부터 소리가 실제로 멈추기까지의 시간을 재고, 대화 기록이 들린 데까지만 남았는지를 확인한다
- 핵심 값의 정확도. 전체 받아쓰기 정확도가 아니라 이름·숫자·코드만 따로 잰다. 이 값들이 과제 성공을 좌우하는데 전체 지표에는 거의 안 잡힌다
그리고 평가 데이터는 실제 통화 녹음으로 만든다. 조용한 방에서 또박또박 읽은 문장으로는 위의 어떤 문제도 드러나지 않는다.
정리
- 음성 에이전트에서 어려운 것은 모델이 아니라 시간 예산이다. 통화에서 800 ms를 넘기면 상대가 멈칫한 것처럼 들린다
- 이어 붙인 구성은 단계 시간이 더해지지만 부분 인식·문장 단위 합성·스트리밍 재생으로 상당히 줄일 수 있다
- 음성 대 음성 모델은 빠르고 억양이 살지만, 나가기 전에 내용을 검사하기 어렵다. 기록과 통제가 필요하면 이어 붙인 구성이 안전하다
- 발화 끝 판단은 고정된 침묵 시간 하나로 안 된다. 문장이 끝난 것처럼 들리는지까지 함께 본다
- 끼어들기는 재생 중지가 아니라 되돌리기다. 안 나간 오디오를 버리고, 들린 데까지만 기록하고, 진행 중인 호출을 취소한다
- 도구를 부르는 동안 침묵을 두지 않는다. 먼저 짧게 말하고 그 소리가 나가는 사이에 조회한다. 도구마다 시간 상한을 둔다
- 깨지는 자리는 정해져 있다 — 고유명사·숫자, 언어가 섞인 구간, 전화망 음질, 자기 소리의 되돌아옴
- 평가는 응답 시간의 꼬리, 끼어들기 성공률, 핵심 값만 따로 잰 정확도 셋을 실제 통화 녹음으로 본다
읽어주셔서 감사합니다. 😊

