Graph 통화 봇
Graph 통화 봇
Microsoft Teams 내에서 동료처럼 이름으로 ElevenLabs 에이전트에 전화하거나 채팅하세요.
개요
이 방식은 에이전트를 전화할 수 있는 Teams ID로 만듭니다. 사용자는 이름으로 검색해 1:1 통화를 걸고, 에이전트는 실시간으로 응답합니다. 전화번호, PSTN 또는 Communications Credits가 필요하지 않습니다. 이름으로 전화를 걸 수 있는 유일한 방식이며, 운영 난이도도 가장 높습니다.
Microsoft Graph 실시간 미디어 봇(Cloud Communications 통화 플랫폼)을 사용합니다. 미디어 SDK(Microsoft.Skype.Bots.Media)는 Windows Server의 .NET 전용이며, Teams 통화에서 원시 오디오를 처리할 수 있는 Linux 또는 비-.NET 경로는 없습니다.
작동 방식
봇은 애플리케이션 호스팅 미디어로 응답하고 초당 50개 오디오 프레임(20ms PCM 16kHz)을 수신하여 WebSocket을 통해 ElevenLabs 에이전트에 연결한 뒤, 에이전트 오디오를 통화로 다시 스트리밍합니다.
요구 사항
- Azure Bot 등록 + 앱(Entra 앱 등록).
- 관리자 동의가 적용된 Graph 애플리케이션 권한:
Calls.AccessMedia.All(원시 미디어) 및Calls.Initiate.All. - 공개 IP와 열린 미디어 포트를 갖춘 Windows Server VM(≥ 물리 코어 2개 — 예:
Standard_D4s_v3). - 미디어/시그널링 엔드포인트용 공개 FQDN의 CA 서명 TLS 인증서(미디어 플랫폼은 자체 서명 인증서를 거부함).
- 양쪽 모두 PCM 16000Hz로 설정된 ElevenLabs 에이전트: Voice 탭의 TTS 출력 형식 및 Advanced 탭의 사용자 입력 오디오 형식.
D2s_v3(vCPU 2개 = 물리 코어 1개)는 MediaPlatform needs a system with at least 2 cores 오류로 실패합니다. 물리 코어가 2개 이상인 크기(예: D4s_v3)를 사용하세요.
권한 및 역할
1단계 — 봇 및 Graph 권한 등록
앱 등록을 만들고 연결된 Azure Bot을 생성한 다음, 통화 권한을 부여하고 동의하세요(동의하려면 전역 관리자 / 권한 있는 역할 관리자가 필요합니다).
두 Graph 애플리케이션 역할을 부여하고 관리자 동의를 제공한 다음(전역 관리자 / 권한 있는 역할 관리자 필요), 할당이 적용되었는지 확인하세요.
admin-consent가 Consent validation failed를 반환하는 경우, 대신 서비스 주체에서 앱 역할을 직접 부여하세요.
포털의 Entra 관리 센터에서 앱 등록 → 내 앱 → API 권한으로 이동해 확인하세요. 두 권한 모두 녹색 체크 표시와 함께 부여됨으로 표시되어야 합니다.

