레이블이 LLM인 게시물을 표시합니다. 모든 게시물 표시
레이블이 LLM인 게시물을 표시합니다. 모든 게시물 표시

20251025

LLM 추론 비용 프로파일링

"LLM 비용이 너무 많이 나와요"라는 말은 있는데 구체적으로 어디서 나는지 모르는 경우가 90%. 직접 계측 안 하면 영영 모름.

최소 schema

llm_call(
  id, parent_id, session_id, user_id,
  provider, model, purpose,           -- "summary" / "router" / "final"
  prompt_tokens, completion_tokens,
  cost_usd,                           -- 산정식으로 기록
  latency_ms, ttft_ms,
  cache_hit, retried,                 -- 플래그
  created_at
)

핵심은 purposeparent_id. 하나의 유저 요청에 내부적으로 LLM 5~10번 도는 게 보통인데, 이걸 트리로 묶어야 진짜 비용이 보임.

대시보드

세 가지 관점:

  1. per-feature: 검색, 요약, 챗 등 기능별 비용 비중.
  2. per-user: 상위 1% 유저가 쓴 양. 대부분 파워유저가 비용 50%+ 먹음.
  3. per-call-in-tree: 한 요청 트리 안에서 가장 비싼 노드. "router가 쓸데없이 긴 context 받고 있음" 같은 낭비 발견.

실제 개선

  • router 모델을 Sonnet → Haiku로 교체, 응답 품질 차이 미미. 해당 노드 비용 1/6.
  • summary 단계에서 "원문 전체"를 넣던 걸 pre-extractive로 800 토큰 추림. 품질 거의 유지, 토큰 -68%.
  • 캐시 계층 추가. session_id 단위로 embedded 질문 유사도 기준 answer cache. hit율 18%.
  • output max_tokens를 케이스별로 조정. 무한히 길게 답하는 거 방지. output 토큰 -22%.

숫자

4주 작업으로 월 LLM 비용 $18K → $6.8K. 품질 지표(faithfulness, user CSAT)는 오히려 소폭 상승. 기술적 계측은 엔지니어링 시간의 최고 ROI였다.

provider-side 사용량 대시보드 믿지 말 것. 단위(1M 기준 가격)와 round up 방식이 달라 실제 청구서와 미묘하게 어긋남. 내 DB의 cost_usd를 ground truth로, provider는 sanity check용.

그리고 월 고정 예산 alarm을 Slack에 연결. 일 단위 3σ 이상이면 즉시 알림. 모델 롤아웃 초반에 이게 두 번 살려줌.

20251013

vLLM 0.6 Speculative Decoding 체감

vLLM 0.6대에 들어 speculative decoding이 꽤 안정됐다. 드래프트 모델로 작은 거 쓰고, 타깃 모델로 검증하는 구조.

원리 요약

드래프트가 여러 토큰을 미리 예측 → 타깃이 한번의 forward로 그 토큰들을 다 검증 → 일치하는 만큼 수용, 불일치 지점에서 재시작. 평균적으로 타깃 호출 횟수가 줄어 latency 감소.

세팅

  • 타깃: Llama 3.1 70B Instruct (FP8, H100 4장 tp=4)
  • 드래프트: Llama 3.2 1B Instruct
vllm serve meta-llama/Llama-3.1-70B-Instruct \
  --tensor-parallel-size 4 --quantization fp8 \
  --speculative-model meta-llama/Llama-3.2-1B-Instruct \
  --num-speculative-tokens 5

결과

조건TTFTtok/s
no spec280 ms74
spec (k=5)310 ms118
spec (k=8)330 ms127

throughput 약 1.6~1.7배. TTFT가 살짝 오르지만 체감 안 됨(인터랙티브 채팅).

주의

  • 드래프트가 너무 다른 분포면 수용률이 낮아져서 오히려 느려짐. 같은 제품군(같은 토크나이저, 가까운 학습셋)이 중요.
  • temperature 높은 케이스(창의적 생성)는 수용률 하락. 코드·문서 생성에서 이점이 크고, 시적 생성에선 덜.
  • num_speculative_tokens를 너무 키우면 이득이 역전됨. 5~7 근처가 우리 sweet spot.

비용

GPU는 그대로. throughput 1.6배 = 비용 1/1.6. 같은 QPS 감당에 인스턴스 수를 줄일 수 있어서 월 단위로 실질 절감. 특히 출력 길이 긴 요약 같은 워크로드에 드라마틱.

spec decoding은 "공짜 점심"처럼 보이지만 드래프트/타깃 조합·분포 매칭이 관건. 운영 전 워크로드별 수용률부터 측정할 것.

20231216

Function calling 실제 서비스 적용

6월에 OpenAI가 function calling을 내놓고 반년 지났다. 그 사이 실제 서비스에 몇 개 붙였는데, 문서엔 안 나오는 함정들.

함정 1. 모델이 "함수를 부른 척"한다

분명 tool을 정의해놨는데 텍스트로 calculate(3,4) 같은 걸 그냥 응답 본문에 적는 경우가 있다. 특히 gpt-3.5-turbo에서 자주. 해결책은 tool_choice="required"로 강제하거나, 시스템 프롬프트에 "function만 사용"을 명시.

함정 2. 병렬 tool call

gpt-4-turbo(11월)에서 parallel tool calling이 들어왔다. 한 턴에 여러 함수를 동시에 부를 수 있음. 좋은데, 이게 오히려 불필요한 호출을 남발하는 경향. "사용자가 명시적으로 요청한 것만 호출"을 시스템 프롬프트에 박지 않으면 DB 쿼리 5개씩 날린다.

