LLM Live 기기 Runtime
이 문서는 Live가 기기 안에서 실행되는 과정만 설명한다. Broker 설정은 현재 Broker, 조절값은 튜닝 파라미터, 사용자 정책은 음성 인터랙션 기획에서 관리한다.
동작 요약
- Wake-up 후 AudioRecord를 한 번 연다.
- 같은 16 kHz PCM을 로컬 판단과 Live Shadow에 전달한다.
- 제품 명령이면 기존
FunctionCallHandler를 실행하고 Shadow를 닫는다. SK_19면 Shadow를 Primary로 바꾸고 Gemini 응답 권한을 연다.- Gemini의 24 kHz PCM을 재생하며 Barge-in을 감시한다.
- 답변 후 5초 동안 다음 말이 없으면 마이크와 WebSocket을 함께 닫는다.
주요 용어
| 용어 | 의미 |
|---|---|
| PCM | 압축하지 않은 음성 데이터. 입력은 16 kHz, Gemini 응답은 24 kHz다. |
| VAD | 사람의 발화 시작과 끝을 찾는 기능이다. |
| Shadow | 로컬 분류 전 Live 연결과 입력만 준비하는 상태다. 답변 권한은 없다. |
| Primary | SK_19 판단 후 Gemini Live가 실제 답변을 담당하는 상태다. |
| WebSocket | 연결을 유지하며 음성과 이벤트를 계속 주고받는 통신이다. |
| Barge-in | 답변 중 사용자가 말하면 기존 답변을 멈추고 새 질문을 받는 기능이다. |
| Echo | 스피커의 답변 음성이 다시 마이크에 들어오는 현상이다. |
Runtime 흐름
Wake-up / F2
-> AudioRecord 시작
-> 동일 PCM: 로컬 STT/LLM + Live Shadow
-> 로컬 분류
-> 제품 명령: 기존 Device API, Shadow 종료
-> SK_19: Live Primary 전환
-> Gemini PCM 재생
-> Barge-in 또는 답변 소진
-> 다음 입력 대기
-> 5초 무발화 또는 종료 신호
-> AudioRecord + AudioTrack + WebSocket + UI 정리
상태와 종료
| 상태 | PCM 처리 | 상태를 벗어나는 조건 |
|---|---|---|
STREAMING_INPUT |
사용자 PCM을 Gemini에 전송 | Gemini VAD가 약 800ms 무음을 확인. F3는 약 1.1초 뒤 보조 |
WAITING_RESPONSE |
마이크는 유지하지만 PCM 전송은 중지 | 첫 응답, 20초 timeout 또는 오류 |
PLAYING_RESPONSE |
Gemini PCM 재생, 마이크로 Barge-in 감시 | Interrupted, 재생 소진 또는 종료 신호 |
LISTENING_FOR_NEXT_INPUT |
500ms 잔향 보호 후 입력 전송 재개 | 새 발화 또는 5초 무발화 |
F1은 발화 시작 힌트이고 F3는 현재 입력 턴 종료를 돕는다. 둘 다 Live 전체 세션을 직접 끝내는 신호로 사용하지 않는다. 700ms보다 짧은 F1-F3 구간은 발화 종료 근거에서 제외한다.
명시 종료, Gemini end_conversation, Device LLM_END, timeout과 오류는
LiveConversationTerminationGate로 모인다. 첫 종료 요청만 처리해 중복 AudioTrack 정지와
이전 턴의 지연 event가 새 상태를 덮는 것을 막는다.
maxSessionAgeMs는 14분으로 정의돼 있지만 RotateSession 후속 처리가 모든 경로에 연결되지
않았다. 14분 자동 교체는 아직 운영 보장 항목이 아니다.
Barge-in과 Echo
큰 소리만으로 답변을 끊으면 기기 자신의 음성을 사용자 발화로 오인한다. 현재는 로컬 Echo
gate가 전송 후보를 거르고, Gemini VAD의 Interrupted 또는 의미 있는 입력 전사가 최종 중단을
확정한다.
- 20ms 마이크 chunk의 최근 60ms를 분석한다.
- 음량과 유효 sample 조건을 통과한 입력을 후보로 둔다.
- 24 kHz 재생 PCM과 16 kHz 마이크 PCM을 0~1초 범위에서 시간 정렬한다.
- 파형 상관도와 5ms 음량 윤곽 상관도 중 큰 값을
echoSimilarity로 사용한다. 0.74이상이면ECHO_REJECTED로 기록하고 Gemini 전송을 차단한다.0.74보다 낮은 후보가 2 x 20ms 이어지면 후보 PCM까지 Gemini에 보낸다.- 중단이 확정되면 AudioTrack을 멈추고 남은 PCM queue를 비운다.
이 방식은 Echo를 신호에서 제거하는 완전한 AEC가 아니다. 재생 음성과 비슷한 입력을 막는 gate이므로 거리, 기기 볼륨과 주변 소음별 실기기 검증이 필요하다.
| 로그 | 해석 |
|---|---|
ECHO_REJECTED, echoSimilarity=0.91 |
스피커 답변과 매우 비슷해 전송 차단 |
CANDIDATE, echoSimilarity=0.15 |
Echo가 아닌 사용자 발화 후보 확인 중 |
CONFIRMED |
로컬 40ms 조건 통과 |
SERVER_VAD_FORWARDED |
Gemini VAD가 최종 발화 경계를 판단 중 |
기존 방식과 시간 비교
| 항목 | 기존 온디바이스 | Gemini Live |
|---|---|---|
| 답변 후 다음 입력 대기 | 필요한 멀티턴에서 최대 10초 | 모든 답변 뒤 5초 |
| 발화 종료 | F3 후 약 1.65초 | Gemini VAD 약 800ms, F3 보조 |
| 처음부터 무발화 | 약 3.5초 뒤 정리 | 다음 입력 대기 상태에서 5초 뒤 정리 |
| 녹음 | 한 입력 최대 10초 | 장치는 세션 동안 유지, VAD/F3는 현재 턴만 종료 |
| Live 응답 대기 | 해당 없음 | 기본 20초, Barge-in 이후 10초 |
대화 기록
1턴은 사용자 질문과 어시스턴트 답변 한 쌍이다.
| 경로 | 보존 범위 | 세션 종료 시 |
|---|---|---|
| Cloud REST | 완료 16턴 + 처리 중 user 1개, 최대 33개 message | 마지막 완료 1턴만 유지 |
| Gemini Live | 앱의 16턴 제한과 무관한 WebSocket 문맥 | 연결 종료와 함께 종료 |
| Broker | 별도 대화 기록 없음 | 1:1 중계 연결 해제 |
CloudConversationHistoryPolicy는 REST 요청 크기를 제한한다. 이 값을 바꿔도 Gemini Live의
문맥 길이는 바뀌지 않는다.
코드와 검증
| 역할 | 코드 |
|---|---|
| 전체 상태 연결 | ForegroundService.kt, LiveConversationController.kt |
| 마이크 fan-out | AudioCaptureLine.kt, GeminiLiveStreamingConsumer.kt |
| Echo 판정과 재생 | LivePlaybackEchoMatcher.kt, GeminiLiveAudioPlayer.kt |
| 응답과 tool | GeminiLiveResponseRouter.kt, GeminiLiveToolBridge.kt |
| 종료 | LiveStopCommandPolicy.kt, LiveConversationTerminationGate.kt |
JVM 테스트 뒤에도 Release APK 실기기에서 첫 응답 시간, Barge-in 정지 시간, Echo 오중단, 첫 음절 보존, 재생 연속성, 5초 종료와 반복 세션 자원 회수를 확인해야 한다.