Uraion-Agent-Small
A compact tool-calling agent model — fine-tuned from first principles.
Uraion-Agent-Small is a 2-billion parameter model fine-tuned from Qwen/Qwen3.5-2B for agentic tool use and function calling. It is a research artifact in Uraion Labs' systems-first approach: studying the harness, orchestration, evaluation, and deployment layers that make foundation models useful in real workflows.
This model was trained via QLoRA (4-bit NF4 base + LoRA adapters, merged for deployment simplicity) on a curated mix of function-calling and instruction-following datasets — prioritizing data signal over data volume, in keeping with our systems philosophy.
Intelligence is a systems problem. This model is one piece of that system.
Qwen3.5-2B — hybrid linear + full attention, 262K native context
Harness
QLoRA fine-tuned for structured tool call output via qwen3_coder parser
Orchestrate
Multi-turn function calling, API composition, agent loops
Evaluate
Benchmarked on BFCL-v4, IFEval; tested in real multi-turn agent workflows
Adapt
4-bit merged — runs on consumer GPUs, deployable via vLLM
Deploy
OpenAI-compatible API, local-first, no opaque cloud dependence
This model sits in the Harness layer of our research pipeline — the tooling and runtime that makes foundation models useful, inspectable, and composable.
qwen35 hybrid (24 layers: 18 Gated DeltaNet linear + 6 full-attention every 4th)
Context length
262,144 tokens (native, inherited)
Parameters
~1.9B total, 21.8M LoRA trainable
Precision
4-bit NF4 (QLoRA base), LoRA in BF16, merged to 4-bit
License
Apache 2.0 (inherited from Qwen3.5)
Tool parser
qwen3_coder (native vLLM support)
On-disk size
~2.6 GB (Transformers NF4); GGUF variants range 1.8–3.4 GB
Hub layout
GGUF files at repo root (quantization selector); NF4 Transformers weights in transformers/
Hybrid architecture
Qwen3.5-2B uses a hybrid attention design: 18 Gated DeltaNet (linear attention) layers for efficient long-context inference, interleaved with 6 full-attention layers every 4th position for full expressive power where it matters. This is the systems-over-scale principle applied at the architecture level — better composition of attention mechanisms, not just more parameters.
⚠️ Known issues before you start
1. GGUF files have an open shape issue
The GGUF files at repo root were generated from the NF4 QLoRA weights without a full dequantization step. As a result, some tensors have incorrect shapes (1×N instead of 2D), and certain llama.cpp / llama-cpp-python builds reject them with:
Workaround: Use the Transformers + bitsandbytes path instead (see below). If you need a working GGUF, follow the conversion guide to rebuild from the NF4 weights.
2. Qwen3.5 qwen35 architecture requires a recent llama.cpp
The hybrid attention arch (qwen35) was added to llama.cpp in mid-2026. If you're using llama-cpp-python:
Pre-built CPU/GPU wheels at version 0.3.32 do not support qwen35
You must install from git or compile from source with the latest llama.cpp
3. NF4 is slow on CPU
The Transformers weights are stored in bitsandbytes NF4 format. On GPU this is fast, but on CPU each weight is dequantized on-the-fly — expect 0.1–0.5 tok/s at 2B params. For CPU use, prefer GGUF after conversion.
Setup guides by use case
LM Studio / Ollama (recommended for beginners)
LM Studio (and soon Ollama) can pull models directly from HuggingFace. The model card renders a GGUF variant selector — pick one and click "Open in LM Studio".
For Ollama, import a GGUF file manually:
bash
1# After downloading e.g. Q4_K_M from the repo2ollama create uraion-agent-small -f ./Modelfile
3ollama run uraion-agent-small
With a Modelfile:
dockerfile
1FROM ./Uraion-Agent-Small-Q4_K_M.gguf2TEMPLATE """{{ if .System }}<|im_start|>system
3{{ .System }}<|im_end|>
4{{ end }}<|im_start|>user
5{{ .Prompt }}<|im_end|>
6<|im_start|>assistant
7"""
8PARAMETER temperature 0.0
9PARAMETER top_p 0.95
10PARAMETER stop "<|im_end|>"
1import torch
2from transformers import AutoModelForCausalLM, AutoTokenizer
34model_id ="UraionLabs/Uraion-Agent-Small"5subfolder ="transformers"67tokenizer = AutoTokenizer.from_pretrained(8 model_id, subfolder=subfolder, trust_remote_code=True9)10model = AutoModelForCausalLM.from_pretrained(11 model_id, subfolder=subfolder,12 trust_remote_code=True, device_map="auto",13)1415messages =[16{"role":"system","content":"You are a helpful assistant."},17{"role":"user","content":"What is the capital of France?"},18]1920text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)21inputs = tokenizer(text, return_tensors="pt").to(model.device)2223outputs = model.generate(24**inputs, max_new_tokens=256, temperature=0.0, do_sample=False,25)26response = tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokens=True)27print(response)
Function calling (agentic)
python
1import torch, json
2from transformers import AutoModelForCausalLM, AutoTokenizer
34model_id ="UraionLabs/Uraion-Agent-Small"5subfolder ="transformers"67tokenizer = AutoTokenizer.from_pretrained(8 model_id, subfolder=subfolder, trust_remote_code=True9)10model = AutoModelForCausalLM.from_pretrained(11 model_id, subfolder=subfolder,12 trust_remote_code=True, device_map="auto",13)1415tools =[16{17"type":"function",18"function":{19"name":"get_weather",20"description":"Get the current weather for a city",21"parameters":{22"type":"object",23"properties":{24"location":{"type":"string","description":"City name"}25},26"required":["location"]27}28}29}30]3132messages =[33{"role":"system","content":"You are a helpful assistant with access to function calling. When the user asks about weather, use the get_weather tool."},34{"role":"user","content":"What's the weather like in Paris?"}35]3637# Inject tool definitions into system message38tool_text = json.dumps({"tools": tools})39sys_msg = messages[0]["content"]+"\n\nAvailable tools:\n"+ tool_text
40messages[0]["content"]= sys_msg
4142text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)43inputs = tokenizer(text, return_tensors="pt").to(model.device)4445outputs = model.generate(46**inputs, max_new_tokens=512, temperature=0.0, do_sample=False,47)48response = tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokens=True)49print(response)50# Expected: <tool_call>\n<function=get_weather>\n<parameter=location>\nParis\n</parameter>\n</function>\n</tool_call>
llama-cpp-python (GPU or CPU)
For GGUF files. Requires a recent build with qwen35 architecture support.
Installation (build from source)
bash
1# CPU only (fastest build)2CMAKE_ARGS="-DGGML_CUDA=off" pip install llama-cpp-python \3 --no-binary llama-cpp-python
45# CUDA (takes 5-10 min, needs nvcc)6CMAKE_ARGS="-DGGML_CUDA=on" pip install llama-cpp-python \7 --no-binary llama-cpp-python
89# Or from git for the absolute latest llama.cpp10CMAKE_ARGS="-DGGML_CUDA=off" pip install\11"llama-cpp-python @ git+https://github.com/abetlen/llama-cpp-python.git"\12 --no-build-isolation
Basic inference
python
1from llama_cpp import Llama
23llm = Llama.from_pretrained(4 repo_id="UraionLabs/Uraion-Agent-Small",5 filename="Uraion-Agent-Small-Q4_K_M.gguf",# or Q6_K, Q3_K_M, etc.6 n_ctx=8192,7 n_gpu_layers=-1,# -1 = all on GPU, 0 = CPU only8 flash_attn=True,9)1011response = llm.create_chat_completion(12 messages=[13{"role":"system","content":"You are a helpful assistant."},14{"role":"user","content":"What is the capital of France?"}15],16 temperature=0.0,17 max_tokens=256,18)19print(response["choices"][0]["message"]["content"])
Function calling with tool-use
python
1tools =[2{3"type":"function",4"function":{5"name":"get_weather",6"description":"Get the current weather for a city",7"parameters":{8"type":"object",9"properties":{10"location":{"type":"string","description":"City name"}11},12"required":["location"]13}14}15}16]1718response = llm.create_chat_completion(19 messages=[20{"role":"system","content":"You are a helpful assistant with access to function calling."},21{"role":"user","content":"What's the weather in Tokyo?"}22],23 tools=tools,24 temperature=0.0,25 max_tokens=512,26)2728if response["choices"][0]["message"].get("tool_calls"):29for tc in response["choices"][0]["message"]["tool_calls"]:30print(f"Tool: {tc['function']['name']}")31print(f"Args: {tc['function']['arguments']}")
Troubleshooting: If you get ValueError: Failed to load model from file, your llama-cpp-python version is too old and doesn't support the qwen35 architecture. Build from source as shown above, or use the Transformers + bitsandbytes path.
vLLM (OpenAI-compatible API server, recommended for production agents)
For production agent deployments, use the Transformers weights from the transformers/ subfolder:
bash
1pip install vllm
23# Serve the model (NF4 weights, requires bitsandbytes)4vllm serve UraionLabs/Uraion-Agent-Small \5 --trust-remote-code \6 --enable-auto-tool-choice \7 --tool-call-parser qwen3_coder \8 --host 0.0.0.0 \9 --port 8000\10 --dtype auto
OpenAI-compatible client (works with LangChain, AutoGen, CrewAI, etc.):
python
1from openai import OpenAI
23client = OpenAI(base_url="http://localhost:8000/v1", api_key="not-needed")45response = client.chat.completions.create(6 model="UraionLabs/Uraion-Agent-Small",7 messages=[{"role":"user","content":"What's the weather in Tokyo?"}],8 tools=[{9"type":"function",10"function":{11"name":"get_weather",12"description":"Get current weather for a city",13"parameters":{14"type":"object",15"properties":{16"location":{"type":"string"}17},18"required":["location"]19}20}21}],22 temperature=0.0,23)24tool_calls = response.choices[0].message.tool_calls
25if tool_calls:26for tc in tool_calls:27print(f"{tc.function.name}({tc.function.arguments})")
LangChain integration example
python
1from langchain_openai import ChatOpenAI
23llm = ChatOpenAI(4 model="uraion-agent-small",5 base_url="http://localhost:8000/v1",6 api_key="not-needed",7 temperature=0.0,8)910# Define tools11from langchain_core.tools import tool
1213@tool14defget_weather(location:str)->str:15"""Get the current weather for a city."""16returnf"The weather in {location} is sunny, 22°C."1718tools =[get_weather]19llm_with_tools = llm.bind_tools(tools)2021response = llm_with_tools.invoke("What's the weather in Paris?")22print(response.tool_calls)
CPU-only / edge devices (slow)
Running NF4 weights on CPU is possible but slow (~0.1 tok/s). Use this for verification or throwaway agent loops on low-end hardware.
python
1import torch
2from transformers import AutoModelForCausalLM, AutoTokenizer
3import os
4os.environ["BITSANDBYTES_NOWELCOME"]="1"56model_id ="UraionLabs/Uraion-Agent-Small"7subfolder ="transformers"89tokenizer = AutoTokenizer.from_pretrained(10 model_id, subfolder=subfolder, trust_remote_code=True11)12model = AutoModelForCausalLM.from_pretrained(13 model_id, subfolder=subfolder,14 trust_remote_code=True, device_map="cpu",15)1617# Use very short max_new_tokens to keep wait times bearable18messages =[{"role":"user","content":"Hello, what's 2+2?"}]19text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)20inputs = tokenizer(text, return_tensors="pt")2122outputs = model.generate(23**inputs, max_new_tokens=64, temperature=0.0, do_sample=False,24)25print(tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokens=True))
If you need usable CPU speed, follow the GGUF conversion guide below, then use llama.cpp with Q4_K_M — expect ~5–10 tok/s on a modern CPU.
Apple Silicon (MLX / LM Studio)
The GGUF variants work with LM Studio and llama.cpp on Apple Silicon:
bash
1# Via llama.cpp (after downloading a GGUF file)2./llama-cli -m Uraion-Agent-Small-Q4_K_M.gguf \3 -p "What city is the capital of France?"\4 -n 128 -t 8
For MLX, convert from the Transformers weights:
bash
1pip install mlx-lm
2mlx_lm.convert --hf-path UraionLabs/Uraion-Agent-Small \3 --subfolder transformers
45mlx_lm.generate --model ./mlx_model \6 --prompt "What is the capital of France?"\7 --temp 0.0
Fixing the GGUF files (advanced)
If you need working GGUF files (for Ollama, LM Studio, or speed on CPU), rebuild them from the NF4 weights. This is a one-time procedure.
Step 1: Dequantize NF4 → FP32
python
1import torch
2from transformers import AutoModelForCausalLM, AutoTokenizer
3from peft import PeftModel
45model_id ="UraionLabs/Uraion-Agent-Small"6subfolder ="transformers"78# Load the NF4 model (this is the critical step)9model = AutoModelForCausalLM.from_pretrained(10 model_id, subfolder=subfolder,11 trust_remote_code=True, device_map="cpu",12)13tokenizer = AutoTokenizer.from_pretrained(14 model_id, subfolder=subfolder, trust_remote_code=True15)1617# The NF4 quantized model is loaded; now save in FP3218model = model.to(torch.float32)19model.save_pretrained("./uraion-agent-small-fp32", safe_serialization=True)20tokenizer.save_pretrained("./uraion-agent-small-fp32")
Step 2: Convert FP32 safetensors → GGUF FP16
Using the convert_hf_to_gguf.py script from llama.cpp:
1# Build llama-quantize2cmake -B build -DGGML_CUDA=OFF
3cmake --build build --target llama-quantize -j4
45# Create all quant variants6./build/bin/llama-quantize ./uraion-agent-small-f16.gguf ./Q4_K_M.gguf Q4_K_M
7./build/bin/llama-quantize ./uraion-agent-small-f16.gguf ./Q5_K_M.gguf Q5_K_M
8./build/bin/llama-quantize ./uraion-agent-small-f16.gguf ./Q6_K.gguf Q6_K
9# ... and any other quants you need
Step 4: Verify
bash
1./build/bin/llama-cli -m ./Q4_K_M.gguf \2 -p "What is the capital of France?"\3 -n 20 --temp 0
This produces a fully working, slimmed-down GGUF that loads in any llama.cpp-based runner.
GGUF Quantizations available
Filename
Type
Size
Quality
Uraion-Agent-Small-F16.gguf
F16
~3.4 GB
Reference
Uraion-Agent-Small-Q6_K.gguf
Q6_K
~2.2 GB
Very high
Uraion-Agent-Small-Q5_K_M.gguf
Q5_K_M
~2.0 GB
High
Uraion-Agent-Small-Q5_K_S.gguf
Q5_K_S
~1.9 GB
High
Uraion-Agent-Small-Q4_K_M.gguf
Q4_K_M
~1.9 GB
Good (recommended)
Uraion-Agent-Small-Q4_K_S.gguf
Q4_K_S
~1.8 GB
Good
Uraion-Agent-Small-Q3_K_L.gguf
Q3_K_L
~1.7 GB
Acceptable
Uraion-Agent-Small-Q3_K_M.gguf
Q3_K_M
~1.6 GB
Acceptable
Uraion-Agent-Small-Q3_K_S.gguf
Q3_K_S
~1.5 GB
Acceptable
Uraion-Agent-Small-Q2_K.gguf
Q2_K
~1.9 GB
Low
Uraion-Agent-Small-IQ4_XS.gguf
IQ4_XS
~1.9 GB
Good+ (I-quant)
Uraion-Agent-Small-IQ3_XXS.gguf
IQ3_XXS
~1.8 GB
Good (I-quant)
Uraion-Agent-Small-IQ3_XS.gguf
IQ3_XS
~1.8 GB
Good (I-quant)
Uraion-Agent-Small-IQ3_S.gguf
IQ3_S
~1.8 GB
Good (I-quant)
Uraion-Agent-Small-IQ3_M.gguf
IQ3_M
~1.8 GB
Good (I-quant)
Uraion-Agent-Small-IQ2_XXS.gguf
IQ2_XXS
~1.8 GB
Acceptable (I-quant)
Uraion-Agent-Small-IQ2_XS.gguf
IQ2_XS
~1.8 GB
Acceptable (I-quant)
Uraion-Agent-Small-IQ2_S.gguf
IQ2_S
~1.9 GB
Acceptable (I-quant)
Uraion-Agent-Small-IQ2_M.gguf
IQ2_M
~1.9 GB
Acceptable (I-quant)
Uraion-Agent-Small-IQ1_S.gguf
IQ1_S
~1.8 GB
Low (I-quant)
Uraion-Agent-Small-IQ1_M.gguf
IQ1_M
~1.8 GB
Low (I-quant)
Note: Q8_0, Q4_0, Q5_0, Q5_1, and IQ4_NL are unavailable — Qwen3.5's hybrid architecture (Gated DeltaNet) has irregular 1D tensors incompatible with those block quant formats.
Hardware comparison
Setup
Memory needed
Speed
Quality
Effort
vLLM (A100/H100)
4 GB VRAM
~2000 tok/s
N/A
Low
vLLM (RTX 3090/4090)
6 GB VRAM
~500 tok/s
N/A
Low
Transformers + bitsandbytes (GPU)
6 GB VRAM
~50 tok/s
Good
Low
llama.cpp (GPU offload, Q4_K_M)
4 GB VRAM
~80 tok/s
Good
Medium
llama.cpp (CPU, Q4_K_M)
4 GB RAM
~8 tok/s
Good
Medium
Transformers + bitsandbytes (CPU)
8 GB RAM
~0.1 tok/s
Good
Low
MLX (Apple Silicon, M2+)
8 GB unified
~40 tok/s
Good
Low
Ollama / LM Studio
4 GB
~8 tok/s
Good
Minimal
Troubleshooting
"Failed to load model from file" with GGUF
Cause: Your llama.cpp / llama-cpp-python version doesn't support the qwen35 architecture.
Tool-calling agents — function calling, API orchestration, multi-turn tool use
Agent frameworks — drop-in replacement for agent runtimes behind an OpenAI-compatible API
Local / edge inference — runs on consumer GPUs (6 GB+ VRAM) due to 4-bit quantization
Systems research — studying harness behavior, evaluation loops, and model composition at a manageable scale (~2B params)
Out-of-scope
Multimodal tasks — despite Qwen3.5-2B's vision backbone, this fine-tune was text-only and unevaluated on image/video inputs
High-stakes decision making — research artifact; not intended for medical, legal, or financial advice without human oversight
Unsupported languages — trained exclusively on English data
Limitations
Trained for 1 epoch on ~27K examples. More data and more epochs would improve tool-calling reliability.
May produce malformed JSON tool calls in edge cases — validate output before execution.
4-bit quantization introduces minor rounding error in the merged weights.
This is a research-stage model, not a production product. We publish methods, configs, and artifacts that others can inspect, rerun, and improve — in keeping with our reproducible research principle.
Training Data
The training mix sampled 26,893 examples across three datasets — prioritizing signal density over raw scale:
General instruct/chat data (curated sample from 100K)
All data formatted via tokenizer.apply_chat_template() with the Qwen2.5-ChatML template. Examples without a user role were filtered. Sequence length capped at 2048 tokens for this training run.
Training Procedure
Framework
Training: HuggingFace TRL SFTTrainer (v1.7.0) with SFTConfig
1@misc{qwen3.5,
2 title = {Qwen3.5: A New Generation of Large Language Models},
3 author = {Qwen Team},
4 year = {2026},
5 publisher = {GitHub},
6 url = {https://github.com/QwenLM/Qwen3.5}
7}
TRL
bibtex
1@software{vonwerra2020trl,
2 title = {{TRL: Transformers Reinforcement Learning}},
3 author = {von Werra, Leandro and Belkada, Younes and Tunstall, Lewis and Beeching, Edward and Thrush, Tristan and Lambert, Nathan and Huang, Shengyi and Rasul, Kashif and Gallouédec, Quentin},
4 license = {Apache-2.0},
5 url = {https://github.com/huggingface/trl},
6 year = {2020}
7}
QLoRA
bibtex
1@article{dettmers2023qlora,
2 title = {QLoRA: Efficient Finetuning of Quantized Language Models},
3 author = {Dettmers, Tim and Pagnoni, Artidoro and Holtzman, Ari and Zettlemoyer, Luke},
4 journal = {arXiv preprint arXiv:2305.14314},
5 year = {2023}
6}
Hermes Function Calling
bibtex
1@misc{hermesfc,
2 title = {NousResearch Hermes Function Calling},
3 author = {Nous Research},
4 year = {2024},
5 url = {https://huggingface.co/datasets/NousResearch/hermes-function-calling-v1}
6}
APIGen
bibtex
1@misc{apigen2024,
2 title = {APIGen: Automated Pipeline for Generating Verifiable and Diverse Function-Calling Datasets},
3 author = {Salesforce AI Research},
4 year = {2024},
5 url = {https://huggingface.co/datasets/Salesforce/APIGen-MT-5k}
6}
FineTome
bibtex
1@misc{finetome2024,
2 title = {FineTome-100k: A Curated Instruction Tuning Dataset},
3 author = {Labonne, Maxime},
4 year = {2024},
5 url = {https://huggingface.co/datasets/mlabonne/FineTome-100k}
6}
Uraion Labs — Foundational systems research.
uraionlabs.com
Intelligence is a systems problem.
Licensed under Apache 2.0.