함정 3. schema 검증

tools = [{
    "type": "function",
    "function": {
        "name": "search_products",
        "description": "상품 검색. 카테고리와 가격 범위를 받아 결과 반환.",
        "parameters": {
            "type": "object",
            "properties": {
                "category": {"type": "string", "enum": ["electronics","apparel","food"]},
                "min_price": {"type": "integer", "minimum": 0},
                "max_price": {"type": "integer"},
            },
            "required": ["category"],
            "additionalProperties": False
        }
    }
}]

enum과 required를 확실히 박는다. 그래도 min_price > max_price 같은 케이스는 모델이 그냥 낸다. 서버에서 pydantic으로 한번 더 검증하고 에러면 role: "tool"로 에러 메시지 돌려주면 대부분 자기 교정함.

함정 4. 응답 루프

tool 결과 → 모델이 또 다른 tool call → 결과 → ... 이게 무한루프 나는 경우 있음. max_turns 제한을 꼭 걸고, 같은 인자로 같은 함수 3번 부르면 break 하는 watchdog 추가.

잘 쓰면 RAG + agent의 핵심 축. 다만 "프롬프트로 시키면 될 일을 function으로 처리"하는 과설계를 조심해야 한다. 함수 10개 넘어가기 시작하면 모델이 고민을 많이 해서 latency가 눈에 띄게 증가한다.

20230905

Llama 2 서빙 — vLLM 실전 구축

7월에 Llama 2 나온 뒤로 다들 셀프호스팅 한번씩 해보는 분위기. 나도 13B chat 모델을 vLLM으로 올려봤다.

환경

  • GPU: A100 40GB 1장
  • 모델: meta-llama/Llama-2-13b-chat-hf
  • vLLM 0.1.4
python -m vllm.entrypoints.openai.api_server \
  --model meta-llama/Llama-2-13b-chat-hf \
  --dtype float16 \
  --max-model-len 4096 \
  --gpu-memory-utilization 0.90 \
  --tensor-parallel-size 1

vLLM이 신기한 건 PagedAttention. KV cache를 page 단위로 쪼개서 관리하니까 batch가 커져도 OOM이 잘 안 난다. 기존 HuggingFace generate()로 돌릴 때 대비 throughput이 4~5배 차이.

측정

조건throughput (tok/s)p50 latency
concurrency 142720ms TTFT
concurrency 8285980ms TTFT
concurrency 326401.9s TTFT

실전에서 걸린 것들

첫 번째: chat template. Llama 2 chat은 [INST] ... [/INST] 포맷이 있는데, 이거 안 맞추면 모델이 헛소리. Tokenizer의 apply_chat_template이 아직 없던 시절이라 수동으로 조립했다.

두 번째: stop token. </s>[INST]를 stop에 안 박으면 계속 생성됨. 유저 턴까지 스스로 만들어버리는 "self-dialogue" 현상.

세 번째: OpenAI 대비 품질. 13B chat 정도로는 gpt-3.5-turbo 대체가 안 된다. 특히 한국어는 눈에 띄게 어색. 70B 올리면 달라지는데 A100 2장 필요.

결론: 완전한 OpenAI 대체는 무리. 민감 데이터만 사내로 넘기고 나머진 API 쓰는 하이브리드가 현실적. 그래도 vLLM이 있으니 서빙 자체는 생각보다 할 만하다.

20230208

OpenAI API 운영 비용 최적화

한 달 OpenAI 청구서가 $1,200 나왔다. 개인 토이 프로젝트 수준인데 이정도면 사이드로 돌릴 수가 없다. 이틀 동안 파보면서 정리한 것.

어디서 돈이 새는가

usage export 뽑아보면 대부분 gpt-3.5-turbo 호출에서 나온다. 문제는 입력 토큰이 생각보다 길다는 것. 시스템 프롬프트 + few-shot 예제 + retrieved context를 매 요청마다 보내니 초기엔 평균 input이 2,800 토큰.

input  : $0.0015 / 1K  →  월 $840
output : $0.002  / 1K  →  월 $360

한 것들

  1. Few-shot 예제 압축 — 5개 × 평균 400토큰 = 2K. 이걸 문체와 포맷 규칙만 남기고 120토큰으로 축소. 품질은 거의 안 떨어짐.
  2. System prompt 단축 — "친절하고 정확하게..." 같은 말 다 걷어냄. 모델 성능에 영향 없음.
  3. Function calling 스타일로 출력 강제 — JSON 파싱 재시도 루프 없어짐. 실패율 17% → 0.3%.
  4. Semantic cache — 같은 질문에 가까운 거 들어오면 embedding 유사도 0.95 이상이면 캐시 결과 반환. 히트율 22%.

결과

평균 input 토큰 2,800 → 640. 전체 비용 $1,200 → $310. 품질은 유저 5명한테 blind test 시켰는데 구분 못함.

한 가지 교훈: LLM 비용은 token 길이의 문제가 아니라 "왜 그 토큰이 거기 있는지"의 문제다. 매 요청에 쓰레기가 섞여 나가고 있는지 먼저 봐야 한다. 프롬프트를 한번 찍어놓고 "이 중 뭐가 진짜 필요한가" 줄 단위로 점검하는 게 제일 빨랐다.

다음 달은 gpt-3.5-turbo-0301이 나온다고 하는데 pricing이 또 달라지지 않을까 싶다.