AI가 먼저 거는 전화는 어떻게 만들까?
채널톡 FDE 이야기 - 2
Mango • AX Team / FDE
- Backend
- AI
안녕하세요. 채널 FDE팀의 망고입니다.
여러 고객사와 이야기를 나누던 중, 미납 안내나 가입 경로 설문처럼 기업이 고객에게 먼저 전화를 거는 아웃바운드 콜(Outbound Call)에 대한 수요를 확인했어요. 당시에는 이를 지원하는 제품이 없었기에, FDE가 먼저 아웃바운드 콜 엔진을 개발하고 실제 고객사에 적용하기 시작했어요.
이번 글에서는 고객사의 통화 시나리오를 AI 전화로 구현하는 과정에서 어떤 기술적 문제를 마주했고, 이를 해결하기 위해 대화 엔진을 어떻게 설계했는지 소개할게요.
1. 아웃바운드 콜 특성
고객사들은 입금 안내, 방문 일정 확인, 미납 상담처럼 전화로 진행하고 싶은 시나리오를 보내주셨어요. 방문 일정을 확인하는 전화라면 방문 가능한 날짜를, 미납을 안내하는 전화라면 납부 가능 여부와 예정일을 물어야 했어요.
회사에서 고객에게 먼저 거는 이런 전화를 아웃바운드 콜이라고 해요.
1-1. 미리 정의할 수 있는 대화 흐름
인바운드 콜은 고객의 용건을 들어야 대화의 방향을 정할 수 있어요. 반면 아웃바운드 콜은 먼저 전화를 걸기 때문에, 통화의 대본을 준비되어 있었어요.
따라서 모든 발화를 LLM이 실시간으로 생성할 필요는 없었어요. 질문과 답변에 따른 다음 대사를 미리 작성하고, LLM이 고객의 답변을 판단하면 엔진이 해당 대사를 실행하는 방식으로 시작할 수 있었어요.
1-2. 통화 중 응답 지연 최소화
아웃바운드 콜은 고객이 예상하지 못한 순간에 전화를 받는 경우가 많아요. 고객이 질문에 답했는데 오랫동안 아무런 반응이 없다면, 통화가 끊겼다고 생각하거나 전화를 종료할 수 있어요.
다음 대사가 미리 준비되어 있더라도, 고객의 답변을 판단하는 데 시간이 오래 걸리면 그만큼 침묵이 발생해요. 따라서 정해진 업무 흐름을 정확하게 처리하는 것뿐 아니라, Latency를 최소화 하고 고객에 빠르게 반응하는 것도 중요했어요.
2. 노드 기반 대화 처리 구현
2-1. LLM을 발화 생성이 아닌 분류에 이용하기
먼저 LLM은 고객의 답변을 미리 정의한 분기 중 하나로 분류하고, 엔진은 그 결과에 따라 다음 노드를 실행하도록 설계했어요.
예를 들어 고객이 “네, 이번에 납부할게요”라고 답하면 LLM은 이를 ‘납부 의사 있음’으로 분류해요. 엔진은 해당 분기에 연결된 노드로 이동해 “납부 예정일을 말씀해 주세요”라는 지정 발화를 들려줘요.
이 구조에서는 LLM이 다음 대사를 직접 생성하지 않아요. 답변의 의미를 판단하는 일은 LLM이, 다음 노드로 이동하고 지정된 발화를 실행하는 일은 엔진이 담당해요.
2-2. 전역 규칙으로 공통 요청 처리하기
고객의 답변은 현재 질문에만 딱 맞게 돌아오지 않았어요. 납부 가능 여부를 묻는 도중에도 “그런데 어디서 전화하셨어요?”라고 되물을 수 있고, “지금 바쁘니까 나중에 전화해 주세요”처럼 재통화를 요청할 수도 있었어요.
문제는 이런 요청이 특정 노드에서만 발생하지 않는다는 점이었어요. 이를 노드마다 분기로 추가하면 같은 조건과 발화를 여러 곳에 반복해서 작성해야 했어요.
그래서 통화 전반에서 공통으로 처리해야 하는 요청을 전역 규칙(Global Interrupt)으로 분리하고, 현재 노드의 분기보다 우선해 처리하도록 했어요.
발신처 안내를 전역 규칙으로 설정한 예시예요. 여러 노드에서 반복되는 요청을 한 곳에서 관리할 수 있어요.
2-3. 주관식 답변의 수집·확인·정정
지금까지는 LLM이 고객의 답변을 분류하고, 엔진이 미리 지정된 발화를 실행했어요. 하지만 고객의 답변에 따라 후속 질문이 달라지는 대화에서는 모든 발화를 미리 작성하기 어려웠어요.
예를 들어 좋아하는 음료를 물었을 때 고객이 “집에서 만든 꿀생강 콜드브루요”라고 답할 수 있어요. 가능한 음료 이름을 전부 분기로 정의할 수는 없으므로, 고객의 발화에서 필요한 값을 추출하는 수집 기능을 추가했어요.
예를 들어 고객에게 좋아하는 음료를 물었을 때 “집에서 만든 꿀생강 콜드브루요”라고 답할 수 있어요. 가능한 음료 이름을 모두 분기로 정의할 수는 없으므로, 고객의 발화에서 필요한 값을 추출하는 수집 기능을 추가했어요.
하지만 값 수집이 항상 한 번의 질문으로 끝나는 것은 아니었어요.
이처럼 대화가 여러 차례 이어지는 상황에서는 다음에 어떤 말을 해야 할지 미리 정하기 어려웠어요. 그래서 하네스 안에서는 LLM이 고객의 답변에 맞춰 후속 발화를 직접 생성하도록 허용했어요.
다만 LLM이 대화의 종료 시점까지 자유롭게 결정하도록 하지는 않았어요. 엔진이 필요한 값과 확인 여부를 관리하고, 미리 설정한 완료 조건을 충족해야 다음 단계로 넘어가도록 했어요.
수집한 값을 고객에게 다시 확인해야 하는 경우도 있어요. 예를 들어 주소처럼 정확한 정보가 중요하다면, 수집한 값을 다시 읽어주고 맞는지 확인하도록 설정할 수 있어요.
그래서 하네스에서는 값 수집뿐 아니라 필요에 따라 확인과 정정까지 처리하도록 했어요. 고객이 잘못된 값을 바로잡으면 수정된 값으로 다시 확인하고, 설정한 완료 조건을 충족하면 다음 단계로 넘어가요.
2-4. Turn Detection: 의미론적 유저 발화 종료 판단
아웃바운드 콜에서는 고객이 예상하지 못한 질문을 받고 잠시 생각하는 경우가 많아요. 실제 통화를 테스트하면서도 “아…”, “음…”처럼 말을 시작했다가 잠시 멈추고 이어서 답하는 상황을 자주 마주했어요.
이때 LLM이 판단을 완료하면 , 고객이 아직 생각하는 도중에 다음 대사를 시작할 수 있어요.
그래서 저희는 발화의 의미를 바탕으로 답변이 끝났는지 판단하는 SVAD(Semantic Voice Activity Detection)를 도입했어요. 현재 질문과 음성 인식 결과를 LLM에 전달해 답변의 완결성을 판단하는 방식이에요.
예를 들어 “이번 달 안에 납부 가능하실까요?”라는 질문에 “네”는 완결된 답변이지만, “음… 아, 그건…”은 아직 답변을 이어갈 가능성이 있는 발화로 판단해요.
둘 다 뒤에 침묵이 있지만, 다음 발화를 시작해도 되는지는 달라요.
고객이 답변을 마쳤다고 판단하면 결과를 바로 적용하고, 아직 말을 이어갈 가능성이 있다면 잠시 더 기다렸어요. 추가 발화가 없으면 보류한 결과를 적용했어요.
이렇게 완결된 답변은 즉시 처리하고, 미완성 답변에만 추가 대기 시간을 주어 불필요한 응답 지연을 줄이면서도 고객이 말을 이어갈 기회를 제공했어요.
2-5. 추가 발화에 따른 이전 판단 결과 무효화
SVAD를 통해 고객의 답변이 끝났다고 판단했더라도, LLM의 응답을 기다리는 동안 고객이 다시 말을 시작할 수 있어요.
예를 들어 고객이 “네, 낼게요”라고 답해 LLM에 판단을 요청했는데, 결과가 도착하기 전에 “아, 잠시만요. 이번 달은 못 낼 것 같아요”라고 정정하는 상황이에요.
이때 처음 답변만으로 다음 단계로 넘어가면, 고객은 납부가 어렵다고 정정했는데 AI는 납부 예정일을 묻게 돼요.
이를 방지하기 위해 고객의 발화 시작 이벤트가 발생하면 아직 적용하지 않은 판단 결과를 무효화하고,추가 발화가 끝나면 앞선 발화와 합쳐, 기존 판단이 반영되기 전의 상태에서 다시 판단해요.
여기서 비동기 처리에 주의해야 했어요. 새 발화가 시작되어 기존 LLM 요청을 취소하더라도, 해당 요청의 결과가 나중에 도착할 수 있기 때문이에요. 이를 방지하기 위해 판단 결과를 적용하는 시점에 호출 ID와 발화 버전을 검증했어요.
이를 통해 새로운 발화가 시작된 이후에는 이전 발화에 대한 판단 결과가 대화 상태에 반영되지 않도록 했어요.
2-6. Jev의 등장
미납 안내에서 고객이 “낼게요”라고 답했다면, 필요한 것은 ‘납부 의사 있음’, ‘납부 어려움’, ‘미정’ 중 하나를 선택하는 결과뿐이었어요. 다음에 들려줄 대사는 이미 준비되어 있었고요.
단순한 분기 판단에도 생성형 LLM을 사용하다 보니 불필요한 응답 지연이 발생했어요.그러던 중 마침 TypeSafe AI의 Jev가 등장했어요. Jev는 자연어 생성 대신 분류, 평가 등의 의사결정에 특화된 모델(Decision Model)이에요.
저희가 생성형 LLM의 입출력을 줄이며 답변 분류에 집중하려던 설계와 맞아떨어졌어요.
Jev의 평균 분류 응답 시간은 196.2ms, p95는 219.3ms였어요. 기존 LLM 대비 평균 응답 시간이 약 65% 줄었어요.
분류 결과를 기다리며 생기던 침묵이 짧아졌고, 그 결과 고객이 답변을 마친 뒤 약 0.2초 만에 다음 발화를 시작할 수 있었어요. 실제 통화에서도 응답 지연을 거의 체감하기 어려웠어요.
3. 실제 운영 성과
실제 운영에서는 상담 의사 확인과 수수료 납부 안내처럼 기존에 상담원이 수행하던 업무에 AI를 적용했어요. 그중 상담 의사 확인 캠페인에서는 약 2,500건의 고객 데이터를 대상으로 전화를 걸어, 상담 희망 고객 83명을 발굴했어요. 기존에 상담원이 연락했지만 연결되지 않았던 고객들이었어요.
아웃바운드 콜 엔진을 실제 고객사의 업무에 적용하면서 확인하고 싶었던 건, AI가 상담원과 비슷한 성과를 내면서, 같은 시간에 더 많은 고객에게 더 낮은 비용으로 연락할 수 있는가였어요.
운영에서 확인한 성과는 세 가지였어요.
상담원과 비슷한 전환율: AI도 고객에게 통화 목적을 설명하고 상담 의사를 확인해, 후속 상담으로 이어질 고객을 확보할 수 있었어요.
같은 시간에 더 많은 연락: 여러 통화를 동시에 진행하면서, 상담원이 한 명씩 연락할 때보다 짧은 시간에 많은 고객의 의사를 확인할 수 있었어요.
더 낮은 고객 확보 비용: 상담원과 비슷한 전환율을 유지하면서 통화 비용을 낮춰, 상담 희망 고객 한 명을 확보하는 데 드는 비용을 줄일 수 있었어요.
상담원과 비슷한 전환 성과를 내면서, 더 많은 고객에게 더 낮은 비용으로 연락하는 것. 이것이 실제 운영에서 확인한 아웃바운드 콜 엔진의 가치였어요.
4. 마치며
FDE는 기존 제품으로 해결하기 어려운 고객의 문제를 직접 해결하기도 하지만, 이번처럼 새로운 제품의 가능성을 시장에서 먼저 검증하는 역할도 해요. 고객과 가까이에서 필요한 기능을 빠르게 만들고 운영하면서 얻은 경험을 제품팀에 전달하고, 정식 제품으로 발전할 수 있는 기반을 마련해요.
고객의 문제를 직접 발견하고, 해결책을 만들어 검증하는 일에 관심이 있다면 채널톡 FDE팀에서 함께해 주세요.
