16GB 맥에서 로컬 어시스턴트 구축에 보낸 6일, 설계를 바꾼 수치들
원문 제목 — Six days building a local assistant on a 16 GB Mac. The numbers that changed my design.
📄 전문 번역
필자는 9월 첫 주를 16GB M1 Pro에서 Ollama로 구동하는 Gemma 4 기반 개인 어시스턴트 구축에 보냈다. Node 백엔드, React 웹 UI, Expo 폰 클라이언트, 두 모델 라우팅, 세션 간 메모리, 개인 노트 대상 RAG, 툴 호출 기반 웹 검색, 커맨드 정책 뒤의 코딩 에이전트 등이다. 시작 전 가정 대부분이 틀린 것으로 드러났고, 실제로 설계를 바꾼 측정값들을 정리한다.
두 모델은 ollama ps가 무엇을 표시하든 함께 상주하지 않는다. ollama ps는 gemma4:latest에 3.2GB, gemma4:12b에 8.1GB를 보여줘 두 모델 상주를 계획했지만 Ollama 스케줄러는 e4b 빌드에 10.6GiB로 예측하고(약 6GB의 임베딩 테이블이 호스트 메모리에 있어 ps에는 나타나지 않는다) 매번 다른 모델을 축출하며 스왑마다 4~7초 콜드 로드가 걸린다. gemma4:e4b-it-qat로 전환하자 점유율이 6.4GiB로 낮아지고 27tok/s에서 33tok/s로 올랐지만 여전히 12B와 공존하지 못해 라우터는 기본 fast, 강한 신호에서만 deep, 대화가 deep에 들어가면 유지하는 스티키 방식이 되었고 대화당 스왑은 최대 한 번이다. 프롬프트에서 날짜 위치도 턴당 1.5초 비용을 가져왔다. Ollama는 가장 긴 공유 접두사에 KV 캐시를 재사용하는데 시스템 프롬프트 상단에 현재 날짜를 두면 후속 질문마다 대화 전체를 재평가해 첫 토큰까지 1.8~2.0초였고, 메시지마다 변하는 것(날짜, 검색 청크, 회상 메모리)을 사용자 턴 상단 블록으로 옮기고 시스템 프롬프트를 고정하자 0.5초, 0.4초로 줄었다.
시스템 프롬프트는 툴 호출을 억제하며 넛지 위치가 문구보다 중요하다. web_search를 제공한 gemma4-fast, 9개 프롬프트(조회 필요 5개), 시드 7 조건에서 시스템 메시지 없는 베어 프롬프트는 9개 중 8개 정답, 모르면 말하라는 내용의 약 700토큰짜리 전체 시스템 프롬프트는 9개 중 5개로 조회 4건을 놓쳤고, 질문 앞 컨텍스트 블록에 최신 정보가 필요하니 먼저 검색하라는 한 줄을 넣으면 10개 중 5개로 넛지 없을 때보다 나빴지만 같은 넛지를 질문 뒤 생성 직전에 두면 9개 중 9개에 불필요한 검색 0건이었다. Gemma에는 시스템 역할이 없어 Ollama가 시스템 프롬프트를 첫 사용자 턴에 접어 넣고 소형 모델은 모르면 말하라를 조회하지 않아도 되는 허가로 읽으며, 넛지가 잘못된 위치에 있으면 툴을 호출하지 않고도 32°C 부분 흐림 [weather.com]이라고 답했기에 이제 실제 툴 결과에 등장한 도메인만 인용하는 별도 규칙을 두고 있다.
메모리는 전부 주입 시 턴당 2,300토큰이 들었다. 유휴 시간 추출기가 필자에 관한 109개 사실을 쌓아 매 프롬프트 고정 비용의 절반을 차지했고, 이제 선호와 고정 사실은 시스템 프롬프트에 넣고 일반 사실은 nomic-embed-text로 임베딩해 메시지마다 L2 1.0 이내 최근접 10개를 사용자 턴으로 회상하며 약 150토큰이다. 같은 사실의 재표현은 0.12~0.30, 같은 주제의 다른 사실은 0.53 이상 떨어져 있어 0.35 이내는 쌓지 않고 기존 표현을 교체하며, 추출기가 테스트 프롬프트였던 바다를 묘사하는 정확히 세 개의 소문자 단어로 답하라는 문장을 상시 선호로 저장해 발견할 때까지 모든 답이 짧고 소문자였던 일도 있었다. 소형 모델은 과수집하는 경향이 있어 모든 사실을 클릭 한 번으로 삭제 가능하게 했다.
코딩 에이전트는 기본값을 safe가 아닌 confirm으로 둔다. 웹 검색과 같은 툴 루프에 read, search, edit_file, run_command, git 검사 등 10개 툴을 쓰며 정확한 기존 텍스트를 쓰는 edit_file은 통합 diff보다 소형 모델에서 훨씬 신뢰롭고, 모든 커맨드는 셸 연산자로 분할해 각 조각을 safe/confirm/deny로 분류하며 인식되지 않는 것은 confirm으로 처리한다. UI는 테스트 커맨드에 모델의 말이 아닌 실제 종료 코드 기준으로 passed 또는 failed 태그를 붙인다. 모델은 실행하지 않고도 테스트 통과를 주장할 수 있기 때문이다. 필자는 전체 내용(아키텍처, 모든 수치, 재구축 순서)을 단일 HTML 문서로 정리해 관심 있으면 댓글로 공유할 뜻이며, 스택은 Node/TypeScript/Fastify, SQLite + sqlite-vec, Vite + React, Expo에 모델은 Ollama, 받아쓰기는 인프로세스 Whisper(transformers.js)로 오디오가 머신 밖으로 나가지 않게 하고 폰과 맥 사이는 Tailscale로 연결한다. 16GB에서 이 작업을 해본 사람에게 deep 모델로 9B 이상을 두면서 실제 공존하는 fast/deep 쌍을 찾았는지 묻고 있으며, 약 2.5GB의 qwen3.5:4b가 가능해 보이지만 아직 측정하지는 않았다.
💬 주요 댓글 번역 (3)
🔍 루키 분석
16GB M1 Pro에서 Ollama로 Gemma 4 기반 개인 어시스턴트를 구축한 6일간의 과정과 실측 수치를 공유한 글이다. ollama ps 표시와 달리 두 모델이 공존하지 못해 스왑마다 4~7초가 걸리고, KV 캐시 재사용을 해치는 날짜 위치 문제와 시스템 프롬프트가 툴 호출을 억제하는 현상 등을 수치로 제시한다. 메모리 회상 방식과 코딩 에이전트의 confirm 기본값 같은 소형 모델 운용 교훈이 담겨 있으며, 9B 이상 deep 모델과 공존하는 fast/deep 쌍에 대한 질문으로 마무리한다.
— 루키 · glm-5.3-flash:cloud
게시물 감사한다. 흥미로워 보이며 HTML 문서를 받아볼 수 있으면 좋겠다. 독일에서 보내는 인사다.
글을 보고 싶고 도움이 될 경험도 있다. 우선 그래프 검색 툴/MCP 계층으로 에이전트가 정확히 필요한 것을 찾게 하는 메모리 툴이 필요하다고 본다. 이는 토큰 오버헤드를 낮추고 정확도를 높인다. 깃 저장소가 있다면 받아서 도울 수 있고, 접근 방식이 내 것과 비슷해 마음에 든다. 다만 나는 컴퓨팅과 저장소가 더 많아 다른 모델을 대신 테스트하고 벤치마크할 수 있다.
Jev