TrendShift 딥다이브 · 2026-07-01

Rapida voice-ai 딥다이브
— 전화 한 통을 STT·TTS·VAD·LLM 14종 공급자로 오케스트레이션하는 오픈소스 음성 에이전트 플랫폼

Rapida(rapidaai/voice-ai)실시간 음성 대화 에이전트를 만드는 데 필요한 전화 연결·음성 인식·음성 합성·발화 감지·LLM 호출·관찰가능성을 하나의 자체 호스팅 플랫폼으로 묶은 Go 모노레포다. Twilio·Vonage·SIP 같은 통신 채널로 걸려온 전화를, 우선순위 채널 기반 이벤트 파이프라인이 잡아 Deepgram·ElevenLabs·Cartesia 등 14개 STT/TTS 공급자, 3개 VAD 엔진, 3개 발화종료 감지 방식, Claude·GPT·Gemini 등 LLM 공급자를 갈아 끼우며 대화를 이어간다. React UI(관리 콘솔) + Go 백엔드 4개 서비스(web/assistant/endpoint/integration) + Python 문서 처리 서비스로 구성되며, GPL-2.0(브랜딩 유지 조건부 추가 조항) 라이선스로 공개돼 있다. (저장소: rapidaai/voice-ai · Go(주력) · ★682 · GPL-2.0)
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

전화가 걸려오는 순간부터 AI 목소리가 응답하기까지, 그 사이 전부를 대신 조립해주는 물건.

핵심 메시지

"음성 AI 콜센터를 처음부터 짓지 말고,
이미 배선이 끝난 건물에 회사 로고만 붙여라."

전화 걸기·받기(통신사 연동), 사람 말을 텍스트로 바꾸기(음성인식), 텍스트를 사람 목소리로 바꾸기(음성합성), "이 사람이 말을 끝냈나?" 판단하기(발화 감지), LLM에게 무슨 말을 할지 물어보기 — 이 다섯 가지를 처음부터 각각 만들려면 수개월이 걸린다. Rapida는 이 다섯 조각을 이미 배선해 두고, 어떤 회사 제품을 쓸지만 설정 파일로 고르게 해준다.

Rapida는 voice AI 오케스트레이션 플랫폼이다. "오케스트레이션(orchestration)"이란 여러 개의 독립된 부품(통신 채널·음성인식·음성합성·LLM 등)이 정확한 순서로, 정확한 타이밍에 서로 데이터를 주고받도록 지휘하는 일을 말한다. 오케스트라 지휘자가 바이올린과 트럼펫에게 각각 언제 소리를 낼지 신호를 주듯, Rapida의 파이프라인은 "지금은 마이크 소리를 들을 차례" "지금은 AI가 답할 차례"를 관리한다.

이 레포가 특별한 이유는 "이걸 자체 서버에서, 자기 데이터로, 자기 브랜드로 돌릴 수 있게" 오픈소스로 공개했다는 점이다. README 첫 문장이 이를 명확히 한다 — Ownership(소유권) · Control(통제권) · Scale(확장성). 관리형 클라우드 서비스로 편하게 시작할 수도 있고, Docker Compose 하나로 자체 서버에 통째로 올릴 수도 있다.

용어
STT / TTS / VAD (첫 등장 정리)
STT (Speech-to-Text, 음성인식): 사람이 말한 소리를 글자로 바꾼다. "안녕하세요"라는 음성 파형 → "안녕하세요"라는 문자열.
TTS (Text-to-Speech, 음성합성): 그 반대. 글자를 사람 목소리처럼 들리는 소리로 바꾼다.
VAD (Voice Activity Detection, 발화 감지): 지금 오디오 신호 안에 "사람이 말하고 있는 구간"이 있는지 없는지를 판단한다. 무음 구간에 STT를 계속 돌리면 낭비이므로, VAD가 "여기부터 말소리 시작"이라고 먼저 알려준다.

2왜 주목받는가

LiveKit·Vapi·Pipecat이 이미 있는데, 왜 또 하나가 뜨는가.

음성 AI 오케스트레이션은 이미 붐비는 시장이다. LiveKit Agents(WebRTC 미디어 서버 기반, Python/Node SDK), Vapi(완전 관리형 SaaS, 코드 없이 대시보드로 설정), Pipecat(Daily.co가 만든 Python 파이프라인 프레임워크)가 대표적이다. Rapida가 트렌딩에 오른 이유는 이들과 겹치지 않는 지점을 정확히 찌르기 때문이다.

