커스텀 LLM 엔진 클래스로 직접 추론하기
커스텀 LLM 엔진 클래스로 직접 추론하기 (LLM Engine Example)
vLLM은 보통 LLM 클래스나 OpenAI 호환 서버처럼 편리한 고수준 인터페이스를 써요. 그런데 LLMEngine 클래스를 직접 쓰면 요청 스케줄링과 스텝 실행을 훨씬 세밀하게 제어할 수 있어요. 이 문서는 바로 그 LLMEngine을 직접 구동하는 예제를 보여주는 페이지예요.
예제의 전체 소스는 저장소의 examples/deployment/llm_engine_example.py에 있어요. 여러 가지 샘플링 파라미터로 프롬프트를 처리하는 LLMEngine 사용법을 보여줍니다.
핵심 아이디어
LLMEngine을 직접 쓰는 흐름은 크게 세 가지로 나눠져요.
- 엔진 초기화:
EngineArgs에서 CLI 인자로 만든 뒤LLMEngine.from_engine_args()로 엔진을 만들어요. - 요청 추가:
engine.add_request(request_id, prompt, sampling_params)로 처리할 프롬프트와 샘플링 설정을 넣어요. - 스텝 실행:
engine.step()을 반복 호출하면서 완료된 요청(request_output.finished)을 출력해요.
LLM 고수준 래퍼는 내부적으로 정확히 이 add_request/step 루프를 돌고 있어요. 그래서 이 예제가 래퍼가 감추고 있는 실제 메커니즘을 그대로 보여줘요.
테스트 프롬프트 준비
예제는 세 종류의 샘플링 파라미터를 테스트해요.
temperature=0.0,logprobs=1,prompt_logprobs=1— 결정적 샘플링 + 로그 확률 출력temperature=0.8,top_k=5,presence_penalty=0.2— top-k 샘플링 + 존재 페널티n=2,temperature=0.8,top_p=0.95,frequency_penalty=0.1— 다중 시퀀스 + top-p 샘플링 + 빈도 페널티
def create_test_prompts() -> list[tuple[str, SamplingParams]]:
"""Create a list of test prompts with their sampling parameters."""
return [
("A robot may not injure a human being",
SamplingParams(temperature=0.0, logprobs=1, prompt_logprobs=1)),
("To be or not to be,",
SamplingParams(temperature=0.8, top_k=5, presence_penalty=0.2)),
("What is the meaning of life?",
SamplingParams(n=2, temperature=0.8, top_p=0.95, frequency_penalty=0.1)),
]
요청 처리 루프
process_requests()는 아직 처리할 테스트 프롬프트가 남아 있거나 엔진이 미완료 요청을 갖고 있는 동안 계속 도는 루프예요. 매 스텝마다 남은 프롬프트 하나를 add_request로 넣고, engine.step()을 호출해 한 스텝을 진행시켜요. 완료된 요청만 출력해서 결과를 보여줘요.
def process_requests(engine: LLMEngine, test_prompts):
request_id = 0
print("-" * 50)
while test_prompts or engine.has_unfinished_requests():
if test_prompts:
prompt, sampling_params = test_prompts.pop(0)
engine.add_request(str(request_id), prompt, sampling_params)
request_id += 1
request_outputs = engine.step()
for request_output in request_outputs:
if request_output.finished:
print(request_output)
print("-" * 50)
engine.has_unfinished_requests()는 엔진에 끝나지 않은 요청이 남아 있는지 알려줘요.engine.step()은 한 추론 스텝을 실행하고, 이 스텝에서 완료된 요청들을RequestOutput리스트로 돌려줘요.
엔진 초기화와 진입점
엔진 초기화는 EngineArgs.from_cli_args(args)로 CLI 인자에서 엔진 인자를 만들어 LLMEngine.from_engine_args()에 넘겨요. CLI 파서는 EngineArgs.add_cli_args(parser)로 vLLM의 모든 엔진 인자를 등록해서 사용해요.
def initialize_engine(args: argparse.Namespace) -> LLMEngine:
"""Initialize the LLMEngine from the command line arguments."""
engine_args = EngineArgs.from_cli_args(args)
return LLMEngine.from_engine_args(engine_args)
def parse_args():
parser = FlexibleArgumentParser(
description="Demo on using the LLMEngine class directly"
)
parser = EngineArgs.add_cli_args(parser)
return parser.parse_args()
def main(args: argparse.Namespace):
"""Main function that sets up and runs the prompt processing."""
engine = initialize_engine(args)
test_prompts = create_test_prompts()
process_requests(engine, test_prompts)
if __name__ == "__main__":
args = parse_args()
main(args)
실행 방법
이 예제는 일반 vLLM CLI처럼 --model 등을 넘겨 직접 실행할 수 있어요. 예를 들어:
python examples/deployment/llm_engine_example.py --model Qwen/Qwen3-0.6B
이렇게 하면 LLMEngine이 직접 요청을 스케줄링하고 스텝을 밟으며, 앞서 정의한 세 프롬프트에 대한 완료 출력이 - 구분선과 함께 순서대로 찍혀요.
이 예제가 주는 시사점
LLMEngine을 직접 다루는 방식은 커스텀 스케줄링, 배치 구성, 또는 서버 수준의 오케스트레이션이 필요한 고급 사용 사례에 맞닿아 있어요. vLLM의 고수준 인터페이스가 내부적으로 하는 일을 그대로 따라 하면서, 더 아래 레벨의 제어 지점을 잡을 수 있다는 점이 핵심이에요.
더 알아보기
- vLLM 공식 문서: LLM Engine Example
- 관련 문서: LLM 클래스