2단계 — Windows VM, 인증서 및 포트 프로비저닝
VM에서 다음을 실행하세요(미디어 플랫폼의 네이티브 코드에 필요하며, Windows Server에는 기본적으로 설치되어 있지 않습니다).
Windows 방화벽에서도 동일한 포트를 열고 인증서 지문을 기록해 두세요. 봇은 Kestrel(443 및 알림 포트)과 미디어 플랫폼(8445)을 이 인증서에 바인딩합니다.
VM 자체의 *.cloudapp.azure.com FQDN으로 Let’s Encrypt 인증서를 사용할 수 있으므로 별도의 도메인이 필요하지 않습니다.
3단계 — 봇 빌드 및 실행
Microsoft의 microsoft-graph-comms-samples PublicSamples/EchoBot에서 시작하세요. 이 샘플은 net6.0을 대상으로 하며 .NET SDK로 빌드됩니다(Visual Studio Build Tools 불필요).
appsettings.json의 AppSettings 섹션을 AadAppId, AadAppSecret, ServiceDnsName/MediaDnsName(VM FQDN), CertificateThumbprint, 포트(통화 443, 알림 9441, 미디어 8445)로 구성하세요. 아래 ElevenLabs 브리지용 설정 두 개도 추가하세요. ElevenLabsAgentId 및 ElevenLabsOrigin(wss://api.elevenlabs.io 또는 데이터 레지던시 호스트)입니다. 재부팅 후에도 계속 실행되도록 Windows 예약 작업 / 서비스로 실행하세요.
작업 스케줄러의 기본 실행 시간 제한(72시간) 은 장기 실행 작업을 조용히 종료합니다. 부팅 시 시작한 봇은 3일 후 종료되고 통화 시 “연결할 수 없습니다” 오류가 발생합니다. 제한을 해제하고 실패 시 다시 시작을 추가하세요.
기본 EchoBot은 표준 포트 443으로 걸려온 통화에서 충돌합니다. HttpHelpers.SetAbsoluteUri가
req.Host.Port.Value를 호출하지만, Host 헤더에 명시적 포트가 없으면 null입니다. 이를
req.Host.Port ?? (req.IsHttps ? 443 : 80)로 패치하세요.
에코를 ElevenLabs로 교체
EchoBot의 오디오 연결 지점은 명확합니다. SpeechService.AppendAudioBuffer(in) 및 OnSendMediaBufferEventArgs(out) 이벤트입니다. Azure Speech 본문을 동일한 인터페이스를 유지하는 ElevenLabs 에이전트 WebSocket 브리지로 교체하세요.
양쪽 모두 PCM 16kHz 모노이므로 base64 패스스루입니다. 에이전트를 pcm_16000으로 설정하세요. ElevenLabs interruption(끼어들기) 발생 시 브리지는 FlushMedia를 발생시킵니다. 이를 미디어 스트림에 연결해 대기 중인 AudioMediaBuffer를 모두 삭제하세요. 그렇지 않으면 에이전트가 발신자 위로 계속 말하게 됩니다. 전체 메시지 레퍼런스는 WebSocket 문서에서 확인할 수 있습니다. 통화 종료 시 끊기와 웜 전송은 아래 섹션에서 다룹니다.
Connect()의 URL은 공개 에이전트에 연결합니다. 비공개 에이전트의 경우 서버 측에서 API 키로
GET /v1/convai/conversation/get-signed-url?agent_id=...를 요청하여 단기 서명 URL을 받은 후, 반환된 URL에 연결하세요.
데이터 레지던시를 사용하는 경우 ElevenLabsOrigin을 데이터 레지던시
호스트(wss://api.eu.residency.elevenlabs.io, .in., 또는 .sg.)로 설정하세요. 서명 URL 요청에는 일치하는
https:// 호스트를 사용합니다.
4단계 — Teams에서 통화 가능하도록 설정
-
Azure Bot의 Teams 채널에서 통화를 활성화하고 통화 웹훅을
https://YOUR_FQDN/api/calling으로 설정하세요.포털에서는 Azure Bot 리소스 → 채널 → Microsoft Teams → 통화 탭에 있습니다.

Azure Bot → 채널 — 연결된 Microsoft Teams 채널 
Microsoft Teams 채널 → 통화 — 봇 웹훅으로 통화 활성화 -
bots[0].supportsCalling: true와 봇의 앱 ID가 포함된 Teams 앱 매니페스트를 빌드한 후 사이드로드하세요(앱 → 앱 관리 → 사용자 지정 앱 업로드). 또는 UI 없이New-TeamsApp -DistributionMethod organization -Path ./bot-app.zip(MicrosoftTeams PowerShell 모듈)으로 조직 전체에 게시할 수 있습니다.
Teams에서 이름으로 앱을 검색해 통화하세요. 봇이 응답하고 ElevenLabs 에이전트가 말합니다.

1:1 이름 기반 통화에는 전화번호나 리소스 계정이 필요하지 않습니다. 이는 PSTN 다이얼인에만 필요합니다.
Calls.AccessMedia.All이 원시 오디오 브리지를 활성화합니다.
텍스트 채팅(동일한 봇)
동일한 Azure Bot이 Teams에서 텍스트에도 응답할 수 있으므로, 사용자는 에이전트에 통화하거나 채팅할 수 있습니다. 통화와 메시징은 봇에서 독립적인 채널입니다. 통화 웹훅은 음성을 처리하고 Bot Framework 메시징 엔드포인트(/api/messages)는 채팅을 처리합니다.

봇의 메시징 엔드포인트를 이를 제공하는 호스트로 지정하세요(미디어 봇 또는 다른 서비스. Windows VM일 필요는 없습니다).
Bot Framework SDK로 엔드포인트를 구현하고, 각 메시지를 음성에 사용하는 것과 동일한 대화 WebSocket을 통해 텍스트 모드로 에이전트에 릴레이하세요. user_message 이벤트를 전송하고 agent_response 이벤트를 읽습니다. 먼저 에이전트 오버라이드 설정에서 첫 번째 메시지 필드를 활성화하세요. 아래 코드는 이를 비어 있는 값으로 오버라이드하므로, 응답이 에이전트의 인사가 아니라 사용자 메시지에 대한 답변이 됩니다.
표준 방식으로 등록하세요(CloudAdapter, AddTransient<IBot, ChatBot>()을 통한 봇 등록, /api/messages 컨트롤러). 그리고 매니페스트의 봇 항목에 채팅 범위를 추가하세요.
이 스니펫은 메시지마다 새 대화를 열기 때문에 각 턴은 독립적입니다. 채팅 메모리를 유지하려면 Teams
conversation.id마다 WebSocket을 하나씩 열어 턴 간에 재사용하고, 유휴 세션을 정리하세요. 그러면 에이전트가 해당 채팅의 이전 메시지를 기억합니다. first_message
오버라이드는 에이전트의 오버라이드 설정에서 활성화해야 합니다.
허용되지 않은 오버라이드가 전송되면 서버가 대화를 종료합니다. 활성화할 수 없다면 오버라이드를 생략하고,
각 세션의 첫 번째 agent_response(인사)를 버린 후 다음 응답을 반환하세요.
채팅 응답이 도착하지 않으면 에이전트의 고급 설정에서 agent_response 클라이언트
이벤트를 활성화하세요. 텍스트 응답은 이 이벤트를 통해 전달됩니다.
통화 종료
ElevenLabs가 대화를 종료하면(통화 종료 도구가 WebSocket을 닫음) Teams 연결도 끊으세요.
상담원에게 웜 전송
에이전트가 사용자 지정 transfer_to_human 클라이언트 도구를 실행하면 봇이 진행 중인 통화에 Teams 사용자를 초대한 후(상담식 추가) 물러납니다.
상담식 전송(replacesCallId)은 양쪽이 동일한 테넌트의 Teams 사용자여야 합니다. PSTN 전송 대상에는 애플리케이션 인스턴스가 필요합니다. 먼저 상담원에게 상황을 설명하려면 에이전트에서 reason 매개변수를 전달하고 연결하기 전에 이를 상담원에게 재생하세요.
문제 해결
MediaPlatform needs a system with at least 2 cores
MediaPlatform needs a system with at least 2 cores
VM에 물리적 코어가 하나만 있습니다. 물리적 코어를 2개 이상으로 조정하고(예: D4s_v3) 다시 시작하세요.
Unable to load DLL 'NativeMedia'
Unable to load DLL 'NativeMedia'
VC++ 재배포 가능 패키지(vcredist140)와 Server-Media-Foundation Windows 기능을 설치한 후 봇을 다시 시작하세요.
수신 통화가 500을 반환하거나 통화가 연결되지 않음
443에서 발생하는 EchoBot 포트 null 버그입니다. HttpHelpers.SetAbsoluteUri를 패치하세요(3단계 참조). 인증서가 CA 서명되었고 443에서 연결 가능한지도 확인하세요.
봇에 통화하면 '연결할 수 없습니다'라고 표시됨
Teams 채널에서 올바른 /api/calling 웹훅으로 통화가 활성화되어 있는지, Graph Calls.AccessMedia.All 권한이 동의되었는지, NSG와 Windows 방화벽 모두에서 포트 443/8445/9441이 열려 있는지 확인하세요. 통화가 이전에는 작동했지만 중단된 경우, VM에서 봇 프로세스가 여전히 실행 중인지 확인하세요. 작업 스케줄러의 기본 72시간 실행 제한은 부팅 후 며칠 뒤 봇을 종료합니다(3단계의 경고 참조).