나만의 LLM 환경 만들기

안녕하세요. 지난 8월 공개된 gpt-oss 모델을 기반으로 vLLM과 MCP를 활용하여 ‘나만의 AI Chat’ 환경을 구성해 본 내용을 공유해 드리려고 합니다. 단순히 모델을 띄우고 채팅 환경을 구성하는 것을 넘어, 실제로 LLM이 외부 도구(Kubernetes 등)와 상호작용할 수 있도록 구성해 보았는데요. 어떤 기술들을 사용했는지 처음 접하시는 분들께 도움이 되었으면 좋겠습니다.

:salt:활용된 오픈소스들

  • gpt-oss: 프라이빗 환경에서 운영가능한 오픈소스 ChatGPT 모델

  • vLLM: LLM의 속도와 처리량을 최적화하는 서빙 엔진

  • MCP: MCP는 LLM이 외부 도구와 상호 작용할 수 있도록 정의한 프로토콜

  • MCPO: MCP 서버의 기능을 OpenAPI로 노출시켜 주는 인터페이스

  • Open-WebUI: LLM 모델과 채팅으로 상호작용할 수 있는 웹 기반 사용자 인터페이스

:house:서빙 환경 구성 및 아키텍처

LLM 서빙 환경

  • 내부 GPU 서버에 프라이빗 ChatGPT인 gpt-oss 모델 배포
  • 모델 서빙 엔진으로 vLLM 으로 구성(Ollama 처럼 초기 구동시간 없이 빠르게 응답)

UI 및 AI, MCP 연동

  • 사용자 인터페이스로 Open-WebUI 구성
  • vLLM 연결 및 외부 도구(MCP)연결

Kubernetes 클러스터 연동

  • 클러스터 자연어 조작을 위한 MCP 구성
  • MCP의 Open-WebUI 연결을 위한 MCPO 구성

MCP Process

1)User
   |
   v
2)Open-WebUI
   |
   v Tool: https://some-mcp-servers.domain.com/kubernetes
3)mcp-proxy
   |
   v all requests forwarded
4)mcpo
   |
   v config.json based routing
   +------------------+-----------+-----------+
   v                  v           v           v
5)mcp-kubernetes    mcp-a       mcp-b       mcp-c ...
  (port 8083)      (8085)      (8086)      (8087)
   |
   v
7)Kubernetes Cluster

:rocket:FastMCP

직접 MCP 서버를 만들고 싶다면, FastMCP도 많은 도움이 될 것 같습니다. 참고로, 프라이빗 모델에 "현재 시간은?"이라고 물으면 마지막으로 학습된 시간을 대답합니다. 아래와 같은 MCP 서버를 FastMCP 프레임워크로 쉽게 만들 수 있습니다.

"""
Time-related tools for MCP Playground

이 모듈은 시간 관련 도구들을 제공합니다.
- 특정 timezone의 현재 시간 조회
- 여러 timezone의 시간 정보 동시 조회
"""

from fastmcp import FastMCP
from datetime import datetime
import pytz


def register_time_tools(mcp: FastMCP):
    """
    시간 관련 도구들을 MCP 서버에 등록합니다.
    
    Args:
        mcp: FastMCP 인스턴스
    """
    
    @mcp.tool() #<-- 이렇게 기존 코드에 FastMCP 를 활용 할 수 있도록 추가
    def get_current_time(timezone: str = "Asia/Seoul") -> str:
        """
        Get the current time in the specified timezone with UTC reference.
        
        Args:
            timezone: Timezone name (e.g., 'Asia/Seoul', 'America/New_York', 'Europe/London')
                      Default is 'Asia/Seoul'
        
        Returns:
            Current time string with local time first and UTC as reference
        """
        try:
            # 지정된 timezone의 현재 시간 가져오기
            # pytz.timezone()으로 timezone 객체 생성
            local_tz = pytz.timezone(timezone)
            local_now = datetime.now(local_tz)
            
            # 로컬 시간을 "YYYY-MM-DD HH:MM:SS" 형식으로 포맷
            local_time_str = local_now.strftime("%Y-%m-%d %H:%M:%S")
            
            # UTC 시간도 함께 가져오기 (참고용)
            utc_now = datetime.now(pytz.UTC)
            utc_time_str = utc_now.strftime("%Y-%m-%d %H:%M:%S")
            
            # 로컬 시간을 먼저 표시하고, UTC는 괄호 안에 참고로 표시
            # 예: "2025-11-03 13:14:00 (UTC: 2025-11-03 04:14:00)"
            return f"{local_time_str} (UTC: {utc_time_str})"
            
        except pytz.exceptions.UnknownTimeZoneError:
            # 잘못된 timezone 이름이 입력된 경우 에러 메시지 반환
            return f"Unknown timezone: {timezone}. Please use a valid timezone name."

    @mcp.tool()
    def get_current_time_info() -> dict:
        """
        Get detailed current time information including multiple timezones.
        
        Returns:
            Dictionary with current time in UTC, Korea, and other common timezones
        """
        # 자주 사용되는 timezone들을 미리 정의
        # 각 지역별로 timezone 객체를 생성
        timezones = {
            "UTC": pytz.UTC,                                    # 협정 세계시
            "Korea": pytz.timezone("Asia/Seoul"),               # 한국 표준시 (KST)
            "Japan": pytz.timezone("Asia/Tokyo"),               # 일본 표준시 (JST)
            "US Eastern": pytz.timezone("America/New_York"),    # 미국 동부 표준시 (EST/EDT)
            "US Pacific": pytz.timezone("America/Los_Angeles"), # 미국 서부 표준시 (PST/PDT)
        }
        
        # 각 timezone별로 현재 시간을 조회하여 딕셔너리로 반환
        result = {}
        for name, tz in timezones.items():
            now = datetime.now(tz)
            # "%Z"는 timezone 약어 (KST, JST 등)를 포함
            result[name] = now.strftime("%Y-%m-%d %H:%M:%S %Z")
        
        return result

:bullseye:마치며

물론 프라이빗 모델은 여러 가지 부족한 면이 있습니다. 다만 vLLM의 빠름과 MCP의 확장성을 결합하여 나만의 대화형 운영 환경을 구성해 보는 일이 크게 어렵지는 않았던 것 같습니다. 글 읽어 주셔서 고맙습니다. :blush:

2 Likes

와우! @JeongsikKang 님의 인사이트있는 정보 공유에 감사합니다. :slight_smile:

1 Like