콘텐츠로 이동

바지인

바지인은 봇 TTS가 재생 중일 때 사용자가 말하기 시작하는 흐름입니다. Gateway가 실제 재생 상태와 duck 적용 경계를 알려 주고, EPD가 그 경계 이후 오디오로 후보를 확정하거나 취소합니다.

TTS 재생 중 SOHRI 바지인

sequenceDiagram
    participant C as Client
    participant G as WebRTC Gateway
    participant S as Socket Backend
    participant E as EPD
    participant D as DM
    participant DB as PostgreSQL

    S-->>G: TTS generation N 오디오
    G-->>C: TTS 재생
    G->>S: tts_playback_state(active=true)
    C->>G: 사용자 발화 PCM
    G->>S: PCM
    S->>E: PCM(ttsPlaybackActive=true)
    E-->>S: candidate(activityId)
    S-->>G: speech_candidate(activityId)
    G->>G: 실제 track에 duck gain 적용
    G->>S: tts_duck_applied(activityId, generation N, ...)
    S->>E: duck 적용 control

    alt EPD가 사용자 발화로 확정
        E-->>S: confirmed(activityId)
        S->>S: utterance generation 증가
        S->>S: response generation N 무효화
        S-->>G: speech_started(activityId)
        G->>G: generation N 재생 중단
        E-->>S: END + STT
        S->>DB: 새 USER 저장
        S->>D: 새 사용자 메시지
        Note over D,DB: 기존 DM 호출·USER/BOT 저장은 롤백하지 않음
        D-->>S: 새 응답
    else 후보 취소
        E-->>S: cancelled(activityId)
        S-->>G: speech_candidate_cancelled
        E-->>S: END + STT(있을 수 있음)
        opt cancelled-end.mode=PERSIST
            S->>DB: speech_phase=CANCELLED로 USER만 보존
        end
        Note over S,D: 취소 발화로 DM 턴은 진행하지 않음
    end

EPD candidate 뒤 tts_duck_applied는 필수 제어 경계입니다. EPD는 ACK 이후의 오디오를 검증하며 gain의 특정 값 자체로 확정하지 않습니다. ACK가 오디오 프레임 기준 기한 안에 오지 않으면 후보가 취소될 수 있습니다.

response generation 계약

기본 대화 경로의 취소는 트랜잭션 롤백이 아니라 출력 세대 무효화입니다.

  • 확정 발화가 시작되면 beginUtterance()가 기존 response generation을 증가시킵니다.
  • 이미 실행 중인 DM HTTP 요청은 계속될 수 있습니다.
  • DM 호출 전 저장한 USER, DM 응답과 함께 저장한 BOT/응답 행은 삭제하지 않습니다.
  • 레거시 TTS는 합성 후·송신 직전·송신 임계 구역에서 generation을 다시 확인합니다.
  • 스트리밍 TTS는 시작 직전과 모든 청크에서 generation을 확인합니다.
  • generation이 오래됐으면 오디오, dialog_response, 오류 프레임을 억제합니다.

이 방식은 이미 기록된 대화 사실을 보존하면서, 늦게 끝난 이전 답변이 다음 질문 위로 재생되는 것을 막습니다.

입력 gate와의 관계

입력 gate가 닫힌 DM/TTS 준비 구간에 candidate가 오면 activity를 억제하고 speech_candidate_cancelled를 보냅니다. gate는 재생 상태가 active라는 이유만으로 닫히지 않습니다. 따라서 실제 TTS 재생 중에는 PCM이 EPD로 가며 정상 바지인이 가능하지만, 새 응답을 만드는 임계 구간에는 중첩 USER 턴을 만들지 않습니다.

OpenAI Realtime 바지인

OpenAI 엔진은 provider의 semantic VAD와 interrupt_response=true를 사용합니다.

  • provider input_audio_buffer.speech_started를 받으면 로컬 출력 세대를 종료하고 realtime_playback_interrupt를 Gateway에 보냅니다.
  • 클라이언트의 cancel_response에 활성 출력이 있으면 upstream으로 response.cancel을 보냅니다.
  • Gateway가 실제 재생 종료 위치를 realtime_playback_truncated로 보내면 upstream으로 conversation.item.truncate를 보냅니다.
  • 취소된 출력은 tts_stream_end{status:"aborted", reason:"cancelled"}로 닫고 BOT 기록 대상으로 확정하지 않습니다.

취소된 EPD END

barge_in_phase=cancelled는 “발화가 아니었다”와 동의어가 아닙니다. 설정이 PERSIST이면 텍스트가 있는 END를 speech_phase=CANCELLED로 저장하되 DM과 TTS는 실행하지 않습니다. 분석·대화 요약·바이오마커 요약은 CONFIRMED 화이트리스트를 사용해 이 행을 제외합니다. DROP 모드도 가능하지만 두 모드 모두 결과 메트릭과 구조화 로그를 남깁니다.