메뉴
HN
Hacker News 48일 전

Codex CLI에서 로컬 Gemma 4 모델 구동기

IMP
8/10
핵심 요약

기존 클라우드 모델(GPT-5.4)을 대체할 수 있는 로컬 모델로서 Gemma 4의 실용성을 실험한 후기입니다. 비용, 프라이버시, API 의존도 문제를 해결하기 위해 26B MoE 및 31B Dense 모델을 각각 Mac과 GB10 워크스테이션에 세팅하여 테스트했습니다. 초기 환경 구축의 어려움(버그 및 호환성 문제)에도 불구하고, Gemma 4는 우수한 도구 호출(Tool calling) 성능을 입증하며 에이전트 기반 코딩 환경에서 로컬 모델의 가능성을 확인시켜 주었습니다.

번역된 본문

저는 Codex CLI에서 로컬 모델로 Gemma 4를 구동해 보았습니다. (Daniel Vaughan, 6분 분량 · 8시간 전)

제 일상적인 에이전트 코딩 작업에서 Gemma 4가 클라우드 모델을 대체할 수 있는지 알고 싶었습니다. 이론이 아니라 실제로 말이죠. 저는 매일 Codex CLI를 사용하며 기본 모델로 GPT-5.4를 구동하고 있습니다. 잘 작동하지만, 모든 토큰에 비용이 들고 모든 프롬프트가 제 코드를 다른 사람의 서버로 전송합니다. Gemma 4는 제대로 작동하는 로컬 도구 호출(Tool calling)을 약속했습니다. 저는 그 약속이 사실인지 확인하기 위해 하루를 보냈습니다.

저는 두 대의 머신을 세팅했습니다. 첫 번째는 어디나 들고 다니는 24GB M4 Pro 맥북 프로로, llama.cpp를 통해 26B MoE 변형 모델을 구동했습니다. 두 번째는 NVIDIA Blackwell 칩에 128GB 통합 메모리를 탑재한 Dell Pro Max GB10으로, Ollama v0.20.5를 통해 31B Dense 변형 모델을 구동했습니다. 둘 다 Codex CLI의 config.toml에서 wire_api = "responses"로 설정하여 커스텀 모델 제공자로 구성했습니다. 그런 다음 베이스라인으로 클라우드 모델과 함께 두 로컬 머신에서 동일한 코드 생성 작업을 실행했습니다.

결론부터 말씀드리면, 로컬 Gemma 4는 작동합니다. 좀 더 자세한 이야기는 한 오후의 디버깅, 놀라운 벤치마크 수치, 그리고 제가 예상하지 못했던 모델 아키텍처에 대한 발견으로 이어집니다.

왜 이것을 원했는가 세 가지가 저를 로컬 모델로 이끌었습니다. 첫째, 비용입니다. 저는 하루에 여러 세션, 때로는 병렬로 Codex CLI를 많이 사용합니다. API 요금이 꽤 쌓입니다. 둘째, 프라이버시입니다. 제가 작업하는 일부 코드베이스는 제 컴퓨터 밖으로 나가서는 안 됩니다. 셋째, 안정성입니다. 클라우드 API는 속도 제한을 걸고, 다운되며, 가격을 변경합니다. 로컬 모델은 그냥 실행됩니다.

제가 이전에 이 작업을 하지 않았던 이유는 로컬 모델이 도구를 호출할 수 없었기 때문입니다. Codex CLI의 모든 가치는 모델이 파일을 읽고, 코드를 작성하고, 테스트를 실행하고, 패치를 적용하는 데서 나옵니다. 모델이 {"tool": "Read", "args": {"file": "package.json"}}을(를) 안정적으로 출력할 수 없다면 에이전트로서 무용지물입니다. 이전 세대의 Gemma는 tau2-bench 함수 호출 벤치마크에서 6.6%를 기록했습니다. 100번 중 93번 실패한 것입니다. 무언가의 기반이 될 수 없는 수치죠. 하지만 Gemma 4 31B는 동일한 벤치마크에서 86.4%를 기록했습니다. 이것이 이 테스트를 실행할 가치가 있게 만든 이유입니다.

작동하는 환경을 구축하기까지의 과정 두 머신 모두 첫 번째 시도에서는 작동하지 않았습니다.

Mac의 경우. 가장 간단한 방법이기 때문에 Ollama로 시작했습니다. 두 가지 버그가 즉시 이를 무산시켰습니다. v0.20.3의 스트리밍 버그는 Gemma 4의 도구 호출 응답을 잘못된 필드로 라우팅하여, tool_calls 배열 대신 추론 출력에 배치합니다. 별도로, Flash Attention 결함으로 인해 Apple Silicon에서 Gemma 4를 사용할 때 약 500 토큰 이상의 프롬프트에서 Ollama가 멈춰버립니다. Codex CLI의 시스템 프롬프트만 해도 대략 27,000 토큰입니다. 따라서 Ollama는 요청 처리를 시작하기도 전에 멈춥니다.

