Highlights
- Emits the native Qwen3 tool-call format (
<tool_call>{"name": ..., "arguments": ...}</tool_call>),
so it works out of the box with the standard Qwen3 tool parsers in
transformers and vLLM (no custom parser needed).
- Non-thinking by default (
enable_thinking=False): deterministic, low-latency
tool calls. generation_config.eos_token_id includes both
and so generation stops cleanly at turn end.
- Single calls, parallel calls, multi-step tool use, JSON-mode output, and
schema-guided information extraction.
- Apache-2.0 base, permissively-licensed training data, fully reproducible recipe.
Results
Exact-match evaluation with the released harness (scripts/eval_toolcall.py).
Full exact match = same set of function names and identical arguments.
Table with columns: Benchmark, valid JSON, tool-name match, full exact match| Benchmark | valid JSON | tool-name match | full exact match |
|---|
| xLAM holdout (1,000 rows never trained on) | 100% | 96.5% | 79.5% |
| ToolACE (external dataset, not trained on) | 100% | 95.5% | 75.3% |
Both sources are disjoint from training: the first 1,000 xLAM rows were held out,
and ToolACE was never used at all. These are the authors' own harness numbers,
not BFCL. Berkeley Function Calling Leaderboard (BFCL v4)
evaluation is in progress and will be added here.
Quickstart
from transformers import AutoModelForCausalLM, AutoTokenizer model_id = "Horizon-Labs/qwen3-1.7b-toolsmith"tok = AutoTokenizer.from_pretrained(model_id)model = AutoModelForCausalLM.from_pretrained(model_id, dtype="bfloat16", device_map="auto") tools = [{ "type": "function", "function": { "name": "get_weather", "description": "Get the current weather for a city", "parameters": { "type": "object", "properties": {"city": {"type": "string", "description": "City name"}}, "required": ["city"], }, },}]messages = [{"role": "user", "content": "What's the weather in Berlin?"}] inputs = tok.apply_chat_template( messages, tools=tools, add_generation_prompt=True, enable_thinking=False, return_tensors="pt",).to(model.device)out = model.generate(inputs, max_new_tokens=256, do_sample=False)print(tok.decode(out[0][inputs.shape[1]:], skip_special_tokens=True))
Expected output:
<tool_call>{"name": "get_weather", "arguments": {"city": "Berlin"}}</tool_call>
Training
- Base model:
Qwen/Qwen3-1.7B-Base
- Objective: full-parameter supervised fine-tuning, assistant tokens only
(prompt/system/tool tokens masked to
-100), including the end-of-turn token.
- Format: the upstream Qwen3 chat template, byte-for-byte unmodified in its
rendering (generation markers were used only for loss masking during prep and
are not present in the released tokenizer).
- Data: 69,246 conversations / ~38.6M tokens (77M token-epochs over 2 epochs):
minpeter/xlam-function-calling-60k-parsed (CC-BY-4.0; derived from
Salesforce/xLAM, generated by APIGen) - 58,647: single, multiple and parallel
calls. First 1,000 rows reserved for evaluation.
NousResearch/hermes-function-calling-v1 (Apache-2.0) - function calling
(1,100), JSON mode agentic (1,008), JSON mode single-turn (1,241):
structured extraction and multi-step tool use.
HuggingFaceTB/smoltalk (Apache-2.0) - 7,950 general-instruction replay
(~11%) to limit capability regression.
- Hyperparameters: 2 epochs, lr 1e-5 (cosine, 150 warmup steps), weight
decay 0.1, global batch 64, max sequence length 4,096, bf16, gradient
checkpointing. Final train loss 0.481, held-out eval loss 0.137.
- Hardware: 1x NVIDIA H100 80GB, ~2.5 hours.
- Reproducibility:
scripts/prepare_data.py, scripts/train_sft.py,
scripts/eval_toolcall.py and the exact command in train.sh.
Intended use and limitations
Intended for agentic tool use, structured extraction, and JSON generation where
a small, locally-runnable model is preferred. It is not a general-purpose
chat model and does not do open-ended reasoning.
Known limitations:
- Trained in English and on tool-use / general-instruction data only; other
languages and long-form reasoning are out of scope.
- Very large tool catalogs (dozens of functions) may exceed the 4,096-token
training context or degrade selection accuracy.
- Arguments are copied/generated, not validated. Always validate against the
schema before executing real tools; the model does not execute anything.
- Like all function-calling models, it can call the wrong function or hallucinate
arguments on ambiguous requests. Use the provided
tools schema, keep catalogs
small, and verify before acting.
- No abstention training on large negative sets: unlike some models it was not
specifically optimized to refuse calls when no tool fits.
Attribution
Training data includes work by Salesforce (xLAM / APIGen, CC-BY-4.0), Nous
Research (Hermes, Apache-2.0) and Hugging Face (smoltalk, Apache-2.0). Base model
by the Qwen team. See the dataset cards for full lineage and terms.
Citation
If you use this model, please cite the base model and the datasets above.