LabHub
시작하기
배우기 러닝패스 코스

음성 AI 에이전트 — 듣고, 찾고, 말하는 파이프라인

언제 대답할까 — VAD·턴 끝 판정·끼어들기

LabHub 에서 이어서 보기

한 줄 요약

음성 비서가 언제 입을 열지는 말이 끝났다는 판정(끝점, endpointing) 이 정한다. VAD(음성 구간 검출)는 소리가 말인지 아닌지를 프레임마다 가르고, 턴 판정은 '말이 멈춘 지 얼마나 됐나' 로 VAD 구간을 턴으로 묶는다. 멈춤 문턱(gap)을 짧게 잡으면 말 중간을 끊고, 길게 잡으면 늦게 대답한다. 비서가 말하는 동안 사람이 끼어들면(barge-in) 스피커 소리가 마이크로 되돌아온 에코를 먼저 빼야 사람의 목소리가 보인다.

왜 이게 필요했나

사람끼리의 대화에서 차례가 넘어가는 간격은 짧다. 10개 언어의 대화를 잰 연구(Stivers et al., 2009, PNAS)는 대답이 시작되기까지의 간격이 대체로 0~200 ms 근처에 몰린다고 보고했다. 사람은 상대가 말을 끝내기 전에 이미 끝을 예측한다. 기계는 그러지 못하고 조용해진 것을 확인한 뒤에야 끝났다고 안다. 그 확인 시간이 곧 대답의 최소 지연이다 — 모델을 아무리 빠르게 해도 줄지 않는다(모듈 8 에서 이 몫을 따로 잰다).

문턱 하나로 해결되지도 않는다. "화요일로 예약했는데… (생각하는 쉼) …목요일로 바꾸고 싶어요" 에서 쉼은 0.5초가 넘을 수 있다. 그 자리에서 끊으면 비서는 '화요일로 예약했는데' 에 대답해 버린다.

어떻게 동작하나

에너지 VAD. 프레임의 dBFS 가 문턱(예: -35)보다 크면 말로 친다. 빠르고 설명하기 쉽지만 큰 소리는 전부 말이다. 실습의 통화에는 6초 지점에 문을 두드리는 짧은 소음을 넣어 두었고, 에너지 VAD 는 이것을 말 구간 하나로 잡는다(오경보). 반대로 작게 말하면 놓친다.

신경망 VAD(silero). 작은 신경망이 프레임(16 kHz 에서 512 샘플, 32 ms)마다 '말일 확률' 을 내고, 확률이 threshold 를 넘으면 말, min_silence_duration 이상 조용하면 구간을 닫는다. 이 이미지의 silero VAD(onnx, MIT)는 16.7초 소리를 CPU 2코어 파드에서 56 ms 에 처리했다(nuc1 실측). 실습 자료에서는 문 두드림을 말로 잡지 않았다. 다만 구간의 끝을 실제 말 끝보다 0.5초 안팎 늦게 닫는다(이 자료에서 실측 0.5~0.7초) — 말끝의 여운과 숨소리를 말로 치기 때문이다. 이 늦음도 대답 지연에 그대로 더해진다.

턴으로 묶기. VAD 는 쉼마다 구간을 끊는다. 턴은 그 구간들 가운데 간격이 gap 보다 짧은 것을 이어 붙인 것이다. gap 을 0.2초로 잡으면 생각하는 쉼에서도 턴을 끊고(말 중간 끊기), 1.2초로 잡으면 다음 사람 차례까지 한 턴으로 붙거나 매번 1초 넘게 기다린다. 실습에서 gap 0.2~1.2초를 훑어 턴 수가 맞는 범위를 찾고, 그중 가장 짧은 값의 대답 지연 — (VAD 가 본 말 끝 + gap) − 실제 말 끝 — 을 잰다.

실무에서는 여기에 의미 신호를 더한다. ASR 의 부분 결과가 문장으로 끝났는지('…바꾸고 싶어요'), 억양이 내려갔는지를 보고 gap 을 줄이거나 늘린다. 스트리밍 ASR 모델에도 끝점 규칙이 있다 — sherpa-onnx 의 rule2 는 '무언가 인식된 뒤 N초 조용하면 끝' 이다.

끼어들기와 에코. 비서가 말하는 동안 마이크에는 사람 목소리뿐 아니라 스피커에서 새어 든 비서 자신의 목소리가 들어온다. 이 상태로 VAD 를 돌리면 재생을 시작하자마자 '사람이 끼어들었다' 고 판단해 스스로 말을 끊는다. 해법은 우리가 내보낸 소리(참조 신호)를 알고 있다는 점을 쓰는 것이다. 마이크 = 사람 + g·(지연 d 만큼 늦은 참조) 라고 보고, 사람이 말하기 전 구간에서 상호상관이 가장 큰 d 와 최소제곱 배율 g 를 구해 빼면 사람 목소리가 남는다. 실제 반향 제거기(AEC)는 방의 반사까지 따라가는 적응 필터이고, 브라우저에서는 getUserMedia 의 echoCancellation 제약으로 켠다. 실습의 에코는 지연 하나·배율 하나로 만든 단순한 모형이라 원리만 본다.

현장에서 만나는 모습

LabHub 의 회화 연습은 끝점 판정을 사람에게 맡긴다 — 녹음 단추를 눌렀다 떼는 방식이다(static/lang/talk.js 의 MediaRecorder). 틀릴 일이 없고 구현이 단순하지만, 사람이 말을 끝낸 뒤 단추를 누르고, 그다음에야 녹음 전체가 전송된다. 전화처럼 손을 쓰지 않는 음성 비서는 이 판정을 기계가 해야 하고, 그 순간 이 모듈의 맞바꿈이 생긴다.

문턱을 정할 때 가장 흔한 실수는 한 녹음에 맞춰 외우는 것이다. 이 코스의 두 통화만 봐도 VAD 구간 사이의 간격이 한 통화에서는 0.29초(말 중간)와 1.06초(차례 바뀜)로 넉넉히 갈리는데, 다른 통화에서는 차례가 바뀐 자리의 간격이 0.73초까지 좁아진다. 한 파일로 0.74초를 고르면 다른 파일에서 두 사람의 차례가 한 턴으로 붙는다. 그래서 규칙은 본 적 없는 자료에서 시험하고, 운영에서는 넘겨진 턴과 끊긴 턴을 표본으로 모아 문턱을 다시 본다.

다음 실습에서 할 것

통화 call_a 에서 에너지 VAD 와 silero VAD 의 구간을 내 정답과 겹쳐 오경보·놓침을 센다. VAD 구간을 턴으로 묶는 turns.py 를 만들어 처음 보는 통화에서도 맞는지 채점기가 시험하고, gap 을 훑어 턴이 맞는 범위와 대답 지연을 잰다. 마지막으로 에코가 섞인 마이크에서 지연·배율을 추정해 빼고, 사람이 끼어든 시각을 찾는다.