안녕하세요. 저희는 AI와 사람 간의 실시간 음성 통신 기술을 개발하고 있습니다.
"단순 일상 대화가 아니라 식당 예약처럼 실제 비즈니스 업무를 수행하는 음성 에이전트를 만드는 게 가능한가?"라는 의문에서 출발해, 실제 데모를 구현하며 겪은 생성형 LLM의 한계와 이를 비생성형 판별 모델인 jev
중심의 파이프라인으로 해결한 과정 및 라이브 데모를 공유합니다.
실시간 음성 예약 데모(한국어만 가능) : https://raixact.com- 아래 요약본의
전체 전문: Medium
왜 생성형 LLM을 걷어냈는가? (직접 구축하며 겪은 시행착오)
화면 UI가 없는 음성 통화는 1초 남짓한 침묵에도 사용자가 장애를 의심하며, 구어체 노이즈와 말실수, 변심이 실시간으로 들어옵니다. 침묵을 메우려고 흔히 넣는 필러("잠시만요...")도 반복되면 큰 피로감을 주기 때문에 결국 무조건 빠른 LLM 모델을 써야 하는 구조적 제약이 있었습니다.
1) 단일 LLM 에이전트 실험- 처음에 많이 시도하듯 프롬프트 하나에 비즈니스 규칙과 Tool Calling을 몰아넣고 LLM을 돌리는 방식입니다. 지시 이행이 잘 안되고, 환각이 심하며, 느립니다.
2) 워크플로우 도구 적용- 비즈니스 로직을 흐름으로 정리해 단계별 LLM을 나눈 방식이라 단계별 처리는 나아졌으나, 극단적으로 말하면 '버튼 대신 목소리로 누르는 ARS'에 가까웠습니다.
- 다중 조건 입력이나 통화 중 인원 변경, 문맥을 벗어난 중간 문의 등 사용자의 자유 발화를 허용하는 순간 한계가 왔습니다.
3) 경량 LLM 의도 판별기 도입- 한국어 처리가 빠른 10B급 경량 LLM을 붙여 슬롯 필링과 의도 판별을 맡기고, 실제 답변은 상태에 따라 코드 기반으로 생성하되 일부만 LLM이 처리하는 형태로 구현해 보았습니다.
- 그러나 경량 LLM은 컨텍스트가 조금만 쌓여도 JSON 출력이 깨졌고, 단일 값 오인식은 물론 "내일이나 모레" 같은 다중 선택 배열 추출은 아예 기대하기 어려웠습니다. 지연 시간도 심하게 요동쳤습니다.
- 빠른 답변과 동시에 사용자가 자유롭게 말하는 구조 자체는 가능해졌지만, 경량 LLM의 성능 한계로 인해 적용하는 비즈니스마다 파인튜닝이 필수라는 점이 큰 부담이었습니다.
현재: 역할 분리형 비생성 파이프라인 (Kiwi
)
추출 능력이 없는 비생성형 판별 모델 jev
의 특성을 고려해, [가벼운 전처리기의 후보 추출 → jev의 순수 의도 판별 → 코드 기반 비즈니스 로직 및 정적 FAQ 매칭] 으로 역할을 철저히 분리했습니다.
전처리 계층 (Kiwi 형태소 분석)- STT가 넘긴 불완전한 문장을 1차 정규화하여 STT 오류와 판별 모델의 한국어 인식 한계 사이에서 완충 장치 역할을 수행합니다.
- "내일 저녁", "이번 주 금요일" 같은 상대적 표현을 시스템 시각 기준으로 변환하고, 다중 선택("화요일이나 수요일")인지 말 바꾸기("화요일 아니 수요일")인지
jev
가 판단할 수 있도록 문장 내 모든 날짜/시간 후보를 수 밀리초 내에 추출해 선택지로 넘깁니다.
판별 계층 (jev
)- 이전 대화 컨텍스트와 전처리기가 뽑은 선택지를 입력받아 최종 발화의 의도(신규/변경 의도, 각 날짜·시간·인원 후보 부합 여부, 문의 포함 여부)만 순수 판별합니다.
- 테스트 결과 문맥 파악과 말 바꾸기, 2개의 복수 선택까지 상위 LLM 수준으로 정확히 잡아냈으며, 질문지 항목이 늘어나도 속도 저하 없이
300~500ms내에 안정적으로 판별을 끝냅니다.
복수 선택지 재질의 처리- 사용자가 복수 선택을 시도하면 단일 선택 질문지 구조상 해당 항목들에 낮은 스코어가 매겨집니다. 이때 추가 질의로 지연을 늘리는 대신, 슬롯을 배열화하여 실제 DB 잔여석을 확인한 뒤 유효한 선택지만 고객에게 되묻는 대화 흐름(
"내일은 마감되었고 모레 가능하신데, 모레로 진행해 드릴까요?")으로 풀었습니다.
- 사용자가 복수 선택을 시도하면 단일 선택 질문지 구조상 해당 항목들에 낮은 스코어가 매겨집니다. 이때 추가 질의로 지연을 늘리는 대신, 슬롯을 배열화하여 실제 DB 잔여석을 확인한 뒤 유효한 선택지만 고객에게 되묻는 대화 흐름(
문의사항(FAQ) 격리와 경량 ColBERT*"내일 7시 4명 갈 건데요, 주차 되나요?"*같은 발화를 전담 LLM에 통째로 넘기면 앞뒤 예약 단어들에 주의가 분산되어 묻지도 않은 예약 절차를 지어내는 환각이 발생했습니다.- 이를 막기 위해 생성 모델을 배제하고 매장 표준 질문과 답변을 1:1로 사전 임베딩해 두었습니다.
jev
가 문의 유무를 감지하면경량 ColBERT의 토큰 단위 Late Interaction으로 매칭합니다. - 단일 벡터 임베딩과 달리 한 문장에 주차와 아기 의자 문의가 섞여 있어도 벡터가 희석되지 않고, 언어 모델 없이 수십 밀리초 내에 각각의 검증된 정적 답변을 정확히 뽑아냅니다.
음성 통화 특화 설계: '비동기 발화'와 중요도 기반 제어
음성에서는 텍스트 채팅처럼 1:1 방식으로 한 턴에 긴 복수 문장을 만들어 내보내는 대신, 잘게 쪼개어 발화하게 하는 것이 유리합니다.
사용자 발화 복창과 제안의 분리- 고객이
"내일 점심때 예약 돼요?"라고 물으면[발화 1: 복창]("내일, 10월 21일 수요일 점심 예약이시고요.")을 먼저 송출해 인식을 확인시켜 주는 동시에 침묵을 지우는 유의미한 필러 역할을 하게 하고, 연이어[발화 2: 잔여석 제안]**("점심은 12시, 12시 반, 1시 가능하신데...")을 독립된 오디오 스트림으로 송출합니다.
- 고객이
발화 중요도에 따른 끼어들기(Barge-in) 제어****저중요도 발화: 봇이 복창하는 도중 고객이*"아 1시로 해주세요"라고 끼어들면, 굳이 복창을 마저 읽을 필요가 없으므로 기존 오디오를 즉시 폐기(Drop)하고 '1시' 슬롯 처리로 곧바로 넘어갑니다.고중요도 발화: 반면"내일 1시, 4분 예약 확정되었습니다"나"만석이라 불가능합니다"*같은 핵심 결과 발화는 고객의 단순 개입으로 누락되면 안 되므로, 끼어들기 처리 후에도 해당 상태를 다시 명확히 반복 송출하도록 코드로 강제합니다.
아키텍처 및 비용에 대한 생각
- 최근 주목받는 S2S(Speech-to-Speech) 모델들은 지연 시간을 크게 줄였지만, 아키텍처 관점에서는 결국 첫 번째로 실패했던 '단일 LLM 에이전트'라는 거대 블랙박스 구조와 다르지 않습니다. 더 큰 문제는 음성 토큰의 매우 높은 비용입니다.
- 판별 모델과 코드 기반으로 상태를 통제하면 추론 연산 비용이 극히 미미해져 전체 운영 비용은 예측 가능한 STT/TTS 비용으로 수렴합니다.
- 어차피 비즈니스 정책을 제대로 적용하려면 AI의 발화는 기계적이고 좀 멍청해질 수밖에 없습니다. 사람 같은 발화를 억지로 구현할 필요성 자체가 의문입니다.
- AI 코딩 도구가 발달한 지금 환경에서는, AI가 극적인 도움을 주지 못하는 '프롬프트 가드레일 땜질'에 매달리는 것보다 명확한 소프트웨어 코드로 비즈니스 로직을 구현하는 편이 디버깅과 검증, 유지보수 전 과정에서 훨씬 쉽고 저렴하다고 말할 수 있습니다.