본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 06. 03:23

Asterisk, LLM, RAG를 활용하여 1인 개발자로 음성 AI 에이전트 시스템을 구축한 방법

요약

1인 개발자가 Asterisk, LLM, RAG를 결합하여 고객 지원용 음성 AI 에이전트 시스템을 구축한 기술 사례를 소개합니다. Asterisk PBX와 Python AGI를 활용해 전화 통화와 AI 로직을 연결하는 아키텍처를 다룹니다.

핵심 포인트

  • Asterisk를 활용한 오픈소스 기반 전화 교환 시스템 구축
  • Python AGI 스크립트를 통한 실시간 통화 제어 및 로직 연결
  • SIP 및 RTP 프로토콜을 이용한 음성 데이터 처리 흐름 이해
  • 전화 기술과 AI 에이전트를 결합한 실무적 아키텍처 설계

고객 지원 팀이 바쁠 때, AI가 전화를 받습니다.

우리가 해결하려 했던 문제

사용자가 오후 7시에 회사의 기술 지원 라인으로 전화하는 상황을 상상해 보세요. 지원 팀의 모든 직원이 로그아웃했습니다. 전화벨이 울리고... 계속 울립니다. 사용자는 답답함에 전화를 끊어버립니다.

그것이 바로 우리 회사가 해결하고자 했던 정확한 문제였습니다. 목표는 설명하기는 간단하지만 구축하기는 복잡했습니다. 인간 상담원이 없을 때, AI 에이전트가 전화를 받아 사용자의 문제를 이해하고, 마치 지원 담당자처럼 답변하는 것이었습니다.

저는 1인 개발자로서 이 과제를 부여받았습니다. 그 이후의 과정은 제가 작업했던 프로젝트 중 기술적으로 가장 흥미로운 프로젝트 중 하나였습니다. 전화 기술(Telephony), AI, 음성 처리(Speech Processing), 그리고 실시간 대시보드(Real-time Dashboards)를 하나의 작동하는 시스템으로 결합하는 과정이었습니다.

제가 구축한 모든 것, 작동 방식, 그리고 배운 점들을 소개합니다.

아키텍처 개요

심층적으로 들어가기 전에, 시스템을 통한 통화의 상위 수준 흐름(High-level flow)은 다음과 같습니다:

사용자 호출 → Asterisk PBX → Python AGI 스크립트
                                    ↓
                          음성-텍스트 변환 (Google STT)
...

이 체인의 모든 단계에는 각각의 과제들이 있었습니다. 하나씩 살펴보겠습니다.

파트 1: Asterisk — 전화 시스템의 두뇌

Asterisk란 무엇인가?

Asterisk를 소프트웨어 기반의 전화 교환기라고 생각하세요. 대형 콜센터에서 사용하는 것과 같은 종류의 시스템이지만, 오픈 소스이며 Linux 서버에서 실행됩니다. 이는 들어오는 전화 호출을 여러분의 애플리케이션 로직에 연결해 주는 역할을 합니다.

실생활에서 은행에 전화했을 때 _"잔액 확인은 1번, 대출 문의는 2번을 눌러주세요"_라는 안내를 듣는다면, 그것은 PBX (Private Branch Exchange, 사설 교환기) 시스템이 라우팅을 수행하고 있는 것입니다. Asterisk는 세계에서 가장 널리 사용되는 오픈 소스 PBX 시스템 중 하나입니다.

우리 프로젝트를 위해 저는 Asterisk 상단의 GUI 레이어인 FreePBX를 사용했습니다. 덕분에 원본 설정 파일(Config files)을 계속 건드리지 않고도 내선 번호(Extensions)와 통화 라우팅을 훨씬 쉽게 관리할 수 있었습니다.

Asterisk가 코드와 연결되는 방식

Asterisk에는 **AGI (Asterisk Gateway Interface)**라고 불리는 기능이 있습니다. 이를 통해 Python(또는 다른 어떤 언어)으로 스크립트를 작성하면, Asterisk가 실시간 전화 세션 중에 해당 스크립트를 호출할 수 있습니다. 전화 통화용 웹훅(Webhook)이라고 생각하면 쉽습니다.

