본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 15. 04:45

이메일로 후속 조치를 수행하는 음성 에이전트 (Voice Agents)

요약

음성 에이전트가 통화 후 이메일로 지침이나 요약본을 발송할 수 있도록 전용 메일함을 제공하는 Nylas의 Agent Account 활용법을 소개합니다. LLM의 Function Calling을 통해 음성 에이전트가 이메일 도구를 호출하고 대화 스레드를 유지하는 인프라 구현 방식을 다룹니다.

핵심 포인트

  • 음성 에이전트 전용 메일함을 통해 통화와 이메일 후속 조치를 하나의 스레드로 통합
  • Nylas Agent Account를 활용한 에이전트 정체성 및 메일함 구축 방법
  • STT-LLM-TTS 흐름 내에서 Function Calling을 통한 이메일 도구 호출 구조
  • LiveKit 및 Vapi 환경에서의 실질적인 구현 워크플로우 제시

지난 스프린트 때, 제가 대화했던 한 팀이 지원 전화(support calls)를 인상적으로 처리하는 음성 에이전트(voice agent)를 시연했습니다. 하지만 한 통화자가 "그 지침들을 이메일로 보내줄 수 있나요?"라고 묻는 순간, 회의실은 정적에 휩싸였습니다. 에이전트는 문서에 대해 말할 수는 있었지만, 그것들을 보낼 이메일 주소가 없었습니다. 이후 화이트보드에 적힌 임시방편은 참담했습니다. 공유된 noreply@ 주소를 통해 전달하고, 답장은 놓치며, 티켓팅 시스템(ticketing system)에서 수동으로 스레드(threads)를 대조하는 방식이었습니다.

음성 에이전트들은 이 벽에 끊임없이 부딪힙니다. 전화 통화는 비밀번호 재설정 지침, 문서, 회의 요약과 같은 후속 결과물(follow-up artifacts)을 생성하며, 통화자는 이메일을 통해 이를 받기를 기대하기 때문입니다. 깔끔한 해결책은 텍스트 에이전트(text agents)에 적용되는 방식과 동일합니다. 바로 음성 에이전트가 자신만의 메일함(mailbox)을 갖는 것입니다.

정체성 측면 (The identity half)

Nylas의 Agent Account는 API를 통해 생성하는 호스팅된 메일함입니다(Agent Accounts는 현재 베타 버전입니다). 제품 문서의 음성 활용 사례는 정확히 위에서 언급한 시나리오를 다룹니다. 지원 전화를 받는 음성 에이전트가 통화자가 요청하는 즉시 자신의 voice-agent@yourcompany.com 주소로 문서, 재설정 지침 또는 회의 요약을 보내는 것입니다. 단순히 메시지를 보내는 통로 이상의 역할을 하는 부분은 다음과 같습니다. 통화자가 답장을 보내면 그 답장이 동일한 계정으로 돌아오기 때문에, 전체 대화가 하나의 메일함 내에서 하나의 스레드(thread)로 유지됩니다. 전화 통화와 그에 따른 서면 후속 조치가 서로 다른 시스템에 분리되어 존재하던 문제가 해결됩니다.

각 계정은 기존의 Messages, Threads, Webhooks 엔드포인트(endpoints)에서 작동하는 grant_id를 가진 실제 권한(grant)이며, 6개의 시스템 폴더와 함께 제공됩니다. 무료 플랜의 경우 계정당 하루 최대 200개의 메시지를 보낼 수 있습니다.

인프라 측면 (The plumbing half)

voice agents recipe에서는 런타임(runtime)이 실제로 이메일 도구를 호출하는 방법을 다룹니다. 벤더(vendor)에 관계없이 흐름은 동일합니다:

speech → STT → LLM (function-calling) → subprocess(nylas …) → JSON → LLM → TTS → speech

LLM이 도구(tool)를 결정하면, 런타임(runtime)은 --json 옵션과 함께 Nylas CLI 서브프로세스(subprocess)를 생성하고, 결과가 돌아오면 모델이 음성 응답을 구성합니다. LiveKit에서는 도구가 단순히 데코레이터(decorated)가 적용된 함수일 뿐입니다:

from livekit.agents import function_tool
import subprocess

...

Vapi도 웹훅(webhooks)을 사용하는 동일한 개념입니다. LLM이 도구를 호출하면 Vapi가 귀하의 백엔드로 JSON을 게시(post)하고, 핸들러(handler)가 CLI를 실행한 뒤, Vapi의 엔벨로프(envelope)에 stdout을 담아 반환합니다:

