OptiGen Voice Gateway
전화 통화를 여러 Voice AI 백엔드에 연결하는 실시간 음성 게이트웨이입니다. PBX와 SIP 연동, 통화별 세션과 오디오 처리를 구현하고, Provider별 통신 차이를 공통 통화 흐름에서 분리했습니다.
프로젝트 개요
전화망과 AI 서비스 사이의 Voice Gateway 전체 설계 및 구현을 담당했습니다. PBX와 SIP의 통화 이벤트, 통화별 Worker와 세션, STT와 TTS, VAD 기반 발화 감지, DTMF 입력과 재연결을 다뤘습니다. 수신 번호로 조회한 봇 설정을 통화에 적용하고, AI의 SSE 응답을 문장별 음성으로 전달했습니다.
NICE PoC에서는 STT 평가 파이프라인과 ITN, 발음 유사도 기반 MCP 보정으로 주소 인식 오류를 개선했습니다. 공급자별 통신과 오디오 형식을 분리하고 음성 패킷이 몰려 전송되는 타이머 문제를 수정했습니다. 배포 과정의 서버 명령 실행도 SSH에서 SSM Run Command로 전환했습니다.
프로젝트 정보
- Node.js
- TypeScript
- WebSocket
- Zod
- Axios
- Vitest
- Pino
- 시스템
- 전화망의 실시간 음성과 OptiGen AI의 대화 요청 및 응답을 연결하는 음성 중계 시스템
- 사용 흐름
- 전화 음성을 STT로 변환하고 OptiGen AI 응답을 생성한 뒤 TTS 음성으로 바꿔 통화에 전달
- 담당 범위
- Voice Gateway 전체 설계 및 구현
OptiGen 경로의 통화와 음성 처리 구조
Main Thread는 ARI 통화 제어와 세션 생성을 담당하고, 통화별 Worker는 RTP 오디오, STT, 턴 전환, LLM 응답과 TTS를 처리합니다. 아래 그림은 전체 Voice Gateway 중 OptiGen 경로를 보여줍니다.
주요 작업
통화별 봇 설정과 음성 응답 처리
개요
전화번호별로 다른 AI 봇과 음성을 사용하고, 전화망의 음성을 STT, AI 응답과 TTS로 이어주는 통화 흐름이 필요했습니다.
봇 설정과 Worker 생성의 경계 통화별 Worker를 만들기 전에 봇 설정이 유효한지 확인하고, 통화마다 사용하는 모델과 음성을 일관되게 적용해야 했습니다.
세부 내용
봇 설정을 통화별 세션에 적용 Admission API의 봇 설정을 Worker 생성 전에 조회하고, 통화별 세션의 STT와 TTS 구성에 반영했습니다. 공통 통화 흐름은 유지하면서 봇마다 모델과 합성 음성을 선택할 수 있도록 했습니다.
STT 전사에서 문장별 TTS까지 연결 최종 전사를 OptiGen에 전달해 SSE 응답을 받고, 완성된 문장부터 TTS로 합성해 통화에 재생했습니다. 전체 답변이 끝날 때까지 기다리지 않도록 응답 파싱과 문장별 합성을 연결했습니다.
청크 경계와 JSON 형식 변화 처리 JSON의 키 순서나 공백이 바뀌거나 객체가 여러 청크로 나뉘어도 완성된 텍스트를 순서대로 추출하도록 수정했습니다. 이미 읽은 위치를 기록해 중복 발화를 막고, 스트리밍에서 한 문장도 추출하지 못한 경우에는 최종 응답으로 복구하도록 했습니다.
성과
봇 설정에 따른 통화와 음성 응답 수신 번호에 맞는 봇 설정으로 통화를 처리하고, AI 응답을 문장 단위로 재생하는 경로를 구현했습니다. JSON 키 순서, 청크 분할과 기존 응답 형식에 대한 회귀 테스트를 남겼습니다.
주요 작업
STT 평가 파이프라인 구축과 주소 인식 개선
개요
실제 통화 조건을 반영한 평가 기준을 마련하고, 배송지 주소에서 반복되는 숫자 표기와 발음 오류를 보정했습니다.
숫자 표기와 연음에 따른 오인식 배송지 주소를 음성으로 입력받을 때 동, 호수가 잘못 전사되어 주소를 정확히 확인하기 어려웠습니다. 특히 ‘102동’이 ‘대기동’처럼 기록되는 오류는 숫자 표기 정규화만으로 해결할 수 없었습니다.
세부 내용
STT 평가 파이프라인 구축 음성 데이터 전처리부터 전화망 조건을 반영한 모델별 반복 실행, 전사 저장과 자동 채점까지 연결했습니다. 실행 설정을 함께 저장하고 글자 오류율과 주소 완전 일치율을 산출해 모델과 보정 설정별 결과를 비교했습니다.
ITN 기반 숫자 표기 정규화 Voice Gateway에서 STT 결과를 LLM에 전달하기 전에 ‘백 이동’을 ‘102동’으로 바꾸는 규칙 기반 ITN을 구현했습니다. 숫자와 한글 수사가 섞이거나 띄어쓰기가 나뉜 경우를 처리하고, 단위와 문장 경계 조건으로 지명과 일반 단어의 오변환을 제한했습니다.
발음 유사도 기반 동 번호 보정 MCP에서 실제 건물의 동 목록을 한글 읽기와 연음이 반영된 발음으로 변환했습니다. 전사 결과와 자음, 모음 단위의 편집 거리를 비교해 ‘대기동’에도 ‘102동’을 후보로 제안하도록 구현했습니다. 가장 가까운 후보가 하나일 때만 제안하고, 동점이거나 차이가 크면 다시 묻도록 구성했습니다.
저장한 전사로 보정 전후 재검증 저장된 전사에 보정 규칙을 재적용해 정답과 대조했습니다. 오류 감소와 함께 기존 정답을 잘못 바꾸는 경우가 생겼는지 확인해, 모델 호출을 반복하지 않고 규칙 변경의 영향을 비교했습니다.
성과
동, 호수 전사 오류 18.9% 감소 gpt-transcribe에 ITN을 적용한 90회 실행에서 수집한 전사 1,426건을 비교했습니다. ITN 규칙 개선으로 동, 호수 오류가 270건에서 219건으로 줄었으며, 이 평가에서 새로 발생한 오류는 0건이었습니다.
동 번호 오인식의 73.4%에 정답 후보 제안 별도 MCP 평가에서는 STT가 동 번호를 놓친 전사 353건을 각 주소의 실제 동 목록과 대조했습니다. 259건에 올바른 동 후보를 제안했고, 해당 평가에서 잘못된 후보 제안은 0건이었습니다.
주요 작업
AI Provider별 통신과 오디오 처리 분리
개요
OpenAI의 Base64 PCM, Uplus의 바이너리 PCM, OptiGen의 REST/SSE처럼 공급자마다 통신과 오디오 형식이 달랐습니다. 새 공급자 추가와 STT, TTS 교체가 공통 통화 처리까지 바꾸지 않도록 경계를 설계했습니다.
한 파일에 결합된 통화 처리 연결과 재연결, 이벤트 해석, STT와 TTS, 오디오 변환이 Voice Client에 함께 있었습니다. 공급자별 차이를 분리하고 통화 세션이 같은 연결 규칙으로 동작하도록 재구성하는 것이 과제였습니다.
세부 내용
공통 Transport 계약 IVoiceTransport로 공통 연결 계약을 정의하고, BaseVoiceTransport에 연결 상태, 지수 백오프와 jitter를 이용한 재연결, keep-alive와 공통 오류 분류를 모았습니다.
설정 기반 Factory VoiceTransportFactory가 설정에 맞는 Transport를 고르게 했습니다. STT와 TTS도 별도 Factory로 분리해 핵심 통화 흐름을 바꾸지 않고 구현체를 교체합니다.
Strategy와 Codec 경계 Provider마다 다른 이벤트 형식은 event strategy로, 오디오 포맷은 codec으로 격리했습니다. 새 Provider의 변경 범위를 해당 Transport, Strategy, Codec 중심으로 제한했습니다.
성과
설정 기반 구현체 교체 OpenAI, Uplus와 OptiGen의 연결 경로를 같은 계약에서 선택할 수 있게 했습니다. 공통 통화 흐름 위에서 공급자별 이벤트와 오디오 형식을 처리하고, STT와 TTS는 각각의 Factory에서 선택하도록 구성했습니다.
주요 작업
음성 패킷 전송 타이머의 시작과 복구 개선
개요
20ms 간격의 음성 패킷이 약 40ms 안에 8개씩 몰리는 원인을 추적하고, 초기화 지연과 실행 중 지연을 나눠 전송 일정을 복구하도록 수정했습니다.
연결 준비 중 밀린 전송 일정 STT와 TTS의 연결 초기화 전에 전송 타이머가 시작됐습니다. 초기화 중 이벤트 루프가 지연되면 전송 일정이 밀렸고, 기존 복구 조건은 따라잡기 과정에서 초기화되어 밀린 패킷을 연속 전송했습니다.
세부 내용
첫 오디오 도착 시 시작 SessionBuilder에서는 타이머를 생성만 하고, 첫 오디오가 enqueue에 들어올 때 시작하도록 바꿨습니다. 연결 준비 시간을 오디오 전송 지연으로 계산하지 않도록 시작 시점을 옮겼습니다.
밀린 시각을 즉시 재설정 실제 실행 시각이 예정 시각보다 50ms 넘게 늦으면 기준 시각을 현재로 재설정했습니다. 밀린 횟수만큼 연속 전송하는 대신 다음 패킷부터 20ms 전송 간격을 다시 잡도록 했습니다.
성과
첫 오디오 기준 시작과 20ms 간격 복구 연결 준비 중의 대기 시간이 밀린 음성 전송 일정으로 누적되지 않도록 실행 순서가 바뀌었습니다. 실행 중 지연이 발생하면 과거 일정을 따라잡으려 연속 전송하지 않고, 다음 패킷부터 20ms 간격을 다시 잡도록 했습니다.