비교 대상일반적 접근Rapida의 접근
배포 형태Vapi처럼 완전 관리형 SaaS이거나, Pipecat처럼 코드 프레임워크만 제공(직접 서버·DB·관제 구축 필요)플랫폼 + 프레임워크 동시 제공 — Docker Compose로 관리 콘솔(UI)·DB·다중 서비스까지 한 번에 자체 호스팅
전화 채널대개 Twilio 하나만 1급 지원, 나머지는 직접 연동Twilio·Vonage·Exotel·Telnyx·Asterisk·순수 SIP 6종을 코드베이스에 이미 내장
공급자 전환공급자 교체 시 파이프라인 코드를 다시 짜야 하는 경우가 많음STT/TTS 14개, VAD 3개, EOS(발화종료) 3개를 설정 값 하나로 스위칭(팩토리 패턴)
관찰가능성별도 APM 도구를 직접 연결Datadog·OpenTelemetry·AWS X-Ray·Azure Monitor·OpenSearch·Google Cloud를 코드에 이미 내장, 토큰 사용량·통화 로그·툴 실행 로그까지 1급 개체로 기록
기존 방식의 한계
"직접 짜자니 너무 크고, SaaS를 쓰자니 통제가 안 된다"

음성 AI 콜센터를 자체 구축하려면 통신 프로토콜(SIP)·실시간 오디오 스트리밍·여러 AI 공급자 API·대화 상태 관리·관측 인프라까지 전부 손대야 한다. 반대로 완전 관리형 SaaS는 빠르지만 내 데이터가 어디 저장되는지, 어떤 모델이 실제로 호출되는지 통제하기 어렵고, 대량 트래픽에서는 비용도 커진다.

Rapida의 해결
오픈소스 코드 + 자체 호스팅 + 공급자 자유 선택

소스 전체가 공개돼 있어 무엇이 실제로 일어나는지 코드로 확인할 수 있고, 자체 인프라에 배포해 데이터를 직접 통제하며, 공급자(모델·STT·TTS)를 언제든 설정만으로 바꿀 수 있다. GPL-2.0이라 상업적 화이트라벨(로고 제거)이 필요하면 별도 상용 라이선스로 전환하는 구조다.

3기술 스택 전체 지도

Go 백엔드 4개 서비스 + Python 문서 서비스 + React 관리 콘솔 + 20종 넘는 AI 공급자 통합.

go.mod 기준 모듈명은 github.com/rapidaai, Go 1.25다. GitHub 언어 통계로는 Go 약 63%·TypeScript 약 26%·Python 약 9%이며, 실행 파일 4개(cmd/web·cmd/assistant·cmd/endpoint·cmd/integration)가 각각 독립 서비스로 뜬다.

백엔드 — Go 서비스 4종

서비스기본 포트역할
web-api9001(내부)조직·프로젝트·유저·자격증명(Vault) 관리 — 관리 콘솔의 백엔드
assistant-api9007핵심 — 실시간 통화 파이프라인. 전화 수신부터 STT/TTS/VAD/LLM/관측까지 전부 여기
endpoint-api9005텍스트 기반 LLM 엔드포인트(음성 없는 챗봇/API 형태) 관리
integration-api9004외부 LLM/임베딩 공급자(OpenAI·Anthropic·Gemini·Cohere·xAI 등) 자격증명 검증·호출 중계
document-api (Python)9010지식베이스 문서 처리 — 청킹·임베딩·RAG 인덱싱(선택 실행, --with-knowledge)

핵심 인프라 라이브러리 (Go)

