개인정보처리방침© 2026 DEV BAK - 기술블로그. All rights reserved.
DEV BAK - 기술블로그
AI

XGrammar-2: 구조화 출력을 80배 빠르게 만든 설계 원리

LLM이 툴을 호출하거나 JSON을 반환할 때, 사실 꽤 무거운 작업이 뒤에서 돌아가고 있습니다. 모델이 토큰을 하나 뱉을 때마다 "이 토큰이 현재 문법 상태에서 유효한가?"를 실시간으로 판단해야 하고, 툴이 수백 개로 늘어나면 요청이 들어올 때마다 문법을 새로 컴파일하는 비용이 조용히 쌓이기 시작합니다. AI 에이전트를 직접 서빙해봤든 아니든, 이 문제는 LLM 기반 서비스를 운영하다 보면 피하기 어려운 병목이 됩니다.

저도 DeepSeek 계열 모델에 함수 호출을 붙이다가 <think> 블록이 끝난 뒤 JSON 파싱이 뒤틀리는 상황을 몇 번 겪었는데, 당시엔 그냥 파서를 직접 짜서 우겨넣었습니다. 그게 얼마나 야만적인 방식이었는지는 XGrammar-2를 보고 나서야 실감했습니다.

2026년 5월, CMU 연구팀과 MLC 프로젝트가 공개한 XGrammar-2는 XGrammar 1세대 대비 최대 80배 효율 향상, 토큰당 처리 속도 6–10배, 컴파일 시간 100배 이상 단축을 달성했습니다. 이 글은 논문과 공식 블로그를 분석하고 코드 예시 일부를 직접 검증한 내용을 담고 있습니다. 읽고 나면 어떤 조건에서 XGrammar-2 전환이 실질적인 효과를 가져오는지, 그리고 vLLM이나 SGLang에서 코드 한 줄을 어떻게 바꾸면 되는지 판단할 수 있습니다.


핵심 개념

XGrammar-2는 단순한 성능 업그레이드가 아닙니다. 에이전트 환경에서 기존 엔진들이 근본적으로 가정을 잘못 세웠다는 것을 지적하고, 그 두 가지 잘못된 전제를 각각 다른 메커니즘으로 해결합니다. 구체적으로 무엇이 문제였는지부터 살펴보겠습니다.

구조화 출력이 왜 에이전트 환경에서 특히 까다로운가

구조화 출력(Structured Generation): LLM이 JSON, 함수 호출 스키마, 특정 프로토콜처럼 미리 정의된 형식에 정확히 맞는 텍스트만 생성하도록 토큰 단위로 제약을 거는 기술. 토큰이 생성될 때마다 토큰 마스크(token mask) — 현재 문법 상태에서 다음에 올 수 있는 토큰을 제한하는 비트 배열 — 를 계산해서 적용합니다.

기존 구조화 생성 엔진들은 크게 두 가지 전제 위에서 설계되었습니다. 첫째, 스키마는 요청 전에 미리 컴파일해둘 수 있다. 둘째, 한 요청 안에서 출력 구조는 바뀌지 않는다. 에이전트 환경은 이 두 전제를 모두 깨버립니다.

요청 간 동적성(Inter-request dynamism): 에이전트마다 쓰는 툴이 다르고, 500개짜리 툴 레지스트리에서 요청마다 다른 서브셋을 골라 스키마를 새로 컴파일해야 합니다. 미리 캐싱해둘 조합의 수가 사실상 무한대에 가까워집니다.

요청 내 동적성(Intra-request dynamism): DeepSeek R1이나 QwQ 같은 chain-of-thought 모델은 한 요청 안에서 <think>...</think> 자유형식 추론 → 툴 콜 JSON → 응답 텍스트 순서로 구조를 전환합니다. 단일 문법으로는 이 흐름을 커버할 수 없습니다.

에이전트 개발 경험이 없더라도 이게 왜 문제인지 직관적으로 이해할 수 있습니다. 전통적인 웹 API는 요청 하나에 스키마 하나가 고정되어 있지만, LLM 에이전트는 "지금 이 순간 모델이 무엇을 출력하고 있느냐"에 따라 허용되는 토큰 집합이 실시간으로 바뀝니다. 정적으로 설계된 파서로는 이 동적성을 효율적으로 다룰 수 없습니다.

XGrammar-2는 이 두 가지 동적성을 각각 다른 메커니즘으로 해결합니다.


