macOS에서 로컬 코딩 에이전트 설정하는 방법

1 hour ago 4
  • 로컬 코딩 에이전트 구성은 인터넷 장애 때도 macOS에서 OpenAI 호환 API로 모델을 실행하고 Pi에서 텍스트와 이미지 입력을 처리하도록 만드는 설정임
  • Apple M1 Max 64GB, macOS 15.7.7에서 llama.cpp Metal과 Gemma 4 26B-A4B GGUF 모델을 사용했으며, 기본 생성 속도는 58.2 tok/s였음
  • MTP draft model을 추가하고 --spec-draft-n-max 3으로 조정한 뒤 생성 속도가 72.2 tok/s로 올라가 약 24% 개선됨
  • mmproj-BF16.gguf를 --mmproj로 로드하고 Pi 모델 입력을 ["text", "image"]로 설정해야 스크린샷 같은 이미지 입력이 전달됨
  • 최종 구성은 llama.cpp 서버를 127.0.0.1:8080/v1에서 실행하고 Pi가 이를 로컬 제공자로 쓰는 방식이며, Qwen3.6 35B-A3B는 더 나은 코딩 에이전트 벤치마크를 보였지만 이 테스트에서는 55 tok/s로 더 느렸음

로컬 코딩 에이전트 구성 목표

  • 인터넷 장애가 몇 번 발생해 코딩 에이전트를 사용할 수 없었던 상황이 로컬 실행 구성을 시도한 계기가 됨
  • 원하는 구성은 Mac에서 실제로 쓸 만큼 빠르고, OpenAI 호환 API를 통해 다른 도구에서도 사용할 수 있어야 했음
  • 필요할 때 스크린샷이나 이미지를 처리해 에이전트가 만든 결과물을 다시 입력으로 줄 수 있는 구성이 목표였음
  • 최종 구성은 llama.cpp, Gemma 4 26B-A4B GGUF, Q8 MTP draft model, Gemma 4 multimodal projector, Pi 터미널 코딩 에이전트로 이뤄짐
  • 테스트 환경은 Apple M1 Max, 64GB 통합 메모리, macOS 15.7.7이었음

모델

  • 메인 모델은 gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf였으며, Hugging Face의 unsloth-gemma-4-26B-A4B-it-GGUF 저장소에 있음
  • 해당 파일 크기는 약 16GB이며, MTP draft head와 multimodal projector를 함께 두면 모델 폴더는 약 17GB가 됨
  • 벤치마크 프롬프트는 Write a compact Python function that parses a unified diff and returns the changed file paths. Then explain two edge cases.였음
  • 각 벤치마크는 약 128개 토큰을 생성함

기본 실행: llama.cpp + Metal

  • 메인 모델을 llama.cpp와 Metal 가속으로 직접 실행했음
  • 실행 명령은 llama-cli에 모델 경로, -ngl 999, -fa on, -c 4096, -n 128을 지정하는 방식이었음
  • 기본 구성의 프롬프트 처리 속도는 298.0 tok/s, 생성 속도는 58.2 tok/s였음
  • 58 tok/s는 빠르지는 않지만 사용할 수 있는 수준이며, 코딩 에이전트 작업에서는 많은 도구 호출 때문에 가능한 한 빠른 속도가 필요함

MTP draft model 추가

  • Gemma 4에는 MTP/gemma-4-26B-A4B-it-Q8_0-MTP.gguf 형태의 MTP draft model이 제공됨
  • llama.cpp에서는 이를 --model-draft, --spec-type draft-mtp, --spec-draft-n-max로 투기적 디코딩(speculative decoding)에 로드함
  • MTP 첫 실행은 draft token 4개에서 69.2 tok/s를 기록함
  • Unsloth 문서는 --spec-draft-n-max 2를 시작점으로 권장하지만, 1부터 6까지 하드웨어별로 테스트해 가장 빠른 값을 쓰라고 함
  • --spec-draft-n-max를 조정한 결과, draft token 3개에서 72.2 tok/s가 가장 빨랐음
  • 메인 모델 단독은 58.2 tok/s였고, Q8 MTP draft model을 더한 구성은 72.2 tok/s였음
  • 프롬프트 처리 속도는 거의 유지됐고, 생성 속도는 약 24% 개선됨

