openwebui/SETUP_MLX.md
MyeonghoeLee bcd17b2951 입출력 토큰 제한 적용 (Ollama + MLX 모두)
- MLX: --use-paged-cache + --max-cache-blocks로 입력 8192 토큰 제한
- MLX: --kv-cache-quantization 기본 적용
- Ollama: Modelfile로 num_ctx(입력), num_predict(출력) 설정
- SETUP_MLX.md에 토큰 제한 설명 추가

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 22:32:57 +09:00

12 KiB
Raw Blame History

Qwen3.5 + Open WebUI — vllm-mlx (Mac 최적화)

환경: MacBook Pro M4 Pro 48GB / Docker Desktop / Python 3.10+


목차

  1. 환경 준비
  2. 서버 시작
  3. Open WebUI 연결
  4. 종료 / 재시작
  5. 파라미터 레퍼런스
  6. 트러블슈팅

1. 환경 준비

⚠️ conda 환경에서 실행하면 MPICH와 MLX가 충돌합니다. 반드시 별도 venv에서 실행하세요.

conda deactivate

python3 -m venv ~/mlx-env
source ~/mlx-env/bin/activate

pip install git+https://github.com/waybarrios/vllm-mlx.git

2. 서버 시작

모델은 첫 시작 시 HuggingFace에서 자동 다운로드됩니다.

텍스트 전용

source ~/mlx-env/bin/activate

vllm-mlx serve mlx-community/Qwen3.5-35B-A3B-4bit \
  --port 8090 \
  --max-tokens 8192 \
  --default-temperature 0.7 \
  --default-top-p 0.9

텍스트 + 이미지 (멀티모달)

source ~/mlx-env/bin/activate

vllm-mlx serve mlx-community/Qwen3.5-35B-A3B-4bit \
  --port 8090 \
  --max-tokens 8192 \
  --default-temperature 0.7 \
  --default-top-p 0.9 \
  --mllm

--mllm 플래그 하나로 이미지 입력이 활성화됩니다.

포그라운드로 실행됩니다. 새 터미널 탭을 열어서 다음 단계를 진행하세요.

용도별 추천 설정

# 문서 생성 (보수적)
vllm-mlx serve mlx-community/Qwen3.5-35B-A3B-4bit \
  --port 8090 --max-tokens 8192 \
  --default-temperature 0.2 --default-top-p 0.95 --mllm

# 대화 / 창의적 응답
vllm-mlx serve mlx-community/Qwen3.5-35B-A3B-4bit \
  --port 8090 --max-tokens 8192 \
  --default-temperature 1.0 --default-top-p 0.8 --mllm

# 동시 요청 최적화 (여러 사용자)
# ⚠️ --continuous-batching은 단일 사용자일 때 오히려 느려질 수 있음
vllm-mlx serve mlx-community/Qwen3.5-35B-A3B-4bit \
  --port 8090 --max-tokens 8192 \
  --default-temperature 0.7 --mllm --continuous-batching

3. Open WebUI 연결

3-1. 서버 동작 확인

curl http://localhost:8090/v1/models

curl http://localhost:8090/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "mlx-community/Qwen3.5-35B-A3B-4bit",
    "max_tokens": 8192,
    "messages": [{"role": "user", "content": "안녕하세요"}]
  }'

3-2. Open WebUI 실행

cd ~/PyCharmMiscProject/openwebui
docker compose -f docker-compose.mlx.yml up -d

docker-compose.mlx.yml이 없으면 부록을 참고하세요.

3-3. 브라우저 접속

http://localhost:3000
  1. 첫 접속 시 회원가입 (로컬 전용, 첫 계정 = admin)
  2. 설정 → Connections → OpenAI API 연결 확인
    • URL: http://host.docker.internal:8090/v1
    • API Key: none
  3. 상단 모델 선택 후 채팅 시작

4. 종료 / 재시작

종료

# vllm-mlx 서버: Ctrl+C

# Open WebUI
cd ~/PyCharmMiscProject/openwebui
docker compose -f docker-compose.mlx.yml down

재시작

# 터미널 1: 서버
source ~/mlx-env/bin/activate
vllm-mlx serve mlx-community/Qwen3.5-35B-A3B-4bit \
  --port 8090 --max-tokens 8192 \
  --default-temperature 0.7 --default-top-p 0.9 --mllm

# 터미널 2: Open WebUI
cd ~/PyCharmMiscProject/openwebui
docker compose -f docker-compose.mlx.yml up -d