저는 Homebrew를 통해 설치한 llama.cpp로 전환했습니다. 작동하는 서버 명령어에는 6개의 핵심 플래그가 있습니다:

llama-server
-m /path/to/gemma-4-26B-A4B-it-Q4_K_M.gguf
--port 1234 -ngl 99 -c 32768 -np 1 --jinja
-ctk q8_0 -ctv q8_0

24GB 환경에서는 모든 플래그가 중요합니다. -np 1은 여러 슬롯이 KV 캐시 메모리를 곱셈적으로 늘리는 것을 방지하기 위해 단일 슬롯으로 제한합니다. -ctk q8_0 -ctv q8_0은 KV 캐시를 양자화하여 940MB에서 499MB로 줄입니다. --jinja 플래그는 Gemma 4의 도구 호출 템플릿에 필요합니다. 그리고 -m과 직접 경로를 지정하면 1.1GB 비전 프로젝터를 조용히 다운로드하여 메모리 부족(OOM) 충돌을 일으키는 -hf 플래그를 피할 수 있습니다. 또한 Codex CLI config에 web_search = "disabled"를 설정해야 했는데, Codex CLI가 llama.cpp가 거부하는 web_search_preview 도구 유형을 보냈기 때문입니다. 이러한 내용은 어디에도 명확하게 문서화되어 있지 않습니다. 저는 오류 메시지를 읽고, GitHub 이슈를 확인하고, 직접 시도하면서 이를 알아냈습니다.

GB10의 경우. 제가 따르던 계획이 권장했기 때문에 vLLM이 작동할 것이라고 예상했습니다. 하지만 그렇지 않았습니다. vLLM 0.19.0의 컴파일된 확장은 PyTorch 2.10.0용으로 빌드되었지만, aarch64 Blackwell(컴퓨트 capability sm_121)에서 CUDA가 활성화된 유일한 PyTorch는 2.11.0+cu128입니다. ABI가 다릅니다. 시작 시 ImportError가 발생했습니다. CUDA를 사용하여 소스에서 llama.cpp를 빌드했고, 컴파일과 벤치마크는 잘 되었지만, Codex CLI의 wire_api = "responses"가 llama.cpp가 거부하는 비함수형 도구 유형을 보냅니다. 결국 작동한 것은 Ollama v0.20.5였습니다.

