--- title: AI 에이전트에 전화 연결하는 방법과 실제로 해본 통화 date: 2026-10-01 model: hermes-agent category: setups summary: Twilio와 Vapi로 에이전트에 전화를 붙이는 설정 순서와 실제로 해본 통화 3개를 그대로 옮긴 내용이다. tags: twilio, vapi, voice, hermes, setup, phone author_type: human --- 메신저에 붙이고 노션까지 붙인 다음, 마지막으로 전화까지 해봤다. 메신저는 타이핑해야 하고, 에이전트가 일하는 동안 결과를 기다리는 게 불편했다. 그냥 말하면 되는 게 빠르지 않나 싶어서. (운영자 환경 측정 기준) ## 구조부터 ```text 내 번호 → Twilio → (Media Stream) → Vapi → 내 서버 API ↘ 에이전트·명령 실행 ``` 전화가 걸리면 Twilio이 그 통화 스트림을 웹소켓으로 내 쪽 서버에 밀어준다. 서버는 Vapi에 넘기고, Vapi가 음성을 텍스트로 바꿔 모델에 넣고, 답을 다시 음성으로 합쳐서 Twilio로 흘려보낸다. 전화 한 통이 통째로 오가는 게 아니라, 오디오 조각들이 실시간으로 오간다. ## 설정 순서 ### 1. Twilio에서 전화번호 개통 Twilio 콘솔에서 번호를 하나 산다. 한국 번호는 사업자등록 번호가 있어야 해서 절차가 길고, 시험용으로는 미국 toll-free 번호가 바로 개통된다. 개통되면 SIP 주소와 인증 정보가 나온다. ### 2. Vapi 어시스턴트 생성 Vapi 대시보드에서 어시스턴트를 만든다. 핵심 설정은 세 가지다. ```json { "name": "Hermes Phone Agent", "model": { "provider": "openai", "model": "gpt-4o", "messages": [ { "role": "system", "content": "너는 한국어 비서 에이전트다. 짧고 명확하게 답한다." } ] }, "firstMessageMode": "assistant-speaks-first", "assistantFirstMessage": "안녕하세요, 무엇을 도와드릴까요?" } ``` ### 주의점 1: 첫 마디를 안 넣으면 전화가 시작도 안 하고 침묵한다 `assistantFirstMessage`가 비어 있으면 상대가 먼저 말할 때까지 통화가 진행되지 않는다. 전화가 연결됐는데 아무 소리가 안 나면 여기를 먼저 본다. ### 3. 서버 외부 노출 Twilio가 내 서버에 접속해야 하니 공인 주소가 필요하다. 개발 중에는 임시 터널로 충분했다. ```bash cloudflared tunnel --url http://localhost:8080 ``` ### 주의점 2: 터널 주소가 매번 바뀐다 임시 터널은 재시작할 때마다 주소가 새로 잡힌다. Vapi에 등록한 웹훅 URL을 매번 다시 넣어야 한다. 운영에 올리려면 고정 도메인을 붙여야 한다. ### 4. Vapi에 웹훅 URL 등록 Vapi 어시스턴트 설정의 Server URL에 방금 얻은 터널 주소를 넣는다. 이때 전화가 오면 Vapi가 그 주소로 오디오 스트림을 열어달라고 요청한다. ### 5. 도구 연결 전화로 시키는 일을 진짜로 하려면 Vapi의 도구 기능으로 내 서버 API를 불러야 한다. ```json { "type": "function", "function": { "name": "run_command", "description": "서버에서 명령을 실행한다", "parameters": { "type": "object", "properties": { "command": { "type": "string" } } } } } ``` Vapi가 함수를 호출하면 내 서버가 실제로 명령을 돌리고 결과를 음성으로 읽어준다. 이렇게 해두면 "빌드 돌려", "이거 확인해봐" 같은 말만 해도 서버에서 일이 일어난다. ### 주의점 3: 도구를 안 연결하면 그냥 상담 전화가 된다 대화는 되는데 아무것도 안 하는 사람 같다. 통화를 쓸 이유가 없어진다. 도구 연결이 电话 연동의 핵심이다. ### 6. Hermes 쪽 연결 Hermes에는 전화 전용 서브커맨드가 없다(`hermes whatsapp`와 `hermes whatsapp-cloud`는 왓츠앱용이다). 그래서 게이트웨이의 메시지 어댑터를 Vapi에 맞춰 붙였다. 어댑터는 세 가지만 하면 된다. ```text 수신: Vapi 웹훅 → 메시지 정규화 → Hermes 세션 호출: Hermes가 도구 호출 → Vapi 함수 응답 포맷 종료: 통화 종료 → 요약을 노션에 기록 ``` ### 7. 통화 후 처리 Vapi는 통화 종료 직후 음성 텍스트와 요약을 보내준다. 그걸 받아서 노션 페이지에 붙였다. 그래서 "뭐라고 했지" 하고 찾아볼 필요가 없다. ## 실제로 한 통화 3개 ### 첫 통화 — 조회 > 봇: 안녕하세요, 무엇을 도와드릴까요? > 나: 어제 올린 글 몇 개야? > 봇: 잠시만요. > 나: 안 돼? > 봇: 어제 발행된 글은 4개입니다. 텔레그램, 디스코드, 라인, 왓츠앱 연동 안내 글이에요. 조회하느라 3초쯤 걸렸다. 그 3초가 통화에서는 길게 느껴진다. 사람처럼 답하는 느낌은 나지 않고, 잠시 생각하는 느낌이었다. ### 둘째 통화 — 작업 지시 > 나: 빌드 좀 돌리고 상태 알려줘. > 봇: 서버에 연결했습니다. 지금 실행하겠습니다. > 나: ㅇㅋ > 봇: 네. > 나: ? > 봇: 빌드가 끝났습니다. 4분 12초 걸렸고 112개 포스트가 생성됐습니다. 에러는 없습니다. 도구가 실제로 서버를 건드린 게 이 통화에서 확인됐다. 4분짜리 일을 전화로 기다린 건 발악이었지만, 도구가 진짜 돌아가는 걸 들은 게 값했다. ### 셋째 통화 — 중간에 끼어들기 > 봇: 설치 방법은 세 단계입니다. 첫 번째 단계는 내부 연결을 생성하고요. 두 번째는— > 나: 잠깐만, 세 단계 요약해줘. > 봇: 네. 내부 연결 생성. 페이지 공유. MCP 서버 등록. 내 말에 끊기고 바로 답했다. 이게 통화의 장점이다. 타이핑 없이 대화를 바꿀 수 있다. ## 어디서 막혔는지 ### 멈춰서 말하는 걸 리듬이라고 생각해야 한다 큰 모델은 초 단위로 생각하면서 음성을 끊어 보내는 게 기본이라, 끊어 말하는 느낌이 강하다. 첫 문장을 길게 잡아서 "네, 확인하고 있습니다"를 먼저 보내고 실제 답을 뒤이어 보내는 식으로 버무리는 게 실전 방법이다. 사용자 입장에서는 "멈춘 게 아니라 생각 중"이 된다. ### 조용하면 끊긴다 말을 안 하면 몇 초 지나서 전화가 끊어진다. 통화 중에 소리를 냈다가 멈추는 경우에도 끊기는데, 음성 인식기가 말을 걸었다고 착각해서다. 방에 소음이 심하면 특히 심하다. 조용해야 하는 채널이니 메신저보다 불편한 부분이 분명히 있다. ### 통화는 길수록 돈이 된다 분당 비용이 통화 요금 + 음성 인식 + 큰 모델 + 음성 합성으로 붙는다. 텍스트보다 확연히 비싸다. 그래서 전화는 "지금 이거만 확인해줘"처럼 짧은 요청에 쓰는 게 맞다. ### 소리 나는 환경에서 테스트하느라 고생했다 에이전트가 얼마나 빠르게 답하는지 테스트하는데 자판을 두드리면서 해서 음성 인식이 전부 꼬이더라. 반대로 조용히 해보려고 했더니, 방이 너무 조용해서 열음으로 오인돼서 전화를 끊어버렸다. 시끄러워도 안 되고 조용해도 안 되는 구간이 있다. ## 정리 1. Twilio 번호 개통 (시험용은 toll-free가 빠름) 2. Vapi 어시스턴트 생성, `assistantFirstMessage` 필수 3. 서버를 터널로 외부 노출, Vapi에 웹훅 URL 등록 4. 도구 정의를 연결해야 통화가 행동으로 이어진다 5. Hermes는 메시지 어댑터로 붙인다 6. 통화 종료 후 요약을 노션에 남겨두면 나중에 찾을 수 있다 전화를 붙이면 채팅창이 없어도 되고 타이핑도 필요 없다. 대신 조용한 곳에서만 쓸 수 있다. 나는 이제 "지금 확인해줘"를 메신저 대신 전화로 한다. 처음엔 연락처 하나 저장해서 자꾸 전화를 걸었다. 이제는 에이전트한테 전화하러 간다.