11 KiB
Cactus Needle — evaluare model de function calling pentru pre-routing local (2026-07-12)
Spike de testare: se poate folosi un model mic de function calling (rulat local pe CPU) ca pre-router înaintea claude_session.py, ca să interceptăm intenții simple (weather, add task, note, query roa2web) fără să consumăm din rate limit-ul Claude?
Context — de ce s-a pornit testul
router.py rutează doar pe prefix (/, text-keywords) — orice mesaj care nu e slash-command pornește un turn Claude CLI complet, inclusiv întrebări triviale ("ce vreme e", "cât e în casă"). Asta consumă rate limit pe Anthropic subscription (nu cost — vezi user_anthropic_subscription) chiar și pe intenții simple, repetitive. Video sursă care a declanșat testul: Cactus Needle — The 26M Function Calling Model (notă: 2026-07-12_cactus-needle-26m-function-calling-model).
Ce s-a testat
Model: Cactus-Compute/needle — 26M parametri, "Simple Attention Network" (attention+gating, fără FFN/MLP), distilat din Gemini 3.1 Flash Lite. Repo: cactus-compute/needle.
- Instalare izolată în
/tmp/needle-test(venv separat, nu a atins.venvde producție din echo-core). JAX/Flax/Optax, CPU-only. - Checkpoint
needle.pkl(~50MB fp32) descărcat direct de pe HF, fără antrenare/fine-tuning suplimentar. - Tools JSON definite manual, mimând funcții reale Echo:
get_weather,add_task,create_note,get_treasury_balance(analog roa2web),set_timer. - 10 query-uri de test, engleză + română, incluzând și mesaje conversaționale banale (fără tool corespunzător).
Rezultate
- Recunoaștere corectă pe intenții clare, inclusiv română (deși modelul e antrenat mai ales pe engleză): "Ce vreme e la Cluj?" →
get_weather(location=Cluj), "Cât e în casă?" →get_treasury_balance(account=casa), "Pune un timer de 10 minute" →set_timer(duration="10 minutes"). - Fals pozitiv critic pe conversație casual: "Bună, ce mai faci?" a declanșat
get_treasury_balance(account="bun")— tool call inventat pe un salut banal. Traficul Echo e majoritar conversație, nu comenzi, deci acest failure mode lovește direct fluxul principal. - Query ambiguu ("Cât e în bancă?") a generat două tool calls duplicate cu argumente diferite, nu un răspuns curat.
- Extragere de conținut incompletă pe note mai lungi (a păstrat doar titlul, a pierdut restul textului).
- Pe query clar conversațional/tehnic ("Explică-mi recursivitatea în Python") a răspuns corect cu listă goală — fără tool call inventat.
- Latență reală pe CPU: 3-9s per query (nu ms-urile din marketing) — setup-ul default din repo nu are encoder JIT-cache-uit/warm, ar necesita inginerie suplimentară (padding fix + server ținut pornit) ca să se apropie de cifrele oficiale (1200 tok/s pe device).
Comparație cu alternative
- Qwen3-0.6B (benchmark extern, heyneo.com, 4-core CPU, 50 query-uri / 5 nivele de dificultate, netestat local): acuratețe mai bună decât Needle cu prompt bun (84% vs 72% overall), dar 4-6x mai lent (~40-56s/query vs ~7-17s Needle) și cu exact același failure mode — pe query "What's 2+2?" a inventat un tool call
run_command.
Hammer2.1-0.5b — testat local (2026-07-12)
MadeAgents/Hammer2.1-0.5b — 0.5 miliarde parametri, ~19x mai mare decât Needle (26M). Fine-tune pe Qwen2.5-Coder-0.5B-Instruct, folosește "function masking" pentru robustețe la tool calling (arxiv 2410.04587). Rulat local pe CPU (torch+transformers, venv izolat /tmp/hammer-test), exact aceleași 10 query-uri și tool-uri ca la testul Needle, pentru comparație directă.
- Fixează problema critică de la Needle: pe ambele mesaje conversaționale ("Bună, ce mai faci?" și "Explică-mi recursivitatea") a răspuns corect cu listă goală — zero fals-pozitive pe chit-chat, spre deosebire de Needle care a inventat un tool call pe salut.
- Dar are propriile probleme:
- Fals-negativ pe RO: "Ce vreme e la Cluj?" (română) → listă goală, deloc recunoscut — deși Needle a rezolvat corect exact acest query. Query-ul englezesc echivalent a mers corect.
- Halucinație de conținut, nu doar omisiune: la
create_notepentru "intalnirea de azi cu Andrei", a generatcontent: "Please check the weather report tomorrow."— text complet inventat, fără nicio legătură cu query-ul. Mai grav decât Needle, care măcar a păstrat conținut relevant (incomplet, dar corect tematic). - Nu extrage argumentul
accountlaget_treasury_balance— "Cât e în casă?" și "Cât e în bancă?" au produs exact același output gol (arguments: {}), pierzând distincția casă/bancă pe care Needle o prindea corect.
- Dimensiune pe disc: checkpoint ~950MB (fp32) vs ~50MB la Needle — proporțional cu raportul de parametri.
- Latență: 2.4-5.6s/query (comparabil cu Needle, 3-9s), plus ~24s load inițial al modelului (Needle: 2.4s).
Qwen2.5-0.5B-Instruct — testat local (2026-07-12)
Qwen/Qwen2.5-0.5B-Instruct — 0.5B parametri, model general-purpose foarte popular (nu specializat pe function calling), cu suport tool calling nativ prin chat template. Testat cu același set de 10 query-uri.
- Corect pe weather RO și EN (mai bine ca Hammer, care rata varianta RO).
- Fals-negativ pe task RO: "Adaugă un task să sun clientul mâine" → nu a emis niciun tool call, a răspuns cu o întrebare de clarificare în engleză. Varianta EN a mers corect.
create_noteRO: nume+titlu corecte, dar a omis complet argumentulcontent(obligatoriu) — tool call incomplet.- Cel mai prost rezultat din tot testul: "Cât e în casă?" → refuz ("I can't assist with that"); "Cât e în bancă?" → răspuns complet halucinat și incoerent, "NO, CAT IS NOT IN THE BANK" — a confundat "cât" (românește) cu "cat" (pisică, engleză) și a ieșit din orice logică de tool calling.
- Pe conversație casual, nu a inventat tool calls (bine), dar calitatea răspunsului conversațional în română a fost slabă/agramat pe salut, deși explicația tehnică despre recursivitate (tot RO) a ieșit surprinzător de coerentă.
- Concluzie: sigur pe fals-pozitive, dar robustețea pe română e inconsistentă și imprevizibilă — de la perfect (weather) la incoerent total (treasury).
FunctionGemma-270M — testat local (2026-07-12)
google/functiongemma-270m-it (testat via mirror-ul unsloth/functiongemma-270m-it, ~520MB) — bazat pe Gemma 3 270M, model Google specializat explicit pe function calling, cel menționat și în videoclipul sursă ca fiind depășit de Needle pe single-shot function calling. README-ul modelului avertizează clar: "not intended for use as a direct dialogue model... designed to be highly performant after further fine-tuning" — adică e gândit ca bază de fine-tuning per-domeniu, nu ca model gata de folosit zero-shot.
- Cel mai rapid dintre toate: 1-2.7s/query (comparabil cu Needle, mai rapid decât Hammer/Qwen).
- Folosește sintaxă proprietară de function call (
<start_function_call>call:nume{...}<end_function_call>), nu JSON standard — ar necesita parser custom pe partea de Echo. - Confirmă empiric ce spunea videoclipul: e clar mai slab decât Needle zero-shot pe cazurile noastre.
- Halucinează date/conținut: la
add_taska inventat date fixe greșite ("2024-05-23", "2026-07-27" — nici "mâine" real) și a înlocuit titlul cu text generic ("add task to task board") în loc de conținutul real al cererii, pe ambele variante RO și EN. - La
create_notea generat un paragraf întreg fabricat, fără legătură cu "Andrei" sau întâlnirea. - Fals-pozitiv periculos: "Cât e în casă?" → a interpretat "cat" ca pisică (aceeași confuzie ca la Qwen) și a generat
create_note(content="Noticed cat in casa")— o notă complet inventată dintr-o interogare financiară, exact tipul de acțiune nedorită de care ne temeam. - Pe conversație casual a refuzat corect (fără tool call), dar cu mesaje generice de eroare, nu conversație reală — așteptat, dat fiind avertismentul din README.
- Halucinează date/conținut: la
Concluzie
Niciun model din cele patru testate/cercetate nu e suficient de fiabil ca să înlocuiască un pas din router.py fără un prag de încredere + fallback obligatoriu la Claude. Tabel rezumat (CPU, aceleași 10 query-uri RO+EN):
| Model | Parametri | Fals-pozitiv pe chit-chat | Robustețe RO | Halucinație conținut/argumente | Latență/query |
|---|---|---|---|---|---|
| Needle 26M | 26M | Da (salut → tool call inventat) | Bună | Extragere incompletă, nu inventată | 3-9s |
| Hammer2.1-0.5b | 0.5B | Nu | Parțială (ratează weather RO) | Da (content fabricat la note) | 2.4-5.6s |
| Qwen2.5-0.5B-Instruct | 0.5B | Nu | Inconsistentă (weather OK, treasury total incoerent) | Da (răspuns halucinat pe "bancă") | 2.4-8.5s |
| FunctionGemma-270M | 270M | Da (interpretare literală "pisică" → notă inventată) | Slabă | Da, sever (date/conținut fabricate) | 1-5.7s (cel mai rapid) |
Cele mai rapide (Needle, FunctionGemma) sunt și cele cu fals-pozitive periculoase pe interogări normale/financiare — corelație clară între "modele hiper-specializate pe function calling, foarte mici" și tendința de a forța un tool call chiar și când nu e cazul. Modelele mai generaliste (Hammer, Qwen) sunt mai reținute pe chit-chat, dar au propriile goluri pe română și pe extragerea corectă a argumentelor.
Decizie: nicio schimbare de cod în echo-core. Nu s-a integrat niciun pre-router local.
Dacă se reia pe viitor: niciunul dintre cele patru nu e gata "as-is". Dacă tot se alege un candidat de rafinat, Hammer2.1-0.5b rămâne cel mai sigur punct de plecare (singurul fără fals-pozitiv periculos și cu extragere de argumente parțial corectă), dar are nevoie de: (a) validare/fine-tuning explicit pe română (fals-negativul pe weather RO e blocant dat fiind traficul majoritar RO), (b) prompt/system message mai strict pentru a reduce halucinația pe câmpuri text libere (title/content). Nu merită reluat testul cu FunctionGemma-270M zero-shot — README-ul propriu spune clar că are nevoie de fine-tuning per-domeniu ca să fie utilizabil.
Artefacte
Foldere de test (/tmp/needle-test, /tmp/hammer-test) sunt în /tmp, se pierd la reboot. Cache-uri de modele rămase în ~/.cache/huggingface/hub/ (nu se pierd la reboot): models--MadeAgents--Hammer2.1-0.5b (~950MB), models--Qwen--Qwen2.5-0.5B-Instruct (~950MB), models--unsloth--functiongemma-270m-it (~520MB) — total ~2.4GB, de șters manual dacă nu se reia testarea (disc la 85% ocupare în timpul testului, atenție la spațiu).