사용자가 전화를 걸면, Asterisk는 통화 도중에 제어권을 사용자의 Python 스크립트로 넘깁니다. 작성된 스크립트는 다음과 같은 작업을 수행할 수 있습니다:

  • 발신자에게 오디오 재생
  • 발신자의 음성 녹음
  • 녹음된 데이터를 처리를 위해 특정 위치로 전송
  • 응답 재생
  • 통화를 다른 내선 번호로 전환

Asterisk가 작동하는 주요 프로토콜

Asterisk는 내부적으로 두 가지 핵심 프로토콜에 의존합니다:

SIP (Session Initiation Protocol) — IP 네트워크를 통해 통화가 시작되고, 유지되며, 종료되는 방식을 처리합니다. 번호를 누르고 상대방이 전화를 받게 하는 과정이라고 생각하면 됩니다.

RTP (Real-time Transport Protocol) — 통화가 연결된 후 실제 음성 오디오를 전달합니다. SIP가 악수(Handshake)라면, RTP는 대화 그 자체입니다.

파트 2: 음성 처리 — 아무도 말하지 않는 가장 어려운 부분

AI가 제대로 "듣게" 만드는 것은 단순히 오디오를 Google로 보내는 것만이 아닙니다. 가공되지 않은 전화 오디오는 배경 소음, 들쭉날쭉한 볼륨, 긴 침묵, 문장 중간에 말을 흐리는 사람 등 매우 지저분합니다.

Google STT (Speech-to-Text)

저는 Google Cloud Speech-to-Text를 사용했습니다. 넉넉한 무료 한도 내에서 사용할 수 있고, 인도식 영어(우리 사용자 기반에 중요함)를 잘 지원하기 때문입니다.

노이즈 필터링 및 볼륨 정규화 (Volume Normalization)

전화 통화 오디오는 8000 Hz로 들어옵니다 (마이크 녹음보다 훨씬 낮은 품질입니다). 배경 소음은 실제적인 문제입니다. 팬 소리, 교통 소음, 다른 사람들의 대화 소리 등이 포함됩니다. 저는 명령줄 오디오 도구인 **SoX (Sound eXchange)**를 사용하여 STT로 보내기 전에 오디오를 전처리했습니다. 이를 통해 볼륨을 일정한 수준으로 정규화하고, 앞뒤의 침묵을 자동으로 제거했습니다.

침묵 감지 및 타임아웃 (Timeout)

미묘하지만 매우 중요한 점이 하나 있습니다: 사용자가 말을 마쳤다는 것을 어떻게 알 수 있을까요?

저는 Asterisk의 record 명령어를 무음 임계값(silence threshold)과 함께 설정했습니다. 사용자가 2초 이상 말을 멈추면 녹음이 자동으로 중단되고 처리가 시작됩니다. 이 시간이 너무 짧으면 문장 중간에 말을 끊어버리게 되고, 너무 길면 통화 반응이 느리게 느껴집니다.

Part 3: AI 브레인 — RAG + OpenAI

왜 단순히 ChatGPT에게 묻는 대신 RAG를 사용하나요?

사용자의 질문을 OpenAI의 GPT 모델에 직접 보내기만 하면, 모델은 일반적인 학습 데이터(training data)를 바탕으로 답변할 것입니다. 이는 귀사의 특정 제품, 정책 또는 지원 절차에 대해서는 아무것도 알지 못한다는 뜻입니다.

**RAG (Retrieval-Augmented Generation, 검색 증강 생성)**는 먼저 자체 지식 베이스(knowledge base)에서 관련 정보를 _검색(retrieving)_한 다음, 이를 컨텍스트(context)로서 LLM에 전달함으로써 이 문제를 해결합니다.

실생활 비유: 신입 사원에게 고객의 질문에 기억만으로 답하라고 하는 대신, 먼저 매뉴얼의 관련 페이지를 건네준 다음 답변을 요청하는 것과 같습니다.

