Ollama의 기본 설정이 내 소형 모델들을 망치고 있었다
요약
본 글은 로컬 환경에서 LLM을 개발하고 사용할 때 발생하는 문제점들을 다루며, 특히 Ollama와 Claude Code 사용 경험을 공유합니다. 저자는 지속적인 메모리를 가진 데스크톱 애플리케이션 'Qwellar'를 개발하며 겪는 디버깅 과정을 통해 실질적인 노하우를 전하고자 합니다.
핵심 포인트
- 로컬 AI 환경 구축 시 발생하는 문제점들을 기록하고 해결하는 과정 공유
- Ollama는 로컬 하드웨어에서 모델을 실행하여 API를 제공함 (클라우드 아님)
- 소형 모델(Small Model)은 클라우드 최첨단 모델보다 설정에 더 민감할 수 있음
로컬 LLM을 실행해 봤는데 기대했던 것보다 멍청하게 느껴졌다면... 환영합니다.
이 블로그는 로컬 AI를 이용해 개발할 때 무엇이 깨지는지, Claude Code와 Ollama를 사용하면서 어떤 문제들이 발생하는지에 대한 내용과 제가 그것들을 고치기 위해 시도하는 방법에 관한 것입니다.
어떤 조언이라도 정말 감사하게 생각합니다. 현재 겪고 있는 질문, 댓글 또는 문제가 있다면 자유롭게 남겨주세요.
나에 대해
저는 컴퓨터 공학(CS) 출신이 아니라 금융 분야 출신입니다. 지난 9개월 동안 Claude Code와 Ollama를 사용해 소프트웨어를 개발해 왔는데, 대부분은 어려운 방법으로 배우고, 무엇이 깨지는지 기록하며, Claude Code와 협력하여 그것들을 고치는 방식으로 진행했습니다. 이 블로그가 제 첫 글이니 너그러이 봐주세요. 제 블로그의 목적은 이러한 배움들을 해당 분야의 다른 사람들에게 유용한 것으로 바꾸는 것이며, 과정 중에 도움을 받는 것도 목표입니다.
먼저, 프로젝트에 대해
그 이름은 Qwellar입니다.
이것은 제가 개발하고 있는 데스크톱 애플리케이션으로, 로컬 AI를 장기적인 존재처럼 다룹니다. 즉, 지속적인 메모리(persistent memory)를 가지고 있으며, Ollama를 통해 로컬 모델과 채팅할 수 있고, 시간이 지남에 따라 AI와 작업하도록 설계된 워크스페이스입니다. 이것은 순전히 학습 과정이자 사이드 프로젝트이며, 개발하면서 계속 이러한 문제들에 부딪히기 때문에 디버그 스토리로 남게 됩니다. 그리고 저는 비슷한 작업을 하는 다른 사람들도 같은 문제들을 겪을 것이라고 생각합니다.
무엇에 대해 글을 쓸 것인가
- 로컬 LLM 실행: Ollama의 실제 사용법, 소형 모델의 신뢰성(reliability), 대부분의 사람들이 생각하는 것보다 더 중요한 설정 선택지들.
- Claude Code를 이용한 개발 패턴: 서브 에이전트(Subagent) 구성, 워크플로우 규율(workflow discipline), 무엇이 효과적이고 무엇이 시간을 낭비하게 만드는가.
- AI 측면의 디버그 스토리: 이상한 실패들, 막다른 길, 나중에 돌아보니만 말이 되는 수정 방법들.
- 내가 시도해 본 도구들에 대한 솔직한 견해: 리뷰가 아닙니다.
여러분이 원하는 것
제가 틀렸다고 생각하면 — 말씀해주세요. 제가 아직 파악하지 못한 것이 있다면 — 공유해 주세요. 저는 이곳을 방송 채널이 아니라 학습 공간으로 만들고 있습니다. 댓글에서 때로는 가장 큰 가치가 발생합니다.
Ollama가 실제로 무엇인지
Ollama는 사용자의 컴퓨터, 자체 하드웨어에서 AI 모델을 실행하는 프로그램입니다. 클라우드가 아닙니다. 만약 저처럼 llama3.2:3b(Meta의 Llama 3.2, 30억 개의 파라미터) 같은 모델을 다운로드했다면, Ollama는 이 모델에 localhost:11434에서 API를 제공합니다. 숫자는 기본적으로 Ollama가 수신 대기하는 포트일 뿐입니다. 제 프로젝트는 그 API로 프롬프트를 보내고 응답을 받습니다. OpenAI도, Anthropic도 필요 없습니다. 인터넷 연결도 필요하지 않습니다.
이것이 바로 '로컬 AI'의 전체 약속입니다. 모델이 사용자의 기기에 존재하므로 채팅 내용이 외부로 나가지 않습니다.
제가 말하는 '소형 모델(small model)'이란 사실 일반 노트북에서도 실행할 수 있을 만큼 작은 모델을 의미합니다. Llama 3.2 3B는 약 2GB의 RAM에 들어갑니다. GPT-4나 Opus 4 시리즈 같은 최첨단 클라우드 모델(Anthropic)은 수천억 개에서 1조 개 이상의 파라미터에 달하는 것으로 알려져 있습니다. Llama 3.2 3B는 이들에 비해 매우 작습니다.
작은 두뇌일수록 어떻게 대화하느냐에 더 민감합니다.
모든 것을 망가뜨린 두 개의 다이얼
Ollama API는 그저 HTTP입니다. 제 프로젝트가 Ollama와 통신할 때, JSON 본문과 함께 http://localhost:11434/api/chat으로 POST 요청을 보내는 것일 뿐입니다. 어떤 웹 API를 호출하는 것과 같습니다.
여기가 최소 버전입니다 — 옵션 없이 프롬프트만 있는 경우:
POST http://localhost:11434/api/chat
{
총 세 개의 필드(field)가 있습니다: model, messages, 그리고 options입니다. options 내부의 각 항목은 하나의 다이얼(dial)이며, 온도(temperature), num_ctx, top_p, top_k, repeat_penalty, seed, num_predict, stop 등이 있습니다. 저는 그중 가장 중요했던 두 가지에 초점을 맞추겠습니다.
이제 Ollama는 기본값 대신 제가 설정한 다이얼을 사용하여 동일한 모델로 동일한 프롬프트(prompt)를 실행합니다. 만약 options를 아예 생략하면, Ollama는 기본값(temperature 0.8, num_ctx 2048)으로 돌아갑니다. 이것이 함정입니다. 침묵은 곧 기본값을 의미합니다. 대부분의 사람들은 이 필드 자체가 존재한다는 사실조차 모릅니다.
### 다이얼 1: temperature
온도(Temperature)는 모델이 다음 단어를 선택할 때 얼마나 '창의적'일지를 제어합니다.
- temperature = 0 → 모델은 항상 가장 가능성이 높은 단어 하나만을 선택합니다. 지루하지만 예측 가능합니다.
- temperature = 0.15 → 주로 가장 가능성 높은 단어를 사용하며, 가끔 근접한 두 번째 단어를 사용합니다. 신뢰할 수 있습니다.
- temperature = 0.8 → 모델이 주사위를 더 많이 굴립니다. 다섯 번째나 열 번째로 가능성이 높은 단어를 선택할 수도 있습니다. 시(poetry)에는 재미있지만, 구조화된 출력(structured output)에는 형편없습니다.
Ollama는 기본적으로 temperature = 0.8로 설정되어 배포됩니다.
이것이 왜 중요할까요? 사용자는 AI가 단순히 답변하는 것 이상을 하기를 원하기 때문입니다. 제 프로젝트의 경우, 확인 카드(confirmation card)를 표시하거나, 검색을 트리거하거나, 새로운 채팅 컨텍스트(chat context)를 시작하는 등의 작업을 수행하길 바랍니다. 문제는 모델이 텍스트만 출력하는 방법을 안다는 것입니다. 버튼을 누를 수는 없습니다.
그래서 저는 모델에게 코드 단어들을 가르쳤습니다. 시스템 프롬프트(system prompt)에서
0.15에서 모델은 지루하고 신뢰할 수 있게 됩니다. 제가 가르친 대로 정확하게 태그를 방출합니다.
### 다이얼 2: num_ctx (컨텍스트 창)
이것은 모델이 한 번에 '볼' 수 있는 토큰의 개수입니다. 토큰은 대략 단어의 3/4 정도입니다.
Ollama는 기본적으로 num_ctx = 2048로 제공됩니다. 2048 토큰은 약 1500단어, 아마도 채팅 턴(turn)을 10번 할 수 있는 분량입니다.
여기서 치명적인 문제가 있습니다: Ollama는 사용자가 이 한계를 초과하는지 알려주지 않습니다. 만약 대화가 3000 토큰인데 num_ctx가 2048이라면, Ollama는 시작 부분을 조용히 잘라냅니다. 모델은 말 그대로 채팅이 어떻게 시작되었는지 볼 수 없습니다. 사용자가 "제가 처음에 뭐라고 했었죠?"라고 물으면, 첫 메시지가 사라졌기 때문에 지어내서 대답합니다.
사용자는 이를 알지 못합니다. 오류가 없습니다. AI는 단지 10턴 정도 지나면 이상하게 기억을 잃습니다.
## 제가 개선한 다섯 가지 사항
1. 온도(temperature) 기본값을 0.8에서 0.15로 변경했습니다. 즉시, 소형 모델들이 구조화된 출력(structured output)을 망가뜨리는 것을 멈췄습니다. `<confirm_start_fresh>`가 제대로 작동하기 시작했고, JSON 핸드오프도 더 이상 손상되지 않았습니다.
2. num_ctx를 2048에서 16384로 변경했습니다. 컨텍스트가 8배 증가했습니다. 모델은 고갈되기 전에 약 80턴을 볼 수 있습니다. 기억이 나지 않는 단편적인 조각이 아닌, 전체 대화가 가능합니다.
3. 옵션 자체가 Ollama에 도달하지 않던 버그를 수정했습니다. 알고 보니 electron/main.js에서 IPC 호출을 Ollama로 전달할 때 options 객체를 누락하고 있었습니다. 그래서 앱에서 temperature = 0.15로 설정하더라도, Ollama는 아무것도 받지 못하고 어쨌든 0.8을 사용했습니다. 이런 버그는 실제로 테스트를 해봐야 발견할 수 있는 종류입니다. 이 버그가 숨겨져 있었던 이유가 있고, 곧 설명드리겠습니다.
4. 프롬프트를 강화했습니다. 소형 모델들은 지시사항을 문자 그대로 받아들이고 뉘앙스를 건너뜁니다. 그래서 "JSON 핸드오프를 출력해 주세요" 대신, 저는 "이 JSON만 출력하세요. 앞이나 뒤에 어떤 텍스트도 포함하지 마세요. 마크다운 코드 울타리도 포함하지 마세요."라고 작성합니다. 부정적인 예시(negative examples)를 추가합니다. 모델이 실제로 생성했던 나쁜 출력을 보여주고
큰 모델들은 알아서 해낸다. 소형 모델들은 가이드라인이 필요하다. 솔직히 말해서, 나는 이게 마음에 들지 않는다. 프롬프트를 강화한다는 것은 코드가 아닌 영어에 유지보수 비용을 지불한다는 의미이다. 새로운 기능이 추가될 때마다 더 많은 프롬프트 규칙이 생긴다. 작동은 하지만, 내가 원하는 방향은 아니다.
5. 파서를 더 관대하게 만들었다. 가끔 모델이 출력물을 약간 망치기도 한다 (추가 공백, 태그 뒤의 추가 텍스트, 또는 사용해서는 안 되는 마크다운 형식으로 전체 응답을 감싸기 등). 이제 내 파서는 실패하는 대신 이러한 작은 실수를 처리한다(실패를 던지는 대신 허용한다). 이것은 깊이 방어(defense-in-depth)이다. 나는 모델이 완벽할 것이라고 기대하지 않고, 내가 만든 파서가 모델의 오류를 잡아낼 만큼 충분히 강력하기를 바라는 것이다.
## 'API가 200을 반환하는지'에 대한 짧은 언급
잠시 IPC 버그로 돌아가 보자. 이 버그가 오랫동안 숨겨져 있던 이유는 '200'이 실제로 무엇을 의미하느냐에 달려 있다.
HTTP 응답에는 상태 코드(status code)가 포함되어 있다. 이는 서버가 모든 응답과 함께 보내는 100부터 599 사이의 숫자이다. 이것은
## 왜 이것이 좋은 주제라고 생각하는가
현재 Ollama를 사용하는 사람들의 99%는 기본 설정(stock defaults)을 그대로 사용하고 있으며, 그 사실조차 모릅니다. 그들은 llama3.2:3b를 시도해보고 나쁜 결과를 얻은 뒤, '소형 모델은 별로다'라고 결론 내립니다. 그리고 모델의 탓을 합니다. 하지만 모델 자체가 사람들이 생각하는 것만큼 나쁘지는 않습니다. 기본 설정이 그것을 망치고 있었던 것입니다.
## 실제로 설정을 어떻게 변경했는지 — 코드 관점
이것은 기술적인 부분입니다. 세 개의 파일에 걸쳐 네 가지 작은 수정만 했습니다. 코드를 작성하지 않는 사람에게는 복잡해 보일 수 있지만, 한번 보면 사실 꽤 간단합니다.
앞서 말씀드렸듯이: 제 프로젝트는 다음과 같은 JSON 본체를 Ollama로 전송합니다:
{
"model": "llama3.2:3b",
"messages": [...],
...
}
질문은 이것입니다. 이 `options` 객체는 어디서 오는 것일까요?
### 1단계: 기본값 상수 정의하기
제 프로젝트가 Ollama로 요청을 구성하는 파일(packages/ai-engine/src/ollama-client.ts)에 다음 코드를 추가했습니다:
const BASELINE_MODEL_OPTIONS = {
temperature: 0.15,
num_ctx: 16384
...
이것은 단순히 이름 붙여진 객체일 뿐입니다. 코드베이스의 한 곳에
그게 전부입니다. 세 군데만 건드렸습니다. 전체 코드는 코드베이스를 통틀어 아마 20줄 정도에 불과합니다.
### 단계 4: 사용자가 설정을 통해 재정의하도록 허용하기
기본 기능이 출시된 후, 저는 드롭다운 메뉴가 있는 Settings UI를 추가했습니다:
- 응답 스타일 → 정확 (Precise) (0.15) / 균형 잡힘 (Balanced) (0.4) / 창의적 (Creative) (0.8) / 사용자 지정 (Custom)
- 컨텍스트 윈도우 (Context window) → 4k / 8k / 16k / 32k / 모델 기본값
이 UI 선택 사항들은 localStorage에 저장되고, React hook을 거쳐 모든 채팅 메시지의 옵션 객체에 병합됩니다. 창의적인 작문을 원하는 사용자는 temperature를 높일 수 있습니다. 작은 노트북을 사용하는 사용자는 RAM 절약을 위해 num_ctx를 낮출 수 있습니다. 기본 기능이 기본값이며, 설정은 재정의(override)입니다.
## 이것이 저에게 가르쳐 준 것들
저는 이것을 찾기 위해 컴퓨터 과학 학위가 필요하지 않았습니다. 제가 필요했던 것은 다음이었습니다:
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기