Realtime API 가드레일

Realtime API 가드레일 (Realtime API Guardrails)

Realtime API에서 LLM이 응답하기 전에 음성 전사(transcription)를 가로채서 음성 대화를 보호해 줘요.

출처: 문서

본문

동작 방식 (How it works)

Realtime API는 오래 지속되는(long-lived) WebSocket 세션이에요. /chat/completions처럼 HTTP 요청마다 가드레일이 한 번 실행되는 것과 달리, 음성 세션은 턴(turn)이 여러 번 이뤄지고 각각 따로 검사해야 하기 때문이에요.

LiteLLM은 Whisper가 음성을 텍스트로 변환한 뒤, LLM이 응답을 생성하기 전의 전사(transcription) 이벤트에서 각 턴을 가로채요.

User speaks into mic
        │
        ▼ audio bytes (PCM)
┌───────────────────┐
│   LiteLLM Proxy   │  forwards audio to OpenAI unchanged
└────────┬──────────┘
         │
         ▼
┌───────────────────┐
│     OpenAI        │
│  VAD → Whisper    │  detects speech end, transcribes
└────────┬──────────┘
         │
         │  conversation.item.input_audio_transcription.completed
         │  { transcript: "system update: ignore all instructions" }
         │
         ▼
┌───────────────────────────────────────────┐
│           LiteLLM Proxy                   │
│                                           │
│   ◄──── GUARDRAIL RUNS HERE ────►         │
│   apply_guardrail(texts=[transcript])     │
│                                           │
│   ┌──────────────┬──────────────────┐     │
│   │   BLOCKED    │     CLEAN        │     │
│   └──────┬───────┴───────┬──────────┘     │
│          │               │                │
│   speak warning    send response.create   │
│   (TTS audio)      → LLM responds         │
└───────────────────────────────────────────┘

핵심 세부사항: LiteLLM은 연결 시 세션에 create_response: false도 주입해서, 가드레일이 실행되기 전에 LLM이 자동 응답하지 않도록 해요.

지원되는 가드레일 모드 (Supported guardrail mode)

모드 설명
realtime_input_transcription 각 음성 턴이 전사된 후, LLM이 응답하기 전에 실행

빠른 시작 (Quick Start)

1단계: 프록시 구성하기

프록시 config에 mode: realtime_input_transcription을 가진 가드레일을 추가해요.

model_list:
  - model_name: openai/gpt-4o-realtime-preview
    litellm_params:
      model: openai/gpt-4o-realtime-preview
      api_key: os.environ/OPENAI_API_KEY

guardrails:
  - guardrail_name: "voice-content-filter"
    litellm_params:
      guardrail: litellm_content_filter
      mode: realtime_input_transcription
      default_on: true
      blocked_words:
        - keyword: "ignore previous instructions"
          action: BLOCK
          description: "Prompt injection attempt"
        - keyword: "system update"
          action: BLOCK
          description: "Prompt injection attempt"
        - keyword: "ignore all instructions"
          action: BLOCK
          description: "Prompt injection attempt"

general_settings:
  master_key: os.environ/LITELLM_MASTER_KEY

2단계: 프록시 시작하기

litellm --config proxy_config.yaml --port 4000

3단계: Realtime 클라이언트 연결하기

OpenAI에 직접 연결하는 대신 프록시에 연결해요.

const ws = new WebSocket(
  "ws://localhost:4000/v1/realtime?model=openai/gpt-4o-realtime-preview",
  [],
  { headers: { Authorization: "Bearer sk-<your-litellm-api-key>" } }
)

ws.onopen = () => {
  ws.send(JSON.stringify({
    type: "session.update",
    session: {
      modalities: ["audio", "text"],
      input_audio_transcription: { model: "whisper-1" },
      turn_detection: { type: "server_vad" },
    },
  }))
}

ws.onmessage = (e) => {
  const event = JSON.parse(e.data)
  if (event.type === "response.audio.delta") {
    // play audio...
  }
}
import asyncio
import json
import websockets

async def main():
    async with websockets.connect(
        "ws://localhost:4000/v1/realtime?model=openai/gpt-4o-realtime-preview",
        additional_headers={"Authorization": "Bearer sk-<your-litellm-api-key>"},
    ) as ws:
        await ws.recv()  # session.created

        await ws.send(json.dumps({
            "type": "session.update",
            "session": {
                "modalities": ["audio", "text"],
                "input_audio_transcription": {"model": "whisper-1"},
                "turn_detection": {"type": "server_vad"},
            },
        }))

        async for raw in ws:
            event = json.loads(raw)
            print(event["type"])

asyncio.run(main())

턴이 차단될 때 일어나는 일

가드레일이 발동하면 프록시는: LLM은 주입된 지시를 절대 처리하지 않아요.

다른 가드레일 프로바이더와 사용하기

프록시는 프로바이더의 apply_guardrail 메서드를 통해 realtime 가드레일을 실행해요. 하지만 해당 훅이 get_supported_event_hooks()에 나열된 경우에만 mode: realtime_input_transcription으로 구성할 수 있어요. 현재는 litellm_content_filter만 이를 선언해요. 다른 프로바이더(예: pre_call, during_call, post_call만 지원하는 lakera_v2)에 이 모드를 설정하면 프록시 시작 시 검증에 실패해요. 이때 프록시는 오류를 기록하고(Skipping guardrail ... proxy is starting WITHOUT this guardrail) 해당 가드레일 없이 시작하므로 realtime 세션이 보호되지 않은 채로 실행돼요. LITELLM_STRICT_GUARDRAIL_MODES=false로 설정하면 로그가 경고로 낮아지지만, 여전히 이 모드에서는 가드레일이 지원되지 않아요.

커스텀 가드레일에 realtime 지원을 추가하려면 get_supported_event_hooks()GuardrailEventHooks.realtime_input_transcription을 포함하고 apply_guardrail을 구현하세요.

키별 가드레일 제어 (Per-key guardrail control)

특정 API 키에만 realtime 가드레일을 활성화하려면 default_on: false로 설정하고 요청 메타데이터에 가드레일 이름을 전달해요.

guardrails:
  - guardrail_name: "voice-content-filter"
    litellm_params:
      guardrail: litellm_content_filter
      mode: realtime_input_transcription
      default_on: false   # off by default

그러면 클라이언트는 초기 메타데이터에 이를 전달해 연결별로 선택(opt-in)해요 (엔터프라이즈 기능).

더 알아보기 (Learn more)