영역라이브러리역할
웹 프레임워크Gin + gRPC + grpc-webHTTP 라우팅과 서비스 간 고성능 통신을 겸용
실시간 오디오Pion WebRTC v4(dtls·rtcp·rtp·interceptor)브라우저 위젯에서 오는 실시간 오디오 스트림 처리
SIP 전화sipgo(emiago)순수 SIP 프로토콜 스택 — 통신사 없이 직접 SIP 트렁크 연결
ORM/DBGORM + PostgreSQL + golang-migrate스키마 마이그레이션과 데이터 접근
캐시/큐Redis (go-redis)세션 상태·속도 제한
LLM SDKanthropic-sdk-go · openai-go(v1/v3) · OpenRouter · Cohere · mark3labs/mcp-go다양한 LLM/툴 공급자와 직접 통신, MCP(Model Context Protocol) 지원 포함
관측(Observability)OpenTelemetry(otel) · OTLP gRPC/HTTP exporter표준 텔레메트리 파이프라인, 여러 백엔드로 팬아웃
오디오 코덱g711(zaf) · go-audio-resampler(soxr 포함)전화망 표준 코덱(G.711 μ-law/A-law) 인코딩·리샘플링

프론트엔드 (관리 콘솔, ui/)

React 18.2 + TypeScript 4.6, 빌드는 CRA를 감싼 craco, 스타일은 Tailwind CSS 4. 상태 관리·폼·데이터 테이블 등을 자체 컴포넌트 라이브러리(components/carbon/ — IBM Carbon Design System 스타일 네이밍)로 구성했다. 대화 로그 뷰어, 텔레메트리 모달, 프롬프트 에디터, 실시간 미리보기 음성 에이전트 화면까지 포함된 대형 SPA다.

인프라 / 배포

도구정체역할
Docker Composedocker-compose.yml + docker-compose.knowledge.ymlpostgres·redis·nginx·ui·4개 API·(선택)opensearch+document-api를 한 번에 기동
Makefile28KB, make help로 전체 목록up-all·build-all·logs-*·rebuild-* 등 배포 전 과정을 명령 하나로
기본 이미지 6종docker/base/rapida-*.Dockerfilegolang(bookworm/alpine)·alpine·debian-slim·node-alpine·python — 서비스별 최적화된 베이스 이미지를 직접 빌드해 재사용
nginxAPI 게이트웨이외부에서 들어오는 요청을 각 내부 서비스로 라우팅(포트 8080 진입점)
gRPC + Protobufprotos/, buf.yaml서비스 간 계약을 .proto로 고정, talk-api·assistant-api·vault-api 등 다수의 내부 API 정의
용어
MCP (Model Context Protocol)
AI 모델이 외부 도구·데이터에 접근하는 표준 프로토콜(Anthropic 제안). Rapida는 mark3labs/mcp-go SDK로 이를 지원해, 어시스턴트가 외부 MCP 서버의 도구를 호출할 수 있게 한다. UI에도 tools/mcp/ 전용 설정 화면이 있다.

4아키텍처 심화 분석

우선순위 채널 기반 이벤트 파이프라인 — "패킷"이라는 작은 이벤트가 파이프를 타고 흐른다.

가장 먼저 전체 그림을 보자. 전화(또는 브라우저 마이크)에서 시작해 assistant-api 서비스 하나가 핵심 실시간 처리를 전담한다. 외부 전화망 → 텔레포니 채널 → 이벤트 파이프라인 → (VAD·발화종료·STT·LLM·TTS를 순서대로 거쳐) → 다시 전화망으로 음성이 나간다.

┌──────────────────────────────────────────────────────────────────────┐ │ 전화망 / SIP 트렁크 / 브라우저(WebRTC) │ │ Twilio · Vonage · Exotel · Telnyx · Asterisk · SIP · Web Widget │ └────────────────────────────────┬───────────────────────────────────────┘ │ 오디오 스트림(RTP/WebSocket) ▼ ┌───────────────────────────────┐ │ channel/telephony │ 통신 채널 정규화 │ (Inbound/Outbound Dispatcher) │ └────────────────┬────────────────┘ │ CallReceivedPipeline ▼ assistant-api 서비스 안 — 이벤트 파이프라인(Packet 기반) ┌──────────────────────────────────────────────────────────────────┐ │ genericRequestor.OnPacket() → adapter_router.Classify() │ │ │ │ │ ┌─────┴──────┬──────────┬──────────┬──────────┬──────────┐ │ │ ▼ ▼ ▼ ▼ ▼ ▼ │ │ Control Bootstrap Ingress Egress Data Background │ │ (즉시) (초기화) (입력) (출력) (부가작업) (낮은우선순위)│ │ └──────────────┴──────────┴──────────┴──────────┴──────────┘ │ │ │ 각 채널은 goroutine 1개가 처리 │ │ ▼ │ │ Denoise → VAD → EndOfSpeech → SpeechToText → (LLM 호출) → │ │ TextToSpeech → 사용자에게 전송 │ └──────────────────────────────────────────────────────────────────┘ │ ▼ ┌───────────────────────────────┐ │ observability.Recorder │ 통화 전체를 관측 채널로 팬아웃 │ (Datadog/OTLP/X-Ray/Azure...) │ └───────────────────────────────┘

