8B 모델이 도구를 안정적으로 호출하게 만드는 GBNF 문법 컴파일러를 만들었습니다 (심층 분석)
I built a GBNF grammar compiler that makes 8B models reliably call tools - here's how it works (deep dive)
핵심 요약
작은 모델의 도구 호출 정확도를 높이기 위해 JSON 스키마를 GBNF 규칙으로 변환하여 강제하는 로컬 에이전트 시스템을 개발했습니다.
- GBNF 문법 컴파일 — JSON 스키마를 GBNF 규칙으로 변환해 모델의 출력 형식을 강제함
- 동적 문법 적용 — 세션 시작 시 도구별 문법을 컴파일하고 상황에 맞춰 필요한 도구만 활성화함
- 로컬 에이전트 Eris — Rust 기반으로 llama.cpp를 활용하며 Obsidian과 연동되는 메모리 구조를 가짐
- 성능 최적화 — 12B 모델과 32k 컨텍스트를 활용해 VRAM 효율성을 극대화함
[블록 1/7] llama.cpp에서 돌아가고 Obsidian 호환 볼트를 메모리로 사용하는 Rust 기반 로컬 에이전트(Eris)를 만들고 있음. 약 50개의 도구(볼트 읽기/쓰기, 메모리, 알림, 웹 가져오기, 이메일, 캘린더, 비전)를 지원함.
[블록 2/7] 가장 힘들었던 건 작은 모델들이 유효한 도구 호출 JSON을 내뱉게 만드는 거였음. 다들 알다시피 모델이 JSON을 코드 펜스로 감싸거나, 없는 도구 이름을 지어내거나, 닫는 중괄호를 빼먹거나, 객체 뒤에 쓸데없는 말을 덧붙이는 문제들임.
[블록 3/7] 내 해결책: 세션 시작 시 각 도구의 JSON 스키마를 GBNF 규칙으로 컴파일해서, 샘플러가 단순히 '유효한 JSON'뿐만 아니라 '이 특정 도구에 딱 맞는 키, 타입, 열거형 값을 가진 유효한 JSON'을 강제하도록 함. 그리고 LLM 호출 전마다 시맨틱 라우터가 해당 턴에 매칭한 도구들로만 문법을 좁힘. 50개 대신 3개 도구 중에서 선택하게 하니 8B 모델도 훨씬 안정적으로 작동함.
[블록 4/7] 프로젝트의 실제 코드를 활용해 이게 어떻게 작동하는지 상세한 기술 블로그를 작성함: https://eris-system.dev/blog/gbnf-grammars
[블록 5/7] 4080(16GB VRAM)에서 Gemma 4 12B를 돌리는 중. 채팅 + 약 32k 컨텍스트 + 비전까지 아주 잘 작동함.
[블록 6/7] 레포: https://github.com/janpauldahlke/eris (Apache 2.0)
[블록 7/7] 문법 컴파일러, 복구 루프, 혹은 아키텍처에 대해 궁금한 점 있으면 언제든 물어봐.


