vLLM 워커에 요청 보내기

vLLM 워커에 요청 보내기

vLLM 워커는 다른 RunPod Serverless 엔드포인트와 같은 /run, /runsync 연산을 써요. 차이는 입력 형식이에요. 텍스트 생성을 위해 vLLM이 기대하는 건 프롬프트, 메시지, 샘플링 파라미터랍니다.

출처: vLLM 워커에 요청 보내기

입력 형식

메시지(Messages, 채팅 모델)

instruction-tuned 모델에 쓰는 형식이에요. 워커가 모델의 채팅 템플릿을 자동으로 적용해 줘요.

{
  "input": {
    "messages": [
      {"role": "system", "content": "You are a helpful assistant."},
      {"role": "user", "content": "What is the capital of France?"}
    ],
    "sampling_params": {
      "temperature": 0.7,
      "max_tokens": 100
    }
  }
}

프롬프트(Prompt, 텍스트 완성)

베이스 모델이나 채팅 템플릿 없이 원문 텍스트를 줄 때 쓰는 형식이에요.

{
  "input": {
    "prompt": "The capital of France is",
    "sampling_params": {
      "temperature": 0.7,
      "max_tokens": 50
    }
  }
}

프롬프트에 모델의 채팅 템플릿을 적용하고 싶다면 "apply_chat_template": true를 추가해요.

요청 보내기

비동기(/run) — 백그라운드에서 처리되는 잡을 제출하고 /status/{job_id}를 폴링해 결과를 받아요.

import requests

response = requests.post(
    "https://api.runpod.ai/v2/ENDPOINT_ID/run",
    headers={
        "Authorization": "Bearer RUNPOD_API_KEY",
        "Content-Type": "application/json"
    },
    json={
        "input": {
            "messages": [{"role": "user", "content": "Explain quantum computing."}],
            "sampling_params": {"temperature": 0.7, "max_tokens": 200}
        }
    }
)

job_id = response.json()["id"]
print(f"Job ID: {job_id}")

# Poll for results
status = requests.get(
    f"https://api.runpod.ai/v2/ENDPOINT_ID/status/{job_id}",
    headers={"Authorization": "Bearer RUNPOD_API_KEY"}
)
print(status.json())

동기(/runsync) — 한 번의 요청으로 완전한 응답을 기다려요.

import requests

response = requests.post(
    "https://api.runpod.ai/v2/ENDPOINT_ID/runsync",
    headers={
        "Authorization": "Bearer RUNPOD_API_KEY",
        "Content-Type": "application/json"
    },
    json={
        "input": {
            "messages": [{"role": "user", "content": "Explain quantum computing."}],
            "sampling_params": {"temperature": 0.7, "max_tokens": 200}
        }
    }
)

print(response.json())

스트리밍

완전한 응답을 기다리는 대신 토큰이 생성되는 대로 받아 볼 수 있어요.

import requests
import json

# Submit with streaming enabled
response = requests.post(
    "https://api.runpod.ai/v2/ENDPOINT_ID/run",
    headers={
        "Authorization": "Bearer RUNPOD_API_KEY",
        "Content-Type": "application/json"
    },
    json={
        "input": {
            "prompt": "Write a short story about a robot.",
            "sampling_params": {"temperature": 0.8, "max_tokens": 500},
            "stream": True
        }
    }
)

job_id = response.json()["id"]

# Stream results
stream_url = f"https://api.runpod.ai/v2/ENDPOINT_ID/stream/{job_id}"
with requests.get(stream_url, headers={"Authorization": "Bearer RUNPOD_API_KEY"}, stream=True) as r:
    for line in r.iter_lines():
        if line:
            print(json.loads(line))

샘플링 파라미터

모델이 텍스트를 어떻게 생성할지 제어하는 파라미터예요. 요청의 sampling_params 객체에 넣어요.

공통 파라미터

Parameter Type Default Description
max_tokens int 16 생성할 최대 토큰 수
temperature float 1.0 샘플링 무작위성. 낮을수록 결정적
top_p float 1.0 고려할 상위 토큰의 누적 확률
top_k int -1 고려할 상위 토큰 수. -1 = 전부
stop string or list None 이 문자열이 나오면 생성 중단
presence_penalty float 0.0 출력에 등장한 토큰 페널티
frequency_penalty float 0.0 빈도 기반 토큰 페널티

고급 파라미터

Parameter Type Default Description
n int 1 생성할 출력 시퀀스 수
best_of int n 이만큼 생성 후 상위 n 반환
repetition_penalty float 1.0 반복 토큰 페널티. 1보다 크면 반복 억제
min_p float 0.0 상위 토큰 대비 최소 확률 임계값
min_tokens int 0 EOS 허용 전 최소 토큰 수
use_beam_search bool false 샘플링 대신 beam search 사용
length_penalty float 1.0 beam search 길이 페널티
early_stopping bool false beam search 조기 중단
stop_token_ids list[int] None 생성을 중단하는 토큰 ID
ignore_eos bool false EOS 토큰 뒤에도 계속 생성
skip_special_tokens bool true 출력에서 특수 토큰 생략
spaces_between_special_tokens bool true 특수 토큰 사이에 공백 추가
truncate_prompt_tokens int None 프롬프트를 이 토큰 수로 자르기

스트리밍 파라미터

Parameter Type Default Description
stream bool false 스트리밍 출력 활성화
max_batch_size int env default 스트리밍 청크별 최대 토큰 수
min_batch_size int env default 스트리밍 청크별 최소 토큰 수
batch_size_growth_factor int env default 배치 크기 성장 계수

오류 처리

네트워크 문제, 요금 제한, 콜드 스타트를 다루려면 지수 백오프(exponential backoff) 방식의 재시도 로직을 넣는 걸 권장해요.

import requests
import time

def send_request(url, headers, payload, max_retries=3):
    for attempt in range(max_retries):
        try:
            response = requests.post(url, headers=headers, json=payload, timeout=300)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.HTTPError as e:
            if e.response.status_code == 429:  # Rate limit
                time.sleep(5)
            elif e.response.status_code >= 500:
                time.sleep(2 ** attempt)
            else:
                raise
        except requests.exceptions.RequestException:
            time.sleep(2 ** attempt)
    raise Exception("Max retries exceeded")

더 알아보기 (Learn more)