{"id":17546,"date":"2026-08-08T21:01:21","date_gmt":"2026-08-08T21:01:21","guid":{"rendered":"https:\/\/techtrendfeed.com\/?p=17546"},"modified":"2026-08-08T21:01:21","modified_gmt":"2026-08-08T21:01:21","slug":"small-language-fashions-with-hugging-face-transformers-library-smollm3","status":"publish","type":"post","link":"https:\/\/techtrendfeed.com\/?p=17546","title":{"rendered":"Small Language Fashions with Hugging Face transformers Library + smolLM3"},"content":{"rendered":"<p> <br \/>\n<\/p>\n<div id=\"post-\">\n<p><img decoding=\"async\" alt=\"Small Language Models with Hugging Face transformers Library + smolLM3\" width=\"100%\" class=\"perfmatters-lazy\" src=\"https:\/\/www.kdnuggets.com\/wp-content\/uploads\/kdn-small-language-models-with-hugging-face-transformers-library-smollm3-feature.png\"\/><br \/>\u00a0<\/p>\n<h2><span>#\u00a0<\/span>Small However Highly effective<\/h2>\n<p>\u00a0<br \/>Working a 70B mannequin in manufacturing might be costly, sluggish, and, for a lot of duties, pointless. For those who&#8217;re constructing a centered pipeline like a doc classifier or a multilingual help responder, a well-trained 3B mannequin will match or beat the 70B in your particular process at a fraction of the fee. The 3B mannequin matches fully in a single client GPU. It masses in seconds. It prices nothing per token. And on constrained {hardware}, it is the one choice that runs in any respect.<\/p>\n<p>That is the precise case for small language fashions (SLMs). This text makes use of <strong><a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/huggingface.co\/HuggingFaceTB\/SmolLM3-3B\" target=\"_blank\">SmolLM3<\/a><\/strong>, Hugging Face&#8217;s flagship 3B mannequin launched on July 8, 2025, because the working mannequin all through. It is essentially the most technically fascinating SLM out there on the 3B scale proper now, skilled on 11.2 trillion tokens, supporting a 128k context window, dual-mode reasoning, native instrument calling, six languages, and an Apache 2.0 license with the complete coaching blueprint printed alongside the weights.<\/p>\n<p>The undertaking thread woven by means of each part: a multilingual buyer help ticket router that classifies incoming tickets by class, detects the ticket language, generates a reply in that very same language, and flags low-confidence outputs for human escalation. By the top, you may have a working pipeline you may adapt to your individual area.<\/p>\n<p>\u00a0<\/p>\n<h2><span>#\u00a0<\/span>Why Small Language Fashions Deserve Extra Consideration<\/h2>\n<p>\u00a0<br \/>The parameter-count fixation in AI is comprehensible however deceptive. Uncooked scale issues, up to some extent. After that time, information high quality, coaching curriculum, and architectural decisions matter extra.<\/p>\n<p>Analysis from the <strong><a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/arxiv.org\/abs\/2502.02737v1\" target=\"_blank\">SmolLM2 paper (arxiv, February 2025)<\/a><\/strong> confirmed that on the 1B\u20143B scale, rigorously curated coaching information constantly outperforms naively scaling parameters. SmolLM3 takes that additional: 11.2 trillion coaching tokens throughout a staged curriculum \u2014 internet, code, math, and reasoning information \u2014 plus 140 billion reasoning tokens in post-training. The result&#8217;s a mannequin that, on zero-shot benchmarks, <a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/huggingface.co\/HuggingFaceTB\/SmolLM3-3B\" target=\"_blank\">outperforms each Llama-3.2-3B and Qwen2.5-3B and rivals Qwen3-4B on a number of duties<\/a>.<\/p>\n<p>Take the IFEval instruction-following benchmark, the place SmolLM3 scores 76.7, increased than Qwen3-4B at 68.9. On BFCL (instrument calling), it ties Llama&#8217;s tool-call fine-tune at 92.3. On International MMLU (multilingual QA), it scores 53.5 towards Llama-3.1-3B&#8217;s 46.8.<\/p>\n<p>The place SLMs genuinely fall quick: duties requiring deep, broad world information, aggressive trivia, complicated multi-hop reasoning over huge information graphs, and really long-form artistic writing with wealthy historic context. For these, you need the large mannequin. For all the pieces centered and domain-specific, the SLM with fine-tuning in your information will match it at a tenth of the working value.<\/p>\n<p>The <strong><a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/huggingface.co\/collections\/HuggingFaceTB\/smollm3-686d33c1fdffe8e635317e23\" target=\"_blank\">Hugging Face SLM assortment<\/a><\/strong> at the moment consists of SmolLM3-3B (instruction-tuned, what this text makes use of), SmolLM3-3B-Base (untuned pretrained weights), SmolLM2-1.7B (lighter predecessor), and SmolVLM (the vision-language variant). SmolLM3 is the best alternative for many new tasks as a result of dual-mode reasoning, instrument calling, and the 128k context window are uncommon at this parameter scale.<\/p>\n<p>\u00a0<\/p>\n<h2><span>#\u00a0<\/span>Understanding SmolLM3&#8217;s Structure<\/h2>\n<p>\u00a0<br \/>SmolLM3 is a decoder-only transformer, which is commonplace. Three architectural choices inside that commonplace body are much less frequent and price understanding as a result of they immediately have an effect on the way you deploy and tune the mannequin.<\/p>\n<ol>\n<li><strong>Grouped Question Consideration<\/strong>: Normal multi-head consideration maintains separate key and worth projections for every of the 16 consideration heads. SmolLM3 teams these 16 heads into 4 shared question projections, decreasing key-value (KV) cache reminiscence by roughly 25% with out measurable accuracy loss. <a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/learnopencv.com\/smollm3-explained\/\" target=\"_blank\">This issues at inference time<\/a>: a smaller KV cache means decrease peak VRAM, which implies you may course of longer contexts or bigger batches on the identical {hardware}.<\/li>\n<li><strong>NoPE (No Positional Encoding on choose layers)<\/strong>: SmolLM3 removes rotary positional encoding (RoPE) from each fourth transformer layer, implementing a 3:1 RoPE-to-NoPE ratio. This method comes from the 2025 paper &#8220;<a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/arxiv.org\/abs\/2410.06205\" target=\"_blank\">RoPE to NoRoPE and Again Once more<\/a>&#8221; and helps the mannequin generalize over lengthy contexts with out the positional embedding degradation that impacts most different small fashions at lengthy sequence lengths.<\/li>\n<li><strong>Twin-mode reasoning<\/strong>: A single set of weights handles two modes: <code style=\"background: #F5F5F5;\">assume<\/code> and <code style=\"background: #F5F5F5;\">no_think<\/code>. In <code style=\"background: #F5F5F5;\">assume<\/code> mode, the mannequin generates a chain-of-thought hint inside <code style=\"background: #F5F5F5;\"><think>...<\/think><\/code> tags earlier than the ultimate reply, equal to what separate &#8220;reasoning fashions&#8221; do. In <code style=\"background: #F5F5F5;\">no_think<\/code> mode, it solutions immediately. You management this per-request by way of the system immediate or the <code style=\"background: #F5F5F5;\">enable_thinking<\/code> kwarg within the chat template. No further mannequin, no further checkpoint.<\/li>\n<\/ol>\n<p>\u00a0<\/p>\n<h2><span>#\u00a0<\/span>Setting Up Your Atmosphere<\/h2>\n<p>\u00a0<br \/><strong>{Hardware} minimums<\/strong>:<\/p>\n<p>\u00a0<\/p>\n<table style=\"width: 100%; border-collapse: collapse; font-family: Arial, sans-serif; font-size: 14px; color: #333;\">\n<thead>\n<tr style=\"background-color: #ffd29a;\">\n<th style=\"padding: 12px; border: 1px solid #ddd; text-align: left;\"><strong>Function<\/strong><\/th>\n<th style=\"padding: 12px; border: 1px solid #ddd; text-align: left;\"><strong>Minimal<\/strong><\/th>\n<th style=\"padding: 12px; border: 1px solid #ddd; text-align: left;\"><strong>Really useful<\/strong><\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"padding: 12px; border: 1px solid #ddd;\">GPU VRAM<\/td>\n<td style=\"padding: 12px; border: 1px solid #ddd;\">6 GB (bfloat16)<\/td>\n<td style=\"padding: 12px; border: 1px solid #ddd;\">8 GB+ (RTX 3060 or higher)<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 12px; border: 1px solid #ddd;\">System RAM<\/td>\n<td style=\"padding: 12px; border: 1px solid #ddd;\">16 GB<\/td>\n<td style=\"padding: 12px; border: 1px solid #ddd;\">32 GB<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 12px; border: 1px solid #ddd;\">Disk<\/td>\n<td style=\"padding: 12px; border: 1px solid #ddd;\">8 GB free<\/td>\n<td style=\"padding: 12px; border: 1px solid #ddd;\">20 GB+ SSD<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 12px; border: 1px solid #ddd;\">Apple Silicon<\/td>\n<td style=\"padding: 12px; border: 1px solid #ddd;\">M2 8 GB<\/td>\n<td style=\"padding: 12px; border: 1px solid #ddd;\">M2 Professional \/ M3 16 GB<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\u00a0<\/p>\n<p>CPU-only works. Anticipate roughly 3x slower inference for text-to-speech (TTS) synthesis and 5\u20148 tokens\/second on technology duties relying in your machine. Advantageous-tuning on CPU is impractical; use Google Colab&#8217;s free T4 GPU if you do not have a neighborhood GPU.<\/p>\n<p><strong>Python and packages:<\/strong><\/p>\n<div style=\"width: 98%; overflow: auto; padding-left: 10px; padding-bottom: 10px; padding-top: 10px; background: #F5F5F5;\">\n<pre><code># Python 3.10 or newer required&#13;\npython --version&#13;\n&#13;\n# Create and activate a digital atmosphere&#13;\npython -m venv smollm-env&#13;\nsupply smollm-env\/bin\/activate       # macOS \/ Linux&#13;\nsmollm-envScriptsactivate          # Home windows&#13;\n&#13;\n# Set up all dependencies&#13;\npip set up &#13;\n  \"transformers&gt;=4.53.0\" &#13;\n  \"torch&gt;=2.3.0\" &#13;\n  \"speed up&gt;=0.30.0\" &#13;\n  \"bitsandbytes&gt;=0.43.0\" &#13;\n  \"sentencepiece\" &#13;\n  \"trl&gt;=0.9.0\" &#13;\n  \"peft&gt;=0.11.0\" &#13;\n  \"datasets&gt;=2.19.0\"<\/code><\/pre>\n<\/div>\n<p>\u00a0<\/p>\n<blockquote>\n<p>\n<strong>Be aware<\/strong>: <code style=\"background: #F5F5F5;\">transformers&gt;=4.53.0<\/code> is required; SmolLM3&#8217;s modeling code shipped in that launch. Earlier variations will fail with an unrecognized structure error.\n<\/p>\n<\/blockquote>\n<p>\u00a0<\/p>\n<p><strong>Gadget detection helper (run this primary):<\/strong><\/p>\n<div style=\"width: 98%; overflow: auto; padding-left: 10px; padding-bottom: 10px; padding-top: 10px; background: #F5F5F5;\">\n<pre><code># device_check.py&#13;\n# Run this earlier than the rest to substantiate your setup and choose the best dtype.&#13;\n&#13;\ndef detect_device():&#13;\n    \"\"\"&#13;\n    Detect the most effective out there compute gadget.&#13;\n    Returns (device_str, dtype_str, load_kwargs) to be used with from_pretrained.&#13;\n    \"\"\"&#13;\n    strive:&#13;\n        import torch&#13;\n    besides ImportError:&#13;\n        elevate RuntimeError(\"PyTorch not discovered. Set up with: pip set up torch\")&#13;\n&#13;\n    if torch.cuda.is_available():&#13;\n        vram_gb = torch.cuda.get_device_properties(0).total_memory \/ 1e9&#13;\n        print(f\"CUDA GPU detected: {torch.cuda.get_device_name(0)} ({vram_gb:.1f} GB VRAM)\")&#13;\n        # bfloat16 is advisable for SmolLM3 -- it is the coaching dtype&#13;\n        return \"cuda\", torch.bfloat16, {\"device_map\": \"auto\", \"torch_dtype\": torch.bfloat16}&#13;\n&#13;\n    elif hasattr(torch.backends, \"mps\") and torch.backends.mps.is_available():&#13;\n        print(\"Apple Silicon MPS detected\")&#13;\n        # MPS helps float16 however not all bfloat16 ops -- use float16 on Apple Silicon&#13;\n        return \"mps\", torch.float16, {\"device_map\": \"mps\", \"torch_dtype\": torch.float16}&#13;\n&#13;\n    else:&#13;\n        print(\"No GPU discovered -- operating on CPU (slower however purposeful)\")&#13;\n        return \"cpu\", torch.float32, {\"device_map\": \"cpu\", \"torch_dtype\": torch.float32}&#13;\n&#13;\n&#13;\nif __name__ == \"__main__\":&#13;\n    gadget, dtype, kwargs = detect_device()&#13;\n    print(f\"Gadget : {gadget}\")&#13;\n    print(f\"Dtype  : {dtype}\")&#13;\n    print(f\"Kwargs : {kwargs}\")<\/code><\/pre>\n<\/div>\n<p>\u00a0<\/p>\n<p><strong>Tips on how to run:<\/strong><\/p>\n<p>\u00a0<\/p>\n<p>Anticipated output (NVIDIA GPU instance):<\/p>\n<div style=\"width: 98%; overflow: auto; padding-left: 10px; padding-bottom: 10px; padding-top: 10px; background: #F5F5F5;\">\n<pre><code>CUDA GPU detected: NVIDIA GeForce RTX 3060 (12.0 GB VRAM)&#13;\nGadget : cuda&#13;\nDtype  : torch.bfloat16&#13;\nKwargs : {'device_map': 'auto', 'torch_dtype': torch.bfloat16}<\/code><\/pre>\n<\/div>\n<p>\u00a0<\/p>\n<h2><span>#\u00a0<\/span>Loading SmolLM3 and Working Your First Inference<\/h2>\n<p>\u00a0<br \/>With the atmosphere confirmed, this is the whole load-and-generate sample. This covers dtype choice, <code style=\"background: #F5F5F5;\">device_map=\"auto\"<\/code> for multi-GPU or CPU offload, and each considering modes facet by facet.<\/p>\n<div style=\"width: 98%; overflow: auto; padding-left: 10px; padding-bottom: 10px; padding-top: 10px; background: #F5F5F5;\">\n<pre><code># first_inference.py&#13;\n# Stipulations: transformers&gt;=4.53.0, torch, speed up&#13;\n# Run: python first_inference.py&#13;\n&#13;\nimport re&#13;\nimport torch&#13;\nfrom transformers import AutoTokenizer, AutoModelForCausalLM&#13;\n&#13;\nMODEL_ID = \"HuggingFaceTB\/SmolLM3-3B\"&#13;\n&#13;\n# \u2500\u2500 1. Load tokenizer and mannequin \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;\n&#13;\nprint(f\"Loading {MODEL_ID}...\")&#13;\n&#13;\ntokenizer = AutoTokenizer.from_pretrained(MODEL_ID)&#13;\n&#13;\nmannequin = AutoModelForCausalLM.from_pretrained(&#13;\n    MODEL_ID,&#13;\n    torch_dtype=torch.bfloat16,    # Match the coaching dtype; use float16 on Apple Silicon&#13;\n    device_map=\"auto\",             # Spreads throughout all out there GPUs, or CPU if none&#13;\n)&#13;\nmannequin.eval()&#13;\n&#13;\nprint(f\"Mannequin loaded on: {mannequin.gadget}\")&#13;\n&#13;\n# \u2500\u2500 2. Era helper \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;\n&#13;\ndef generate(messages: checklist[dict], max_new_tokens: int = 512) -&gt; str:&#13;\n    \"\"\"&#13;\n    Apply the SmolLM3 chat template, tokenize, generate, and decode.&#13;\n    Strips the <think>...<\/think> block from the output routinely&#13;\n    so callers all the time obtain the ultimate reply solely.&#13;\n    \"\"\"&#13;\n    # apply_chat_template codecs messages utilizing SmolLM3's built-in chat template&#13;\n    textual content = tokenizer.apply_chat_template(&#13;\n        messages,&#13;\n        tokenize=False,&#13;\n        add_generation_prompt=True,&#13;\n    )&#13;\n    inputs = tokenizer(textual content, return_tensors=\"pt\").to(mannequin.gadget)&#13;\n&#13;\n    with torch.no_grad():&#13;\n        output_ids = mannequin.generate(&#13;\n            **inputs,&#13;\n            max_new_tokens=max_new_tokens,&#13;\n            temperature=0.6,   # Really useful by the SmolLM3 crew for balanced output&#13;\n            top_p=0.95,        # Nucleus sampling -- retains output centered with out being repetitive&#13;\n            do_sample=True,&#13;\n        )&#13;\n&#13;\n    # Decode solely the newly generated tokens, not the enter immediate&#13;\n    new_tokens = output_ids[0][inputs[\"input_ids\"].form[-1]:]&#13;\n    uncooked = tokenizer.decode(new_tokens, skip_special_tokens=True)&#13;\n&#13;\n    # Strip the chain-of-thought block if current.&#13;\n    # In assume mode the mannequin prefixes its response with <think>...<\/think>.&#13;\n    # Callers often solely want the ultimate reply that follows.&#13;\n    remaining = re.sub(r\"<think>.*?<\/think>\", \"\", uncooked, flags=re.DOTALL).strip()&#13;\n    return remaining&#13;\n&#13;\n&#13;\n# \u2500\u2500 3. Examine assume vs no_think on the identical immediate \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;\n&#13;\nimmediate = \"A buyer is charged twice for a similar order. What are three concrete steps help ought to take?\"&#13;\n&#13;\n# no_think: quick, direct reply -- good for high-throughput classification and replies&#13;\nno_think_messages = [&#13;\n    {\"role\": \"system\", \"content\": \"\/no_think\"},&#13;\n    {\"role\": \"user\",   \"content\": prompt},&#13;\n]&#13;\n&#13;\n# assume: reasoning hint earlier than reply -- good for complicated choices and edge circumstances&#13;\nthink_messages = [&#13;\n    {\"role\": \"system\", \"content\": \"\/think\"},&#13;\n    {\"role\": \"user\",   \"content\": prompt},&#13;\n]&#13;\n&#13;\nprint(\"n\u2500\u2500 no_think mode \u2500\u2500\")&#13;\nprint(generate(no_think_messages, max_new_tokens=256))&#13;\n&#13;\nprint(\"n\u2500\u2500 assume mode \u2500\u2500\")&#13;\nprint(generate(think_messages, max_new_tokens=512))<\/code><\/pre>\n<\/div>\n<p>\u00a0<\/p>\n<p><strong>Tips on how to run:<\/strong><\/p>\n<div style=\"width: 98%; overflow: auto; padding-left: 10px; padding-bottom: 10px; padding-top: 10px; background: #F5F5F5;\">\n<pre><code>python first_inference.py<\/code><\/pre>\n<\/div>\n<p>\u00a0<\/p>\n<p>The mannequin downloads to <code style=\"background: #F5F5F5;\">~\/.cache\/huggingface\/hub\/<\/code> on first run (~6.7 GB). On subsequent runs, it masses from cache in a number of seconds.<\/p>\n<p>Whenever you evaluate the 2 outputs, <code style=\"background: #F5F5F5;\">assume<\/code> mode produces a noticeably extra structured reply; it causes by means of the steps earlier than committing. <code style=\"background: #F5F5F5;\">no_think<\/code> is quicker and infrequently enough for routine duties. The correct mode depends upon your latency finances and process complexity. For the ticket router undertaking coming subsequent, we&#8217;ll use <code style=\"background: #F5F5F5;\">no_think<\/code> for classification (latency-sensitive) and <code style=\"background: #F5F5F5;\">assume<\/code> for escalation choices (accuracy-sensitive).<\/p>\n<p>\u00a0<\/p>\n<h2><span>#\u00a0<\/span>Constructing a Multilingual Help Ticket Router<\/h2>\n<p>\u00a0<br \/>Now the core undertaking. The <code style=\"background: #F5F5F5;\">TicketRouter<\/code> class takes a help ticket in any of SmolLM3&#8217;s six natively supported languages (English, French, Spanish, German, Italian, Portuguese), classifies it right into a class, generates a reply within the ticket&#8217;s personal language, and flags low-confidence outputs for human evaluation.<\/p>\n<p>This can be a sample used at scale in actual help operations. The SmolLM3 model runs fully offline, with no API key, no information leaving the server, and no per-ticket value. That issues for any help system dealing with personally identifiable info (PII).<\/p>\n<div style=\"width: 98%; overflow: auto; padding-left: 10px; padding-bottom: 10px; padding-top: 10px; background: #F5F5F5;\">\n<pre><code># ticket_router.py&#13;\n# Stipulations: transformers&gt;=4.53.0, torch, speed up&#13;\n# Run: python ticket_router.py&#13;\n&#13;\nimport re&#13;\nimport json&#13;\nimport torch&#13;\nfrom dataclasses import dataclass&#13;\nfrom transformers import AutoTokenizer, AutoModelForCausalLM&#13;\n&#13;\nMODEL_ID      = \"HuggingFaceTB\/SmolLM3-3B\"&#13;\nESCALATE_AT   = 0.70   # Tickets with confidence under this go to a human agent&#13;\n&#13;\n# \u2500\u2500 Information class for a routing end result \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;\n&#13;\n@dataclass&#13;\nclass RoutingResult:&#13;\n    ticket: str&#13;\n    class: str           # billing | technical | account | common&#13;\n    confidence: float       # 0.0-1.0 self-reported by the mannequin&#13;\n    reply: str              # Generated in the identical language because the ticket&#13;\n    escalate: bool          # True when confidence &lt; ESCALATE_AT&#13;\n    raw_output: str         # Full mannequin output for debugging&#13;\n&#13;\n&#13;\n# \u2500\u2500 System immediate \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;\n&#13;\nSYSTEM_PROMPT = \"\"\"You're a multilingual buyer help router for a SaaS firm.&#13;\nYour job is to categorise help tickets and draft a useful, skilled reply.&#13;\n&#13;\nGuidelines:&#13;\n- Detect the language of the ticket routinely.&#13;\n- Classify into EXACTLY ONE of: billing, technical, account, common.&#13;\n- Reply within the SAME language because the ticket.&#13;\n- Fee your confidence actually from 0.0 to 1.0. Low confidence means the ticket is ambiguous or exterior your information.&#13;\n- Reply ONLY with a single JSON object -- no preamble, no clarification exterior the JSON.&#13;\n&#13;\nRequired format:&#13;\n{\"class\": \"<billing>\", \"confidence\": &lt;0.0-1.0&gt;, \"reply\": \"<reply in=\"\" ticket=\"\" language=\"\">\"}\"\"\"&#13;\n&#13;\n&#13;\n# \u2500\u2500 Router class \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;\n&#13;\nclass TicketRouter:&#13;\n    def __init__(self, model_id: str = MODEL_ID):&#13;\n        print(f\"Loading {model_id}...\")&#13;\n        self.tokenizer = AutoTokenizer.from_pretrained(model_id)&#13;\n        self.mannequin = AutoModelForCausalLM.from_pretrained(&#13;\n            model_id,&#13;\n            torch_dtype=torch.bfloat16,&#13;\n            device_map=\"auto\",&#13;\n        )&#13;\n        self.mannequin.eval()&#13;\n        print(f\"Prepared on {self.mannequin.gadget}\")&#13;\n&#13;\n    def _call_model(self, ticket: str) -&gt; str:&#13;\n        \"\"\"&#13;\n        Format the ticket right into a chat message, run inference in no_think mode&#13;\n        (quicker for classification), and return the uncooked decoded output.&#13;\n        \"\"\"&#13;\n        messages = [&#13;\n            {\"role\": \"system\", \"content\": SYSTEM_PROMPT},&#13;\n            {\"role\": \"user\",   \"content\": ticket},&#13;\n        ]&#13;\n        textual content = self.tokenizer.apply_chat_template(&#13;\n            messages,&#13;\n            tokenize=False,&#13;\n            add_generation_prompt=True,&#13;\n            enable_thinking=False,   # Quick path -- no chain-of-thought for routine classification&#13;\n        )&#13;\n        inputs = self.tokenizer(textual content, return_tensors=\"pt\").to(self.mannequin.gadget)&#13;\n&#13;\n        with torch.no_grad():&#13;\n            output_ids = self.mannequin.generate(&#13;\n                **inputs,&#13;\n                max_new_tokens=256,&#13;\n                temperature=0.3,   # Decrease temp for classification -- extra deterministic output&#13;\n                top_p=0.9,&#13;\n                do_sample=True,&#13;\n            )&#13;\n&#13;\n        new_tokens = output_ids[0][inputs[\"input_ids\"].form[-1]:]&#13;\n        return self.tokenizer.decode(new_tokens, skip_special_tokens=True).strip()&#13;\n&#13;\n    def _parse_output(self, uncooked: str) -&gt; dict:&#13;\n        \"\"\"&#13;\n        Extract the JSON object from the mannequin's output.&#13;\n        Falls again to a default 'common' class with zero confidence if parsing fails.&#13;\n        This prevents a JSON parse failure from crashing the pipeline.&#13;\n        \"\"\"&#13;\n        # Discover any JSON object within the output, even when surrounded by stray textual content&#13;\n        match = re.search(r\"{.*?}\", uncooked, re.DOTALL)&#13;\n        if not match:&#13;\n            return {\"class\": \"common\", \"confidence\": 0.0, \"reply\": uncooked}&#13;\n        strive:&#13;\n            return json.masses(match.group())&#13;\n        besides json.JSONDecodeError:&#13;\n            return {\"class\": \"common\", \"confidence\": 0.0, \"reply\": uncooked}&#13;\n&#13;\n    def route(self, ticket: str) -&gt; RoutingResult:&#13;\n        \"\"\"&#13;\n        Route a single ticket. Returns a RoutingResult with classification,&#13;\n        confidence, reply, and escalation flag.&#13;\n        \"\"\"&#13;\n        uncooked = self._call_model(ticket)&#13;\n        parsed = self._parse_output(uncooked)&#13;\n&#13;\n        class   = parsed.get(\"class\", \"common\")&#13;\n        confidence = float(parsed.get(\"confidence\", 0.0))&#13;\n        reply      = parsed.get(\"reply\", \"Thanks for reaching out. We'll comply with up shortly.\")&#13;\n&#13;\n        return RoutingResult(&#13;\n            ticket=ticket,&#13;\n            class=class,&#13;\n            confidence=confidence,&#13;\n            reply=reply,&#13;\n            escalate=confidence &lt; ESCALATE_AT,&#13;\n            raw_output=uncooked,&#13;\n        )&#13;\n&#13;\n    def route_batch(self, tickets: checklist[str]) -&gt; checklist[RoutingResult]:&#13;\n        \"\"\"Route an inventory of tickets sequentially. Returns ends in enter order.\"\"\"&#13;\n        return [self.route(t) for t in tickets]&#13;\n&#13;\n&#13;\n# \u2500\u2500 Run it \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;\n&#13;\nif __name__ == \"__main__\":&#13;\n    router = TicketRouter()&#13;\n&#13;\n    test_tickets = [&#13;\n        \"I was charged twice for my subscription this month. Please refund the duplicate charge.\",&#13;\n        \"L'application se bloque chaque fois que j'essaie d'exporter un fichier PDF.\",   # French&#13;\n        \"No puedo iniciar sesi\u00f3n en mi cuenta desde hace dos d\u00edas.\",                     # Spanish&#13;\n        \"Die Rechnung f\u00fcr M\u00e4rz fehlt in meinem Abrechnungsbereich.\",                     # German&#13;\n        \"Il mio abbonamento non si rinnova automaticamente nonostante il pagamento.\",    # Italian&#13;\n    ]&#13;\n&#13;\n    print(\"n\" + \"=\" * 70)&#13;\n    outcomes = router.route_batch(test_tickets)&#13;\n&#13;\n    for r in outcomes:&#13;\n        flag = \"\ud83d\udd34 ESCALATE\" if r.escalate else \"\ud83d\udfe2 AUTO\"&#13;\n        print(f\"n{flag}\")&#13;\n        print(f\"Ticket     : {r.ticket[:70]}...\")&#13;\n        print(f\"Class   : {r.class}\")&#13;\n        print(f\"Confidence : {r.confidence:.2f}\")&#13;\n        print(f\"Reply      : {r.reply[:100]}...\")&#13;\n&#13;\n    escalated = [r for r in results if r.escalate]&#13;\n    print(f\"n{'\u2500'*70}\")&#13;\n    print(f\"Whole tickets : {len(outcomes)}\")&#13;\n    print(f\"Auto-routed   : {len(outcomes) - len(escalated)}\")&#13;\n    print(f\"Escalated     : {len(escalated)}\")<\/reply><\/billing><\/code><\/pre>\n<\/div>\n<p>\u00a0<\/p>\n<p>Tips on how to run:<\/p>\n<p>\u00a0<\/p>\n<p><strong>What to search for within the output<\/strong>: tickets the place the mannequin returns a confidence under 0.70 will likely be flagged for escalation. Ambiguous tickets, quick messages, mixed-language content material, and requests that would match two classes reliably produce decrease confidence scores. That is the sign you need: the mannequin being sincere about uncertainty moderately than guessing confidently and propagating a improper classification downstream.<\/p>\n<p>\u00a0<\/p>\n<h2><span>#\u00a0<\/span>Including Device Calling to SmolLM3<\/h2>\n<p>\u00a0<br \/>The ticket router works effectively for classification and reply technology. However what occurs when a buyer asks a few particular order? The mannequin does not have entry to your database. With out instrument calling, it both hallucinates a solution or deflects with &#8220;please contact help&#8221; \u2014 neither of which is beneficial.<\/p>\n<p>SmolLM3 helps instrument calling natively. You outline a instrument as a JSON Schema, go it by way of <code style=\"background: #F5F5F5;\">xml_tools<\/code> within the chat template, and the mannequin emits a structured <code style=\"background: #F5F5F5;\"><tool_call\/><\/code> block when it decides the instrument is required. You parse that block, name the true perform, inject the end result, and let the mannequin generate the ultimate response.<\/p>\n<p>Here is the complete round-trip for an order lookup:<\/p>\n<div style=\"width: 98%; overflow: auto; padding-left: 10px; padding-bottom: 10px; padding-top: 10px; background: #F5F5F5;\">\n<pre><code># tool_calling.py&#13;\n# Stipulations: transformers&gt;=4.53.0, torch, speed up&#13;\n# Run: python tool_calling.py&#13;\n&#13;\nimport re&#13;\nimport json&#13;\nimport torch&#13;\nfrom transformers import AutoTokenizer, AutoModelForCausalLM&#13;\n&#13;\nMODEL_ID  = \"HuggingFaceTB\/SmolLM3-3B\"&#13;\ntokenizer = AutoTokenizer.from_pretrained(MODEL_ID)&#13;\nmannequin     = AutoModelForCausalLM.from_pretrained(&#13;\n    MODEL_ID, torch_dtype=torch.bfloat16, device_map=\"auto\"&#13;\n)&#13;\nmannequin.eval()&#13;\n&#13;\n# \u2500\u2500 Device definition \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;\n# SmolLM3 accepts instrument definitions as JSON Schema objects below xml_tools.&#13;\n# The mannequin makes use of the identify and outline to resolve when to name the instrument.&#13;\n# The parameters schema tells it what arguments to incorporate within the name.&#13;\n&#13;\nTOOLS = [&#13;\n    {&#13;\n        \"name\": \"lookup_order_status\",&#13;\n        \"description\": (&#13;\n            \"Look up the current status, estimated delivery date, and carrier \"&#13;\n            \"for a specific customer order. Call this when the customer mentions \"&#13;\n            \"an order number or asks where their order is.\"&#13;\n        ),&#13;\n        \"parameters\": {&#13;\n            \"type\": \"object\",&#13;\n            \"properties\": {&#13;\n                \"order_id\": {&#13;\n                    \"type\": \"string\",&#13;\n                    \"description\": \"The order ID, usually in the format ORD-XXXXXX.\"&#13;\n                }&#13;\n            },&#13;\n            \"required\": [\"order_id\"]&#13;\n        }&#13;\n    }&#13;\n]&#13;\n&#13;\n# \u2500\u2500 Simulated order database \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;\n&#13;\ndef lookup_order_status(order_id: str) -&gt; dict:&#13;\n    \"\"\"&#13;\n    In manufacturing, change this with an actual database or API name.&#13;\n    Returns a dict the mannequin can learn and summarize for the shopper.&#13;\n    \"\"\"&#13;\n    database = {&#13;\n        \"ORD-4821\": {\"standing\": \"shipped\",    \"eta\": \"June 18, 2026\", \"provider\": \"DHL\"},&#13;\n        \"ORD-3307\": {\"standing\": \"processing\", \"eta\": \"June 20, 2026\", \"provider\": None},&#13;\n        \"ORD-1190\": {\"standing\": \"delivered\",  \"eta\": None,            \"provider\": \"FedEx\"},&#13;\n    }&#13;\n    return database.get(order_id, {\"standing\": \"not_found\", \"eta\": None, \"provider\": None})&#13;\n&#13;\n# \u2500\u2500 Device name parser \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;\n&#13;\ndef parse_tool_call(output: str):&#13;\n    \"\"\"&#13;\n    Extract a instrument name from the mannequin's output.&#13;\n    SmolLM3 emits: <tool_call>{\"identify\": \"...\", \"arguments\": {...}}<\/tool_call>&#13;\n    Returns (tool_name, arguments) or (None, None) if no instrument name is current.&#13;\n    \"\"\"&#13;\n    match = re.search(r\"<tool_call>(.*?)<\/tool_call>\", output, re.DOTALL)&#13;\n    if not match:&#13;\n        return None, None&#13;\n    strive:&#13;\n        payload = json.masses(match.group(1).strip())&#13;\n        return payload.get(\"identify\"), payload.get(\"arguments\", {})&#13;\n    besides json.JSONDecodeError:&#13;\n        return None, None&#13;\n&#13;\n# \u2500\u2500 Full tool-call spherical journey \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;\n&#13;\ndef respond_with_tools(user_message: str) -&gt; str:&#13;\n    \"\"\"&#13;\n    Full agentic loop:&#13;\n    1. Ship person message + instrument definitions to the mannequin.&#13;\n    2. If the mannequin emits a instrument name, execute it and inject the end result.&#13;\n    3. Generate the ultimate customer-facing response.&#13;\n    \"\"\"&#13;\n    # Flip 1: give the mannequin the person message and out there instruments&#13;\n    messages = [{\"role\": \"user\", \"content\": user_message}]&#13;\n&#13;\n    inputs = tokenizer.apply_chat_template(&#13;\n        messages,&#13;\n        xml_tools=TOOLS,           # Go instrument definitions right here&#13;\n        enable_thinking=False,&#13;\n        add_generation_prompt=True,&#13;\n        tokenize=True,&#13;\n        return_tensors=\"pt\",&#13;\n    ).to(mannequin.gadget)&#13;\n&#13;\n    with torch.no_grad():&#13;\n        output_ids = mannequin.generate(&#13;\n            inputs, max_new_tokens=256, temperature=0.3, top_p=0.9, do_sample=True&#13;\n        )&#13;\n    turn1 = tokenizer.decode(&#13;\n        output_ids[0][inputs.shape[-1]:], skip_special_tokens=True&#13;\n    )&#13;\n&#13;\n    # Examine if the mannequin desires to name a instrument&#13;\n    tool_name, tool_args = parse_tool_call(turn1)&#13;\n&#13;\n    if tool_name == \"lookup_order_status\":&#13;\n        # Execute the true perform&#13;\n        tool_result = lookup_order_status(**tool_args)&#13;\n        print(f\"  [Tool called] {tool_name}({tool_args}) \u2192 {tool_result}\")&#13;\n&#13;\n        # Flip 2: inject the instrument end result and ask for the ultimate response&#13;\n        messages += [&#13;\n            {\"role\": \"assistant\", \"content\": turn1},&#13;\n            {\"role\": \"tool\",      \"content\": json.dumps(tool_result), \"name\": tool_name},&#13;\n        ]&#13;\n        inputs2 = tokenizer.apply_chat_template(&#13;\n            messages,&#13;\n            xml_tools=TOOLS,&#13;\n            enable_thinking=False,&#13;\n            add_generation_prompt=True,&#13;\n            tokenize=True,&#13;\n            return_tensors=\"pt\",&#13;\n        ).to(mannequin.gadget)&#13;\n&#13;\n        with torch.no_grad():&#13;\n            output_ids2 = mannequin.generate(&#13;\n                inputs2, max_new_tokens=256, temperature=0.3, top_p=0.9, do_sample=True&#13;\n            )&#13;\n        return tokenizer.decode(&#13;\n            output_ids2[0][inputs2.shape[-1]:], skip_special_tokens=True&#13;\n        ).strip()&#13;\n&#13;\n    # No instrument name -- mannequin answered immediately&#13;\n    return turn1.strip()&#13;\n&#13;\n&#13;\n# \u2500\u2500 Check it \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;\n&#13;\nif __name__ == \"__main__\":&#13;\n    queries = [&#13;\n        \"Where is my order ORD-4821? It's been a week.\",&#13;\n        \"My order ORD-3307 hasn't shipped yet -- what's the status?\",&#13;\n        \"I just want to change my email address.\",  # No tool needed&#13;\n    ]&#13;\n&#13;\n    for question in queries:&#13;\n        print(f\"nCustomer : {question}\")&#13;\n        response = respond_with_tools(question)&#13;\n        print(f\"Agent    : {response}\")<\/code><\/pre>\n<\/div>\n<p>\u00a0<\/p>\n<p>Tips on how to run:<\/p>\n<p>\u00a0<\/p>\n<p>The mannequin routes order-related queries by means of the <code style=\"background: #F5F5F5;\">lookup_order_status<\/code> instrument and generates the ultimate reply utilizing the true database end result. For the email-change question, it solutions immediately with out calling any instrument. That selective invocation \u2014 calling instruments solely after they&#8217;re wanted \u2014 is what makes the agentic sample sensible.<\/p>\n<p>\u00a0<\/p>\n<h2><span>#\u00a0<\/span>Advantageous-Tuning SmolLM3 on Area Information<\/h2>\n<p>\u00a0<br \/>A 3B mannequin is sufficiently small to fine-tune on a single client GPU in minutes, not hours. The result&#8217;s a mannequin that is aware of your area vocabulary, your response fashion, and your escalation logic, as a substitute of counting on immediate engineering to approximate it at each inference name.<\/p>\n<p>This part makes use of the <strong><a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/github.com\/huggingface\/trl\" target=\"_blank\">TRL library&#8217;s<\/a><\/strong> <code style=\"background: #F5F5F5;\">SFTTrainer<\/code> with LoRA adapters from <strong><a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/github.com\/huggingface\/peft\" target=\"_blank\">PEFT<\/a><\/strong>, which implies we&#8217;re coaching solely a small fraction of parameters \u2014 sometimes below 1% \u2014 and merging the adapter again into the bottom mannequin on the finish.<\/p>\n<div style=\"width: 98%; overflow: auto; padding-left: 10px; padding-bottom: 10px; padding-top: 10px; background: #F5F5F5;\">\n<pre><code># finetune.py&#13;\n# Further stipulations: pip set up trl&gt;=0.9.0 peft&gt;=0.11.0 datasets&gt;=2.19.0&#13;\n# Run: python finetune.py&#13;\n# Time: ~8-12 minutes on an RTX 3060 for 3 epochs over 50 examples&#13;\n&#13;\nimport json&#13;\nimport torch&#13;\nfrom datasets import Dataset&#13;\nfrom transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig&#13;\nfrom peft import LoraConfig, get_peft_model&#13;\nfrom trl import SFTTrainer, SFTConfig&#13;\n&#13;\nMODEL_ID   = \"HuggingFaceTB\/SmolLM3-3B\"&#13;\nOUTPUT_DIR = \".\/smollm3-ticket-router\"&#13;\n&#13;\n# \u2500\u2500 System immediate (similar because the inference router) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;\n&#13;\nSYSTEM_PROMPT = \"\"\"You're a multilingual buyer help router for a SaaS firm.&#13;\nClassify the help ticket and generate a useful reply in the identical language because the ticket.&#13;\nReply ONLY with JSON: {\"class\": \"<billing>\", \"confidence\": &lt;0.0-1.0&gt;, \"reply\": \"<reply>\"}\"\"\"&#13;\n&#13;\n# \u2500\u2500 Coaching information \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;\n# In manufacturing you'll load a whole bunch of actual labelled tickets.&#13;\n# This minimal set demonstrates the format -- broaden together with your actual information.&#13;\n&#13;\nraw_examples = [&#13;\n    (\"I was charged twice for my subscription.\", \"billing\",&#13;\n     \"We're sorry for the duplicate charge. Our billing team will review and issue a refund within 3-5 business days.\"),&#13;\n    (\"The app crashes every time I try to export a PDF.\", \"technical\",&#13;\n     \"We apologize for the inconvenience. Our engineering team has been notified and will investigate.\"),&#13;\n    (\"I can't log into my account since yesterday.\", \"account\",&#13;\n     \"We're sorry you're having trouble. Please try resetting your password. If the issue continues, we'll escalate to our account team.\"),&#13;\n    (\"Die App st\u00fcrzt beim Exportieren von PDFs ab.\", \"technical\",&#13;\n     \"Wir entschuldigen uns f\u00fcr die Unannehmlichkeiten. Unser Technikteam wurde benachrichtigt und untersucht das Problem.\"),&#13;\n    (\"L'application se bloque quand j'exporte un fichier.\", \"technical\",&#13;\n     \"Nous nous excusons pour la g\u00eane occasionn\u00e9e. Notre \u00e9quipe technique a \u00e9t\u00e9 inform\u00e9e et travaille sur ce probl\u00e8me.\"),&#13;\n    (\"My March invoice is missing from the billing section.\", \"billing\",&#13;\n     \"Thank you for flagging this. Our billing team will locate your March invoice and resend it within 24 hours.\"),&#13;\n    (\"No puedo iniciar sesi\u00f3n desde ayer por la noche.\", \"account\",&#13;\n     \"Lamentamos el problema de acceso. Por favor, restablezca su contrase\u00f1a. Si el problema persiste, escalaremos su caso.\"),&#13;\n    (\"How do I upgrade my plan to the Pro tier?\", \"general\",&#13;\n     \"You can upgrade to Pro directly from Settings \u2192 Subscription. The new rate applies from your next billing cycle.\"),&#13;\n]&#13;\n&#13;\ndef format_example(ticket: str, class: str, reply: str) -&gt; dict:&#13;\n    \"\"\"&#13;\n    Format a single instance into the SmolLM3 messages format.&#13;\n    The assistant flip accommodates the goal JSON the mannequin ought to be taught to provide.&#13;\n    \"\"\"&#13;\n    return {&#13;\n        \"messages\": [&#13;\n            {\"role\": \"system\",    \"content\": SYSTEM_PROMPT},&#13;\n            {\"role\": \"user\",      \"content\": ticket},&#13;\n            {\"role\": \"assistant\", \"content\": json.dumps({&#13;\n                \"category\": category, \"confidence\": 0.95, \"reply\": reply&#13;\n            })},&#13;\n        ]&#13;\n    }&#13;\n&#13;\ndataset = Dataset.from_list([format_example(*ex) for ex in raw_examples])&#13;\n&#13;\n# \u2500\u2500 Tokenizer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;\n&#13;\ntokenizer = AutoTokenizer.from_pretrained(MODEL_ID)&#13;\ntokenizer.pad_token = tokenizer.eos_token   # SmolLM3 has no separate pad token&#13;\n&#13;\n# \u2500\u2500 Mannequin (4-bit quantized base for QLoRA) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;\n&#13;\nbnb_config = BitsAndBytesConfig(&#13;\n    load_in_4bit=True,&#13;\n    bnb_4bit_compute_dtype=torch.bfloat16,&#13;\n    bnb_4bit_use_double_quant=True,&#13;\n    bnb_4bit_quant_type=\"nf4\",&#13;\n)&#13;\nmannequin = AutoModelForCausalLM.from_pretrained(&#13;\n    MODEL_ID,&#13;\n    quantization_config=bnb_config,&#13;\n    device_map=\"auto\",&#13;\n)&#13;\n&#13;\n# \u2500\u2500 LoRA config \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;\n# We goal the eye and MLP projection layers -- these carry essentially the most&#13;\n# task-specific sign and provides the most effective accuracy\/parameter trade-off.&#13;\n&#13;\nlora_config = LoraConfig(&#13;\n    r=16,              # Rank of the LoRA replace matrices -- increased = extra expressive, extra reminiscence&#13;\n    lora_alpha=32,     # Scaling issue; conventionally set to 2*r&#13;\n    lora_dropout=0.05,&#13;\n    bias=\"none\",&#13;\n    task_type=\"CAUSAL_LM\",&#13;\n    target_modules=[&#13;\n        \"q_proj\", \"k_proj\", \"v_proj\", \"o_proj\",   # Attention projections&#13;\n        \"gate_proj\", \"up_proj\", \"down_proj\",        # MLP projections (SwiGLU)&#13;\n    ],&#13;\n)&#13;\nmannequin = get_peft_model(mannequin, lora_config)&#13;\nmannequin.print_trainable_parameters()&#13;\n# Anticipated: trainable params: ~13M (0.4% of 3B complete)&#13;\n&#13;\n# \u2500\u2500 Coaching config \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;\n&#13;\nsft_config = SFTConfig(&#13;\n    output_dir=OUTPUT_DIR,&#13;\n    num_train_epochs=3,&#13;\n    per_device_train_batch_size=2,&#13;\n    gradient_accumulation_steps=4,   # Efficient batch measurement = 8&#13;\n    learning_rate=2e-4,&#13;\n    warmup_ratio=0.1,&#13;\n    lr_scheduler_type=\"cosine\",&#13;\n    bf16=True,&#13;\n    logging_steps=5,&#13;\n    save_strategy=\"epoch\",&#13;\n    max_seq_length=512,              # Tickets are quick -- no want for the complete context window&#13;\n)&#13;\n&#13;\n# \u2500\u2500 Practice \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;\n&#13;\ncoach = SFTTrainer(&#13;\n    mannequin=mannequin,&#13;\n    tokenizer=tokenizer,&#13;\n    train_dataset=dataset,&#13;\n    args=sft_config,&#13;\n)&#13;\ncoach.practice()&#13;\n&#13;\n# \u2500\u2500 Save and merge \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;\n# Save the LoRA adapter -- small file, straightforward to share or model.&#13;\ncoach.save_model(f\"{OUTPUT_DIR}\/adapter\")&#13;\n&#13;\n# Merge the adapter again into the bottom mannequin weights for standalone deployment.&#13;\n# The merged mannequin masses precisely like the bottom mannequin -- no PEFT dependency at inference.&#13;\nmerged = mannequin.merge_and_unload()&#13;\nmerged.save_pretrained(f\"{OUTPUT_DIR}\/merged\")&#13;\ntokenizer.save_pretrained(f\"{OUTPUT_DIR}\/merged\")&#13;\n&#13;\nprint(f\"nFine-tuned mannequin saved to {OUTPUT_DIR}\/merged\")&#13;\nprint(\"Load it with: AutoModelForCausalLM.from_pretrained('.\/smollm3-ticket-router\/merged')\")<\/reply><\/billing><\/code><\/pre>\n<\/div>\n<p>\u00a0<\/p>\n<p><strong>Tips on how to run:<\/strong><\/p>\n<div style=\"width: 98%; overflow: auto; padding-left: 10px; padding-bottom: 10px; padding-top: 10px; background: #F5F5F5;\">\n<pre><code>pip set up trl&gt;=0.9.0 peft&gt;=0.11.0 datasets&gt;=2.19.0&#13;\npython finetune.py<\/code><\/pre>\n<\/div>\n<p>\u00a0<\/p>\n<p><strong>Anticipated coaching output:<\/strong><\/p>\n<div style=\"width: 98%; overflow: auto; padding-left: 10px; padding-bottom: 10px; padding-top: 10px; background: #F5F5F5;\">\n<pre><code>trainable params: 13,631,488 || all params: 3,085,123,584 || trainable%: 0.4420&#13;\n{'loss': 1.842, 'learning_rate': 2e-04, 'epoch': 0.5}&#13;\n{'loss': 0.923, 'learning_rate': 1.4e-04, 'epoch': 1.0}&#13;\n{'loss': 0.461, 'learning_rate': 6e-05, 'epoch': 2.0}&#13;\n{'loss': 0.287, 'learning_rate': 0.0, 'epoch': 3.0}<\/code><\/pre>\n<\/div>\n<p>\u00a0<\/p>\n<p>Advantageous-tuned mannequin saved to <code style=\"background: #F5F5F5;\">.\/smollm3-ticket-router\/merged<\/code>.<\/p>\n<p>The loss dropping from 1.8 to 0.3 throughout three epochs tells you the mannequin is studying the duty format. On actual information (a whole bunch of examples throughout your particular classes), you may see the classification accuracy and reply high quality enhance noticeably in comparison with the bottom mannequin with immediate engineering alone.<\/p>\n<p>After coaching, swap <code style=\"background: #F5F5F5;\">MODEL_ID<\/code> in <code style=\"background: #F5F5F5;\">ticket_router.py<\/code> for <code style=\"background: #F5F5F5;\">\".\/smollm3-ticket-router\/merged\"<\/code> and also you&#8217;re operating your domain-tuned router.<\/p>\n<p>\u00a0<\/p>\n<h2><span>#\u00a0<\/span>Conclusion<\/h2>\n<p>\u00a0<br \/>SmolLM3 makes the case that parameter rely shouldn&#8217;t be the first metric. A 3B mannequin skilled on 11.2 trillion tokens with the best architectural decisions \u2014 grouped question consideration (GQA), NoPE, and dual-mode reasoning \u2014 delivers production-viable outcomes on centered duties at a fraction of the latency, value, and {hardware} necessities of 70B alternate options.<\/p>\n<p>The ticket router undertaking on this article covers the complete manufacturing sample: load as soon as, route many, escalate on low confidence, name instruments for reside information, fine-tune on area information, and quantize for constrained {hardware}. Every of these methods applies to any centered pure language processing (NLP) process. Swap the ticket examples on your area, modify the class labels, and you&#8217;ve got a basis price deploying.<\/p>\n<p>The <strong><a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/github.com\/huggingface\/smollm\" target=\"_blank\">SmolLM3 GitHub repo<\/a><\/strong> has the complete coaching code, information combination particulars, and analysis configs. The <a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/huggingface.co\/HuggingFaceTB\/SmolLM3-3B\" target=\"_blank\">mannequin web page<\/a> has the benchmark tables in full and the quantized mannequin assortment. The <a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/huggingface.co\/blog\/smollm3\" target=\"_blank\">SmolLM3 weblog put up<\/a> covers the coaching choices in depth if you wish to perceive the architectural decisions earlier than constructing on high of them.<\/p>\n<p><strong>Sources:<\/strong><\/p>\n<p>\u00a0<br \/>\u00a0<\/p>\n<p><a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/www.linkedin.com\/in\/olumide-shittu\"><strong><strong><a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/www.linkedin.com\/in\/olumide-shittu\/\" target=\"_blank\" rel=\"noopener noreferrer\">Shittu Olumide<\/a><\/strong><\/strong><\/a> is a software program engineer and technical author obsessed with leveraging cutting-edge applied sciences to craft compelling narratives, with a eager eye for element and a knack for simplifying complicated ideas. You too can discover Shittu on <a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/twitter.com\/Shittu_Olumide_\">Twitter<\/a>.<\/p>\n<\/p><\/div>\n<p><template id="tPCXjink8UZbO4MDk4wF"></template><\/script><br \/>\n<br \/><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u00a0 #\u00a0Small However Highly effective \u00a0Working a 70B mannequin in manufacturing might be costly, sluggish, and, for a lot of duties, pointless. For those who&#8217;re constructing a centered pipeline like a doc classifier or a multilingual help responder, a well-trained 3B mannequin will match or beat the 70B in your particular process at a fraction [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":17548,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[55],"tags":[3102,9044,634,3424,266,2239,10089,7101],"class_list":["post-17546","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-machine-learning","tag-face","tag-hugging","tag-language","tag-library","tag-models","tag-small","tag-smollm3","tag-transformers"],"_links":{"self":[{"href":"https:\/\/techtrendfeed.com\/index.php?rest_route=\/wp\/v2\/posts\/17546","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/techtrendfeed.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/techtrendfeed.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/techtrendfeed.com\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/techtrendfeed.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=17546"}],"version-history":[{"count":1,"href":"https:\/\/techtrendfeed.com\/index.php?rest_route=\/wp\/v2\/posts\/17546\/revisions"}],"predecessor-version":[{"id":17547,"href":"https:\/\/techtrendfeed.com\/index.php?rest_route=\/wp\/v2\/posts\/17546\/revisions\/17547"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/techtrendfeed.com\/index.php?rest_route=\/wp\/v2\/media\/17548"}],"wp:attachment":[{"href":"https:\/\/techtrendfeed.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=17546"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/techtrendfeed.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=17546"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/techtrendfeed.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=17546"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}<!-- This website is optimized by Airlift. Learn more: https://airlift.net. Template:. Learn more: https://airlift.net. Template: 69d9690a190636c2e0989534. Config Timestamp: 2026-04-10 21:18:02 UTC, Cached Timestamp: 2026-08-08 23:11:06 UTC -->