TagDispatch: 요청 내 구조 전환을 자동으로 처리

TagDispatch는 출력 스트림에서 특정 태그를 감지하면 활성 문법을 동적으로 교체하는 디스패치 레이어입니다. <tool_call> 태그가 나타나면 해당 툴의 JSON 스키마 문법으로, <think> 태그가 나타나면 무제약 자유형식 문법으로 자동 전환됩니다.

python
# TagDispatch 개념 구조 (의사 코드)
grammar_map = {
    "<think>":     FreeFormGrammar(),
    "<tool_call>": JSONSchemaGrammar(tool_schema),
    "<response>":  ResponseFormatGrammar(),
}
 
dispatcher = TagDispatch(grammar_map)
for token in token_stream:
    active_grammar = dispatcher.dispatch(token)
    # 현재 문법의 상태 기계(state machine) 기반으로 토큰 마스크 계산
    mask = active_grammar.get_token_mask(current_state)

저도 처음엔 "그냥 파서 레벨에서 구간을 쪼개면 안 되나?" 싶었는데, 문제는 토큰 마스크 계산이 문법 상태 기계의 현재 상태에 의존한다는 점입니다. 문법이 바뀌면 상태 기계 자체가 바뀌어야 하고, 이걸 런타임에 오버헤드 없이 전환하는 게 핵심 난제입니다. TagDispatch는 이 상태 기계 전환을 태그 감지 시점에 원자적으로(atomically) 처리합니다.


Cross-Grammar Cache: 서로 다른 스키마 간 공유 구조 재사용

Cross-Grammar Cache: 서로 다른 문법(툴 스키마) 사이에서 공통으로 등장하는 서브구조의 토큰 마스크를 한 번만 계산하고 재사용하는 캐싱 레이어. 중요한 건 이 캐시가 스키마 레벨이 아닌 문법 상태 기계의 상태 단위에서 동작한다는 점입니다.

