toolRouter-1B / README.md
kimjaegeun
docs: measured VRAM β€” shared-base+2LoRA 1.5GB vs two merged 2.8GB
5cce7a2 verified
|
Raw History Blame Contribute Delete
8.58 kB
---
base_model: meta-llama/Llama-3.2-1B-Instruct
language:
- ko
- en
library_name: peft
pipeline_tag: text-generation
tags:
- function-calling
- tool-use
- korean
- qlora
---
# toolRouter-1B
ν•œκ΅­μ–΄ μš°μ„  **툴콜 λΌμš°ν„°** β€” μ‚¬μš©μž λ°œν™”λ₯Ό λ°›μ•„ 항상 툴콜 ν•˜λ‚˜λ‘œ μ‘λ‹΅ν•˜λŠ” λΌμš°νŒ… λͺ¨λΈμž…λ‹ˆλ‹€. **Llama-3.2-1B-Instruct** μœ„μ— μ–ΉλŠ” QLoRA μ–΄λŒ‘ν„°(~45MB)둜, 4bit λ‘œλ“œ μ‹œ VRAM μ•½ 2.5GB, GGUF Q4 λ³€ν™˜ μ‹œ μ•½ 1GB둜 κ΅¬λ™λ©λ‹ˆλ‹€.
- 자유 ν…μŠ€νŠΈλ₯Ό μƒμ„±ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λͺ¨λ“  좜λ ₯은 `<tool_call>{"name": ..., "arguments": {...}}</tool_call>` ν˜•μ‹μž…λ‹ˆλ‹€.
- 일반 λŒ€ν™”Β·μƒμ‹ 응닡은 `reply(text=...)`, 처리 λΆˆκ°€ μš”μ²­μ€ `escalate(query=원문)` 툴둜 ν‘œν˜„ν•©λ‹ˆλ‹€.
- μΈμžλŠ” μ‚¬μš©μž λ¬Έμž₯의 ν‘œν˜„μ„ 원문 κ·ΈλŒ€λ‘œ μΆ”μΆœν•˜λ„λ‘ ν•™μŠ΅λμŠ΅λ‹ˆλ‹€ (ν•œκ΅­μ–΄ 인자의 μ˜μ–΄ λ²ˆμ—­Β·μš”μ•½Β·μ§€μ–΄λƒ„ μ–΅μ œ).
- 처음 λ³΄λŠ” 툴 μŠ€ν‚€λ§ˆ(zero-shot toolset)μ—μ„œλ„ λ™μž‘ν•˜λ„λ‘ ν•™μŠ΅λμŠ΅λ‹ˆλ‹€.
- 멀티턴이 ν•„μš”ν•˜λ©΄ [recallResolver-1B](https://huggingface.co/kimjg/recallResolver-1B)와 짝으둜 μ“°μ„Έμš” β€” 같은 베이슀λ₯Ό κ³΅μœ ν•΄ μ–΄λŒ‘ν„° μ „ν™˜λ§ŒμœΌλ‘œ ν•¨κ»˜ λ™μž‘ν•˜λ©°, λͺ¨λΈμ„ λ”°λ‘œ λ„μš°λŠ” 것 λŒ€λΉ„ VRAM이 μ ˆλ°˜μž…λ‹ˆλ‹€ β€” μ‹€μΈ‘: llama.cpp 베이슀 Q4 + LoRA 2개 ν•œ μ„œλ²„ = **1.5GB**, λ¨Έμ§€ Q4 μ„œλ²„ 2개 = 2.8GB (각 2048 ctx, μΆ”λ‘  버퍼 포함).
## μ΅œμ‹  버전 β€” 2026-09.2 (recall 지원)
ν•œκ΅­μ–΄ μ‹€μ‚¬μš© 문체 6,000λ¬Έμž₯ 직접 μž‘μ„± λ°μ΄ν„°λ‘œ κ°•ν™”ν•˜κ³ , **κ³Όκ±° λŒ€ν™” μ°Έμ‘° 감지(`recall`)**λ₯Ό μΆ”κ°€ν•œ λ²„μ „μž…λ‹ˆλ‹€.
| 평가 | 수치 |
|---|---|
| ν•œκ΅­μ–΄ 평가셋 396 (decision / tool / args) | 97.2 / 95.7 / 83.8 |
| held-out 1,100 (decision / tool / args) | 99.5 / 99.2 / 97.0 |
| BFCL v3 single-turn (simple / multiple / irrelevance / overall) | 86.0 / 85.5 / 80.0 / **84.2** |
| recall νŒμ • (경계 μΌ€μ΄μŠ€ 포함 120) | μž¬ν˜„ 86.7% / μ •λ°€ 91.2% / 함정 λ°©μ–΄ 52/55 |
## μ‚¬μš©λ²•
```python
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
base = "meta-llama/Llama-3.2-1B-Instruct"
model = AutoModelForCausalLM.from_pretrained(base, device_map="auto")
model = PeftModel.from_pretrained(model, "kimjg/toolRouter-1B")
tokenizer = AutoTokenizer.from_pretrained("kimjg/toolRouter-1B")
```
μ±„νŒ… ν…œν”Œλ¦Ώμ€ μ €μž₯μ†Œμ˜ `chat_template.jinja`에 포함돼 μžˆμŠ΅λ‹ˆλ‹€. greedy 디코딩을 ꢌμž₯ν•˜λ©°, ν”„λ‘œλ•μ…˜μ—μ„œλŠ” xgrammar λ“± JSON μŠ€ν‚€λ§ˆ 문법 κ°•μ œ(constrained decoding)λ₯Ό ν•¨κ»˜ μ“°λ©΄ νŒŒμ‹±Β·μŠ€ν‚€λ§ˆ ν†΅κ³Όμœ¨ 100%λ₯Ό 보μž₯ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
## 툴 μ§€μ • 방법
μ‹œμŠ€ν…œ ν”„λ‘¬ν”„νŠΈμ— 툴 μŠ€ν‚€λ§ˆλ₯Ό **ν•œ 쀄에 ν•˜λ‚˜μ”© compact JSON**으둜 λ‚˜μ—΄ν•©λ‹ˆλ‹€. ν•™μŠ΅ λ•Œ μ‚¬μš©ν•œ ν…œν”Œλ¦Ώ κ·ΈλŒ€λ‘œ μ“°λŠ” 것을 ꢌμž₯ν•©λ‹ˆλ‹€:
```text
λ„ˆλŠ” κ²Œμž„ 곡랡 λ„μš°λ―Έμ˜ 툴콜 λΌμš°ν„°λ‹€. μ‚¬μš©μžμ˜ μš”μ²­μ„ 읽고 μ•„λž˜ 툴 쀑 μ •ν™•νžˆ ν•˜λ‚˜λ₯Ό ν˜ΈμΆœν•˜λŠ” κ²ƒμœΌλ‘œλ§Œ μ‘λ‹΅ν•œλ‹€. 자유 ν…μŠ€νŠΈλ₯Ό 좜λ ₯ν•˜μ§€ μ•ŠλŠ”λ‹€.
- 일반 λŒ€ν™”λ‚˜ 직접 λ‹΅ν•  수 μžˆλŠ” μ§ˆλ¬Έμ€ reply νˆ΄μ„ ν˜ΈμΆœν•œλ‹€.
- λͺ©λ‘μ— μžˆλŠ” 툴둜 μ²˜λ¦¬ν•  수 μ—†κ±°λ‚˜ λ²”μœ„λ₯Ό λ²—μ–΄λ‚œ μš”μ²­μ€ escalate νˆ΄μ„ ν˜ΈμΆœν•œλ‹€.
- 툴 μΈμžλŠ” μ‚¬μš©μžμ˜ μš”μ²­μ—μ„œ μΆ”μΆœν•œλ‹€. μš”μ²­μ— μ—†λŠ” 값을 μ§€μ–΄λ‚΄μ§€ μ•ŠλŠ”λ‹€.
응닡 ν˜•μ‹μ€ λ°˜λ“œμ‹œ λ‹€μŒκ³Ό κ°™λ‹€:
<tool_call>{"name": "<툴 이름>", "arguments": {<인자>}}</tool_call>
μ‚¬μš© κ°€λŠ₯ν•œ 툴 λͺ©λ‘:
<tools>
{tools_json}
</tools>
```
`{tools_json}` μžλ¦¬μ— λ“€μ–΄κ°€λŠ” 툴 μŠ€ν‚€λ§ˆ ν˜•μ‹ (OpenAI function μŠ€ν‚€λ§ˆμ™€ λ™μΌν•œ `parameters` ꡬ쑰):
```json
{"name": "get_weather", "description": "μ§€μ—­μ˜ ν˜„μž¬ 날씨λ₯Ό μ‘°νšŒν•œλ‹€", "parameters": {"type": "object", "properties": {"location": {"type": "string", "description": "λ„μ‹œ 이름"}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}}, "required": ["location"]}}
{"name": "reply", "description": "툴이 ν•„μš” μ—†λŠ” μ§ˆλ¬Έμ— 직접 λ‹΅ν•œλ‹€", "parameters": {"type": "object", "properties": {"text": {"type": "string"}}, "required": ["text"]}}
{"name": "escalate", "description": "μ²˜λ¦¬ν•  수 μ—†λŠ” μš”μ²­μ„ μƒμœ„λ‘œ λ„˜κΈ΄λ‹€", "parameters": {"type": "object", "properties": {"query": {"type": "string"}}, "required": ["query"]}}
```
`reply`와 `escalate`λŠ” 항상 λͺ©λ‘μ— 포함해야 ν•©λ‹ˆλ‹€. λ‚˜λ¨Έμ§€ νˆ΄μ€ 자유둭게 ꡐ체할 수 있고, ν•™μŠ΅ λ•Œ λ³Έ 적 μ—†λŠ” νˆ΄μ…‹λ„ λ™μž‘ν•©λ‹ˆλ‹€(zero-shot). 툴 μ„€λͺ…은 μ˜μ–΄Β·ν•œκ΅­μ–΄ λͺ¨λ‘ μ§€μ›ν•©λ‹ˆλ‹€.
## κΈ°λ³Έ 툴 3μ’… (λΌμš°ν„°μ˜ ν”„λ‘œν† μ½œ)
이 λΌμš°ν„°λŠ” "λͺ¨λ“  응닡이 툴콜"μ΄λΌλŠ” κ·œμ•½ μœ„μ—μ„œ λ™μž‘ν•˜λ©°, 도메인 툴 외에 λ‹€μŒ κΈ°λ³Έ 툴둜 λͺ¨λ“  λ°œν™”λ₯Ό μ»€λ²„ν•©λ‹ˆλ‹€:
| κΈ°λ³Έ 툴 | μ–Έμ œ λ‚˜μ˜€λ‚˜ | 인자 | μ„œλ²„κ°€ ν•  일 |
|---|---|---|---|
| `reply` | 툴이 ν•„μš” μ—†λŠ” μΈμ‚¬Β·μž‘λ‹΄Β·μƒμ‹ 질문 ("κ³ λ§ˆμ›Œ!", "μ•ˆλ…•") | `text`: 짧은 ν•œκ΅­μ–΄ λ‹΅λ³€ | textλ₯Ό κ·ΈλŒ€λ‘œ μ‚¬μš©μžμ—κ²Œ |
| `escalate` | λͺ©λ‘μ˜ 툴둜 처리 λΆˆκ°€ν•œ μš”μ²­ β€” λ‹€λ₯Έ 도메인, 볡합 μž‘μ—…, ν•„μˆ˜ 인자λ₯Ό μ•Œ 수 μ—†μŒ, κΈ΄ κΈ€μ“°κΈ° λ“± | `query`: **μ‚¬μš©μž μš”μ²­ 원문 κ·ΈλŒ€λ‘œ** | μƒμœ„ λͺ¨λΈΒ·λ‹΄λ‹Ήμžμ—κ²Œ 전달 |
| `recall` | λ°œν™”κ°€ κ³Όκ±° λŒ€ν™”μ˜ 값을 가리킬 λ•Œ ("κ·Έκ±° 재고 μ–Όλ§ˆ λ‚¨μ•˜μ–΄?", "μ•„κΉŒ κ·Έ λ„μ‹œ 내일 λ‚ μ”¨λŠ”?") | `needs`: μ°Ύμ•„μ•Ό ν•  κ°’μ˜ ν•œκ΅­μ–΄ μ„€λͺ… | [recallResolver-1B](https://huggingface.co/kimjg/recallResolver-1B)둜 νžˆμŠ€ν† λ¦¬μ—μ„œ 값을 μ°Ύμ•„ λ°œν™”λ₯Ό 보강 ν›„ λΌμš°ν„° 재호좜 |
섀계 μ˜λ„: λΌμš°ν„°λŠ” 자유 ν…μŠ€νŠΈλ₯Ό μƒμ„±ν•˜μ§€ μ•ŠμœΌλ―€λ‘œ "λŒ€λ‹΅ν•œλ‹€/λ„˜κΈ΄λ‹€/κ³Όκ±°λ₯Ό μ°ΎλŠ”λ‹€"κΉŒμ§€ μ „λΆ€ 툴콜둜 ν‘œν˜„λ©λ‹ˆλ‹€. 덕뢄에 λ‹€μš΄μŠ€νŠΈλ¦Όμ€ λΆ„κΈ° 처리만 ν•˜λ©΄ 되고, 좜λ ₯ 문법 κ°•μ œ(constrained decoding)λ₯Ό 전체 응닡에 μ μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
**reply 운용 팁**: `reply.text`λ₯Ό κ·ΈλŒ€λ‘œ μ“Έ μˆ˜λ„ μžˆμ§€λ§Œ(μ§€μ—° μ΅œμ†Œ), replyλ₯Ό **"λŒ€ν™” νŠΈλž™" μ‹ ν˜Έλ‘œλ§Œ μ“°κ³  λ°œν™”λ₯Ό 별도 μ±— λͺ¨λΈλ‘œ λ„˜κΈ°λŠ”** ꡬ성도 λΆ„κΈ° ν•œ 쀄이면 λ©λ‹ˆλ‹€. 페λ₯΄μ†Œλ‚˜ μžˆλŠ” 본격 챗봇과 뢙일 λ•Œ μœ μš©ν•©λ‹ˆλ‹€:
```python
call = route(utterance)
if call["name"] == "reply":
return chat_model(persona, utterance) # textλŠ” 버리고 큰 λͺ¨λΈμ΄ λŒ€ν™”
```
> **recall 연동 팁** (recallResolver-1Bμ™€μ˜ end-to-end κ²€μ¦μ—μ„œ ν™•μ •ν•œ μ„œλ²„ κ·œμΉ™): ①리쑸버가 값을 찾으면 κ·Έ κ°’μ˜ 좜처 인자 μŠ€ν‚€λ§ˆ μ„€λͺ…을 라벨둜 λ°œν™”λ₯Ό 보강해 μž¬λΌμš°νŒ… β€” `"{λ°œν™”} ({라벨}: {κ°’})"` β‘‘λΌμš°ν„°μ˜ needs둜 리쑸버가 λͺ» 찾으면 ν•΄λ‹Ή 기둝의 인자 μ„€λͺ…λ“€λ‘œ 재질의 β‘’needs와 κ°’ 라벨의 μœ ν˜•μ΄ μ•ˆ 맞으면 μžλ™ μ£Όμž… λŒ€μ‹  μ‚¬μš©μžμ—κ²Œ 확인. 재호좜이 또 recall이면 escalate(루프 κ°€λ“œ). recall을 μ“°μ§€ μ•ŠμœΌλ €λ©΄ 툴 λͺ©λ‘μ—μ„œ λΉΌλ©΄ λ©λ‹ˆλ‹€ β€” 그러면 κ³Όκ±° μ°Έμ‘° λ°œν™”λŠ” escalate둜 λ‚˜μ˜΅λ‹ˆλ‹€.
λ™μž‘ 예:
```text
user: μš°μ‚° 챙겨야 ν•˜λ‚˜, λŒ€μ „μΈλ°
assistant: <tool_call>{"name": "get_weather", "arguments": {"location": "λŒ€μ „"}}</tool_call>
user: κ³ λ§ˆμ›Œ!
assistant: <tool_call>{"name": "reply", "arguments": {"text": "λ³„λ§μ”€μ„μš”, 또 ν•„μš”ν•˜λ©΄ 뢈러 μ£Όμ„Έμš”!"}}</tool_call>
user: 이 μ‚¬μ§„μ—μ„œ κΈ€μž μ’€ μ½μ–΄μ€˜
assistant: <tool_call>{"name": "escalate", "arguments": {"query": "이 μ‚¬μ§„μ—μ„œ κΈ€μž μ’€ μ½μ–΄μ€˜"}}</tool_call>
```
## GGUF (llama.cpp)
μ–΄λŒ‘ν„°λ₯Ό λ¨Έμ§€ν•΄ μ–‘μžν™”ν•œ 단일 νŒŒμΌλ„ μ œκ³΅ν•©λ‹ˆλ‹€. 손싀은 자체 평가셋 μ‹€μΈ‘μΉ˜μž…λ‹ˆλ‹€:
| 파일 | 크기 | ν•œκ΅­μ–΄ 평가셋 (decision/tool/args) | ꢌμž₯ μš©λ„ |
|---|---|---|---|
| `toolRouter-1B-Q8_0.gguf` | 1.3GB | 96.5 / 95.0 / 83.4 (원본 λŒ€λΉ„ ~-0.8%p) | ν’ˆμ§ˆ μš°μ„  |
| `toolRouter-1B-Q4_K_M.gguf` | 0.8GB | 95.5 / 94.2 / 83.4 (~-1.6%p) | μ΅œμ†Œ VRAM |
```bash
llama-server -m toolRouter-1B-Q8_0.gguf -ngl 99 -c 2048
```
μ±„νŒ… ν…œν”Œλ¦Ώμ΄ GGUF에 포함돼 μžˆμŠ΅λ‹ˆλ‹€. greedy(temperature 0) ꢌμž₯, 좜λ ₯은 `<tool_call>...</tool_call>` ν˜•μ‹ κ·ΈλŒ€λ‘œμž…λ‹ˆλ‹€. recall νŒμ •μ€ μ–‘μžν™”μ— λ‹€μ†Œ λ―Όκ°ν•˜λ‹ˆ(경계 μΌ€μ΄μŠ€ -4~5%p) recall을 μ“°λŠ” νŒŒμ΄ν”„λΌμΈμ΄λ©΄ Q8을 κΆŒν•©λ‹ˆλ‹€.
## μ œν•œ
- 단일 ν„΄ λΌμš°νŒ… μ „μš©μž…λ‹ˆλ‹€ (λ©€ν‹°ν„΄ λ¬Έλ§₯ μ°Έμ‘°λŠ” 미지원).
- 세계지식 좔둠이 ν•„μš”ν•œ 인자 보정(예: λ„μ‹œλͺ…β†’ν†΅ν™”μ½”λ“œ), μƒλŒ€ λ‚ μ§œ 해석은 λ‹€μš΄μŠ€νŠΈλ¦Όμ—μ„œ μ²˜λ¦¬ν•˜μ„Έμš”. μƒλŒ€ λ‚ μ§œλŠ” μ‹œμŠ€ν…œ ν”„λ‘¬ν”„νŠΈμ— ν˜„μž¬ λ‚ μ§œλ₯Ό μ œκ³΅ν•˜λ©΄ κ°œμ„ λ©λ‹ˆλ‹€.
- base λͺ¨λΈμ˜ Llama 3.2 Community Licenseλ₯Ό λ”°λ¦…λ‹ˆλ‹€.