Tool Call 판단 주체 — 모델이 결정한다
llama.cpp 서버에서 채팅 요청이 들어왔을 때, "이게 tool을 쓰는 요청인가?"를 판단하는 건 서버 로직이 아니라 모델 자체다.
동작 흐름
1. 클라이언트가 tools를 포함해서 요청
{
"messages": [...],
"tools": [
{
"type": "function",
"function": {
"name": "search_web",
"description": "웹을 검색한다",
"parameters": {...}
}
}
]
}
2. llama.cpp 서버가 Chat Template에 tools를 주입
모델의 tokenizer config에는 Jinja 기반 chat template이 있다. 서버는 tools 필드를 받으면 이를 시스템 프롬프트 영역에 삽입한다.
모델이 실제로 보는 텍스트는 이런 형태:
<|im_start|>system
You are a helpful assistant.
# Tools
You may call one or more functions...
{tools 정의 JSON}
<|im_end|>
<|im_start|>user
오늘 날씨 어때?
<|im_end|>
<|im_start|>assistant
3. 모델이 next token prediction으로 결정
이후는 순수하게 모델의 언어 생성. 입력 컨텍스트(질문 내용 + tools 정의)를 바탕으로:
- tool이 필요하다고 판단 →
<tool_call>토큰으로 시작하는 JSON 생성 - 아니면 → 일반 텍스트 생성
4. 서버가 출력을 파싱해서 응답 포맷 결정
생성 완료 후 서버가 출력물을 확인:
<tool_call>패턴 있음 →finish_reason: "tool_calls"+ tool_calls 파싱 결과 반환- 없음 →
finish_reason: "stop"+ 텍스트 반환
tools를 안 보내면?
// tools 필드 없음
{
"messages": [...]
}
Chat template이 tools를 주입하지 않으므로, 모델은 tool의 존재 자체를 모른다. tool call이 일어날 수 없다.
브라우저 UI vs 직접 API 호출
| 클라이언트 | tools 포함 여부 | tool call 가능 | |------------|-----------------|----------------| | llama.cpp 기본 웹 UI | ❌ (미구현) | ❌ | | curl / Python 직접 호출 | ✅ (내가 넣으면) | ✅ | | tool call 지원 UI (Claude.ai 등) | ✅ (UI가 처리) | ✅ |
→ tool call 여부는 포트나 클라이언트 종류가 아니라, 요청에 tools를 넣느냐의 문제.
클라이언트가 져야 하는 두 가지 책임
tool call을 "사용"한다는 건 클라이언트가 두 가지를 모두 처리해야 한다는 뜻이다:
- 요청 시:
tools정의를 포함해서 보내기 - 응답 후:
finish_reason: "tool_calls"감지 → tool 실행 → 결과를 messages에 추가 → 재요청
서버(llama.cpp)는 이 루프에서 아무것도 해주지 않는다. 신호만 줄 뿐이고, 실제 실행과 루프는 전부 클라이언트 몫.
브라우저 UI: 요청 → 텍스트 응답 표시. 끝.
tool_calls.py: 요청 (tools 포함)
→ finish_reason: tool_calls 감지
→ search_web() 직접 실행
→ 결과를 messages에 추가
→ 재요청
→ 최종 텍스트 응답
관련
- overview — 전체 개념 지도