500개의 툴이 있다고 해도, 모든 툴 스키마는 결국 JSON 오브젝트 구조를 따릅니다. {, "key":, 숫자 파싱, 배열 닫기 같은 패턴은 대부분의 스키마에서 반복됩니다. 기존 방식은 스키마마다 이 계산을 독립적으로 수행했지만, Cross-Grammar Cache는 공유 서브구조를 문법 상태 레벨에서 식별하고 한 번 계산된 토큰 마스크를 여러 문법에서 재사용합니다.

직관적으로 설명하면 이렇습니다. 툴 A의 JSON 스키마와 툴 B의 JSON 스키마가 내용은 달라도, 두 문법이 "JSON 오브젝트를 열고 첫 번째 키를 기다리는 상태"에서는 동일한 상태 기계 구조를 공유합니다. 이 공통 상태에서의 토큰 마스크는 한 번만 계산하면 두 문법 모두에서 재사용할 수 있습니다.


그 외 핵심 메커니즘

Earley 파서: 문맥 자유 문법(CFG)을 파싱하는 알고리즘으로, JSON 스키마처럼 재귀적 구조를 가진 문법을 정확하게 처리할 수 있습니다. 일반적인 입력에서는 O(N²) 이하로 동작하지만, 고도로 모호한 문법에서는 O(N³)까지 올라갈 수 있습니다.

기술 해결하는 문제 핵심 원리
JIT 컴파일 불필요한 사전 컴파일 비용 실제 요청이 들어왔을 때만 해당 문법을 컴파일. 500개 툴이 있어도 실제 요청에 쓰이는 서브셋만 처리
반복 상태 압축 배열·리스트 패턴의 메모리 낭비 [item, item, ...] 같은 반복 구조를 상태 공간에서 압축해 메모리와 연산량 절감
Earley 적응형 마스크 복잡한 재귀 문법 처리 Earley 파서 기반으로 문맥 의존 문법에서도 효율적인 토큰 마스크 계산 지원
TagDispatch 요청 내 구조 전환 태그 감지 시점에 문법 상태 기계를 원자적으로 교체 (위에서 상세 설명)
Cross-Grammar Cache 요청 간 스키마 중복 계산 공통 상태 서브구조의 토큰 마스크를 상태 단위로 재사용 (위에서 상세 설명)

Structural Tag: 에이전트 출력 형식의 추상화

에이전트 출력 형식은 모델마다, 프레임워크마다 제각각입니다. OpenAI 형식, Anthropic 형식, 커스텀 형식이 혼재하는 상황에서 XGrammar-2는 Structural Tag라는 추상화 레이어를 도입합니다.

솔직히 이 부분은 논문보다 실제 운영 코드에서 어떻게 표현되는지가 아직 충분히 공개되지 않아 체감하기 어려운 부분이 있습니다. 기본적인 아이디어는 다음과 같습니다.

json
{
  "type": "tool_call",
  "name": "search",
  "arguments": {"query": "서울 날씨"},
  "reasoning": "<think> ... </think>"
}

TagDispatch가 태그 감지 레이어라면, Structural Tag는 그 위에서 에이전트 출력을 일관된 JSON 구조로 표현하는 프로토콜입니다. OpenAI의 응답 형식을 둘러싼 표준화 논의가 진행 중인 가운데, 다양한 형식을 하나의 추상화로 통일하려는 시도입니다.


실전 적용

예시 1: SGLang에서 대규모 툴 콜링 에이전트 서빙

환경: SGLang 최신 버전 (XGrammar-2 통합 여부는 버전별 릴리스 노트에서 확인을 권장합니다), Python 3.10+

SGLang은 XGrammar-2를 기본 구조화 출력 백엔드로 통합했습니다. guided_json 파라미터로 스키마를 넘기면 Cross-Grammar Cache가 자동으로 작동합니다.

python
import sglang as sgl
from pydantic import BaseModel, ConfigDict
from typing import Literal
 
class ToolCallResponse(BaseModel):
    model_config = ConfigDict(extra="forbid")  # 추가 필드 차단
    tool_name: Literal["search", "calculate", "fetch_data"]
    arguments: dict
    confidence: float
 
@sgl.function
def tool_call_agent(s, user_query: str):
    s += sgl.system("You are a helpful assistant with access to tools.")
    s += sgl.user(user_query)
    s += sgl.assistant(
        sgl.gen(
            "response",
            guided_json=ToolCallResponse.model_json_schema(),
            max_tokens=512,
        )
    )
 
# XGrammar-2 백엔드로 런타임 초기화
runtime = sgl.Runtime(model_path="deepseek-ai/DeepSeek-V3", backend="xgrammar")
result = tool_call_agent.run(
    user_query="현재 서울 날씨를 검색해서 섭씨로 알려줘",
)
print(result["response"])
코드 포인트 설명
guided_json=ToolCallResponse.model_json_schema() Pydantic 모델의 JSON 스키마를 그대로 넘깁니다. XGrammar-2가 이 스키마를 JIT 컴파일하여 토큰 마스크를 생성
backend="xgrammar" SGLang 런타임에서 XGrammar-2 백엔드를 명시적으로 활성화
ConfigDict(extra="forbid") 스키마에 없는 추가 필드를 차단합니다. 이게 없으면 모델이 예상 외의 키를 넣는 상황이 생길 수 있습니다

예시 2: 추론 채널이 있는 모델에서 혼합 출력 처리

DeepSeek R1, QwQ처럼 <think> 블록을 먼저 출력하고 이후 구조화된 응답을 내놓는 모델을 다룰 때가 솔직히 가장 골치 아픈 케이스입니다. TagDispatch가 이 전환을 자동으로 처리하는 방식을 보면 꽤 깔끔합니다.

주의: 아래 코드는 XGrammar-2 공식 문서 기반의 의사 코드입니다. 실제 클래스명·메서드명은 라이브러리 버전에 따라 다를 수 있으므로, 실행 전 공식 GitHub의 API 문서를 확인해보시는 것을 권장합니다.

python
# pip install xgrammar
# 아래는 XGrammar-2 개념 기반 의사 코드입니다 (실제 API명과 다를 수 있습니다)
from xgrammar import GrammarCompiler, TagDispatchGrammar
 
# GrammarCompiler 인스턴스는 재사용해야 Cross-Grammar Cache 효과를 누릴 수 있습니다
compiler = GrammarCompiler()
 
tag_dispatch_grammar = TagDispatchGrammar(
    tag_grammar_map={
        # <think> 블록: 자유형식 (토큰 마스크 제약 없음)
        "think": compiler.compile_free_form(),
 
        # <tool_call> 블록: 엄격한 JSON 스키마
        "tool_call": compiler.compile_json_schema({
            "type": "object",
            "properties": {
                "name": {"type": "string"},
                "arguments": {"type": "object"},
            },
            "required": ["name", "arguments"],
            "additionalProperties": False,
        }),
 
        # <response> 블록: 자유형식 응답
        "response": compiler.compile_free_form(),
    },
    default_grammar=compiler.compile_free_form(),
)
처리 단계 활성 문법 토큰 마스크
<think> 시작 ~ </think> 자유형식 전체 어휘 허용
<tool_call> 시작 이후 JSON 스키마 문법 스키마에 맞는 토큰만 허용
</tool_call> 이후 기본 문법 태그 감지 대기

GrammarCompiler 인스턴스를 재사용하는 것이 핵심입니다. 이 컴파일러 안에 Cross-Grammar Cache가 살아있어서, 여러 요청에 걸쳐 공통 서브구조의 토큰 마스크를 누적해서 재사용합니다. 요청마다 새 컴파일러를 만들면 캐시가 초기화되어 성능 이점이 사라집니다.


예시 3: vLLM에서 기존 코드에 XGrammar-2 통합

환경: vLLM v0.4+ (XGrammar-2 지원 여부는 버전별 릴리스 노트에서 확인을 권장합니다), Python 3.10+

vLLM을 이미 쓰고 있다면 guided_decoding_backend 설정 하나로 전환할 수 있습니다. 기존 코드는 그대로 두고 백엔드만 바꾸는 방식이라 마이그레이션 리스크가 거의 없습니다.

python
from vllm import LLM, SamplingParams
 
llm = LLM(
    model="Qwen/Qwen2.5-72B-Instruct",
    guided_decoding_backend="xgrammar",  # 이 한 줄만 추가
)
 
response_schema = {
    "type": "object",
    "properties": {
        "status": {"type": "string", "enum": ["success", "error", "partial"]},
        "results": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "id": {"type": "integer"},
                    "content": {"type": "string"},
                    "score": {"type": "number"},
                },
                "additionalProperties": False,
            },
        },
        "metadata": {"type": "object"},
    },
    "additionalProperties": False,
}
 
