로컬 LLM에 도구를 쥐여주기 — 함수 호출(tool calling) 첫걸음

로컬 LLM이 계산, 검색, DB 조회 같은 실제 작업을 수행할 수 있게 하려면 모델이 도구를 직접 실행하는 대신 '이 도구를 이 인자로 호출해 달라'는 구조화된 JSON 요청만 생성하게 하고, 그 실행과 결과 반환은 애플리케이션 코드가 전담하는 전체 흐름을 만들어야 한다.

한 줄 요약: 로컬 LLM은 도구를 직접 실행하지 않고 '호출 요청'만 출력하며 실제 작업 실행은 우리 앱이 책임진다.

로컬 LLM은 도구를 직접 실행하지 않는다 — 모델은 '이 도구를 이 인자로 호출하라'는 구조화된 JSON 요청만 내고, 실제 실행·결과 반환은 앱 코드가 한다

함수 호출(tool calling) 실행 구조 · 2026-07

왜 모델이 도구를 '직접' 실행하지 않을까?#

대형 언어 모델은 다음에 올 단어를 통계적으로 예측하는 시스템일 뿐이다. 모델 내부에는 외부 프로그램을 호출하거나 파일을 읽거나 계산을 수행할 수 있는 실행 환경이 전혀 없다. 따라서 모델은 '무엇을 해야 하는지'를 계획하거나 요청하는 텍스트·구조를 출력할 수 있을 뿐, 실제 행동은 언제나 외부 코드가 수행해야 한다.

초보자 비유로 쉽게 이해해보자. 모델은 요리사에게 "소금 2g 넣어줘"라고 적힌 주문서를 작성해 전달하는 역할만 한다. 실제로 소금통을 열고 정확히 계량해서 넣는 행동은 주방(우리 앱 코드)이 담당한다. 모델이 직접 주방에 들어가 소금을 집을 수는 없는 것이다.

세 가지 접근은 어떻게 다를까?#

로컬 LLM에 도구를 붙이는 방법은 모델이 도구 사용 의사를 어떻게 표현하느냐, 그 표현을 실제 실행으로 연결하는 책임이 어디에 있느냐에 따라 크게 세 가지로 나뉜다. 아래 비교표는 2026년 7월 기준 주요 런타임의 정성적 특성을 정리한 것이다.

로컬 LLM에 도구를 붙이는 세 가지 접근 비교 (정성 평가, 2026-07 기준) · columns: 접근, 무엇을 하나, 대표 지원, 초보 난이도, 주의점 · 출처 Hax hax.moche.ai/p/1245?ref=ai_answer

접근 | 무엇을 하나 | 대표 지원 | 초보 난이도 | 주의점
프롬프트로 흉내 | 모델이 "이 툴 써"라는 텍스트만 냄, 실행·파싱은 앱이 직접 | 사실상 전부 | 낮음 | 출력 파싱이 깨지기 쉬움
네이티브 tool calling | 모델이 구조화된 tool_call(JSON)을 반환 | Ollama·llama.cpp·OpenAI 호환 | 중간 | 도구 스키마를 미리 정의해야
에이전트 루프(ReAct) | 생각→도구 호출→결과 관찰을 반복 | LangChain 등 프레임워크 | 높음 | 무한 루프·토큰 비용 관리 필요

프롬프트로 흉내 내는 방식은 가장 오래된 접근이다. 모델에게 미리 "계산이 필요하면 CALCULATE: 식 형태로 출력해"라고 지시하면 모델이 자유로운 텍스트로 도구 사용 의사를 표현한다. 앱은 그 텍스트를 정규식이나 문자열 파싱으로 해석해 실제 함수를 호출한다. 코드가 간단하고 어떤 모델에서도 바로 쓸 수 있지만, 모델 출력이 조금만 달라져도 파싱이 실패하는 경우가 잦다.

네이티브 tool calling은 Ollama, llama.cpp 등 최근 로컬 런타임이 공식 지원하는 기능이다. 미리 도구 이름, 설명, 파라미터 스키마(JSON Schema)를 정의해 모델에게 알려주면, 모델이 필요할 때 정확한 형식의 tool_call JSON을 출력한다. 앱은 이 JSON을 신뢰성 있게 파싱해 함수를 실행하고 결과를 다시 모델 컨텍스트에 넣어준다. 출력이 구조화되어 파싱 오류가 크게 줄지만, 도구 스키마를 정확하고 상세하게 작성해야 하는 준비 작업이 필요하다.

에이전트 루프(ReAct)는 모델이 '생각 → 도구 호출 → 결과 관찰'의 사이클을 여러 번 반복하며 복잡한 문제를 단계적으로 풀도록 만든다. LangChain 같은 프레임워크가 이 루프를 자동으로 관리해 주기 때문에 개발자가 직접 루프 코드를 짤 필요가 적다. 한 번의 호출로 끝나지 않는 다단계 작업에 매우 강력하지만, 루프가 무한히 돌거나 토큰 비용이 급증할 위험이 있어 중단 조건과 비용 제어 로직이 필수적이다.

무엇부터 시작해야 하나?#

로컬 환경에서 처음 tool calling을 도입할 때는 난이도와 목적에 따라 순서를 정하는 것이 좋다.

가장 먼저 네이티브 tool calling을 1개 도구부터 시도하라. Ollama나 llama.cpp 최신 버전에서 지원하는 tool calling 기능을 확인하고, 단순한 계산기나 날짜 포맷 변환 도구 하나만 정의해 테스트하면 구조화된 호출의 안정성과 편리함을 바로 경험할 수 있다.

하나의 도구 호출로 해결되지 않는 복잡한 다단계 작업(검색 → 분석 → 저장 등)이 필요하다면 에이전트 루프를 검토하라. LangChain이나 유사 프레임워크를 사용하면 ReAct 스타일 에이전트를 비교적 빠르게 구성할 수 있다.

아이디어를 빠르게 검증하는 프로토타입 단계라면 프롬프트로 흉내 내는 방식으로 시작해도 충분하다. 나중에 실제 서비스화할 때 네이티브 tool calling으로 점진적으로 업그레이드하면 된다.

이렇게 단계적으로 접근하면 모델이 '계획을 세우는 역할'과 앱이 '그 계획을 실행하는 역할'을 명확히 분리하는 원리를 자연스럽게 익힐 수 있다.

참고: 2026-07-12 KST 기준으로 Ollama, llama.cpp, vLLM 등 주요 로컬 LLM 런타임의 tool calling 지원 여부와 출력 형식(JSON 스키마, 특수 토큰 사용 여부 등)은 버전마다 차이가 있을 수 있다. 실제 배포 전 사용 중인 런타임의 최신 공식 문서를 반드시 확인하고 호환성 테스트를 진행하기 바란다.

이 시리즈: 이 시리즈 허브 — 로컬 LLM·RAG 7가지 관문 · 구조화 출력으로 파싱 실패 없애기 · speculative decoding로 속도 올리기

함께 읽기: 로컬 LLM에 JSON을 강제하기 — 구조화 출력으로 파싱 실패 없애기, Phi-4-mini 3.8B로 128K 장문을 로컬에서 처리한다

참고 링크

Ollama 도구 지원

OpenAI 함수 호출 가이드

llama.cpp

JSON Schema 공식