코드 상에서 이 흐름은 단계별 파이프라인 구조체로 못 박혀 있다. channel/pipeline/types.go를 보면 통화 하나가 CallReceivedPipelineWebhookParsedPipelineAssistantResolvedPipelineConversationCreatedPipelineProviderAnsweringPipelineSessionConnectedPipeline 순서로 상태를 넘겨받는다. 각 단계는 Validate() 메서드로 "이 단계에 꼭 있어야 할 값이 다 채워졌는지"를 스스로 검증한다 — 앞 단계가 부실하면 다음 단계로 못 넘어가게 코드 레벨에서 강제하는 것이다.

흐름 워크드 예제 — "음성 한 턴이 들어와 응답이 나가기까지"

전화가 연결된 이후, 사용자가 한 마디 말하고 AI가 답하는 "한 턴"이 실제로 어떻게 도는지 손으로 따라가 보자. 해피패스(정상 경로)만 본다 — 에러 처리·재시도는 생략.

① 사용자가 말한다 → 오디오 프레임이 UserAudioReceivedPacket 으로 들어옴 ② (선택) 잡음 제거 → DenoiseAudioPacket 발행 → denoiser(krisp/rn_noise) 처리 → DenoisedAudioPacket 반환 ③ 발화 구간 탐지 → VadAudioPacket 발행 → vad.New()가 고른 엔진(silero/ten/firered)이 판단 → VadSpeechActivityPacket ("지금 사람이 말하는 중") 발행 ④ 말이 끝났는지 판단 → EndOfSpeechAudioPacket 발행 → end_of_speech 엔진(silence_based / livekit_eos / pipecat_smart_turn_eos)이 "턴이 끝났다" 판정 → EndOfSpeechPacket 발행 ← 이 시점부터 "사용자 발화 완료"로 취급 ⑤ 텍스트로 변환 → SpeechToTextAudioPacket 발행 → transformer.GetSpeechToTextTransformer() 로 고른 STT 공급자(Deepgram 등) 호출 → SpeechToTextPacket("안녕하세요, 예약 확인하고 싶어요") 발행 ⑥ LLM 호출 (integration-api 경유) → 대화 기록 + 시스템 프롬프트 + 등록된 도구 목록을 붙여 요청 → LLMResponseDelta 스트리밍으로 토큰 단위 응답 수신 ⑦ 답변을 음성으로 변환 → 문장 단위로 잘라 SpeakText 커맨드 발행 → transformer.GetTextToSpeechTransformer() 로 고른 TTS 공급자 호출 → TextToSpeechAudioPacket(오디오 바이트) 발행 ⑧ 사용자에게 재생 → 전화망/WebRTC로 오디오 프레임 전송 → observability.Recorder 가 전 과정의 지연시간·토큰 사용량· 이벤트를 기록(RecordEvent/RecordLog/RecordWebhook)

이 8단계 각각이 "Packet(패킷)"이라는 작고 이름 붙은 이벤트로 표현된다는 게 이 레포의 핵심 설계다. internal_type.Packet은 인터페이스일 뿐이고(ContextId()·PacketName() 두 메서드만 요구), UserAudioReceivedPacket·SpeechToTextPacket·EndOfSpeechPacket 같은 60개 넘는 구체 타입이 이를 구현한다. 코드 주석에 명명 규칙이 명시돼 있다 — 커맨드(동사로 시작, 예: ExecuteLLM·SpeakText)는 "무언가를 하라"는 지시, 이벤트(과거형/명사, 예: SpeechToText·EndOfSpeech)는 "무언가 일어났다"는 보고다.

