AI 리셉셔니스트를 만들어보니 - Part 1
형의 고급 정비소를 위해 AI 리셉셔니스트를 만들었던 경험을 공유합니다.
왜 이걸 만들게 됐나
형이 운영하는 고급 자동차 정비소가 매달 수천 달러의 손실을 보고 있었어요. 이유는 간단했습니다. 주당 수백 통의 전화를 놓치고 있었거든요.
형은 하루종일 차 밑에서 작업하고 있는데 전화가 울리면 받을 수가 없어요. 그러면 고객은 기다리다 전화를 끊고 다른 정비소를 찾아갑니다. 브레이크 정비($450)든 엔진 수리($2,000)든 관계없이 그냥 일이 날아가는 거죠.
그래서 AI 리셉셔니스트를 만들기로 했습니다. 이름은 "Axle"이라고 지었어요. 자동차 액슬(차축)에서 따온 거죠. 😏
단순한 챗봇이 아닌 맞춤형 음성 에이전트
이건 일반적인 챗봇이 아닙니다. 형의 정비소 전화를 받을 수 있고, 정확한 가격, 영업시간, 정책을 알고 있으며, 답변하지 못할 때 콜백을 남길 수 있는 맞춤형 음성 에이전트예요.
제대로 만들려면 맞춤형 개발이 필요했습니다. 먼저 형의 웹사이트 데이터를 수집하고 요구사항 문서(PRD)를 작성한 다음, 프로젝트를 3단계로 나눴어요.
Step 1: AI의 두뇌 만들기 (RAG 파이프라인)
가장 먼저 할 일은 AI가 정확하게 답변하도록 만드는 것이었습니다. 가격을 멋대로 지어내거나 잘못된 정보를 주면 안 되거든요.
기본 LLM만으로는 위험해요. 고객이 "브레이크 정비 얼마예요?"라고 물었을 때 AI가 $200이라고 대답했는데 실제로는 $450이라면? 고객은 실망하고 신뢰를 잃게 됩니다. 해결책은 RAG(Retrieval-Augmented Generation)입니다. 즉, AI가 마음대로 답하도록 두지 말고 실제 정보로 이루어진 지식베이스를 제공해서 그 안에서만 답하도록 강제하는 거죠.
웹사이트 데이터 수집
먼저 형의 웹사이트를 크롤링했습니다. 서비스 페이지와 가격 정보를 마크다운 파일로 변환했어요. 그 결과 21개 이상의 문서로 된 구조화된 지식베이스를 만들 수 있었습니다. 모든 서비스 종류, 가격, 소요 시간, 영업시간, 결제 수단, 취소 정책, 보증 정보, 대여 차량, 그리고 취급하는 자동차 브랜드까지 포함했어요.
벡터 임베딩으로 의미 이해하기
이 지식베이스를 MongoDB Atlas에 저장했습니다. 각 문서는 Voyage AI(voyage-3-large)를 사용해 1024차원 벡터로 변환됩니다. 단순한 키워드가 아니라 문장의 의미를 수치화하는 거예요. 이 벡터는 원본 텍스트와 함께 MongoDB Atlas에 저장되며, 임베딩 필드에는 Atlas Vector Search 인덱스가 설정되어 있습니다.
검색 파이프라인 구축
고객이 질문하면 그 질문도 같은 Voyage AI 모델로 벡터화한 후 Atlas Vector Search 인덱스와 비교합니다. 의미가 가장 유사한 문서 3개를 반환하는 방식이에요. 덕분에 "브레이크 정비 얼마?"라고 물으면 "brake job"이라는 단어가 정확히 일치하지 않더라도 브레이크 서비스 가격 문서를 제대로 찾아냅니다.
Claude로 답변 생성
검색된 문서들을 Claude(claude-sonnet-4-6)에 컨텍스트로 전달합니다. 여기에 엄격한 시스템 프롬프트를 붙여요. 지식베이스에 있는 정보로만 답변하고, 짧고 자연스럽게 얘기하며, 모르는 건 모른다고 말하고 콜백을 남기도록 강제하는 거죠. 절대 지어낸 답변을 하면 안 됩니다.
결과적으로 터미널에서 질문을 입력하면 정확한 답변이 돌아옵니다. "오일 교환 얼마예요?"라고 물으면 "$45(일반유), $75(합성유). 오일 필터, 액체 토핑, 타이어 공기압 체크 포함. 약 30분 소요"라고 답하죠.
Step 2: 실제 전화 연결하기
이제 이 두뇌를 고객이 실제로 걸 수 있는 전화 시스템에 연결해야 했습니다.
음성 플랫폼은 Vapi를 선택했어요. 통신 부분을 모두 처리해줍니다. 전화번호 구매, 음성 인식(Deepgram), 음성 합성(ElevenLabs), 실시간 함수 호출까지요. 내가 할 일은 이 플랫폼이 호출할 웹훅을 만드는 것뿐입니다.
FastAPI 웹훅 서버 구축
발신자가 질문을 하면 Vapi는 내 서버의 /webhook 엔드포인트로 tool-calls 요청을 보냅니다. 여기에는 발신자의 질문이 포함되어 있어요. 서버는 이를 RAG 파이프라인으로 라우팅하고, Claude로부터 응답을 받아 Vapi로 돌려보냅니다. Vapi가 그 응답을 음성으로 읽어주는 거죠. 자연스러운 대화처럼 느껴지려면 전체 왕복 시간이 빨라야 합니다.
Ngrok으로 로컬 서버 공개
개발 중에는 로컬 포트 8000에서 서버를 실행하고 있었어요. Ngrok을 쓰면 이걸 공개 HTTPS URL로 만들 수 있습니다. 이 URL을 Vapi 대시보드에 웹훅 엔드포인트로 설정하면, Vapi가 전화가 들어올 때 실시간으로 내 로컬 서버에 접근할 수 있어요. 프로덕션으로 가려면 클라우드 호스팅을 사용해야 하지만, 개발과 테스트 단계에서는 Ngrok으로 2분 만에 준비가 됩니다.
Vapi 어시스턴트 설정
Vapi 대시보드에서 어시스턴트를 설정했습니다. 인사말("안녕하세요, Dane's Motorsport입니다. 무엇을 도와드릴까요?")을 정하고, 두 가지 도구를 연결했어요.
첫 번째는 answerQuestion으로 RAG 기반 응답을 처리하고, 두 번째는 saveCallback으로 답변할 수 없을 때 이름과 전화번호를 수집합니다. 둘 다 웹훅 URL로 연결됩니다.
대화 기록으로 맥락 유지
Vapi는 매 요청마다 전체 대화 기록을 함께 보냅니다. 그래서 RAG 파이프라인이 이전 대화를 컨텍스트로 이용할 수 있어요. 발신자가 "당신은 뭐예요?"라고 물으면 앞의 대화를 기억하고 있는 거죠.