지식 베이스 준비하기

회사는 웹사이트에서 스크래핑한 판매 및 기술 지원 시나리오를 모두 포함하는 Q&A 쌍이 담긴 JSON 파일을 저에게 제공했습니다. 저는 이 데이터를 정제, 처리하여 벡터 데이터베이스(vector database)에 저장된 벡터 임베딩(vector embeddings)으로 변환했습니다. 각 항목은 인덱싱(indexed)되어 시스템이 들어오는 어떤 질문에 대해서도 가장 관련성 높은 답변을 즉시 찾을 수 있도록 했습니다.

관련 컨텍스트 검색 + OpenAI 쿼리

사용자가 말을 하면, 시스템은 질문을 임베딩(embed)하고 코사인 유사도(cosine similarity)를 사용하여 지식 베이스에서 가장 관련성이 높은 상위 3개 항목을 찾아낸 뒤, 해당 컨텍스트를 시스템 프롬프트(system prompt)와 함께 OpenAI에 전달합니다. 이때 프롬프트에 포함된 매우 중요한 지침이 하나 있습니다: 답변을 짧게 유지할 것 — 전화 통화에서 소리 내어 읽어줄 것이기 때문입니다. 아무도 3문단이나 되는 긴 글을 소리로 듣고 싶어 하지 않습니다.

Complete flow

Part 4: Text-to-Speech — AI에게 목소리 부여하기

저는 **Google Text-to-Speech (gTTS)**를 사용했습니다. 무료이며, 다양한 목소리를 지원하고, 고객 지원 전화에 사용하기에 충분히 자연스러운 소리를 제공합니다. 생성된 오디오는 Asterisk가 요구하는 8000 Hz 모노(mono) 형식으로 변환된 후, 실시간 통화에 직접 재생되었습니다. 저는 남성과 여성 목소리 옵션을 모두 구성하였고, AI의 목소리가 명확하고 듣기 편하도록 볼륨 게인(volume gain) 조정을 적용했습니다.

Part 5: Call Routing — 라운드 로빈(Round Robin) 및 상담원 연결(Human Escalation)

상담원 연결을 위한 라운드 로빈(Round Robin)

AI가 문제를 해결할 수 없거나(또는 사용자가 상담원 연결을 요청할 경우), 통화는 전환되어야 합니다. 하지만 첫 번째 상담원이 전화를 받지 않는다면 어떻게 될까요?

저는 **라운드 로빈 다이얼링 (round robin dialing)**을 구현했습니다. Asterisk가 첫 번째 상담원의 내선 번호로 연결을 시도하고, 20초 동안 응답이 없으면 다음 상담원, 그다음 상담원으로 넘어가는 방식입니다. 모든 상담원이 통화 중인 경우, 발신자는 안내 메시지를 듣게 되며 통화는 정상적으로 종료됩니다.

각 상담원의 전화기(모바일 앱 또는 데스크폰)는 Asterisk에 SIP 내선(extension)으로 등록되었습니다. 이는 사무실의 데스크폰이 회사의 교환기에 연결되는 방식과 동일합니다.

모바일 앱

상담원들은 저희 Asterisk 서버에 등록된 SIP 기반 모바일 앱(Zoiper 또는 Linphone과 같은 무료 앱)을 사용했습니다. 덕분에 상담원들은 물리적으로 책상 앞에 앉아 있지 않더라도 어디에서나 휴대폰으로 전환된 전화를 받을 수 있었습니다.

Part 6: 실시간 대시보드 (The Real-Time Dashboard)

AI 에이전트 자체를 넘어, 저는 지원 팀이 모든 통화에서 어떤 일이 일어나고 있는지 모니터링할 수 있도록 대시보드를 구축했습니다.

실시간으로 추적되는 통화 상태:

  • 🟢 통화 중 (On Call)
  • 🟡 대기 중 (On Hold)
  • 🔵 처리 중 (Pending, AI가 처리 중)
  • ⏭️ 건너뜀 (Skipped, 상담원이 받지 않음)
  • 🔁 전환됨 (Transferred, 상담원에게 연결됨)