핵심 설계 패턴 ①
우선순위 채널 라우팅 (Priority Channel Dispatch)
모든 패킷은 adapter_router.Classify()가 6개 채널(Control·Bootstrap·Ingress·Egress·Data·Background) 중 하나로 분류하고, 각 채널은 자기 전용 goroutine에서 순서대로 처리된다. "통화 종료" 같은 긴급 제어 신호가 TTS 오디오 재생 같은 대량 데이터 처리에 밀려 늦게 처리되는 사고를 막는다 — 병원 응급실이 중증도별로 대기줄을 나누는 것과 같은 이치.
핵심 설계 패턴 ②
공급자 팩토리 패턴 (Provider Factory)
VAD·STT·TTS·발화종료감지 등 "교체 가능해야 하는 부품"은 전부 같은 모양의 New(opts ...Option) (Interface, error) 팩토리 함수를 가진다. 내부에서 문자열 하나(microphone.vad.provider 같은 설정 키)로 switch해 실제 구현체를 고른다. 새 공급자를 추가해도 파이프라인 코드는 건드릴 필요가 없다 — 이 인터페이스만 만족하면 끼워 넣을 수 있다.

5디렉토리 구조 해부

서비스 4개 + 공유 패키지 + SDK/예제까지, 큰 그림에서 작은 그림으로.

voice-ai/ ├── api/ 모노레포의 심장 — Go/Python 서비스 4+1개 │ ├── assistant-api/ ★ 핵심 — 실시간 통화 파이프라인 전부 │ │ ├── internal/channel/ 텔레포니(6종)·webrtc·pipeline·adapters │ │ ├── internal/transformer/ STT/TTS 14개 공급자별 폴더 │ │ ├── internal/vad/ 발화감지 3개 엔진(silero/ten/firered) │ │ ├── internal/end_of_speech/ 턴 종료 판정 3방식 │ │ ├── internal/denoiser/ 잡음제거(krisp/rn_noise) │ │ ├── internal/llm/ LLM 호출·agentkit·websocket 스트리밍 │ │ ├── internal/observability/ 이벤트·메트릭·수집기(collectors/) │ │ └── sip/ SIP 프로토콜 자체 스택(inbound/outbound) │ ├── document-api/ (Python) 지식베이스 RAG — 청킹/임베딩/벡터DB │ ├── endpoint-api/ 텍스트 전용 LLM 엔드포인트 관리 │ ├── integration-api/ 외부 LLM/임베딩 공급자 자격증명·호출 │ └── web-api/ 조직/프로젝트/유저/Vault(비밀키) 관리 ├── pkg/ 서비스 4개가 공유하는 라이브러리 │ ├── plugins/ 캘린더·이메일·SMS 플러그인(공급자별 구현) │ ├── ciphers/ keyrotators/ 암호화·키 순환(보안 공통 로직) │ └── telemetry/providers/ Datadog/OTLP/X-Ray/Azure/GCP 어댑터 ├── cmd/ 4개 서비스의 진입점(main 함수) ├── protos/ gRPC 서비스 계약(.proto → .pb.go) ├── ui/src/ React 관리 콘솔 │ ├── app/pages/ 어시스턴트·엔드포인트·지식베이스·활동로그 │ ├── providers/ UI 쪽 공급자 설정 스키마(20+ 폴더) │ └── llm-tools/ 어시스턴트가 쓸 수 있는 툴 정의(UI 측) ├── sdks/ Go·Node·Python·React 클라이언트 SDK(서브모듈) ├── examples/ golang/nodejs/python/react/widget 샘플 ├── docker/ 서비스별 Dockerfile + 베이스 이미지 6종 ├── .claude/skills/ .codex/skills/ AI 에이전트용 통합 가이드 스킬 9종(§6 참고) ├── docker-compose.yml Makefile go.mod AGENTS.md CLAUDE.md
파일/폴더역할
api/assistant-api/internal/adapters/internal/dispatch.go패킷을 우선순위 채널로 분류·라우팅하는 핵심 진입점(99줄, 작지만 전체 흐름의 중심)
api/assistant-api/internal/type/packet.go60개 넘는 패킷 타입이 정의된 이벤트 어휘집 — 이 레포의 "동사·명사 사전"
api/assistant-api/internal/channel/pipeline/통화 단계별 상태 구조체(types.go)와 실제 실행 로직(dispatcher/session)
api/assistant-api/internal/transformer/transformer.goSTT/TTS 공급자 팩토리 — 새 음성 공급자를 추가할 때 가장 먼저 볼 파일
AGENTS.md / CLAUDE.mdAI 코딩 에이전트가 이 레포에서 작업할 때 지켜야 할 파일 경계·테스트 규칙을 정의
주목할 관행
AI 에이전트 온보딩을 코드로 문서화
.claude/skills/.codex/skills/ 밑에 stt-integration·tts-integration·vad-integration·telephony-integration·system-understanding 등 9개 "스킬" 폴더가 있다. 각 스킬은 "새 STT 공급자를 추가하려면 어느 파일을 봐야 하는지"를 명시한 매뉴얼이다. 사람 신입 개발자용 온보딩 문서를 AI 코딩 에이전트(Claude Code·Codex)가 그대로 읽고 따라가도록 설계한 것 — 이 레포를 딥다이브하는 이 문서도 실제로 그 스킬 파일들을 참고해 작성됐다.

