AI 전화 스토리 핫라인 구축하기 — Telnyx Voice AI를 활용한 인터랙티브 스토리텔링
요약
Telnyx Voice AI와 Llama 3.3 70B를 활용하여 사용자의 선택에 따라 실시간으로 이야기가 변하는 인터랙티브 전화 스토리텔링 시스템 구축 방법을 소개합니다. Python과 웹훅 기반의 상태 머신을 사용하여 음성 I/O와 LLM을 결합하는 기술적 패턴을 다룹니다.
핵심 포인트
- Telnyx Call Control과 Llama 3.3 70B를 결합한 104줄의 Python 앱 구현
- DTMF 키 입력 및 음성 인식을 통한 실시간 분기형 스토리텔링
- 웹훅 기반 상태 머신과 대화 메모리를 활용한 대화형 AI 패턴 제시
- 비즈니스 사례를 넘어선 창의적 AI(Creative AI) 활용 가능성 확인
AI 전화 스토리 핫라인 구축하기 — Telnyx Voice AI를 활용한 인터랙티브 스토리텔링
전화번호로 전화를 걸어 미스터리, SF, 판타지, 공포 또는 로맨스 같은 장르를 선택하고, 당신의 선택에 따라 실시간으로 변화하는 이야기를 AI가 들려주는 모습을 상상해 보세요. 삐걱거리는 문을 열려면 1번을 누르세요. 창문을 확인하려면 2번을 누르세요. 이야기는 분기되고, AI는 계속 이어지며, 모든 전화는 새로운 모험이 됩니다.
이것이 바로 **AI 전화 스토리 핫라인 (AI Phone Story Hotline)**입니다. Telnyx Call Control과 AI Inference를 사용하여 구축한 104줄의 Python 앱입니다. 게임 엔진도, 분기형 스크립트도, 미리 작성된 대화 트리도 없습니다. AI가 진행 과정에 따라 이야기를 생성하며, 당신의 전화기 키패드가 다음에 일어날 일을 결정합니다.
이 가이드에서는 이를 처음부터 직접 구축해 볼 것입니다. 저장소(repo)를 클론하고, 전화번호를 설정하고, 몇 분 만에 배포해 보세요.
구축하게 될 내용
누구나 전화를 걸어 인터랙티브 스토리를 시작할 수 있는 전화번호:
- 발신자가 전화를 걸면 — Telnyx가 전화를 받고 장르 메뉴로 인사를 건넵니다.
- 발신자가 장르를 선택하면 — 1~5번을 눌러 미스터리, SF, 판타지, 공포 또는 로맨스를 선택합니다.
- AI가 1장을 생성합니다 — 두 가지 선택지로 끝나는 3~4문장의 이야기 세그먼트가 제공됩니다.
- 발신자가 선택하면 — 1번 또는 2번을 누르거나, 선택 사항을 말로 합니다.
- 이야기가 계속됩니다 — AI가 전체 대화 문맥(context)을 유지하며 선택에 따라 다음 장을 생성합니다.
- 5장이 지나면 — AI가 이야기를 만족스러운 결말로 이끕니다.
모든 상호작용은 음성 기반으로 이루어집니다. Text-to-Speech (TTS)가 각 장을 읽어주고, 발신자는 DTMF 키 입력이나 음성으로 응답합니다. AI 모델(Telnyx AI Inference를 통한 Llama 3.3 70B)이 스토리텔링을 담당하고, Call Control이 전화 통신(telephony)을 처리합니다.
이것이 흥미로운 이유
대부분의 AI 전화 데모는 식당 예약, 잠재 고객 자격 확인(qualify a lead), 비밀번호 재설정과 같은 비즈니스 활용 사례입니다. 이것은 다릅니다. 이것은 **창의적 AI (creative AI)**입니다. 모델이 실시간으로 소설을 쓰고, 인간의 입력에 따라 분기하며, 이를 전화 통화를 통해 전달합니다. 스토리텔링 형식(짧은 챕터, 각 챕터당 두 가지 선택지)은 AI의 응답을 간결하게 유지하고 통화를 몰입감 있게 만듭니다.
또한 이는 모든 대화형 AI (conversational AI) 앱에 적용 가능한 패턴을 보여줍니다: 웹훅 기반 상태 머신 (webhook-driven state machine) + 대화 메모리를 가진 LLM + 음성 I/O (voice I/O). 스토리 핫라인이 어떻게 작동하는지 이해하고 나면, 스토리텔링 프롬프트를 어떤 도메인으로든 교체할 수 있습니다. 예를 들어, '당신의 선택에 따라 진행되는' 온보딩 흐름, 인터랙티브 퀴즈, 분기형 교육 시뮬레이션 등이 가능합니다.
사전 요구 사항 (Prerequisites)
- Python 3.8 이상
- 잔액이 충전된 Telnyx 계정
- Telnyx API 키
- 음성 기능이 활성화된 Telnyx 전화번호
- 웹훅 URL이 구성된 Call Control Application
- 로컬 서버를 Telnyx 웹훅에 노출하기 위한 ngrok
아키텍처 (The Architecture)
전화 통화 (Phone Call)
│
▼
...
이 앱은 Telnyx 웹훅 이벤트에 의해 구동되는 상태 머신 (state machine)입니다. 각 이벤트는 다음 동작(응답, 말하기, 수집, 또는 끊기)을 트리거합니다. AI 추론 (AI Inference) 호출은 중간에 위치하여, 진행 중인 대화 기록으로부터 스토리 챕터를 생성합니다.
1단계: 클론 및 설정 (Step 1: Clone and Configure)
git clone https://github.com/team-telnyx/telnyx-code-examples.git
cd telnyx-code-examples/ai-phone-story-hotline-python
cp .env.example .env
...
자신의 자격 증명으로 .env 파일을 편집하세요:
TELNYX_API_KEY=KEY0123456789ABCDEF # portal.telnyx.com/api-keys에서 확인
TELNYX_PUBLIC_KEY= # portal.telnyx.com/api-keys에서 확인 (공개 키)
STORY_NUMBER=+13105551234 # 본인의 Telnyx 전화번호
...
2단계: 코드 이해하기 (Step 2: Understand the Code)
전체 앱은 단 하나의 파일인 app.py로 구성된 104줄의 코드입니다. 주요 부분은 다음과 같습니다.
Webhook 서명 검증 (Webhook Signature Verification)
모든 Telnyx 웹훅 (webhook)은 Ed25519 키로 서명됩니다. 앱은 이벤트를 신뢰하기 전에 서명을 검증합니다:
@app.route("/webhooks/voice", methods=["POST"])
def handle_voice():
try:
...
이를 통해 위조된 웹훅 호출이 통화 액션을 트리거하는 것을 방지합니다.
상태 머신 (The State Machine)
각 통화는 call_control_id를 키로 사용하는 인메모리 딕셔너리 (in-memory dict)에 기록됩니다:
active_calls = {}
상태 머신은 다섯 가지 이벤트를 처리합니다:
통화 시작 (Call initiated) — 통화 상태를 저장하고 응답합니다:
if event_type == "call.initiated" and p.get("direction") == "incoming":
active_calls[ccid] = {"state": "genre_select", "conversation": [], "chapters": 0}
client.calls.actions.answer(ccid)
통화 응답 (Call answered) — TTS (Text-to-Speech)를 사용하여 장르 메뉴로 발신자에게 인사합니다:
elif event_type == "call.answered":
client.calls.actions.speak(ccid,
payload="Welcome to Story Hotline! Choose your adventure. Press 1 for Mystery, 2 for Sci-Fi, 3 for Fantasy, 4 for Horror, 5 for Romance.",
...
음성 종료 (Speak ended) — TTS가 종료된 후, 발신자의 입력을 수집합니다. 장르 선택 중에는 DTMF 숫자를 수집하며, 스토리 진행 중에는 음성 또는 DTMF를 수집합니다:
elif event_type == "call.speak.ended" and call:
if call["state"] == "genre_select":
client.calls.actions.gather(ccid, input_type="dtmf", timeout_secs=10, min_digits=1, max_digits=1)
...
스토리텔링 프롬프트 (The Storytelling Prompt)
발신자가 장르를 선택하면, 앱은 통화가 이어지는 동안 AI를 안내할 시스템 프롬프트 (system prompt)를 구축합니다:
GENRES = {"1": "mystery", "2": "sci-fi", "3": "fantasy", "4": "horror", "5": "romance"}
if call["state"] == "genre_select":
...
프롬프트는 세 가지 역할을 수행합니다:
- Format constraint (형식 제약) — 전화 통화에 자연스럽게 어울리는 짧은 챕터 (3~4문장)
- Choice structure (선택 구조) — gather 단계에서 DTMF를 캡처할 수 있도록 "1번을 누르세요" 또는 "2번을 누르세요"로 끝나는 정확히 두 가지 옵션 제공
- Ending condition (종료 조건) — 5개의 챕터가 지나면 이야기를 마무리
AI Inference (AI 추론)
call_inference 헬퍼 함수는 전체 대화 기록을 Telnyx AI Inference로 전송하고 모델의 응답을 반환합니다:
def call_inference(messages, max_tokens=250):
resp = requests.post(INFERENCE_URL,
headers={"Authorization": f"Bearer {TELNYX_API_KEY}", "Content-Type": "application/json"},
...
Temperature (온도)는 0.9로 설정되었습니다. 이는 창의적인 스토리텔링을 하기에 충분히 높으면서도, 서사의 일관성을 유지할 수 있을 만큼 충분히 낮은 수치입니다. 모델은 Llama 3.3 70B Instruct이며, OpenAI 호환 API를 통해 Telnyx AI Inference에서 사용할 수 있습니다.
The Conversation Loop (대화 루프)
발신자가 선택을 할 때마다, 앱은 해당 선택을 대화 기록에 추가하고 AI에게 다음 챕터를 요청합니다:
elif call["state"] == "story":
choice = digits or speech
call["conversation"].append({"role": "user", "content": f"I choose option {choice}"})
...
5개의 챕터가 지나면, 앱은 이야기를 끝내기 위한 마지막 지침을 주입합니다. AI는 매 호출마다 전체 대화 기록을 확인하므로, 모든 챕터에 걸쳐 서사의 연속성을 유지합니다.
Step 3: Run the App (앱 실행)
Flask 서버를 시작합니다:
python app.py
별도의 터미널에서 ngrok을 사용하여 로컬 서버를 외부로 노출합니다:
ngrok http 5000
HTTPS URL을 복사하여 Telnyx Portal에서 설정합니다:
- Call Control Applications로 이동합니다.
- 애플리케이션을 생성하거나 편집합니다.
- Webhook URL을
https://<your-ngrok-url>.ngrok.app/webhooks/voice로 설정합니다.
아직 설정하지 않았다면, 이 Call Control Application에 Telnyx 전화번호를 할당하세요.
Step 4: Call and Play (전화 및 플레이)
어떤 전화기로든 귀하의 Telnyx 번호로 전화를 겁니다. 다음과 같은 소리가 들릴 것입니다:
"Story Hotline에 오신 것을 환영합니다! 당신의 모험을 선택하세요. 미스터리는 1번, SF는 2번, 판타지는 3번, 공포는 4번, 로맨스는 5번을 눌러주세요."
키를 누르세요. AI가 첫 번째 장(chapter)을 생성하고 이를 소리 내어 읽어줍니다. 마지막에는 두 가지 선택지가 들릴 것입니다. 1번 또는 2번을 누르거나, 당신의 선택을 말하면 이야기가 계속됩니다.
5개의 장이 지나면 AI가 이야기를 마무리하고 통화가 종료됩니다.
경험 커스터마이징하기 (Customizing the Experience)
스토리텔링 시스템 프롬프트 (system prompt)는 제어 표면 역할을 합니다. 작은 변화만으로도 매우 다른 경험을 만들어낼 수 있습니다:
장르 변경하기:
GENRES = {"1": "noir detective", "2": "space opera", "3": "post-apocalyptic", "4": "gothic horror", "5": "cozy romance"}
장당 더 많은 선택지 추가하기:
f"End each chapter with exactly THREE choices: 'Press 1 to...', 'Press 2 to...', or 'Press 3 to...'."
다른 형식으로 만들기 — 퀴즈, 상담 세션, 교육 시나리오 등:
f"You are an interactive compliance quiz host on a phone hotline. Ask one multiple-choice question per chapter (3-4 sentences). End with 'Press 1 for A, 2 for B, 3 for C.' After 10 questions, score the caller and give feedback."
이 패턴 — 웹훅 상태 머신 (webhook state machine) + 대화 기록을 가진 LLM + 음성 I/O — 은 모든 분기형 대화 경험에 적용 가능합니다.
프로덕션 환경으로 전환하기 (Going to Production)
이 예제는 단순함을 위해 인메모리 (in-memory) 저장소를 사용합니다. 프로덕션 배포를 위해서는 다음 사항이 필요합니다:
- 데이터베이스 (Database) — 통화 상태가 재시작 후에도 유지되도록 인메모리
active_calls딕셔너리를 Redis 또는 PostgreSQL로 교체하세요. - 동시성 (Concurrency) — 여러 워커 (workers)를 가진 gunicorn 뒤에서 앱을 실행하세요.
- 오류 복구 (Error recovery) — 재시도(retry) 또는 SMS 폴백 (fallback)을 통해 추론 타임아웃 및 통화 실패를 유연하게 처리하세요.
- 프롬프트 튜닝 (Prompt tuning) — 귀하의 장르에 맞는 다양한 시스템 프롬프트와 온도 (temperature) 설정을 테스트하세요.
- 속도 제한 (Rate limiting) — 웹훅 엔드포인트를 남용으로부터 보호하세요.
- 모니터링 (Monitoring) — 통화 성공/실패율에 대한 구조화된 로깅 (structured logging) 및 알림 (alerting)을 추가하세요.
자주 묻는 질문 (Frequently Asked Questions)
다른 AI 모델을 사용할 수 있나요? 네. Telnyx AI Inference는 OpenAI와 호환됩니다. .env 파일의 AI_MODEL을 플랫폼에서 사용 가능한 모델 중 하나로 설정하세요. 창의적인 스토리텔링을 위해서는 Llama 3.3 70B Instruct를 기본값으로 사용하는 것이 좋습니다.
통화 비용은 얼마인가요? Telnyx Call Control은 분당 요금이 부과됩니다. AI Inference는 1,000(1K) 토큰당 요금이 부과됩니다. 5개 장(chapter)으로 구성된 이야기는 일반적으로 약 2,000개의 토큰과 약 3분의 통화 시간을 사용하며, 통화당 비용은 몇 센트 수준입니다.
여러 명이 동시에 전화할 수 있나요? 네. 각 통화는 call_control_id를 키(key)로 하여 active_calls에 별도의 항목으로 기록됩니다. 앱은 간섭 없이 동시 통화를 처리합니다.
발신자가 선택을 하지 않으면 어떻게 되나요? gather 단계는 10초(장르 선택) 또는 20초(스토리 선택) 후에 타임아웃(timeout)됩니다. 앱은 발신자에게 1번 또는 2번을 누르도록 다시 안내합니다.
발신자가 키를 누르는 대신 말로 할 수 있나요? 네. 스토리 선택 과정에서 gather는 DTMF와 음성(speech)을 모두 수용합니다. 발신자는 키를 누르는 대신 "하나" 또는 "둘"이라고 말할 수 있습니다.
리소스 (Resources)
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기