아키텍처¶
Socket Backend는 WebSocket 연결, 엔진 라우팅, 외부 음성·대화 서비스, 공통 저장·후처리를
분리합니다. 세션별 상태는 WebSocketSessionManager가 소유하고, 실제 PCM은 한 번 선택된
ConversationEngine으로만 전달됩니다.
flowchart LR
C["클라이언트 또는<br/>WebRTC Gateway"]
W["/sohri<br/>인증 · 세션 · 계측"]
R["ConversationEngineRouter"]
S["기본 대화 경로<br/>EPD·DM·TTS 오케스트레이션"]
O["OpenAI Realtime 경로"]
E["EPD<br/>발화 감지 + STT"]
D["Graph Builder<br/>Dialog Manager"]
SR["Skill Runtime"]
SE["Sohri Skill Engine"]
T["TTS 프로바이더"]
AI["Vocal Biomarkers<br/>요약 LLM"]
DB[("PostgreSQL")]
M["Maumon<br/>선택 연동"]
C <-->|"JSON · PCM"| W
W --> R
R --> S
R --> O
S <-->|"PCM / END+STT"| E
S <-->|"세션 · 메시지"| D
D -->|"skill 실행"| SR
SR -->|"계약 변환"| SE
S <-->|"텍스트 · 오디오"| T
O <-->|"Realtime WebSocket"| OAI["OpenAI"]
S --> DB
O --> DB
DB <--> AI
DB -. "비동기 요약 회신" .-> M
계층별 책임¶
| 계층 | 주요 컴포넌트 | 책임 |
|---|---|---|
| 연결 | ApiKeyHandshakeInterceptor, WebSocketConnectionHandler |
credential 검증, 용량·IP 제한, 대화 행 생성, 연결 ACK |
| 라우팅 | ConversationEngineRouter |
세션별 엔진 최초 확정, PCM·취소·종료 위임 |
| 기본 경로 어댑터 | SohriConversationEngine, EpdConnectionManager |
입력 게이트 확인, EPD 지연 연결, PCM 순서 보존 |
| 턴 | EpdResponseProcessor, EndDetectProcessService |
바지인 단계, END 수락, USER 저장, DM/TTS 처리 |
| 출력 | DialogManagerIntegrationService |
응답 세대 확인, TTS·텍스트 송신, 스트리밍 수명주기 |
| Realtime | OpenAIRealtimeConversationEngine |
16→24 kHz 입력 변환, upstream 이벤트·오디오 중계 |
| 저장 | DmMessagePersistence, ConversationRecordSink |
USER/BOT/EPD 기록과 분석 작업 직렬화 |
| 종료 | DialogFinalizationService |
분석 flush, 대화·바이오마커 요약, 외부 회신 예약 |
SohriConversationEngine은 외부 Sohri Skill Engine이 아닙니다
SohriConversationEngine은 Socket Backend 안에서 기본 EPD·DM·TTS 경로를 구현하는
ConversationEngine 어댑터의 코드 이름입니다. 실제 Sohri Skill Engine은 Graph Builder가
Skill Runtime을 통해 호출하며, Socket Backend가 직접 호출하지 않습니다.
동시성 모델¶
- 블로킹 외부 I/O는 가상 스레드 실행기에서 수행합니다. EPD 연결과 송신은 각각
epd-connect-*,epd-send-*실행기로 분리됩니다. - 세션별 EPD 송신은 future tail로 직렬화합니다. 연결 준비 중 도착한 PCM도 READY 이후 순서대로 전송하되, 입력 gate epoch가 바뀐 프레임은 폐기합니다.
DmProcessingGuard는 사용자 턴을 한 번에 하나만 허용합니다. 확정 USER 턴과 DM/TTS 사이를 하나의 처리권으로 보호합니다.AudioInputGate는 소유권 토큰을 쓰는 pause입니다. END 처리와 TIMEOUT 처리가 겹쳐도 한 주체가 다른 주체의 pause를 열 수 없습니다.ResponseCancellationTracker는 발화 세대와 응답 세대를 별도로 관리합니다.cancel_response는 응답만 무효화하고 이미 확정된 발화의 END 처리는 취소하지 않습니다.
공통 상태와 수명주기¶
연결 제거는 SessionEndEvent를 발행합니다. 이 이벤트를 기준으로 엔진 연결, DM 처리권,
응답 세대, 입력 게이트, 무응답 타이머와 지표 상태를 정리합니다. 반면 턴 단위 정리는
TurnCompletedEvent와 END 처리의 finally가 담당합니다. 두 수명주기를 혼합하면 활성 세션을
정리하거나, 반대로 종료된 세션 상태를 남기는 문제가 생깁니다.