저는 실시간 통화 이벤트를 스트리밍하는 소켓 기반 API인 **Asterisk의 AMI (Asterisk Manager Interface)**를 사용하여 대시보드에 실시간 데이터를 공급했습니다. 통화 상태가 변경될 때마다(응답, 전환, 종료 등), AMI가 이벤트를 발생시키고 대시보드가 즉시 업데이트되었습니다.

통화 녹음 및 재생

모든 통화는 Asterisk의 내장 기능인 MixMonitor를 사용하여 자동으로 녹음되었으며 오디오 파일로 저장되었습니다. 대시보드는 지원 팀이 녹음된 통화를 찾아보고, 날짜나 상태별로 필터링하며, 재생할 수 있는 간단한 UI를 제공했습니다.

혼자서 이를 구축하며 배운 점

1. 전화 오디오는 마이크 오디오가 아니다. 웹 프로젝트에서 알고 있던 오디오 품질에 대한 모든 지식은 무용지물이 됩니다. 8000 Hz, 모노(mono), 심한 압축(compression) — 이러한 환경에 맞춰 설계해야 합니다.

2. 음성 서비스에서는 AI 응답의 품질보다 길이가 더 중요하다. 완벽하게 정확한 200단어 분량의 답변은 전화 통화에서 무용지물입니다. AI 프롬프트(prompt)는 반드시 응답 길이를 명시적으로 제한해야 합니다.

3. 침묵(Silence)은 버그가 아니라 기능이다. STT(Speech-to-Text) 입력 측과 TTS(Text-to-Speech) 일시 중지 측 모두에서 침묵 감지 임계값(threshold)을 정확하게 설정하는 것이 에이전트가 자연스럽게 느껴지느냐, 아니면 고장 난 IVR(Interactive Voice Response)처럼 느껴지느냐의 차이를 만들었습니다.

4. RAG 품질은 전적으로 데이터 품질에 달려 있다. 제가 받은 JSON 데이터는 형식이 일관되지 않았고 일부 중복 항목이 있었습니다. 해당 데이터를 정제하는 것이 전체 작업의 30%를 차지했습니다.

5. 초기 단계부터 실제 전화 통화로 테스트하라. 시스템은 로컬 테스트에서는 완벽하게 작동했습니다. 하지만 실제 전화 통화에서는 배경 소음으로 인해 통제된 환경에서는 전혀 보이지 않았던 세 가지 버그가 드러났습니다.

최종 시스템 개요

구성 요소사용된 기술
PBX / TelephonyAsterisk + FreePBX
...

마치며

1인 개발자로서 이 시스템을 구축하며 배운 점은, 실제 서비스되는 AI(production AI)는 AI 모델 자체보다 그 주변의 배관 작업(plumbing) — 즉, 오디오 품질, 지연 시간(latency), 데이터 준비, 그리고 AI가 답을 모를 때의 우아한 폴백(fallback) 처리 — 에 훨씬 더 많은 비중이 실린다는 것입니다.

가장 보람찼던 순간은 첫 번째 실제 통화가 이루어지는 것을 지켜볼 때였습니다. 사용자가 전화를 걸었고, AI가 응답했으며, 질문을 이해하고, 지식 베이스(knowledge base)에서 올바른 답을 찾아 명확하게 다시 말해주었습니다. 인간의 개입은 전혀 없었습니다.

만약 이와 유사한 것을 구축하는 것을 고민하고 있다면, 작게 시작하세요. Asterisk를 로컬에서 실행하고, 단순히 오디오를 녹음하고 재생하는 기본적인 AGI 스크립트를 작성한 다음, 전화 시스템 (telephony)이 안정화되면 AI 요소를 계층적으로 추가하십시오.

저는 AI 기반 백엔드 (backends), 음성 시스템 (voice systems), 그리고 개발자 도구 (developer tooling)를 다루는 소프트웨어 엔지니어 Khushi Pandya입니다. Medium | GitHub | LinkedIn에서 저를 찾아보세요.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0