6학습 포인트 (기술별)

이 레포에서 뽑아 갈 수 있는 실전 기술 6가지.

포인트 1

이벤트 기반 아키텍처를 Go 인터페이스로 실전 구현

패킷 하나가 ContextId()·PacketName() 두 메서드만 요구하는 얇은 인터페이스라는 점이 핵심이다. 새 이벤트 타입을 추가해도 기존 라우팅 코드는 건드릴 필요가 없다(개방-폐쇄 원칙). api/assistant-api/internal/type/packet.go를 정독하면 "실무에서 이벤트 이름을 어떻게 짓는가"(커맨드=동사, 이벤트=과거형)까지 배울 수 있다.

포인트 2

팩토리 패턴으로 20개 넘는 외부 API를 갈아 끼우기

transformer.go·vad.go·end_of_speech.go 세 파일 모두 같은 뼈대를 쓴다 — 옵션 구조체 + 함수형 옵션(functional options) + switch 팩토리. 아래는 VAD 팩토리를 단순화한 형태다.

// 함수형 옵션 패턴: New(옵션...) 형태로 유연하게 구성
func New(opts ...Option) (internal_type.VoiceActivityDetectorExecutor, error) {
    options := &options{ctx: context.Background()}
    for _, opt := range opts {
        opt(options)
    }
    provider, _ := options.options.GetString("microphone.vad.provider")
    switch VADIdentifier(provider) {
    case FIRERED_VAD:
        return internal_vad_firered.New(...)
    case TEN_VAD:
        return internal_vad_ten.New(...)
    case SILERO_VAD:
        return internal_vad_silero.New(...)
    default:
        return internal_vad_silero.New(...) // 기본값도 명시적으로 정함
    }
}
포인트 3

단계별 파이프라인 상태를 구조체로 강제하기

channel/pipeline/types.go의 각 파이프라인 구조체(CallReceivedPipeline 등)는 자체 Validate() bool을 갖고 있다. 통화 처리 도중 "이 단계에 필요한 값이 비어 있다"는 사고를 컴파일 타임이 아니라 런타임 초입에서 빠르게 잡아낸다 — 상태 기계(state machine)를 타입으로 표현하는 실전 예제.

포인트 4

전화망 프로토콜 6종을 한 코드베이스에서

channel/telephony/internal/ 밑에 asterisk·exotel·sip·telnyx·twilio·vonage가 각자 폴더를 갖고 있고, 순수 SIP는 sip/자체 SIP 스택(inbound/outbound/registration/middleware)까지 구현돼 있다. 전화 시스템(SIP·RTP·오디오 코덱 G.711)을 실무 코드로 배우기 좋은 자료다.

포인트 5

관측가능성(Observability)을 나중에 붙이지 말고 처음부터 1급 개체로

observability.RecorderRecordEvent·RecordLog·RecordWebhook·RecordMetadata 같은 구조화된 레코드 타입을 받아, 등록된 여러 collectors(요청 로그·툴 로그·webhook·telemetry·billing 등)로 팬아웃한다. "일단 기능부터 만들고 로그는 나중에"가 아니라, 파이프라인 설계 단계부터 관측을 끼워 넣은 구조를 볼 수 있다.

포인트 6

모노레포에서 언어를 섞어 쓰는 기준

Go(성능이 중요한 실시간 오디오/네트워크)·Python(document-api, RAG·임베딩 생태계가 Python에 풍부)·TypeScript(관리 콘솔 UI)로 역할을 명확히 나눴다. "왜 이 서비스는 이 언어로 짰을까"를 각 서비스의 책임과 대조해보면 언어 선택 기준을 배울 수 있다.