MTP 튜닝 결과

  • --spec-draft-n-max 값 1부터 6까지 테스트했음
  • 값 1은 프롬프트 295.5 tok/s, 생성 68.4 tok/s였음
  • 값 2는 프롬프트 299.1 tok/s, 생성 72.0 tok/s였음
  • 값 3은 프롬프트 295.6 tok/s, 생성 72.2 tok/s로 가장 빨랐음
  • 값 4는 생성 70.7 tok/s, 값 5는 63.7 tok/s, 값 6은 61.2 tok/s로 느려졌음
  • M1 Max 환경에서는 3이 가장 빨랐고, 2도 충분히 가까운 결과를 냈음

MLX 비교

  • Mac에서 모델을 실행하는 더 빠른 방식을 확인하기 위해 mlx-lm 기반 MLX 모델도 테스트했음
  • llama.cpp Metal + MTP는 Unsloth GGUF Q4와 Q8 MTP 조합에서 72.2 tok/s를 기록함
  • llama.cpp Metal 단독은 Unsloth GGUF Q4에서 58.2 tok/s를 기록함
  • MLX-LM은 Unsloth UD MLX 4-bit에서 45.8 tok/s를 기록함
  • MLX-LM은 mlx-community 4-bit에서 43.9 tok/s, mlx-community OptiQ 4-bit에서 38.1 tok/s를 기록함
  • 이 특정 구성에서는 llama.cpp가 MLX보다 빨랐고, MTP를 적용한 llama.cpp가 가장 좋은 선택이었음
  • gemma-4-swift-mlx로 Gemma 4 MTP도 시도했지만, 테스트한 26B 4-bit MLX 체크포인트가 로더의 예상 weight key와 맞지 않아 새 모델을 다시 내려받아 조정하지 않고 중단함

이미지 지원 추가

  • Pi에서 스크린샷을 첨부하려면 모델 입력이 텍스트 전용이면 안 됨
  • 원래 로컬 모델 항목은 "input": ["text"]로 설정되어 있었고, 이 경우 Pi가 이미지 도구 출력을 모델에 제대로 보내지 못함
  • llama.cpp 서버도 멀티모달 기능을 위해 Gemma 4 multimodal projector인 mmproj-BF16.gguf가 필요함
  • --mmproj로 projector를 로드하면 llama.cpp가 멀티모달 지원을 알리고 Pi가 이미지를 보낼 수 있음
  • projector 없이 llama.cpp Metal + MTP를 실행한 테스트는 프롬프트 120.3 tok/s, 생성 71.4 tok/s였음
  • mmproj-BF16.gguf를 로드한 최종 실행은 프롬프트 297.4 tok/s, 생성 72.2 tok/s였음
  • projector를 로드한 최종 실행에서 텍스트 생성 속도 저하는 나타나지 않았음

llama.cpp 설치

  • 의존성은 Homebrew로 cmake, git, tmux, python@3.11을 설치함
  • ~/Developer/ML-Models/Gemma4/repos 경로를 만들고 ggml-org/llama.cpp 저장소를 repos/llama.cpp로 클론함
  • 빌드는 cmake -B build -DCMAKE_BUILD_TYPE=Release -DGGML_METAL=ON -DGGML_ACCELERATE=ON으로 구성함
  • 이후 cmake --build build --config Release -j로 릴리스 빌드를 수행함
  • 테스트한 빌드는 GGML_METAL=ON, GGML_ACCELERATE=ON, GGML_BLAS=ON, GGML_BLAS_VENDOR=Apple 설정을 가짐

모델 파일 다운로드

  • Python 3.11 가상환경을 만들고 huggingface_hub와 hf_xet를 설치함
  • huggingface-cli download로 Gemma 4 메인 모델, mmproj-BF16.gguf, MTP draft model을 내려받음
  • 다운로드 대상 파일은 gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf, mmproj-BF16.gguf, MTP/gemma-4-26B-A4B-it-Q8_0-MTP.gguf임
  • 최종 모델 폴더는 models/unsloth-gemma-4-26B-A4B-it-GGUF/ 아래에 세 파일을 포함함