5. 파라미터 레퍼런스

서버 시작 플래그

토큰 제한

구분 의미 설정 방법
max_tokens (출력) 모델이 생성하는 최대 토큰 수 --max-tokens 8192
context length (입력) 모델이 받을 수 있는 최대 입력 토큰 수 모델 자체 설정 (Qwen3.5: 최대 262K)

입력 길이를 제한하려면 --use-paged-cache--max-cache-blocks를 조합합니다. max-cache-blocks × 64(블록 사이즈) = 최대 입력 토큰 수

예: 입력 8192 토큰 제한 → --use-paged-cache --max-cache-blocks 128

생성 파라미터

플래그 기본값 설명
--max-tokens 32768 최대 출력 토큰 수 (thinking + 응답 합산)
--default-temperature 모델 기본값 Temperature (0.0~2.0)
--default-top-p 모델 기본값 Nucleus Sampling
--reasoning-parser qwen3 off thinking 내용을 별도 필드로 추출

모달리티

플래그 기본값 설명
--mllm off 멀티모달 (이미지 입력) 활성화

성능

플래그 기본값 설명
--continuous-batching off 동시 요청 배칭 (단일 사용자 시 오히려 느림)
--prefill-step-size 2048 프리필 처리 단위
--stream-interval 1 스트리밍 배치 토큰 수 (1=부드러움, 클수록 처리량↑)

메모리

플래그 기본값 설명
--kv-cache-quantization off KV 캐시 양자화 활성화 (메모리 절약)
--kv-cache-quantization-bits 8 KV 캐시 양자화 비트 (4 또는 8)
--cache-memory-percent 0.20 RAM의 몇 %를 캐시에 할당

서버

플래그 기본값 설명
--port - 서버 포트
--host - 서버 호스트
--api-key - API 키 (미설정 시 인증 없음)
--rate-limit 0 분당 요청 제한 (0 = 무제한)
--timeout 300 요청 타임아웃 (초)

각 파라미터가 하는 일

--default-temperature (Temperature)

모델의 출력 랜덤성을 조절합니다.

0.0   ←──────────────────────────→   2.0
결정적                              랜덤
(같은 질문 = 같은 답)         (창의적, 횡설수설 가능)
  • 0.0 → 항상 가장 확률 높은 토큰 선택
  • 0.7 → 일반 대화에 적합
  • 1.0+ → 창의적 글쓰기

--default-top-p (Nucleus Sampling)

누적 확률 상위 p%에 드는 토큰만 후보로 남깁니다.

예: top-p = 0.9
  [토큰A: 50%] [토큰B: 30%] [토큰C: 10%] | [토큰D: 5%] [토큰E: 3%] ...
  ─────────── 상위 90% (후보) ──────────   ──── 제외 ────
  • 0.9 → 희귀한 토큰 배제, 안정적
  • 1.0 → 필터 없음

--max-tokens (출력 토큰 제한)

한 요청에서 모델이 **생성(출력)**할 수 있는 최대 토큰 수입니다.

  • Qwen3.5는 thinking 토큰 + 응답 토큰이 합산됩니다
  • 너무 크면 thinking이 끝없이 돌 수 있음
  • 8192 권장

입력 토큰 제한(context length)은 별도 플래그가 없으며, 모델의 context window(Qwen3.5: 최대 262K)를 따릅니다. 메모리가 부족하면 --kv-cache-quantization이나 --cache-memory-mb로 간접 제한하세요.

--mllm

멀티모달 모드를 활성화합니다. 이 플래그가 없으면 텍스트만 처리합니다.

--continuous-batching

여러 요청을 동시에 처리합니다. 단일 사용자일 때는 오히려 느려지므로, 동시 사용자가 있을 때만 사용하세요.

--reasoning-parser qwen3

Qwen3.5의 <think>...</think> thinking 내용을 별도 reasoning_content 필드로 추출합니다. Open WebUI에서 thinking 과정을 보고 싶을 때 유용합니다.


6. 트러블슈팅

빠른 참조