params = SamplingParams(
    temperature=0.0,
    guided_json=response_schema,
    max_tokens=1024,
)
 
outputs = llm.generate(["검색 결과를 JSON으로 정리해줘"], params)

툴이 고정적이고 10개 이하인 환경에서는 기존 엔진으로도 충분할 수 있습니다. 반면 요청마다 다른 툴 서브셋을 동적으로 선택하거나, 추론 채널이 있는 모델을 서빙하는 경우라면 전환 효과가 체감될 가능성이 높습니다.


장단점 분석

장점

항목 내용
극적인 성능 향상 XGrammar 1세대 대비 최대 80× 효율, 6–10× 토큰당 처리 속도, 100× 이상 컴파일 시간 단축
동적 에이전트 최적화 요청 간·요청 내 구조 변화를 모두 효율적으로 처리하는 유일한 엔진
범용 프레임워크 호환 vLLM, SGLang, TensorRT-LLM, MLC-LLM 모두 통합 완료
추론 모델 네이티브 지원 DeepSeek, Qwen 계열의 <think> 패턴을 TagDispatch로 자연스럽게 처리
오픈소스 MIT GitHub 공개, 커스터마이징 가능

단점 및 주의사항

항목 내용 대응 방안
복잡도 증가 TagDispatch, Cross-Grammar Cache 등 새로운 추상화 레이어로 디버깅 난이도 상승 로그 레벨을 높여 문법 전환 시점을 추적하는 것을 권장합니다
단순 고정 스키마엔 이점 적음 스키마가 항상 동일한 경우 XGrammar 1세대 대비 실질 이점이 크지 않을 수 있음 툴이 10개 이하·고정 스키마라면 기존 엔진으로도 충분합니다
Earley 파서 복잡도 최악의 경우 O(N³) — 매우 복잡한 재귀 문법에서는 병목 가능 실제 스키마로 벤치마크 후 도입 여부를 결정하는 것을 권장합니다
신규 기술 2026년 5월 발표, 프로덕션 피드백 누적 기간이 짧음 초기엔 스테이징 환경에서 충분히 검증 후 전환하는 것을 권장합니다

