macOS에서 로컬 코딩 에이전트 설정하는 방법
(ikyle.me)- 로컬 코딩 에이전트 구성은 인터넷 장애 때도 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를 사용함
댓글과 토론
Hacker News 의견들
-
벤치마크 프롬프트가 “unified diff를 파싱해 변경된 파일 경로를 반환하는 간결한 Python 함수를 작성하고, 엣지 케이스 두 가지를 설명하라”였고, 각 벤치마크가 약 128토큰을 생성했다면 좋은 결과를 얻기엔 128토큰이 너무 적어 보임
MTP 가속은 예측 토큰이 얼마나 자주 채택되는지에 달려 있는데, 경험상 출력 초반부는 채택률이 더 높아서 짧은 테스트가 거짓 양성 가속을 만들 수 있음
llama.cpp에는 서버를 재시작하고 프롬프트를 보낼 필요 없이 인자를 훑어주는 벤치마크 전용 도구가 있음: https://github.com/ggml-org/llama.cpp/blob/master/tools/llam...
모델 다운로드 섹션도 llama.cpp의-hf인자가 모델을 대신 내려받아 준다는 점을 언급했어야 함. 작성자가 경험을 공유한 건 고맙지만, 초보자에게는 최선의 가이드가 아닐 수 있음- 제대로 된 개발자 가이드로 쓴 글은 아니었음. 화면 녹화가 즐겨찾기를 많이 받고 설정 방법을 묻는 메시지가 오기 시작해서, 이 테스트를 어떻게 구성했는지 빠르게 정리한 것임
Unclothe의 “속도 2배” 발표를 보고 “이 정도면 실제로 쓸 만큼 빨라질까?” 싶어서 직접 설정해 봤음
작년에도 Devstral 같은 걸로 테스트했지만 너무 느리고 멍청해서 계속 써 볼 마음이 안 들었고, 이번에는 드디어 속도와 지능 모두에서 쓸 만하다는 느낌에 도달함 - 현실적으로는 임의의 사용자 프롬프트에 더해 충분한 시스템 프롬프트까지 넣고 실험해야 함. 최소 1000토큰 이상, 실제로는 3000토큰 정도가 좋아 보임
llama.cpp에는 이를 위한 도구가 있고, 제대로 측정하려면 토큰 생성 전에 프리필(prefill) 을 넣어야 함. 점점 32k나 64k 같은 긴 문맥에서의 토큰 생성 속도 측정도 중요해지고 있음 - 128토큰이면 오페라가 아니라 서곡만 벤치마크하는 셈임
- 실제 문제를 살피지 않고 “내 컴퓨터에서는 돌아감”이라고 말하는 것과 비슷함. 128토큰은 정말 아무것도 아니고, 짧은 인사 응답보다 조금 긴 수준임
- 제대로 된 개발자 가이드로 쓴 글은 아니었음. 화면 녹화가 즐겨찾기를 많이 받고 설정 방법을 묻는 메시지가 오기 시작해서, 이 테스트를 어떻게 구성했는지 빠르게 정리한 것임
-
예전에 ollama와 opencode를 써서 비슷한 글을 쓴 적이 있음: https://blog.kulman.sk/running-local-llm-coding-server/
- Ollama는 좋은 선택이 아님: https://sleepingrobots.com/dreams/stop-using-ollama/
opencode는 시스템 프롬프트가 문맥을 너무 많이 먹지 않나? 로컬 모델은 문맥 제약이 큰데, 기억으로 opencode는 그중 10k 정도 또는 그에 가까운 양을 사용함 - 실제로 유용하고, ollama GUI를 쓰면 아마 더 단순화할 수도 있음
- Ollama는 좋은 선택이 아님: https://sleepingrobots.com/dreams/stop-using-ollama/
-
llama.cpp만 쓴다면 무언가를 다운로드하려고
huggingface-cli가 꼭 필요하진 않아 보임.-hf ...를 넘기면 모델을 내려받아 줌
다운로드 위치를 바꾸려면LLAMA_CACHE를 설정하면 됨:
LLAMA_CACHE="models" ./llama-server \
-hf unsloth/gemma-4-31B-it-GGUF:UD-Q4_K_XL \
...- 드래프트 모델에는
-hfd를 쓰면 됨
- 드래프트 모델에는
-
통합 메모리 RAM은 크지만 테라플롭스와 대역폭 GB/s가 중간 이하라면 보통 MoE가 가장 희망적임. 내 환경인 M2 Max 96GB에서
(지능, tok/s, 문맥 깊이)기준 현재 1위는 DeepSeek-V4-Flash REAP25<65gb gguf+ ds4-server + pi agent임
물론 클라우드 API보다 낫진 않지만, 필요하다면 감수하고 쓸 만큼은 됨. 인터넷 없는 4시간 비행에서도 로컬 LLM이 60W를 먹는데 배터리가 충분히 버텼음
REAP을 지원하는 ds4 브랜치는 여기 있음: https://github.com/ljubomirj/ds4/tree/reap-compact-support
DS4F가 784K 문맥에서야 10 tok/s 미만의 사용 불가 수준으로 떨어지는 점이 큰 차이를 만듦 -
이런 로컬 모델이 특정 프로그래밍 언어에 전문가가 아닌 사용자에게도 정말 문제를 해결해 줄 수 있을지 궁금함
인라인 자동완성이나 단위 구현을 넘어서, 실제로 작동하는 기술 명세를 설계하고 조합할 수 있는지 확신이 안 듦 -
llama.cpp/server를 써서 로컬 LLM을 띄우고 Claude Code나 Codex-CLI와 함께 쓰는 건 비교적 간단함
필요한 llama server 설정이 여기저기 흩어져 있는 경우가 많아서, 인기 있는 오픈 LLM 몇 가지에 대한 지침을 여기 관리하고 있음: https://pchalasani.github.io/claude-code-tools/integrations/...- 그걸 일상용으로 쓰고 있나? Claude Code의 프롬프트가 엄청 커서 로컬 모델에서는 프롬프트 처리에 아주 오래 걸리고, 얼마 지나지 않아 문맥도 다 써버리게 됨
-
omlx.ai로 내 하드웨어에 맞는 여러 MLX 모델을 다운로드하고, 그 모델로 오픈소스와 폐쇄형 하네스(Claude Code, Codex)를 자동 실행하는 데 꽤 성공적으로 썼음
웹 또는 데스크톱 UI에서 모두 가능해서, 개인적으로는 omlx를 쓰면 블로그 글을 따라 할 필요가 없음- 64GB M1 Max에서 oMLX나 MLX가 llama.cpp의 GGUF보다 특별히 유리한 점은 못 봤음
지금까지 찾은 Gemma 4 MLX 빌드는 같은 양자화에서 더 느렸고, MTP에서는 훨씬 느렸음
모델을 고른 뒤에는 llama.cpp의 내장 웹 UI가 꽤 좋고, 이것저것 만져볼 때는 LM Studio도 괜찮음
Gemma-4와 Qwen 3.6은 일반적인 opencode 시스템 프롬프트의 큰 덩어리가 전혀 필요 없으며, 빼는 편이 더 나음 - oMLX와 Pi에 붙일 샌드박스를 찾는다면 이게 있음: https://github.com/Dotnaught/pi-sandbox
- Mac에서 로컬 추론을 위한 최첨단이라고 봄. 회귀가 생겨도 개발자들이 엄청 빠르게 대응하고, 최근 본 오픈소스 프로젝트 중 가장 인상적임
- 64GB M1 Max에서 oMLX나 MLX가 llama.cpp의 GGUF보다 특별히 유리한 점은 못 봤음
-
antirez의 ds4로 돌리는 DeepSeek v4 Flash가 꽤 인상적이었음
“저장된 지식” 측면에서는 GPT-4급 모델처럼 느껴지지만, 긴 흐름의 도구 호출은 GPT-4급 모델들보다 더 잘함
128GB MBP M4 Max에서 생성은 약 24 t/s, 프리필은 약 200 t/s가 나옴. 느릴 줄 알았고 코드 생성 같은 작업에서는 실제로 느리지만, 간단한 작업을 위한 머신 오케스트레이터로는 놀랄 만큼 유용함
에이전트형이 아닌 용도에서는 대화하기에 충분히 괜찮은 모델이고, 완전히 자체 구동·비공개라는 장점도 있음
[0]https://github.com/antirez/ds4 -
매우 게으르게 하고 싶다면 터미널에서 Claude Code를 열고, 이 글을 가리킨 다음 그냥 “해줘”라고 시키면 됨
- 이제 Google 검색을 거의 안 하게 됨. 10번 중 9번은 정보 품질이 형편없고, 주변의 스팸 속에서 필요한 내용을 가려내기 어렵기 때문임
반면 Claude는 한 번에 처리하거나 아주 조금만 다듬으면 바로 해줌
지식과 실행으로 가는 관문은 이제 LLM이고, Google Search는 공룡처럼 느껴짐
스마트폰보다도 더 멋질 정도로, 한 세기쯤 미래에 와 있는 느낌임
- 이제 Google 검색을 거의 안 하게 됨. 10번 중 9번은 정보 품질이 형편없고, 주변의 스팸 속에서 필요한 내용을 가려내기 어렵기 때문임