증상 원인 해결
thinking만 하고 응답 없음 --max-tokens가 너무 큼 8192로 설정 후 서버 재시작
conda에서 abort / MPI 에러 MPICH 충돌 conda deactivate 후 venv에서 실행
이미지 입력 안 됨 --mllm 미설정 서버 시작 시 --mllm 추가
모델이 안 보임 서버 미실행 curl http://localhost:8090/v1/models 확인
컨테이너 안 뜸 Docker 문제 docker logs open-webui-mlx 확인
포트 충돌 이미 사용 중 lsof -i :3000 / lsof -i :8090
요청 타임아웃 기본 300초 초과 --timeout 600 으로 늘리기
이미지 입력 시 PyTorch 텐서 에러 transformers fast image processor 호환성 문제 아래 이미지 프로세서 호환성 문제 참고
서버가 Ctrl+C로 안 꺼짐 프로세스가 응답 중 lsof -ti :8090 | xargs kill -9

이미지 프로세서 호환성 문제 (transformers + mlx_vlm)

증상

--mllm 모드에서 이미지를 입력하면 아래 에러가 발생합니다:

ValueError: Failed to process inputs with error: Only returning PyTorch tensors is currently supported.

또는 Open WebUI에서:

Response payload is not completed: <TransferEncodingError: 400, message='Not enough data to satisfy transfer length header.'>

원인

vllm-mlx는 내부적으로 mlx_vlm을 사용하여 이미지를 처리합니다. mlx_vlmtransformers 라이브러리의 이미지 프로세서를 호출하는데, transformers >= 5.x에서는 Fast Image Processor가 기본으로 로드됩니다.

요청 흐름:
  Open WebUI → vllm-mlx → mlx_vlm → transformers (이미지 프로세서) → 에러

문제의 핵심:

Fast 버전 Slow 버전
파일 image_processing_qwen2_vl_fast.py image_processing_qwen2_vl.py
반환 형식 PyTorch 텐서만 지원 numpy 등 다양한 형식 지원
mlx_vlm 호환

transformers는 자동으로 fast 버전을 우선 로드하지만, mlx_vlm은 PyTorch 텐서가 아닌 numpy/MLX 배열을 기대하므로 충돌이 발생합니다.

해결 방법

fast 파일을 slow 클래스로 리다이렉트합니다.

1. fast 파일 위치 확인:

# venv 경로에 따라 다를 수 있음
FAST_FILE="$(python -c "import transformers; import os; print(os.path.join(os.path.dirname(transformers.__file__), 'models/qwen2_vl/image_processing_qwen2_vl_fast.py'))")"
echo "$FAST_FILE"

2. 원본 백업:

cp "$FAST_FILE" "${FAST_FILE}.bak"

3. fast 파일을 slow 버전으로 리다이렉트:

cat > "$FAST_FILE" << 'EOF'
"""
Fast Image processor class for Qwen2-VL.
Patched: mlx_vlm 호환을 위해 slow 버전으로 폴백합니다.
원본: image_processing_qwen2_vl_fast.py.bak
"""

from .image_processing_qwen2_vl import Qwen2VLImageProcessor as Qwen2VLImageProcessorFast

__all__ = ["Qwen2VLImageProcessorFast"]
EOF

4. 서버 재시작 후 이미지 테스트

복원 방법

패치를 되돌리려면:

FAST_FILE="$(python -c "import transformers; import os; print(os.path.join(os.path.dirname(transformers.__file__), 'models/qwen2_vl/image_processing_qwen2_vl_fast.py'))")"
cp "${FAST_FILE}.bak" "$FAST_FILE"

언제 이 패치가 필요 없어지나

  • mlx_vlm이 fast image processor를 지원하게 업데이트되면
  • transformers가 PyTorch 외의 텐서 반환을 지원하면
  • vllm-mlx가 자체 이미지 프로세싱을 구현하면

pip install --upgrade mlx-vlm 후 이미지 테스트가 정상이면 패치를 복원해도 됩니다.


부록: docker-compose.mlx.yml 생성

~/PyCharmMiscProject/openwebui/docker-compose.mlx.yml이 없을 때만 실행하세요.

mkdir -p ~/PyCharmMiscProject/openwebui
cd ~/PyCharmMiscProject/openwebui

cat > docker-compose.mlx.yml << 'EOF'
services:
  open-webui:
    image: ghcr.io/open-webui/open-webui:main
    container_name: open-webui-mlx
    ports:
      - "3000:8080"
    environment:
      - OPENAI_API_BASE_URL=http://host.docker.internal:8090/v1
      - OPENAI_API_KEY=none
      - OLLAMA_BASE_URL=
    volumes:
      - open-webui-mlx-data:/app/backend/data
    extra_hosts:
      - "host.docker.internal:host-gateway"
    restart: unless-stopped

volumes:
  open-webui-mlx-data:
EOF