Instructions to use giux78/nesso2-0.4B-agentic-v2 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use giux78/nesso2-0.4B-agentic-v2 with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="giux78/nesso2-0.4B-agentic-v2") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("giux78/nesso2-0.4B-agentic-v2") model = AutoModelForCausalLM.from_pretrained("giux78/nesso2-0.4B-agentic-v2", device_map="auto") messages = [ {"role": "user", "content": "Who are you?"}, ] inputs = tokenizer.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=256) print(tokenizer.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use giux78/nesso2-0.4B-agentic-v2 with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "giux78/nesso2-0.4B-agentic-v2" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "giux78/nesso2-0.4B-agentic-v2", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/giux78/nesso2-0.4B-agentic-v2
- SGLang
How to use giux78/nesso2-0.4B-agentic-v2 with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "giux78/nesso2-0.4B-agentic-v2" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "giux78/nesso2-0.4B-agentic-v2", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "giux78/nesso2-0.4B-agentic-v2" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "giux78/nesso2-0.4B-agentic-v2", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use giux78/nesso2-0.4B-agentic-v2 with Docker Model Runner:
docker model run hf.co/giux78/nesso2-0.4B-agentic-v2
Nesso2-0.4B-Agentic-v2
Nesso2-0.4B-Agentic-v2 is a bilingual Italian/English Small Language Model (SLM) for function calling, structured output and multi-step agentic execution, with a deliberate emphasis on Italian tool use. It is the successor of Nesso2-0.4B-Agentic, post-trained on the same knowledge-enriched, 32k-context checkpoint of Zagreus-0.4B-ita — a foundation model trained from scratch by the mii-llm community (Made in Italy – Large Language Model) on the Seeweb HPC infrastructure.
What changed versus v1 is how the model is taught to use tools: instead of memorising a fixed toolbox, it learns one compact serving contract and generalises across thousands of procedurally-generated tool schemas (2,531 distinct tools in the synthetic corpus), plus public function-calling data (xLAM, ToolACE, When2Call; about one third translated to Italian). The goal is a model that keeps working when the tools it is given are new, which is the situation you are in at deployment; on our novel-tool robot benchmark it gains +10 points over v1 (with the caveats reported below).
⚠️ This model is at the SFT (Supervised Fine-Tuning) stage. No RL or DPO stage was applied to this checkpoint.
⚠️ Read the Usage section before integrating. This model does not use the
tools=argument ofapply_chat_template(its Llama-3 template ignores it). Tool schemas go inside the system message, in a compact<tools>block — see Chat template & tool contract. Passingtools=silently drops them and the model will behave as if it had none.
Highlights
| Benchmark | Nesso2-0.4B-Agentic-v2 | Nesso2-0.4B-Agentic (v1) | Qwen3-0.6B |
|---|---|---|---|
| Bilingual function calling, 100 cases ↑ | 74 | 68 | 67 (non-thinking) · 76 (thinking) |
| ↳ Italian half (/50) | 38 | 35 | 29 |
| Held-out Italian benchmark, healthcare booking (40 cases) ↑ | 68.5 | 65.3 | 63.9 |
| Production robot benchmark, overall (84 cases) ↑ | 63.6 | 53.3 | 71.1 |
| Generic English tools stress test (33 scenarios) ↑ | 48 | – | 84 |
| Academic average, IT+EN (MMLU / ARC / HellaSwag / IFEval) ↑ | 0.3556 | 0.3532 | 0.3955 |
| Conversation, LLM-as-judge, both languages (/10, mean of 2 runs) ↑ | 3.94 | 4.25 | 4.26 |
In short: the strongest of our models for Italian tool use — ahead of Qwen3-0.6B on the Italian half of the 100-case suite and on the unseen Italian healthcare benchmark — but not a Qwen replacement for English-first or generic-tool deployments, where Qwen3-0.6B is clearly ahead (see Limitations; we report the benchmarks where it loses next to those where it wins).
Model Details
| Property | Value |
|---|---|
| Architecture | Llama-style (dense, GQA) |
| Parameters | ~438M |
| Hidden size | 960 |
| Layers | 32 |
| Attention heads | 15 (KV heads: 5) |
| Head dim | 64 |
| Intermediate size | 2560 |
| Context length | 32,768 tokens |
| RoPE theta | 1,000,000 |
| Tokenizer | Llama-3 (vocab_size: 128,256) |
| Tied embeddings | Yes |
| Precision | BF16 |
| Languages | Italian, English |
| Base model | mii-llm/zagreus-0.4B-ita (+ knowledge CPT + 32k extension) |
| Post-training framework | TRL (SFTTrainer), 8-GPU data parallel |
| Chat template | Llama-3; tool schemas inline in the system message (see below) |
Lineage
zagreus-0.4B-ita (base, pre-trained from scratch, ~1T tokens)
│
▼
+ Knowledge CPT continued pre-training (~4.5B tokens) on a curated
│ knowledge corpus: Italian/English Wikipedia with
▼ grounded QA augmentation + exam-style QA
+ 32k Context Extension long-context adaptation (RoPE θ = 1e6, ~1.4B tokens)
│
▼
+ Agentic SFT (compact contract) this model
The knowledge CPT stage is what keeps Italian MMLU/ARC/HellaSwag competitive despite heavy agentic specialisation; see the Nesso2-0.4B-Agentic card for the academic benchmarks of the shared base.
Training Details
Base pre-training, knowledge CPT, 32k extension
Identical to Nesso2-0.4B-Agentic (Nanotron, mii-llm fork, on the Seeweb HPC infrastructure): ~1T-token pre-training (FineWeb, FineWeb-2 ita, FinePDFs ita, StarCoder), then knowledge CPT and a 32k extension.
Agentic SFT
Supervised fine-tuning with TRL (SFTTrainer), single node / 8 GPUs, on the Seeweb HPC infrastructure. The data mixture has three parts:
| Component | Size | What it teaches |
|---|---|---|
| Conversational floor (omnia: all Italian, English score ≥ 0.6) | ~2.27M rows, ×1 | General chat and instruction following; keeps the model a usable conversationalist |
| Synthetic compact function-calling corpus | 36k rows, ×4 | Single / parallel (same and different tools) / multi-argument calls, argument disambiguation, missing-argument questions, unavailable-tool refusal, no-tool answering, observation grounding, multi-step trajectories — over 2,531 procedurally-generated tool schemas, 50/50 Italian/English |
| Public function-calling data | ~33k rows, ×1 | xLAM-function-calling-60k (CC-BY-4.0, ~20k rows: single and parallel calls on real-world APIs), ToolACE (Apache-2.0, ~7k rows: tool selection, tool-result turns), When2Call (CC-BY-4.0, ~5k rows: ask / "cannot do that" decisions). Re-rendered into the compact contract, reasoning traces removed; about one third translated to Italian with Qwen3.6-35B-A3B and filtered (argument values kept verbatim, language and length checks) |
Why this mix: a 0.4B model locks into one prompt format and over-fits to a fixed toolbox if trained on a handful of tools. Memorising one contract while seeing thousands of schemas is what lets it transfer to unseen tools. Decontamination (8-gram overlap on user turns): the synthetic corpus against the 100-case and healthcare sets; the public data additionally against the robot and generic-tools prompts. Note that the robot benchmark is in a domain close to part of our synthetic data (warehouse/robotics) with different tool schemas — read it as "novel tools", not "novel domain".
Public function-calling data is credited to its authors: APIGen / xLAM (Salesforce), ToolACE (Team-ACE), When2Call (NVIDIA).
Key hyperparameters:
| Hyperparameter | Value |
|---|---|
| Optimizer | AdamW (fused) |
| Learning rate | 1e-3 |
| LR scheduler | Cosine with min-LR floor (min_lr_rate = 0.3) |
| Warmup ratio | 0.03 |
| Epochs | 3 (5,094 optimizer steps) |
| Effective batch | 128 sequences (2 per device × 8 accumulation × 8 GPUs) |
| Max sequence length | 8,192 |
| Gradient checkpointing | On |
| Precision | BF16 |
| Final train loss / eval loss | 1.307 / 1.26 |
| EOS token | <|eot_id|> (128009) |
| Pad token | <|finetune_right_pad_id|> (128004) |
Chat template & tool contract
The model uses the Llama-3 conversation format (not ChatML). Tool schemas are placed inside the system message, after a short context sentence, as compact JSON between <tools> tags. Tool results come back as a tool turn containing the raw JSON. The model emits calls as Hermes-style <tool_call> blocks (one per call; several blocks = parallel calls).
<|begin_of_text|><|start_header_id|>system<|end_header_id|>
Sei un assistente operativo. Usa gli strumenti disponibili quando serve.
<tools>
[{"type":"function","function":{"name":"get_weather","description":"Meteo attuale per una città","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"]}}}]
</tools><|eot_id|><|start_header_id|>user<|end_header_id|>
Che tempo c'è a Roma adesso?<|eot_id|><|start_header_id|>assistant<|end_header_id|>
A tool call and the follow-up turn look like:
<tool_call>
{"name": "get_weather", "arguments": {"city": "Roma"}}
</tool_call>
<|start_header_id|>tool<|end_header_id|>
{"city": "Roma", "temp_c": 24, "condition": "soleggiato"}<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Special tokens:
bos_token:<|begin_of_text|>(128000)eos_token:<|eot_id|>(128009)pad_token:<|finetune_right_pad_id|>(128004)
⚠️ Format-lock. At this size the model is tied to the contract above. Verbose, OpenAI-style tool prompts, ChatML or a different
<tools>layout will degrade it noticeably. Keep the system context short (one sentence) and the tool list compact (separators=(",", ":")).⚠️ The saved inference template does not emit the BOS token itself — tokenize the rendered string with
add_special_tokens=True(as below) so exactly one BOS is prepended.
Usage
Function calling (full loop)
import re, json, torch
from transformers import AutoTokenizer, AutoModelForCausalLM
model_id = "mii-llm/nesso2-0.4B-agentic-v2"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(
model_id, torch_dtype=torch.bfloat16, device_map="auto"
).eval()
CONTEXT = "Sei un assistente operativo. Usa gli strumenti disponibili quando serve."
def system_with_tools(context, tools):
"""The model's native contract: short context + compact <tools> block inside the system message."""
return context + "\n<tools>\n" + json.dumps(tools, separators=(",", ":"), ensure_ascii=False) + "\n</tools>"
def chat(messages, max_new_tokens=256, plain=False):
prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
inputs = tokenizer(prompt, return_tensors="pt", add_special_tokens=True).to(model.device) # exactly one BOS
n = inputs["input_ids"].shape[1]
extra = dict(repetition_penalty=1.15, no_repeat_ngram_size=6) if plain else {}
out = model.generate(
**inputs, max_new_tokens=max_new_tokens,
do_sample=False, # PURE greedy for tool calls
eos_token_id=tokenizer.eos_token_id, pad_token_id=tokenizer.pad_token_id, **extra,
)
text = tokenizer.decode(out[0][n:], skip_special_tokens=False)
return re.split(r"<\|eot_id\|>|<\|end_of_text\|>", text)[0].strip()
def parse_calls(answer):
return [json.loads(c) for c in re.findall(r"<tool_call>\s*(\{.*?\})\s*</tool_call>", answer, flags=re.S)]
tools = [
{"type": "function", "function": {"name": "get_weather", "description": "Meteo attuale per una città",
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}},
{"type": "function", "function": {"name": "convert_currency", "description": "Converte un importo tra due valute",
"parameters": {"type": "object",
"properties": {"amount": {"type": "number"}, "from": {"type": "string"}, "to": {"type": "string"}},
"required": ["amount", "from", "to"]}}},
{"type": "function", "function": {"name": "search_restaurants", "description": "Cerca ristoranti in una città",
"parameters": {"type": "object",
"properties": {"city": {"type": "string"}, "cuisine": {"type": "string"}}, "required": ["city"]}}},
]
system = {"role": "system", "content": system_with_tools(CONTEXT, tools)}
# 1) Selects the right tool among several and fills the arguments
ans = chat([system, {"role": "user", "content": "Quanto sono 250 euro in sterline?"}])
print(parse_calls(ans))
# -> [{'name': 'convert_currency', 'arguments': {'amount': 250, 'from': 'EUR', 'to': 'GBP'}}]
# 2) Emits parallel calls for a multi-part request
ans = chat([system, {"role": "user", "content": "Che tempo fa a Roma e a Torino?"}])
print(parse_calls(ans))
# -> [{'name': 'get_weather', 'arguments': {'city': 'Roma'}}, {'name': 'get_weather', 'arguments': {'city': 'Torino'}}]
# 3) Answers directly when no tool is needed, and says so when no tool fits
print(chat([system, {"role": "user", "content": "Qual è la capitale della Francia?"}]))
# -> La capitale della Francia è Parigi.
print(chat([system, {"role": "user", "content": "Prenota un volo per Parigi."}]))
# -> Non posso prenotare un volo: nessuno degli strumenti disponibili lo consente.
# 4) Ask-for-missing-argument
print(chat([system, {"role": "user", "content": "Convertimi 100 euro."}]))
# -> Per procedere ho bisogno di un dato. In quale valuta desideri convertire?
# 5) Close the loop: run the tool, feed the observation back as a `tool` turn
question = "Che tempo c'è a Roma adesso?"
ans = chat([system, {"role": "user", "content": question}])
call = parse_calls(ans)[0] # {'name': 'get_weather', 'arguments': {'city': 'Roma'}}
observation = {"city": "Roma", "temp_c": 24, "condition": "soleggiato"} # <- result of YOUR get_weather()
final = chat([system,
{"role": "user", "content": question},
{"role": "assistant", "content": ans},
{"role": "tool", "content": json.dumps(observation, ensure_ascii=False)}])
print(final)
# -> Ci sono 24°C e la condizione è soleggiata.
💡 Decoding. Use pure greedy (
do_sample=False, no repetition penalty) for tool calls: the call is short JSON and a repetition penalty suppresses exactly the structural tokens (name,arguments, quotes, braces). For long free-form conversation, a lightrepetition_penalty ≈ 1.15withno_repeat_ngram_size=6avoids loops — keep it off for tool calls.
Plain conversation (no tools)
reply = chat([{"role": "system", "content": "Sei un assistente utile e conciso."},
{"role": "user", "content": "Spiegami in due frasi cosa è il machine learning."}], plain=True)
print(reply)
Example output:
Il machine learning (ML) è una branca dell'intelligenza artificiale che permette ai computer
di apprendere dai dati, migliorando le proprie prestazioni nel tempo senza essere
esplicitamente programmati per ogni task specifico.
Production notes & recommendations
This is a ~0.4B model. Used well it is a fast (≈0.8 s median per call on a single GPU, ~50 generated tokens) and cheap Italian tool-caller; used naively it will make mistakes a 7B model would not. Three application-layer guards make it robust:
- Validate arguments before executing. Check that every required argument value is grounded in the user's message (or the conversation). Treat unsupported values as missing and ask the user. This prevents acting on hallucinated values.
- Treat "I need more information" as a soft failure when the request was complete. On short, fully-specified requests the model sometimes asks for an argument that is already in the prompt (see Limitations). If your application can tell the request was complete, retry once with a rephrased/extended prompt, or fall back to a deterministic parser.
- Feed observations back explicitly (step 5 above) and, if the model re-calls the same tool instead of answering, short-circuit and ask it to answer from the returned data.
Decoding: pure greedy for tool calls; light repetition penalty only for free-form chat.
Evaluation
All benchmarks below are in-house, small, and bilingual; every model is evaluated greedily on its own native prompt contract (our model: the compact contract above; Qwen3-0.6B: its native tools chat template; Nesso2 v1: its own template), scoring tool name, argument presence/values, call count and abstention automatically. See Training Details for what was decontaminated against which suite. They are useful for ranking models of this size, not as absolute guarantees.
1. Agentic function calling — 100 bilingual cases ⭐
100 cases (50 IT / 50 EN), 10 categories of 10: single, parallel-same-tool, parallel-different-tools, multi-argument, disambiguation, missing-argument, unavailable-tool, no-tool, observation grounding, multi-step.
| Model | Italian /50 | English /50 | Total /100 |
|---|---|---|---|
| Nesso2-0.4B-Agentic-v2 | 38 | 36 | 74 |
| Nesso2-0.4B-Agentic (v1) | 35 | 33 | 68 |
| Qwen3-0.6B (non-thinking) | 29 | 38 | 67 |
| Qwen3-0.6B (thinking) | 34 | 42 | 76 |
Per category (out of 10):
| Category | v2 (this model) | Nesso2 v1 | Qwen3-0.6B | Qwen3-0.6B (thinking) |
|---|---|---|---|---|
| single | 7 | 9 | 7 | 8 |
| parallel, same tool | 8 | 10 | 5 | 7 |
| parallel, different tools | 3 | 4 | 4 | 4 |
| multi-argument | 8 | 6 | 10 | 9 |
| disambiguation | 7 | 8 | 8 | 7 |
| missing argument | 9 | 6 | 3 | 10 |
| unavailable tool | 10 | 9 | 10 | 10 |
| no-tool | 10 | 7 | 8 | 8 |
| observation grounding | 6 | 3 | 10 | 10 |
| multi-step | 6 | 6 | 2 | 3 |
| Total | 74 | 68 | 67 | 76 |
What the new training bought over v1: far better abstention (missing-argument 6→9, no-tool 7→10, unavailable-tool 9→10) and observation grounding (3→6). What it gave back: a little on plain single / parallel-same calls (−2 each). Qwen's edge here is observation grounding and multi-argument calls; Qwen in thinking mode reaches 76 but at a very different latency class (≈4.7 s per call versus ≈0.8 s for non-thinking models in our independent benchmark — see the Nesso2 card).
2. Held-out Italian benchmark — healthcare booking (CUP) 🇮🇹
40 Italian cases in a domain no model was trained on: ten Italian healthcare-booking tools (cerca_prestazione, verifica_disponibilita, prenota_appuntamento, verifica_esenzione, calcola_ticket, consulta_ricetta, recupera_referto, invia_promemoria, annulla_prenotazione, elenco_strutture), 10 sub-skills × 4 cases.
| Sub-skill (%) | v2 (this model) | Nesso2 v1 | Qwen3-0.6B |
|---|---|---|---|
| single call | 100 | 100 | 75 |
| multi-argument | 85 | 74 | 80 |
| parallel, same tool | 85 | 70 | 35 |
| parallel, different tools | 85 | 55 | 43 |
| disambiguation | 86 | 93 | 55 |
| missing argument | 50 | 35 | 42 |
| unavailable tool | 75 | 85 | 100 |
| no-tool | 68 | 25 | 100 |
| observation grounding | 22 | 65 | 69 |
| multi-step | 30 | 52 | 40 |
| Overall | 68.5 | 65.3 | 63.9 |
On an unseen Italian domain the model is the best at the productive skills (single, multi-argument, parallel calls including different tools: 85% here, though still only 3/10 on the 100-case set — improved, not solved) and at asking for missing arguments. It is clearly weaker than both baselines at observation grounding and multi-step completion in this benchmark (22% and 30%) — read the Limitations before building multi-step flows.
3. Production robot benchmark (bilingual, 84 cases)
A simulated warehouse-robot deployment with tool schemas the model has not seen (domain close to part of the training data): 60 function-calling cases (30 IT / 30 EN, 10 sub-skills including safety/emergency-stop) + 24 conversation cases (12 IT / 12 EN).
| Model | Overall | Function calling (60) | Conversation (24) |
|---|---|---|---|
| Qwen3-0.6B | 71.1 | 72.5 | 67.6 |
| Nesso2-0.4B-Agentic-v2 | 63.6 | 59.2 (IT 53.7 · EN 64.6) | 74.9 (IT 79.2 · EN 70.6) |
| Nesso2-0.4B-Agentic (v1) | 53.3 | 43.8 | 76.9 |
v2 recovers +10 points overall over v1 on novel tools (the "does it generalise beyond its training toolbox" question this release targets) and beats Qwen on conversation, but Qwen3-0.6B remains clearly ahead on robot function calling, especially in English.
4. Generic English tools stress test (33 scenarios)
A small, deliberately plain suite: seven everyday English-named tools (get_weather, calculate, convert_units, web_search, send_email, get_stock_price, create_event), 18 EN + 15 IT scenarios, plus two-step chains.
| Model | single call | argument precision | tool selection | no-tool | multi-step | Overall |
|---|---|---|---|---|---|---|
| Qwen3-0.6B | 77% | 100% | 71% | 100% | 66% | 84% |
| Nesso-0.4B-Agentic (older sibling) | 88% | 62% | 57% | 16% | 0% | 54% |
| Nesso2-0.4B-Agentic-v2 | 77% | 25% | 14% | 100% | 0% | 48% |
This is the benchmark where we lose clearly. With only seven plain tools the model often invents a tool name (multiply, sqrt) or answers from memory instead of calling calculate / web_search, and it did not complete the two-step chains. Qwen3-0.6B is natively strong on generic tool sets like this one. If your use case is "a handful of generic English tools", prefer Qwen3-0.6B.
5. Academic benchmarks
Run with our fork of lm-evaluation-harness: MMLU is 5-shot acc; HellaSwag / ARC are 0-shot acc_norm; IFEval is inst_level_loose_acc (generative, chat template). Scale 0–1. All four models were run with the same script (Qwen3-0.6B with its default chat template).
Italian
| Model | IFEval IT ↑ | ARC IT ↑ | HellaSwag IT ↑ | MMLU IT ↑ | Avg IT |
|---|---|---|---|---|---|
| Qwen/Qwen3-0.6B | 0.3765 | 0.2678 | 0.3607 | 0.4039 | 0.3522 |
| Nesso2-0.4B-Agentic-v2 | 0.3046 | 0.3028 | 0.4086 | 0.3264 | 0.3356 |
| Nesso2-0.4B-Agentic (v1) | 0.2962 | 0.3037 | 0.4087 | 0.3258 | 0.3336 |
| mii-llm/nesso-0.4B-agentic | 0.3118 | 0.3011 | 0.4072 | 0.2822 | 0.3256 |
English
| Model | IFEval EN ↑ | ARC EN ↑ | HellaSwag EN ↑ | MMLU EN ↑ | Avg EN |
|---|---|---|---|---|---|
| Qwen/Qwen3-0.6B | 0.4640 | 0.3456 | 0.4729 | 0.4728 | 0.4388 |
| Nesso2-0.4B-Agentic-v2 | 0.3897 | 0.3055 | 0.4725 | 0.3343 | 0.3755 |
| Nesso2-0.4B-Agentic (v1) | 0.3789 | 0.3038 | 0.4726 | 0.3356 | 0.3727 |
| mii-llm/nesso-0.4B-agentic | 0.4125 | 0.3038 | 0.4686 | 0.2939 | 0.3697 |
Overall
| Model | Avg IT | Avg EN | Overall |
|---|---|---|---|
| Qwen/Qwen3-0.6B | 0.3522 | 0.4388 | 0.3955 |
| Nesso2-0.4B-Agentic-v2 | 0.3356 | 0.3755 | 0.3556 |
| Nesso2-0.4B-Agentic (v1) | 0.3336 | 0.3727 | 0.3532 |
| mii-llm/nesso-0.4B-agentic | 0.3256 | 0.3697 | 0.3476 |
MMLU 0-shot, for reference: Nesso2-0.4B-Agentic-v2 IT 0.3341 / EN 0.3470; Qwen3-0.6B IT 0.3726 / EN 0.4026.
The agentic SFT did not cost academic ability: v2 is level with v1 on every task (all differences are within noise). Against Qwen3-0.6B the picture is mixed: we are ahead on Italian HellaSwag (+4.8) and Italian ARC (+3.5) and level on English HellaSwag, but Qwen is ahead overall (0.396 vs 0.356): clearly on MMLU (+7.5 IT, +13.9 EN 5-shot), on IFEval in both languages (+7.2 IT, +7.4 EN), and on English ARC. These gaps are pre-training-knowledge and instruction-following gaps, not something the agentic post-training causes or fixes.
ℹ️ Earlier versions of the Nesso2-0.4B-Agentic card listed different Qwen3-0.6B figures (notably IFEval and Italian ARC) and an English MMLU of 0.2700 for Nesso2 v1. Re-running every model with the identical script gives the numbers above (Nesso2 v1 English MMLU: 0.3356).
6. Conversational quality (LLM-as-judge)
20 bilingual multi-turn tasks per language, graded 1–10 by Qwen3.6-35B-A3B on correctness / language-fidelity / helpfulness (greedy answers, repetition-penalised decoding for all models). The same answer sets were judged twice in separate runs; the table shows the mean of the two. The judge is noisy: most models moved by ≤0.1 between runs, but Nesso2-0.4B-Agentic-v2 moved by 0.4 (Both: 3.73 → 4.15) on identical answers, so differences below ~0.4 should not be over-read.
| Model | Italian ↑ | English ↑ | Both | correctness | language | helpfulness |
|---|---|---|---|---|---|---|
| mii-llm/nesso-0.4B-agentic | 3.98 | 6.38 | 5.17 | 4.83 | 9.34 | 5.41 |
| Nesso2-0.4B-Agentic (v1) | 4.50 | 4.00 | 4.25 | 3.80 | 8.66 | 4.30 |
| Qwen/Qwen3-0.6B | 2.75 | 5.78 | 4.26 | 4.15 | 6.94 | 4.22 |
| Nesso2-0.4B-Agentic-v2 | 3.63 | 4.25 | 3.94 | 3.70 | 8.58 | 4.10 |
v2 is somewhat below v1 in free-form Italian chat (3.63 vs 4.50 — the gap is present in both judge runs; 3.40 and 3.85 vs 4.50) and level or slightly ahead in English (4.25 vs 4.00); overall it is a little under v1 and Qwen3-0.6B (3.94 vs ~4.25). It is still clearly ahead of Qwen3-0.6B in Italian (3.63 vs 2.75) and behind it in English (4.25 vs 5.78). Training the model on one strict tool contract with a large amount of function-calling data came at some cost in free-form Italian conversation. If your application is chat-first, Nesso2-0.4B-Agentic (v1) or Nesso-0.4B-agentic is the better choice; v2 is the better choice when tool use is the job.
Limitations
We would rather you hear these from us:
- "Ask-reflex" on complete requests. On short, fully-specified requests the model sometimes asks for an argument that is already in the prompt instead of calling the tool — e.g. "Dimmi il meteo a Milano." → "In quale città?". In a 14-prompt smoke test with three plain tools (7 Italian + 7 English, all arguments present) it produced the correct call for 8 of 14; longer or more natural phrasings ("Che tempo c'è a Torino adesso?") are more reliable. This is the flip side of its good abstention behaviour and is shared by every checkpoint of this family. Use the application-layer guard in Production notes.
- Observation grounding and multi-step are uneven. 6/10 on the 100-case set, but 22% (observation) and 30% (multi-step) on the held-out Italian healthcare benchmark, and 0% on two-step chains in the generic-tools stress test. The model may re-call a tool instead of answering from the returned data, or stop after the first step. Keep multi-step flows short and supervised.
- Generic / English-first tool sets. Tool-name grounding is weaker with few, plainly-named English tools (it can invent a plausible name). Qwen3-0.6B is stronger there (see §4) and on English robot function calling (§3).
- Format-lock. It follows its contract. Different prompt layouts degrade it.
- Different-tool parallel calls and exact multi-step completion remain capacity-bound at ~0.4B (3–4/10 on the 100-case set).
- Evaluation caveat. The agentic suites are small (40–100 cases), in-house, and partly synthetic; differences of 1–2 cases are noise. Academic benchmarks are standard but small differences (≈±0.01) are within noise.
- Free-form Italian conversation is weaker than v1 (judge Italian 3.63 vs 4.50; overall 3.94 vs 4.25, noisy) — see §6.
- Knowledge and instruction following. MMLU (0.33 vs 0.40 IT, 0.33 vs 0.47 EN) and IFEval (0.30 vs 0.38 IT, 0.39 vs 0.46 EN) stay clearly below Qwen3-0.6B: pre-training-knowledge and instruction-following gaps that this post-training does not close.
Nothing here is a safety layer: validate and authorise every tool call in your application.
Related Models
| Model | Description |
|---|---|
| Zagreus-0.4B-ita | Base pre-trained model (this model's foundation) |
| Nesso2-0.4B-Agentic | Previous release: verbose-contract agentic SFT (memorises a fixed set of common tools); better free-form Italian conversation and stronger on the observation/multi-step sub-skills of the held-out healthcare benchmark |
| Nesso-0.4B-agentic | Sibling agentic SFT trained directly on the base (no CPT) |
| Nesso-0.4B-instruct | Optimized for conversational and instruction-following tasks |
Citation
If you use this model in your research, please cite:
@misc{zagreus2025,
title = {The Joy and Pain of Training an LLM from Scratch:
A Technical Report on the Zagreus and Nesso Model Families},
author = {mii-llm community},
year = {2025},
howpublished = {\url{https://github.com/mii-llm/zagreus-nesso-slm}},
}
Acknowledgements
- Antonio Baldassarra (CEO, Seeweb) and Marco Cristofanilli (Head of AI, Seeweb) for infrastructure sponsorship
- The Hugging Face team for Nanotron, datatrove, FineWeb, and FineWeb-2
- Salesforce (APIGen / xLAM), Team-ACE (ToolACE) and NVIDIA (When2Call) for the public function-calling datasets
- The mii-llm open-source community
- Downloads last month
- 128
Model tree for giux78/nesso2-0.4B-agentic-v2
Base model
mii-llm/zagreus-0.4B-ita