Vapi의 오픈소스 대안. 드래그앤드롭으로 전화 받는·거는 AI 비서를 2분 만에 만들고, FastAPI + Next.js + Pipecat 풀스택을 셀프호스팅한다.
개념부터 천천히.
요즘 콜센터에 전화하면 "안녕하세요, AI 상담사 OO입니다"라고 응답하는 곳이 늘었다. 사람처럼 말하고, 듣고, 알아듣고, 다시 말한다. 이런 시스템을 음성 AI 에이전트(Voice AI Agent)라고 부른다. 예약 잡기, 보험 가입 문의 스크리닝, 리드 자격 검증, 고객 만족도 조사 같은 반복적인 통화 업무를 자동화한다.
기술적으로는 네 가지 부품이 합쳐진 파이프라인이다. 마이크로 들어온 음성을 텍스트로 바꾸고(STT), 그 텍스트를 LLM에게 보내서 답변을 받고(LLM), 답변 텍스트를 다시 음성으로 합성하고(TTS), 그걸 전화선이나 WebRTC로 상대방에게 보낸다. 이 4단계를 저지연(low-latency)으로 묶는 게 핵심 난제다. 사람은 0.5초만 침묵해도 어색하게 느끼기 때문.
여기서 등장한 Vapi는 음성 AI 에이전트를 SaaS(클라우드 서비스)로 파는 회사다. 미국에서 빠르게 성장 중이지만 코드가 공개돼 있지 않다(폐쇄형). 데이터를 외부 서버에 보내야 하고, 사용량에 따라 계속 비용이 든다. Dograh는 바로 이 Vapi의 오픈소스 대안으로 등장했다. 내 컴퓨터나 내 회사 서버에 직접 설치해서 쓰는 방식.
한 문장으로 줄이면.
웹 UI에서 노드를 드래그앤드롭으로 연결해 통화 흐름을 설계하고, docker compose up 한 줄로 PostgreSQL · Redis · MinIO · FastAPI · Next.js · TURN 서버가 한꺼번에 뜨는 풀스택 음성 AI 시스템.
STT/LLM/TTS 모델은 자유롭게 갈아 끼울 수 있어 OpenAI든, Deepgram이든, 자체 모델이든 마음대로 붙인다. "다운로드부터 첫 통화까지 2분"이 명시적 SLA.
시대적 배경 + 경쟁 우위.
2026년 들어 음성 에이전트는 텍스트 챗봇 다음의 격전지가 됐다. ElevenLabs Conversational AI, Vapi, Retell AI, Bland AI 같은 SaaS들이 시장을 선점하고 있는데, 다들 비공개 코드 + 분당 과금 모델이다. 통화 1분당 5~15센트씩 쌓이는 비용이 진입 장벽이자 운영 부담이 된다.
Dograh는 "코드 전체 공개 + 셀프호스팅 + BYOK"라는 세 카드를 동시에 들고 나왔다. YC 출신 + Exit 경력 창업자가 메인테이너라는 점도 지속성 시그널.
| 비교 항목 | Vapi (SaaS) | Dograh (셀프호스팅) |
|---|---|---|
| 코드 공개 | 비공개 | BSD-2-Clause 전부 공개 |
| 데이터 위치 | 외부 서버 | 내 MinIO / Postgres |
| 과금 모델 | 분당 5~15센트 | BYOK (모델사에 직접 결제) |
| 모델 교체 | SaaS가 허용한 어댑터만 | STT/LLM/TTS 자유 교체 |
| 디버깅 | 블랙박스 | 코드 추적 가능 |
| 설치 시간 | 회원가입 즉시 | docker compose up 2분 |
| 회귀 테스트 | 제공 안 함 | LoopTalk (AI 페르소나) |
통화 데이터가 외부 서버를 거친다(개인정보·의료·금융 도메인에서 문제). 분당 과금이라 통화량이 늘수록 비용이 선형 증가. 워크플로우 빌더의 동작을 디버깅할 수 없다(블랙박스). 자체 모델을 붙이고 싶어도 SaaS가 허용하는 어댑터만 쓸 수 있다.
BYOK는 "Bring Your Own Key" — 내 OpenAI 키, 내 Twilio 키를 직접 꽂아 쓰는 모델. 분당 과금이 사라지고 모델 회사에 내가 직접 결제한다. 통화 음성과 트랜스크립트는 모두 내 MinIO/Postgres에 남아 외부 유출이 없다. UI 워크플로우는 모두 코드로 표현되므로 동작 추적이 가능하다.
백엔드 · 프론트엔드 · 인프라 한 번에.
Dograh는 단일 언어 프로젝트가 아니다. Python 58.3% + TypeScript 38.2% — 백엔드가 약간 더 비중을 차지한다. 백엔드와 프론트엔드 둘 다 본격적으로 살펴봐야 전체 그림이 잡힌다.
전체 구조도 → 핵심 패턴 하나하나.
전체 시스템은 크게 3개의 평면으로 나뉜다 — 사용자가 봇을 설계하는 제어 평면(Control Plane), 실시간 통화가 흐르는 데이터 평면(Data Plane), 그리고 비동기 잡이 도는 백그라운드 평면.
┌────────────────────────────────────────────────────────────────────┐
│ 사용자 / 전화 발신자 │
└───────────────┬──────────────────────────┬─────────────────────────┘
│ (브라우저) │ (전화)
│ WebRTC │ PSTN/SIP
▼ ▼
┌──────────────────┐ ┌────────────────────┐
│ Next.js UI │ │ Twilio / Vonage │
│ (port 3010) │ │ (telephony) │
│ - 워크플로우 │ │ │
│ 빌더(xyflow) │ └─────────┬──────────┘
│ - 통화 모니터링 │ │ webhook
└────────┬─────────┘ │
│ HTTPS ▼
│ ┌──────────────────────────┐
│ │ cloudflared tunnel │
│ │ (외부→로컬 노출) │
│ └─────────┬────────────────┘
▼ ▼
┌────────────────────────────────────────────┐
│ FastAPI Backend (port 8000) │
│ │
│ ┌────────────────────────────────────┐ │
│ │ Pipecat Pipeline │ │
│ │ STT ─► LLM ─► TTS ─► 전송 │ │
│ │ ▲ │ │ │
│ │ └─ MCP tools ──┘ (외부 도구 호출) │ │
│ └────────────────────────────────────┘ │
│ │
└──┬─────────┬─────────┬─────────┬───────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌──────┐ ┌──────┐ ┌──────┐ ┌──────────┐
│ PG17 │ │Redis │ │MinIO │ │ ARQ │
│+vec │ │ │ │ S3 │ │ Worker │
│ │ │ │ │ │ │ (비동기) │
└──────┘ └──────┘ └──────┘ └──────────┘
메타 큐/캐시 녹음파일 잡 처리
음성 처리는 Pipecat 프레임워크(원래 Daily.co가 만든 OSS, Dograh가 fork해서 tuner-pipecat-sdk로 튜닝)를 통해 구현된다. Pipecat은 음성 입출력을 "프레임(Frame)" 단위로 흘려보내는 파이프라인 추상화를 제공한다.
공장의 컨베이어 벨트와 같다. 마이크 입력이 들어오면 → VAD(음성 활동 감지) 컨베이어 → STT 컨베이어 → LLM 컨베이어 → TTS 컨베이어 → 스피커 출력으로 흐른다. 각 단계는 독립적이고 교체 가능하다.
사용자가 UI에서 그리는 통화 흐름은 내부적으로 방향 비순환 그래프(DAG)로 저장된다. 노드는 "인사 → 의도 분류 → 분기 1/2/3 → 정리" 같은 대화 단위, 엣지는 노드 간 전이 조건. 프론트엔드는 @xyflow/react로 그래프를 시각화하고, 백엔드는 JSON으로 저장한 뒤 통화 중에 한 노드씩 활성화한다.
LLM/STT/TTS 제공자는 모두 추상 클래스(인터페이스)로 정의되고, 구체 구현은 어댑터로 갈아 끼운다. 기본 제공되는 Dograh 자체 API 키를 쓰다가, 운영 단계에서 자기 OpenAI 키로 바꾸는 게 환경변수 1줄 변경으로 끝난다.
Twilio가 통화 이벤트를 보낼 때는 인터넷에서 접근 가능한 공개 URL이 필요하다. 로컬 개발에서 보통 ngrok을 따로 깔아야 하는데, Dograh는 docker-compose에 cloudflared 컨테이너를 넣어 "docker compose up"만으로 외부 접근 URL이 자동 생성된다.
cloudflared 자동 터널, MinIO 자동 버킷 생성, Dograh가 발급한 임시 API 키 자동 주입 — 모두 신규 사용자가 환경 설정에 막혀 떠나는 걸 막기 위한 장치다. 오픈소스 프로젝트에서 "2분 SLA"를 지키려면 이 정도까지 자동화해야 한다는 좋은 사례.
어디에 뭐가 있는지.
dograh/ ├── api/ # 백엔드 (FastAPI) │ ├── requirements.txt # Python 의존성 │ ├── pyproject.toml # Python >=3.13,<3.14 요구 │ ├── .env / .env.test # 환경변수 (개발/테스트 분리) │ ├── services/ # 비즈니스 로직 모듈 │ ├── routes/ (추정) # FastAPI 엔드포인트 │ ├── models/ (추정) # SQLAlchemy 모델 │ ├── alembic/ (추정) # DB 마이그레이션 │ └── tests/ # pytest │ ├── ui/ # 프론트엔드 (Next.js 15) │ ├── package.json │ ├── src/ # 소스 루트 │ │ ├── app/ # App Router 페이지 │ │ ├── components/ # Radix + shadcn 컴포넌트 │ │ └── lib/ # 유틸 / API 클라이언트 │ └── .env # 프론트 환경변수 │ ├── pipecat/ # Pipecat 프레임워크 (git submodule) │ # ↑ 음성 파이프라인의 심장 │ ├── docs/ # Mintlify 기반 공식 문서 ├── evals/ # STT 벤치마크(stt/) + 시각화 도구(visualizer/) ├── scripts/ # 로컬 개발 헬퍼 셸 스크립트 ├── nginx/ # remote 배포용 HTTPS 설정 ├── config/coturn/ # WebRTC TURN 서버 설정 ├── .claude/ # Claude Code용 프로젝트 컨텍스트 │ ├── docker-compose.yaml # 9개 컨테이너 (기본 6개 + remote 프로필 3개) ├── docker-compose-local.yaml # 로컬 개발 전용 │ ├── CLAUDE.md → AGENTS.md # AI 에이전트용 프로젝트 안내 ├── AGENTS.md # 프로젝트 구조와 환경 설명 ├── CONTRIBUTING.md # 기여 가이드 └── CHANGELOG.md # release-please 자동 생성
pipecat/이 git submodule로 들어와 있다. 원본 Pipecat을 fork해서 Dograh 전용 튜닝을 가하면서도, 원본 업데이트를 추적할 수 있게 분리한 구조. 음성 파이프라인 코드의 "심장"이 별도 레포에 있다고 보면 된다. 또 CLAUDE.md가 @AGENTS.md로 리다이렉트되는 것도 흥미롭다. Claude Code뿐 아니라 Cursor, Aider 같은 다른 에이전트도 같은 컨텍스트 파일을 읽도록 표준화하려는 시도.
이 레포에서 무엇을 가져갈지.
api/의 endpoint 구조와 의존성 주입 패턴 — production-grade FastAPI의 모범실습: 내 FastAPI 프로젝트에 ARQ 워커 1개를 추가해 "5초 후 이메일 발송" 같은 지연 작업 구현
실습: 동기 SQLAlchemy 토이 프로젝트를 async로 마이그레이션. session 처리와 트랜잭션 경계가 가장 큰 변화
실습: 블로그 글 100개를 OpenAI embeddings로 벡터화 → pgvector 저장 → "비슷한 글 찾기" API
@xyflow/react + dagre 자동 레이아웃 = 거의 표준 솔루션실습: 자기만의 "조리 레시피 빌더"를 xyflow로 만들기 — 재료/조리 단계/완성 노드 연결
pipecat/ 서브모듈에서 추상화 설계를 배울 수 있다실습: Pipecat 공식 quickstart의 echo bot을 띄우고, STT만 Whisper로 교체
실습: 내 사이드 프로젝트 docker-compose에 healthcheck 추가로 의존성 체이닝 구현
@hey-api/openapi-ts로 FastAPI OpenAPI 스펙에서 TS 클라이언트 자동 생성npm run generate-client만 돌리면 프론트 타입 자동 업데이트노트북에서 돌릴 수 있나?
로컬 데모는 일반 노트북에서 충분히 돌아간다. LLM/STT/TTS는 Dograh가 발급한 클라우드 API 키를 기본으로 쓰기 때문에 GPU가 필요 없다. 기본 컨테이너 6개(postgres·redis·minio·api·ui·cloudflared)의 메모리 부담만 견디면 된다.
| 모드 | 최소 사양 | 외부 의존성 | 용도 |
|---|---|---|---|
| 로컬 개발 | 8GB RAM + Docker | Docker Compose v2 | 학습 · 봇 만들기 |
| 로컬 + BYOK | 위에 더해 OpenAI/Deepgram 키 | 외부 API 키 | 품질 비교 · 튜닝 |
| 프로덕션 | 2 vCPU + 4GB RAM + 도메인 | HTTPS + coturn + S3 | 실제 운영 |
난이도별 — Easy → Hard.
1) README의 curl 한 줄로 docker-compose up 2) localhost:3010 접속 3) "고객 불만 1차 응대" 봇 생성 4) Web Call로 직접 통화 5) 봇 대답의 어색한 부분 기록 — 이 어색함이 음성 에이전트의 핵심 과제다.
배우는 것: 컨테이너 6개 동시 기동(기본 프로필), 워크플로우 빌더 UI 기본, 음성 AI의 "어색함" 체감
1) "인사 → 의도 분류(예약/문의/취소) → 분기 처리 → 마무리" 구조를 노드 5개 이상으로 2) 분기 조건에 LLM이 어떻게 사용되는지 관찰 3) UI에서 노드 간 연결이 백엔드 DB에 어떻게 저장되는지 PostgreSQL을 직접 열어 확인
배우는 것: DAG 구조의 워크플로우 설계, LLM-as-router 패턴, JSON 직렬화
1) Dograh 기본 키 대신 자기 OpenAI / Deepgram 키를 환경변수에 주입 2) 봇 재실행 3) STT 응답 속도, LLM 답변 품질, TTS 자연스러움 비교 4) api/.env의 어떤 변수가 어디로 전달되는지 grep으로 코드 추적
배우는 것: Provider 추상화 패턴, 환경변수 전파 추적, 모델 품질 비교
1) ui/의 xyflow 노드 구현부 분석 2) 새로운 노드 타입 추가 — 예: "외부 API에 GET 요청 후 응답을 다음 노드로 전달" 3) 프론트의 노드 정의 + 백엔드의 노드 실행 핸들러 양쪽 모두 구현
배우는 것: 풀스택 기능 추가의 전체 흐름, xyflow 확장, FastAPI 핸들러 작성
1) evals/ 디렉토리 분석 2) LoopTalk 페르소나 5개 정의 (예: 화난 고객, 영어가 서툰 고객, 사연 길게 말하는 고객) 3) 봇과 실제 통화 → 통과율 측정 4) GitHub Actions로 봇 수정 시 회귀 테스트 자동 실행 CI 구성
배우는 것: AI 페르소나 설계, 음성 회귀 테스트, CI/CD 통합
주차별로 따라가면 Dograh 풀스택 마스터.
Whisper로 음성 받아쓰기 직접 돌려보기(CPU OK) / ElevenLabs API로 텍스트→음성 합성 100자 / VAD를 webrtcvad로 체감 / WebRTC가 P2P · TURN · STUN을 어떻게 쓰는지 개념 학습
Pipecat 공식 quickstart의 echo bot 띄우기 / STT → LLM → TTS 파이프라인 직접 조립 / Frame이 어떻게 흐르는지 디버그 모드로 추적 / 토이 봇에 인터럽션(말 끊기 감지) 추가
SQLAlchemy 2.0 async + Alembic 마이그레이션으로 작은 CRUD API / ARQ로 백그라운드 잡 1개 추가 / pgvector로 100개 문서 검색 API / FastMCP로 첫 MCP 서버 (LLM이 호출 가능한 도구 1~2개)
api/services/와 ui/app/ 코드 한 번 정독 / Pipecat 파이프라인이 백엔드에서 어떻게 활성화되는지 추적 / 커스텀 노드 타입 1개 추가(위 실습 4) / VPS에 docker-compose remote 프로필로 배포해 외부에서 통화
자주 마주칠 단어들.
tuner-pipecat-sdk로 사용.fastmcp는 파이썬 구현.공식 · 프레임워크 · 학습.