로컬 서버 시작

  • 최종 서버는 llama-server로 실행하며, 메인 모델과 MTP draft model, multimodal projector를 모두 지정함
  • 주요 옵션은 --spec-type draft-mtp, --spec-draft-n-max 3, -ngl 999, -fa on, -c 65536, --parallel 1임
  • 서버는 --host 127.0.0.1 --port 8080으로 실행함
  • OpenAI 호환 엔드포인트는 http://127.0.0.1:8080/v1
  • start_server.sh 래퍼는 tmux 세션에서 서버를 실행하고 로그를 logs/llama-server-mtp.log에 남김
  • chmod +x start_server.sh 후 ./start_server.sh로 서버를 시작함
  • 서버 동작 여부는 curl http://127.0.0.1:8080/v1/models로 확인함

Pi 설정

  • Pi는 모델 제공자 설정을 ~/.pi/agent/models.json에서 읽음
  • 로컬 제공자 gemma4-local의 baseUrl은 http://127.0.0.1:8080/v1을 가리킴
  • api는 openai-completions이고, 로컬 서버이므로 authHeader는 false로 둠
  • 모델 ID는 gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf이고, 이름은 Gemma 4 26B-A4B Q4 + MTP로 설정함
  • input은 ["text", "image"]여야 하며, 그렇지 않으면 Pi가 모델을 텍스트 전용으로 취급함
  • 컨텍스트 윈도는 65536, 최대 토큰은 8192로 설정함
  • 필요하면 ~/.pi/agent/settings.json에서 defaultProvider를 gemma4-local, defaultModel을 해당 GGUF 파일명으로 지정함
  • pi --offline --list-models gemma 실행 시 이미지 지원이 yes로 표시되는 결과를 기대함
  • 로컬 모델 실행은 pi --provider gemma4-local --model gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf로 수행함
  • 비대화형 실행은 pi -p --provider gemma4-local --model gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf "Explain what this repository does" 형태로 수행함
  • 스크린샷 입력은 pi -p @"/path/to/screenshot.png" "Describe this image and point out anything relevant to the UI" 형태로 수행함

최종 구성

  • 최종 추론 런타임은 llama.cpp임
  • macOS 가속은 Metal + Accelerate 조합임
  • 메인 모델은 gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf임
  • draft model은 gemma-4-26B-A4B-it-Q8_0-MTP.gguf임
  • MTP 설정은 --spec-draft-n-max 3임
  • multimodal projector는 mmproj-BF16.gguf임
  • 서버는 127.0.0.1:8080의 llama-server임
  • API는 OpenAI 호환 /v1임
  • 코딩 에이전트는 Pi이며, Pi 모델 입력은 ["text", "image"]임
  • MTP draft model은 이 환경에서 Gemma 4 생성 속도를 58.2 tok/s에서 72.2 tok/s로 올렸고, 로컬 OpenAI 호환 서버로 실행할 만큼 구성이 단순함

Qwen3.6 35B-A3B 대안

  • 일부는 Gemma 4 26B-A4B 대신 Qwen3.6 35B-A3B 사용을 제안함
  • 확인 가능한 벤치마크 기준으로 Qwen은 Gemma 4보다 훨씬 더 나은 코딩 에이전트라고 평가됨
  • 하지만 Qwen 구성은 더 느렸으며, Qwen3.6-35B-A3B-UD-Q4_K_XL.gguf, unsloth-Qwen3.6-35B-A3B-MTP-GGUF, mmproj-BF16.gguf 조합에서 55 tok/s를 기록함
  • 72 tok/s 대신 55 tok/s는 사용자가 기다리는 상황에서 상당한 차이로 작용함
  • Qwen 모델 다운로드는 unsloth/Qwen3.6-35B-A3B-MTP-GGUF에서 Qwen3.6-35B-A3B-UD-Q4_K_XL.gguf와 mmproj-BF16.gguf를 받는 방식임
  • Qwen 서버는 같은 llama-server를 쓰되 --port 8081로 실행함
  • Pi 설정의 Qwen 제공자 이름은 qwen36-local, baseUrl은 http://127.0.0.1:8081/v1
  • Qwen 모델 설정은 reasoning: true, input: ["text", "image"], contextWindow: 65536, maxTokens: 8192를 사용함
Read Entire Article