실무에서 가장 흔한 실수

  1. 스키마에 additionalProperties: false 생략: XGrammar-2가 추가 필드를 허용해버려 모델이 예상 외의 키를 마음대로 넣는 상황이 발생합니다. Pydantic v2 모델을 쓴다면 model_config = ConfigDict(extra="forbid")를 함께 설정하는 것을 권장합니다.

  2. TagDispatch 태그 이름을 모델 프롬프트와 불일치하게 설정: 시스템 프롬프트에서 <tool_call>이라고 안내했는데 TagDispatch에는 <function_call>로 등록하면 디스패치가 아예 동작하지 않습니다. 태그 이름을 시스템 프롬프트와 TagDispatch 설정에서 정확히 일치시켜야 합니다.

  3. 단일 프로세스에서 GrammarCompiler를 요청마다 새로 생성: Cross-Grammar Cache의 혜택을 받으려면 GrammarCompiler 인스턴스를 재사용해야 합니다. 요청마다 새 컴파일러를 만들면 캐시가 초기화되어 성능 이점이 사라집니다.


마치며

XGrammar-2는 에이전트 LLM 서빙에서 구조화 출력이 더 이상 성능 병목이 되지 않아도 된다는 걸 보여준 엔진입니다. 특히 동적 툴 레지스트리를 운영하거나, DeepSeek R1·QwQ처럼 추론 채널이 있는 모델을 서빙하고 있다면 전환 효과를 체감할 가능성이 높습니다. 반대로 고정된 단일 스키마로만 운영 중이라면 지금 당장 교체 우선순위가 높지는 않습니다.

지금 바로 시작해볼 수 있는 3단계:

  1. 패키지 설치 및 간단한 벤치마크 확인: pip install xgrammar 또는 pip install "vllm[xgrammar]"으로 설치한 뒤, 현재 사용 중인 스키마를 GrammarCompiler로 컴파일해볼 수 있습니다. 툴 수가 50개를 넘는 환경이라면 컴파일 시간 차이를 바로 체감할 수 있습니다.

  2. 기존 vLLM 또는 SGLang 서빙 코드에 백엔드 옵션 한 줄 추가: guided_decoding_backend="xgrammar" 파라미터 하나만 추가하면 기존 코드 변경 없이 전환이 가능합니다. 스테이징 환경에서 먼저 구조화 출력 관련 레이턴시 지표를 확인해볼 수 있습니다. 구조화 출력 오버헤드가 전체 응답 시간의 5% 이상을 차지하고 있다면 전환을 검토하기에 충분한 신호입니다.

  3. 추론 모델 사용 중이라면 TagDispatch 설정 도입 검토: DeepSeek R1, QwQ, Qwen 계열처럼 <think> 블록을 출력하는 모델에서 JSON 파싱이 불안정했던 경험이 있다면, 공식 문서의 TagDispatch 예시를 참고해서 설정해볼 수 있습니다. 이 경우 단순 백엔드 전환보다 더 큰 안정성 개선을 기대할 수 있습니다.


참고 자료

  • XGrammar-2: Fast and Customizable Structured Generation for Tool Calling and Agents | MLC Blog
  • XGrammar-2: Efficient Dynamic Structured Generation Engine for Agentic LLMs | arXiv
  • XGrammar: Achieving Efficient, Flexible, Portable Structured Generation | MLC Blog
  • XGrammar (원본 논문) | arXiv
  • mlc-ai/xgrammar | GitHub
  • xgrammar | PyPI
  • XGrammar-2 리뷰 | TheMoonlight
#구조화출력#LLM#XGrammar#에이전트AI#JSONSchema#vLLM#SGLang#TagDispatch#JIT컴파일#Pydantic
공유하기

목차

핵심 개념구조화 출력이 왜 에이전트 환경에서 특히 까다로운가TagDispatch: 요청 내 구조 전환을 자동으로 처리Cross-Grammar Cache: 서로 다른 스키마 간 공유 구조 재사용그 외 핵심 메커니즘Structural Tag: 에이전트 출력 형식의 추상화실전 적용예시 1: SGLang에서 대규모 툴 콜링 에이전트 서빙예시 2: 추론 채널이 있는 모델에서 혼합 출력 처리예시 3: vLLM에서 기존 코드에 XGrammar-2 통합장단점 분석장점단점 및 주의사항실무에서 가장 흔한 실수마치며참고 자료

추천 포스트

96개 H100에서 DeepSeek-V3 서빙이 가능한 이유: SGLang Expert Parallelism의 통신 최적화와 메모리 단편화 해법
AI

96개 H100에서 DeepSeek-V3 서빙이 가능한 이유: SGLang Expert Parallelism의 통신 최적화와 메모리 단편화 해법