app.post("/vapi/tools", async (req, res) => {
  const { name, parameters } = req.body.message.toolCall;
  const args = ["nylas", "email", "list",
...

Retell, Bland.ai, 그리고 OpenAI Realtime은 모두 '스키마 정의(define-schema) → 서브프로세스로 디스패치(dispatch-to-subprocess) → JSON 반환(return-JSON)'이라는 일반적인 패턴을 따릅니다. 이 레시피는 왜 이 방식이 음성 런타임 옆에서 MCP 서버를 실행하는 것보다 나은지 명확하게 설명합니다. 음성 프레임워크는 JSON-RPC 피어(peer)가 아니라, JSON 블롭(blob)을 반환하는 함수 호출(function-call) 스타일의 도구를 기대하기 때문입니다. CLI를 통해 라우팅하는 것의 부수적인 이점은 모든 제공업체(provider) 간의 차이를 흡수한다는 점입니다. 따라서 그 뒤에 있는 권한(grants)이 Gmail, Microsoft 365, Exchange, Yahoo, iCloud, IMAP 또는 에이전트 계정(Agent Account)인지에 관계없이 동일한 도구가 작동합니다.

음성은 모든 UX 실수를 즉각적으로 드러낸다

레시피에서 제시하는 네 가지 규칙이며, 어느 것도 선택 사항이 아닙니다:

  1. 목록을 5개로 제한할 것. 50개의 메시지가 담긴 편지함을 소리 내어 읽는 데는 몇 분이 걸립니다. 기본값을 --limit 5로 설정하고, 사용자가 "더 보여줘"라고 말하게 하세요.
  2. 읽지 말고 요약할 것. 제목을 하나씩 낭독하는 대신, LLM이 "계약 건으로 Ada에게서 온 이메일 3통과 Rin으로부터 온 캘린더 초대장이 있습니다"와 같이 생성하도록 하세요.
  3. 전송 전에는 항상 확인할 것. 음성-텍스트 변환(Speech-to-text) 과정에서 수신자와 제목을 잘못 알아들어 잘못된 사람에게 잘못된 메일을 보내는 경우가 발생합니다. 에이전트가 수신자, 제목, 요지를 말하고, 명시적인 "응"이라는 답변이 있어야만 전송 도구(send tool)를 실행합니다:
   에이전트: "acme.test의 Ada에게 제목 'pricing', 본문 'I'm in'으로 보낼까요?"
   사용자: "응."
  1. 오류 번역. "Error 401: invalid grant"는 음성 응답이 아닙니다. 실패 상황을 "지금은 이메일에 접속할 수 없습니다. 다시 인증해야 할 수도 있습니다."와 같이 매핑하세요.

그리고 사실상 SLA (Service Level Agreement)라고 할 수 있는 규칙이 하나 있습니다. 모든 서브프로세스 (subprocess) 호출에는 타임아웃 (timeout)이 필요하며, 30초가 적절한 수치입니다. 음성 사용자는 1분 동안 기다려주지 않습니다. 프레임워크의 침묵 감지 (silence detection) 기능이 작동하여 대화가 무너져 버립니다. 일반적인 도구들에 대해 2초 미만의 왕복 시간 (round-trip)을 목표로 하세요. nylas email list --limit 5 --json은 이를 여유롭게 충족합니다. 타임아웃이 발생했을 때는 예외 (exception)를 그대로 노출하는 대신, 자연스러운 음성 폴백 (fallback)을 반환하세요.

전용 주소가 제품을 변화시키는 이유

후속 전송을 빌려온 사용자의 권한 (human grant) 대신 에이전트 자신의 계정을 통해 실행하면 세 가지가 동시에 개선됩니다:

  • 연속성 (Continuity). 발신자가 요약 내용에 답장을 보내면, 그 답장이 에이전트의 수신함에 도착합니다. 그러면 다음 상호작용(음성 또는 이메일) 시 전체 이력이 하나의 스레드 (thread)에 담기게 됩니다.
  • 감사 가능성 (Auditability). 에이전트가 보낸 모든 메시지가 에이전트의 보낸 편지함에 보관됩니다. 레시피 (recipe)에서는 모든 전송 내역(수신자, 제목, 실행 ID, 승인 출처)을 별도의 저장소에 기록할 것을 권장하며, 메일함은 이를 대조할 수 있는 근거 자료 (ground truth)를 제공합니다.
  • 다중 사용자 라우팅 (Multi-user routing)의 안정성. 많은 사용자를 지원하는 음성 플랫폼은 어차피 사용자별 권한 라우팅이 필요합니다. 각 명령마다 --api-key--grant-id를 전달하세요. 발신자 측의 권한은 변하더라도 에이전트의 발신 신원 (outbound identity)은 일정하게 유지됩니다.

빠른 답변

서브프로세스 도구 대신 MCP를 사용할 수 있나요? 만약 사용 중인 런타임 (runtime)이 실제로 MCP를 지원한다면 (예: Claude Code), 가능하며 문서에서 해당 경로를 별도로 다루고 있습니다. 대부분의 음성 런타임은 이를 지원하지 않기 때문에, 레시피는 기본적으로 서브프로세스 + --json 방식을 사용합니다.

캘린더는 어디에 해당하나요? 동일한 서브프로세스 패턴이 nylas calendar events list에도 적용됩니다. 따라서 "내일 일정이 있나요?"라는 질문은 새로운 통합 (integration)이 아니라, 데코레이터가 적용된 함수 (decorated function) 하나를 추가하는 것과 같습니다.

합리적인 첫 번째 마일스톤: 기존의 음성 스택 (voice stack)에 하나의 도구인 send_recap을 연결하십시오. 이때 테스트용 도메인의 에이전트 주소를 지정하고, 대화 스크립트에는 전송 전 확인 (confirm-before-send) 과정을 포함합니다. 직접 호출하여 요약본 (recap)을 요청한 뒤, 에이전트가 보낸 이메일에 답장을 보내보세요. 만약 답장이 에이전트의 편지함에 스레드 (thread)로 묶여 나타난다면, 루프 (loop)를 구현한 것입니다. 당신의 음성 에이전트는 무엇을 가장 먼저 보낼 것인가요 — 요약본 (recaps), 문서 (docs), 아니면 비밀번호 재설정 링크 (reset links)인가요?

AI 자동 생성 콘텐츠

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

원문 바로가기
0

댓글

0