실습 아이디어: api/assistant-api/internal/vad/vad.go의 팩토리 패턴을 복사해, 내가 좋아하는 언어로 "가짜 STT 공급자 3개를 설정 값으로 스위칭하는" 미니 스켈레톤을 만들어 보라.

7시스템 요구사항

실시간 음성이라 지연시간과 리소스 여유가 중요하다.

항목요구사항
필수 도구Docker & Docker Compose (README 1순위 요구사항)
메모리16GB+ RAM 권장 — postgres·redis·nginx·ui·4개 Go 서비스를 동시에 띄우기 때문(지식베이스까지 켜면 OpenSearch도 추가)
Go 버전(로컬 빌드 시)Go 1.25 이상 (go.mod 명시)
Node/UI 빌드 시Node.js 14+ (package.json engines), yarn
DB/캐시(Docker 없이 로컬 실행 시)PostgreSQL, Redis, (선택)OpenSearch를 별도로 준비해야 함
네트워크텔레포니 공급자(Twilio 등) 웹훅을 받으려면 외부에서 접근 가능한 URL 필요(ngrok 등으로 로컬 테스트 가능)
지연시간(운영 시 고려)실시간 대화형 음성은 체감 지연(latency)이 수백 ms를 넘으면 부자연스럽다 — STT/LLM/TTS 각 단계의 응답 속도, 선택한 공급자의 리전이 실사용 품질에 직결
주의
"일단 Docker Compose로 띄워보자"가 생각보다 무겁다

4개 API 서비스 + DB + 캐시 + 리버스프록시 + React 빌드까지 한 번에 올라가므로, 저사양 노트북에서는 make up-all이 오래 걸리거나 메모리 부족으로 일부 서비스가 죽을 수 있다. 처음에는 make up-dbmake up-assistant처럼 서비스를 하나씩 켜보는 편이 디버깅에 유리하다.

8직접 해볼 수 있는 실습 과제

Docker로 띄워보는 것부터 파이프라인에 새 부품을 끼우는 것까지.

실습 1

Docker Compose로 전체 플랫폼 로컬 기동 난이도 ★☆☆ 입문

git clonemake setup-local && make build-allmake up-all 순서로 실행하고, http://localhost:3000에서 관리 콘솔이 뜨는지 확인한다. docker compose ps로 어떤 서비스가 몇 개 컨테이너로 떴는지 살펴보라. 목표: 4개 API + DB + 캐시 + UI가 실제로 몇 개의 프로세스로 나뉘는지 눈으로 확인.

실습 2

공급자 자격증명 없이 STT/TTS 팩토리 코드만 추적 난이도 ★☆☆ 입문

API 키가 없어도 코드는 읽을 수 있다. transformer.goGetTextToSpeechTransformer()를 열어 지원 공급자 14개를 나열해보고, 그중 하나(예: internal_transformer_deepgram 폴더)를 열어 실제 HTTP/WebSocket 호출이 어디서 일어나는지 찾는다. 목표: "설정 문자열 하나가 실제 코드의 어느 지점으로 연결되는지" 추적하는 연습.

실습 3

패킷 흐름에 로그 한 줄 심어 관찰하기 난이도 ★★☆ 중급

로컬에서 assistant-api를 빌드해 실행하고(go build -o bin/assistant ./cmd/assistant), adapters/internal/dispatch.godispatch() 함수에 log.Printf("packet: %s", p.PacketName()) 한 줄을 추가해본다. 텍스트 기반 endpoint-api로 간단한 요청을 보내며 어떤 순서로 패킷이 찍히는지 관찰한다. 목표: §4에서 설명한 8단계 흐름이 실제 로그에서도 그대로 보이는지 검증.

실습 4

새 VAD 공급자를 "가짜"로 하나 추가해보기 난이도 ★★★ 고급

api/assistant-api/internal/vad/internal/silero_vad/ 폴더 구조를 참고해, 실제 모델 없이 "3초에 한 번씩 무조건 말했다고 판정하는" 더미 VAD 구현체를 만들고 vad.goswitch문에 새 케이스로 등록해본다. .claude/skills/vad-integration/ 스킬 문서를 함께 참고하면 이 레포가 정의한 "정석 추가 절차"를 그대로 따라갈 수 있다. 목표: 팩토리 확장점을 손으로 직접 넓혀보기.