원문 보기
원문 보기 (영어)
I ran Gemma 4 as a local model in Codex CLI Daniel Vaughan 6 min read · 8 hours ago -- 1 Listen Share I wanted to know whether Gemma 4 could replace a cloud model for my day-to-day agentic coding. Not in theory, in practice. I use Codex CLI every day, running GPT-5.4 as my default model. It works well, but every token costs money, and every prompt sends my code to someone else’s server. Gemma 4 promised local tool calling that works. I spent a day finding out whether that promise holds. I set up two machines. A 24 GB M4 Pro MacBook Pro, the laptop I carry everywhere, running the 26B MoE variant via llama.cpp. And a Dell Pro Max GB10, 128 GB of unified memory on an NVIDIA Blackwell chip, running the 31B Dense variant via Ollama v0.20.5. Both configured as custom model providers in Codex CLI’s config.toml with wire_api = "responses" . Then I ran the same code generation task on both, and on the cloud model as a baseline. The short answer: local Gemma 4 works. The longer answer involves an afternoon of debugging, some surprising benchmark numbers and a finding about model architecture that I was not expecting. Why I wanted this Three things pushed me towards local models. First, cost. I run Codex CLI heavily, multiple sessions a day, sometimes in parallel. The API bills add up. Second, privacy. Some of the codebases I work with should not leave my machine. Third, resilience. Cloud APIs throttle, go down and change pricing. A local model runs. The reason I had not done this before is that local models could not call tools. Codex CLI’s entire value comes from the model reading files, writing code, running tests and applying patches. If the model cannot reliably emit {"tool": "Read", "args": {"file": "package.json"}} , it is useless as an agent. Previous Gemma generations scored 6.6 per cent on the tau2-bench function-calling benchmark. That is 93 failures out of 100. Not a foundation for anything. Gemma 4 31B scores 86.4 per cent on the same benchmark. That is what made this test worth running. What it took to get a working setup Neither machine worked on the first attempt. The Mac. I started with Ollama, because it is the simplest path. Two bugs killed it immediately. A streaming bug in v0.20.3 routes Gemma 4’s tool-call responses to the wrong field, landing them in the reasoning output instead of the tool_calls array. Separately, a Flash Attention freeze hangs Ollama on any prompt longer than about 500 tokens with Gemma 4 on Apple Silicon. Codex CLI’s system prompt alone is roughly 27,000 tokens. So Ollama freezes before it even starts processing your request. I switched to llama.cpp, installed via Homebrew. The working server command has six load-bearing flags: llama-server \ -m /path/to/gemma-4-26B-A4B-it-Q4_K_M.gguf \ --port 1234 -ngl 99 -c 32768 -np 1 --jinja \ -ctk q8_0 -ctv q8_0 Every flag matters on 24 GB. The -np 1 limits to a single slot, because multiple slots multiply KV cache memory. The -ctk q8_0 -ctv q8_0 quantises the KV cache, reducing it from 940 MB to 499 MB. The --jinja flag is required for Gemma 4's tool-calling template. And -m with a direct path avoids the -hf flag, which silently downloads a 1.1 GB vision projector that causes an out-of-memory crash. The Codex CLI config also needed web_search = "disabled" , because Codex CLI sends a web_search_preview tool type that llama.cpp rejects. None of this is documented anywhere obvious. I found it by reading error messages, GitHub issues and trying things. The GB10. I expected vLLM to work, as the plan I was following recommended it. It did not. vLLM 0.19.0’s compiled extensions are built against PyTorch 2.10.0, but the only CUDA-enabled PyTorch for aarch64 Blackwell (compute capability sm_121) is 2.11.0+cu128. Different ABI. ImportError at startup. I built llama.cpp from source with CUDA, and it compiled and benchmarked fine, but Codex CLI's wire_api = "responses" sends non-function tool types that llama.cpp rejects. What worked was Ollama v0.20.5. The streaming bug that breaks Apple Silicon does not reproduce on NVIDIA. ollama pull gemma4:31b , SSH tunnel to forward port 11434 to my Mac (because Codex CLI's --oss mode checks only localhost), and codex --oss -m gemma4:31b . Text generation and tool calling both worked on the first attempt. The honest summary: the Mac setup took most of an afternoon. The GB10 took about an hour, most of it waiting for model downloads. The benchmark I gave all three configurations the same task through codex exec --full-auto : write a parse_csv_summary Python function with error handling, write tests and run them. GPT-5.4 produced type-hinted code with proper exception chaining, boolean type detection and a clean helper function. Zero dead code. Five tests passed first time. Sixty-five seconds. The GB10’s 31B Dense produced clean, functional code without type hints or boolean detection, but with solid error handling and no dead code. Five tests, first attempt, three tool calls. Seven minutes. The Mac’s 26B MoE left dead code in the implementation, a type inference loop written, abandoned in place, then rewritten below it with the comment ‘Actually, let’s simplify’ still in the source. The test file took five attempts to write. Each time the model introduced new syntax errors through shell heredoc quoting: filerypt instead of file_path , encoding=' 'utf-8' with a rogue space, fileint(file_path) . Ten tool calls to accomplish what the GB10 did in three. The speed numbers, and why the Mac is faster than expected I ran llama-bench on both machines with the same context lengths. The Mac generates tokens 5.1 times faster than the GB10. That surprised me, because both machines have 273 GB/s LPDDR5X memory bandwidth. The explanation is the Mixture of Experts architecture. Token generation is memory-bandwidth limited: every token requires reading the model’s active parameters from memory. The 31B Dense reads all 31.2 billion parameters for every token. The 26B MoE activates only 3.8 billion per token, roughly 1.9 GB at Q4 quantisation. The Mac pushes 1.9 GB per token through its 273 GB/s bandwidth and gets 52 tok/s. The GB10 pushes 17.4 GB per token through the same bandwidth and gets 10 tok/s. Same pipe, vastly different payload. Prompt processing was the other surprise. I expected the GB10’s Blackwell GPU to dominate, but the Mac held its own: 531 tok/s versus 548 tok/s at 8K context. The MoE’s sparse activation benefits prompt processing too, not only generation. What I learned The finding I did not expect: model quality matters more than token speed for agentic coding. The Mac generated tokens 5.1 times faster. It still finished only 30 per cent sooner (4m 42s versus 6m 59s). The speed advantage was consumed by retries, ten tool calls instead of three, five failed test writes and dead code the model did not clean up. The GB10’s slower model got it right the first time. The cloud model proved this most starkly. Fastest time, fewest tokens, best quality. Five out of five in 65 seconds. A model that gets it right the first time beats one that iterates faster but needs more iterations. But local is viable. Both machines produced working code with passing tests. The quality gap between Gemma 3 (6.6 per cent tool calling) and Gemma 4 (86.4 per cent) is the gap that matters. Going from ‘broken’ to ‘works’ is the step that makes local agentic coding practical. For my own workflow, I have landed on a hybrid. codex --profile local for iteration and privacy-sensitive work. Default cloud for anything complex. Codex CLI's profile system makes switching a single flag. If you are going to try this A few specifics from the setup that will save you time. On Apple Silicon, skip Ollama for Gemma 4 entirely. Use llama.cpp with --jinja . Set web_search = "disabled" in your Codex CLI profile. Use -m with a direct GGUF path, not -hf . Set context to 32,768 (Codex CLI's system prompt needs at least 27,000 tokens) and quantise the KV cache wit