쉬운 설명 · Architecture · 2026-09-28

말 한마디가 글자가 되기까지

마지막 업데이트 2026-09-28

사용자가 말하면 세 명의 받아쓰기 선수(H4 · Soniox · Daglo)가 동시에 받아 적고, 심판(Selector)이 가장 그럴듯한 답안 하나를 고릅니다. 그 한 줄이 대화 LLM으로 가서 대답이 됩니다.

기준: ppi-voice-pipeline-infra ASR_HANDOVER 09·10·15장 (2026-09-23 갱신)대상: PPI Multi-STT (voice-pipeline, STT provider = asr_server)

한 줄 요약. 음성 → Agent(말 구간 자르기) → Composite(세 STT에 뿌리고 답안 모으기) → Selector(A/B/C 중 한 글자로 고르기) → 전사 한 줄 → 대화 LLM → TTS → 다시 귀로.

01큰 그림

위 줄은 가는 길, 가운데는 받아쓰기 시험장, 왼쪽 세로줄은 돌아오는 길입니다.

🧒 사용자 마이크로 말함 LiveKit 서버 소리 배달부 LiveKit Agent VAD: 한 문장씩 자르기 Composite 시험 감독: 뿌리고·모으기 H4 우리 GPU · 말하는 중 Soniox 외부 · 말하는 중 Daglo 외부 · 다 듣고 한 번에 같은 16kHz 음성을 셋에게 Selector (Qwen3.5-9B) 답안 A·B·C 중 “B” 한 글자로 고르기 답안(후보) 제출 최종 전사 1줄 Composite → Agent로 대화 LLM 무슨 대답을 할까 TTS 글자 → 목소리

초록 굵은 선 = 실제 대화가 흐르는 길. 주황 = 외부 회사 API. 파랑 = 고르는 모델.

Selector는 대답하는 LLM이 아닙니다. 답안 중 하나를 고르기만 하고, 대답은 그 뒤의 대화 LLM이 만듭니다.

02H4는 어느 GPU로 가나

H4만 우리 GPU에서 돕니다. 빈 GPU를 찾아 주는 안내 데스크가 따로 있습니다.

Controller 안내 데스크 · 소리 안 만짐 ASR Redis GPU 목록 · 자리표 Composite 감독 Compatibility gw 통역: 옛 규격 ↔ H4 규격 H4 GPU 노드 L40S · 실제 받아쓰기 ② 음성은 여기로 직행 ① “빈 GPU 어디?” “3번 가세요” “나 준비됐어”

점선 = 자리 배정(작은 메시지). 굵은 선 = 음성.

Redis가 두 개입니다. PPI 세션 설정 Redis(웹 ↔ Agent)와 ASR 할당 Redis(Controller ↔ GPU)는 다른 것. 주소·인증을 서로 바꿔 넣으면 안 됩니다.

03한 발화의 일생

시간은 왼쪽 → 오른쪽. 세 선수는 출발 시각이 다릅니다.

start_utterance end_utterance final_transcript 🧒 말하는 중 audio_chunk · audio_chunk · audio_chunk … H4 Soniox Daglo 끝난 뒤 모아서 한 번에 (최대 30초) Selector

한 연결 안에서 발화는 한 번에 하나씩 처리됩니다. 사용자마다 연결(stream)은 따로입니다.

04누구 답을 쓰나

위에서부터 차례로 물어봅니다. 먼저 걸리는 줄이 답입니다.

1쓸 만한 답안이 하나뿐?그걸 씀
2답안이 글자까지 똑같음?그걸 씀
3답안이 서로 다름Selector가 고름
4Selector가 실패·엉뚱한 답·쉬는 중H4 → Soniox → Daglo 순
5전부 실패하거나 빈칸(판독 불가)
Soniox가 “정상인데 빈칸”을 돌려준 경우는 따로 표시됩니다(soniox_returned_blank). 정책이 unreadable인 환경에서는 Selector 답이 있어도 (판독 불가) 복구로 갑니다.

실패한 선수는 잠시 쉬게 합니다: 1 → 2 → 5 → 10초. 한 번 성공하면 초기화.

05얼마나 기다리나

시계는 답안 모으기 시작한 순간부터 잽니다. 말이 끝난 순간이 아닙니다.

0초 5초 15초 다 끝나면 바로 진행 5초에 답안 있으면 → 나머지 취소하고 진행 하나도 없으면 → 최대 15초까지 대기 Selector는 별도로 5초 예산 (그 안에서 최대 2번 시도)
이어말하기 checkpoint 경로는 예산이 다를 수 있습니다. 9/23 Staging 로그에서 Daglo가 약 300ms에 timeout으로 빠진 사례가 있습니다.

06Selector가 보는 쪽지

Selector는 아무것도 기억하지 않습니다(stateless). 그래서 Composite가 매번 앞 대화를 같이 넣어 줍니다.

지침 (system)한국어 받아쓰기 후보 중 실제 말에 가장 가까운 것을 고르세요
AI 턴오늘 뭐 하고 놀았어?
사용자 턴놀이터 갔어
AI 턴누구랑 갔어?
지금 사용자 턴 · 이미 확정된 앞부분엄마랑…
후보A. 그네 탔어  ·  B. 그네 탔어요  ·  C. 근데 탔어
→ “B”

위 대화 예시는 설명용으로 만든 것입니다.

07이어말하기: 잠깐 쉬어도 한 문장

“엄마랑… (쉼) …그네 탔어”를 두 문장으로 쪼개지 않기 위한 장치입니다.

엄마랑… 무음 600ms → VAD “끝” 남은 대기 ~900ms …그네 탔어 말 끝부터 1.5초 창 창 안에서 다시 말함 → 같은 발화 ID, revision +1 → 한 번만 LLM으로

08환경마다 시험장이 따로

환경감독(Composite)선수·심판 GPU한 줄
Devppi-voice-dev-i-compositeH4 1대 · Selector 1대 고정실험용. GPU 하나 죽으면 교체 전까지 0대
Stagingppi-voice-staging-i-composite전용 H4 1대 · Selector 1대Prod GPU 안 씀
Prodppi-voice-prod-compositeshared H4(최소 2) · Prod Selector(최소 2)2개 AZ, 자동 확장

연결은 전부 VPC 안쪽 로드밸런서 + TLS. 바깥으로 나가는 건 Soniox(WSS)·Daglo(HTTPS)뿐이고 NAT를 거칩니다.

Dev/Staging 실험이 Prod backend를 바꾸지 않습니다. ppi-voice-shared-* 이름은 옛 공유 설계의 흔적이라 이름만 보고 지우지 않습니다.

09근거

내용출처
전체 구성도·발화 계약·fallback·5/15초·selector 문맥ppi-voice-pipeline-infra/ASR_HANDOVER/09_서비스_아키텍처와_요청흐름.md
환경별 Composite·GPU 대수, Prod Agent :57 설정ASR_HANDOVER/10_운영서버_스펙과_리소스.md
격리 Dev/Staging, VAD 이어말하기ASR_HANDOVER/15_최근_진행과_VAD_이어말하기.md
후보 수집·selector·H4 재전송 구현Dubu-asr-api composite/service.py · candidates.py · selector.py · h4_retry.py
네트워크·LB·Redis·GPU ASGnetwork.tf · load_balancing.tf · redis.tf · model_nodes.tf · selector_nodes.tf · composite.tf

handover 문서 기준 정리이며, 이번에 AWS 실상태를 새로 조회하지는 않았습니다.

관련 문서