실습 5

SIP 트렁크로 순수 SIP 통화 흐름 재현 난이도 ★★★ 고급

api/assistant-api/sip/의 inbound/outbound/registration 코드를 읽고, 무료 SIP 트렁크 테스트 서비스(또는 로컬 Asterisk/FreeSWITCH)로 실제 통화를 한 번 걸어본다. sip/testdata/freeswitch/에 테스트용 설정 예시가 있다. 목표: 전화망 신호(SIP INVITE 등)가 어떻게 Rapida 내부 이벤트로 번역되는지 끝까지 추적.

9관련 기술 심화 학습 로드맵

음성 AI 오케스트레이션을 처음부터 체계적으로 익히는 5주 코스.

주차주제학습 자료
1주차음성 AI 기초 개념(STT/TTS/VAD/EOS) + Rapida 로컬 실행이 문서 §1·§4 + README + make up-all 실습
2주차Go 이벤트 기반 아키텍처 + 함수형 옵션 패턴packet.go·vad.go·transformer.go 정독, Go 공식 문서의 "Functional Options" 패턴
3주차실시간 통신 프로토콜(SIP·RTP·WebRTC)sip/·channel/webrtc/ 코드 + Pion WebRTC 공식 예제
4주차LLM 오케스트레이션 + MCP + 대화 상태 관리internal/llm/·internal/agent/ 코드 + modelcontextprotocol.io 문서
5주차관측가능성 설계(OpenTelemetry) + 나만의 미니 오케스트레이터 만들기observability/ 코드 + OpenTelemetry Go 공식 튜토리얼로 축소판 구현

10핵심 키워드 사전

본문에 나온 용어 빠른 참조.

용어의미
STT (Speech-to-Text)음성인식. 사람 목소리 → 텍스트. Deepgram·Azure·Google 등 14개 공급자를 팩토리로 스위칭.
TTS (Text-to-Speech)음성합성. 텍스트 → 사람 목소리. ElevenLabs·Cartesia·Rime 등 지원.
VAD (Voice Activity Detection)발화 감지. 오디오에서 "말하는 구간"을 찾아낸다. silero/ten/firered 3개 엔진 내장.
EOS (End of Speech, 발화종료 감지)"사용자가 말을 다 끝냈다"를 판정하는 로직. 너무 빠르면 말 끊고, 너무 느리면 답변이 늦다 — 침묵 기반·LiveKit·Pipecat Smart-Turn 3방식 지원.
턴 테이킹 (Turn-taking)대화에서 "누가 말할 차례인가"를 주고받는 과정. VAD+EOS가 함께 이를 판정한다.
바지 인 (Barge-in)AI가 말하는 도중에 사용자가 끼어들어 말하는 상황. InterruptionDetectedPacket·TextToSpeechInterruptPacket으로 처리.
오케스트레이션 (Orchestration)여러 독립 부품(통신·STT·TTS·LLM)이 정확한 순서·타이밍으로 협력하도록 지휘하는 일. Rapida의 핵심 정체성.
패킷 (Packet)이 레포에서 파이프라인을 흐르는 최소 이벤트 단위. ContextId()·PacketName()만 요구하는 얇은 인터페이스.
팩토리 패턴 (Factory Pattern)설정 값 하나로 실제 구현체(STT 공급자 등)를 골라 생성하는 구조. 이 레포 전역에서 반복되는 핵심 설계.
함수형 옵션 패턴 (Functional Options)New(WithLogger(l), WithContext(ctx))처럼 옵션 함수를 가변 인자로 받아 구조체를 유연하게 구성하는 Go 관용구.
MCP (Model Context Protocol)AI가 외부 도구를 표준화된 방식으로 호출하는 프로토콜. mark3labs/mcp-go로 지원.
Pion WebRTCGo로 작성된 WebRTC 구현체. 브라우저 위젯의 실시간 오디오를 서버가 직접 받는 데 사용.
sipgoGo SIP 프로토콜 스택. 통신사 API 없이도 순수 SIP로 전화를 걸고 받을 수 있게 해준다.
우선순위 채널 라우팅패킷을 Control/Bootstrap/Ingress/Egress/Data/Background 6개 goroutine 채널로 나눠 긴급도별로 처리하는 방식.

11참고 링크