52,300 input tokens/s. 2025년 5월 LMSYS 팀이 96개 H100 GPU에서 DeepSeek-V3를 오픈소스로 최초 배포하며 공개한 수치입니다. 685B짜리 MoE 모델이 이 처리량을 낼 수 있다는 게 처음엔 직관에 반했습니다. 단일 노드로는 VRAM이 절대적으로...

2026년 05월 28일읽는 데 22분
SGLang EPD Disaggregation과 Pipeline Parallelism — Vision-Language Model 서빙을 3단계로 분리해 TTFT 최대 8배 단축하는 아키텍처
AI

SGLang EPD Disaggregation과 Pipeline Parallelism — Vision-Language Model 서빙을 3단계로 분리해 TTFT 최대 8배 단축하는 아키텍처

멀티모달 AI를 직접 서비스해본 경험이 없어도 상관없습니다. 요즘은 이미지를 입력받는 AI 기능이 워낙 빠르게 보편화되고 있어서, 조만간 누구든 Vision-Language Model(VLM) — 텍스트와 이미지를 함께 이해하는 AI 모델 — 을 서빙해야 할 상황이 찾아옵니다. 그리고 ...

2026년 05월 27일읽는 데 23분
같은 GPU로 처리량 6배를 뽑아낸 SGLang 아키텍처 — PD Disaggregation과 HiCache
AI

같은 GPU로 처리량 6배를 뽑아낸 SGLang 아키텍처 — PD Disaggregation과 HiCache

LLM 서빙을 처음 맡았을 때 가장 당황스러웠던 건 "GPU는 충분한데 왜 이렇게 느리지?"라는 질문이었습니다. 모니터링 대시보드를 보면 GPU 메모리는 빠듯하게 차 있고 연산은 계속 돌아가는 것 같은데, 실제 응답 속도는 기대에 한참 못 미쳤죠. 한동안 GPU를 추가로 사는 방향으로만...

2026년 05월 27일읽는 데 27분
SGLang RadixAttention KV Cache Hit Rate: Prometheus 모니터링과 운영 튜닝으로 히트율을 3%에서 78%로 올린 방법 — 심화편
AI

SGLang RadixAttention KV Cache Hit Rate: Prometheus 모니터링과 운영 튜닝으로 히트율을 3%에서 78%로 올린 방법 — 심화편

LLM 서빙 인프라를 운영하다 보면 어느 순간 GPU 비용이 감당하기 어려운 수준에 달합니다. 저도 멀티턴 챗봇 서비스를 운영하던 시절, 요청마다 동일한 시스템 프롬프트를 전부 다시 계산하고 있다는 걸 뒤늦게 깨달았습니다. 알고 보니 SGLang의 RadixAttention이 이미 그 ...

2026년 05월 27일읽는 데 21분
vLLM에서 SGLang으로 LLM 추론 마이그레이션: RAG·멀티턴 워크로드에서 처리 속도 6배 차이가 나는 이유
AI

vLLM에서 SGLang으로 LLM 추론 마이그레이션: RAG·멀티턴 워크로드에서 처리 속도 6배 차이가 나는 이유

솔직히 말하면, 처음 SGLang을 접했을 때 반응은 "또 새 프레임워크야?"였습니다. vLLM도 충분히 잘 쓰고 있었고, 이미 안정적으로 운영 중인 서빙 스택을 건드리는 건 언제나 부담스러운 일이니까요. 그런데 하루 수만 건의 RAG 요청을 처리하던 파이프라인 성능 병목을 파고들다가 ...

2026년 05월 27일읽는 데 21분
vLLM 파드를 스케일아웃하면 KV 캐시 히트율이 0%로 떨어지는 이유, 그리고 llm-d가 이를 해결하는 방식 (prefix-aware routing / distributed KV cache)
AI

vLLM 파드를 스케일아웃하면 KV 캐시 히트율이 0%로 떨어지는 이유, 그리고 llm-d가 이를 해결하는 방식 (prefix-aware routing / distributed KV cache)

LLM 서비스를 운영하다 보면 어느 순간 이런 상황을 맞닥뜨리게 됩니다. vLLM의 Automatic Prefix Caching(APC)을 켜두고 단일 서버에서 멀티턴 챗봇을 돌릴 때는 두 번째 요청부터 확실히 빨라지는 걸 체감했는데, 트래픽이 늘어서 파드를 3개, 5개로 늘리고 나니 ...

2026년 05월 26일읽는 데 26분