Зустрічайте Needle 2: відкрита модель із 45 млн параметрів для виклику інструментів, що постачається як бінарний файл розміром 14 МБ і запускає повну сесію в 28 МБ оперативної пам’яті
MarkTechPost·8 хв читання
Cactus Compute випустила Needle 2 — відкриту модель із 45 млн параметрів для виклику інструментів, керування пристроями та структурованого вилучення даних. Уся модель постачається як один бінарний файл розміром 14 МБ, який запускає повну сесію приблизно у 28 МБ оперативної пам’яті. Ваги навчаються та розгортаються у форматі CQ2-bit за допомогою Cactus Quants, а модель вбудована у власний рушій компанії на C++, тож не потрібно встановлювати середовище виконання або завантажувати щось під час інференсу. Заявлена швидкість декодування становить 500 токенів/с на Raspberry Pi 5, 400–1 500 токенів/с на Meta Quest 3S і Apple Vision Pro та 300–700 токенів/с на телефонах вартістю до 200 доларів. Передумова дизайну вузька й чітко сформульована командою: зіставлення неструктурованого речення з типізованою сигнатурою функції не потребує знань про світ або відкритого генерування прози. Саме тому тут достатньо 45 млн параметрів і саме тому модель орієнтована на обладнання без GPU та NPU.
Чи готова вона до розгортання?
Так, Needle 2 постачається у вигляді готових бінарних файлів і статичної бібліотеки для macOS, Linux (x86-64, ARM64, ARMv7, RISC-V, MIPS32el), Windows, Android, iOS/watchOS/tvOS і WebAssembly. У Cactus зазначають, що Pebble вже локально запускає Needle у застосунку Index 01 для офлайнголосових дій.
Які компанії: будь-які команди, що постачають прошивки або застосунки для пристроїв з обмеженими ресурсами. Стартапи на ранніх стадіях у сфері носимих пристроїв та IoT, виробники споживчої електроніки середнього масштабу, робототехнічні команди та великі виробники пристроїв, яким потрібен офлайн-резервний варіант. Команди SaaS, орієнтовані насамперед на хмарні рішення, отримають менше переваг.
Галузі: розумний дім, носимі пристрої, бюджетні мобільні пристрої, керування автомобілем у салоні, сервісна робототехніка, роздрібні кіоски та POS-системи, маршрутизатори й IP-камери, а також регульовані середовища, де аудіо не може залишати пристрій.
Застосування: перетворення голосу на дію на пристроях без екрана, офлайн-керування побутовими приладами, вилучення полів із чеків та рахунків, маркування значеннями enum і локальна маршрутизація, яка передає запит у хмару лише за низької впевненості.
Архітектура: Simple Attention Network
Needle 2 використовує те, що команда Cactus називає Simple Attention Network. Цей підхід замінює FFN на Hadamard MLP, зберігає увагу GQA, додає енграмну пам’ять ключ-значення з хешованих таблиць n-грам і використовує багатоканальні гіперзв’язки. Мережа має 27 шарів і ширину 512. Відповідне дослідження опубліковане на arXiv під назвою Контрольоване дослідження трансформерів лише з механізмом уваги.
Для попереднього навчання використано власний корпус обсягом 115 млрд токенів, а післянавчання — 38 млрд токенів. Дослідницька команда зазначає, що LFM2.5-230M було попередньо навчено на 19 трлн токенів.
Needle 2 витрачає 70 MFLOPs на токен, причому 35 млн із 45 млн параметрів активно беруть участь у матричному множенні. LFM2.5 230M витрачає 460, FunctionGemma 270M — 540, а Apple FM — близько 6 000.
Рушій, граматика, пошук і впевненість
Ваги ніколи не розпаковуються в оперативну пам’ять. 2-бітові коди розширюються всередині векторних регістрів і об’єднуються з цілочисельними скалярними добутками, тому арифметичний шлях залишається в int8. Один бінарний файл перевіряє CPU під час запуску й обирає відповідний рівень ядра: SDOT, NEON, AVX2, вектори RISC-V, wasm SIMD або скалярний режим.
Граматика на рівні байтів, скомпільована з ваших JSON-схем, обмежує кожен згенерований токен. Оскільки зіставник знає, які токени є допустимими, ще до появи логітів, рушій пропускає до 98% проєкції словника для структурних токенів.
Механізм уваги використовує ковзне вікно на 256 токенів, а системний запит і декларації інструментів закріплюються як KV sink. Обсяг пам’яті залишається близьким до 28 МБ незалежно від довжини розмови.
Якщо оголошено не більше п’яти інструментів, вони виводяться безпосередньо. Якщо їх більше п’яти, контрастивна пошукова голова вбудовує кожну схему один раз, оцінює запит у кожному ході й допускає лише п’ять найкращих. Невибрані інструменти недоступні, а не просто малоймовірні.
Кожна відповідь містить значення confidence — мінімум каліброваної постфактум-голови та ймовірності декодування токенів виклику. Запити не за темою повертають порожній виклик []. Контракт визначається порогом: виконувати дію вище за нього, повторно запитувати або передавати запит далі нижче за нього.
Оцінювання
Команда Cactus оцінює модель на п’яти загальнодоступних бенчмарках виклику функцій за допомогою впорядкованого строгого точного збігу, де мають збігатися назви, порядок викликів і кожен аргумент. Needle 2 працює наскрізно через поставлений рушій у форматі CQ2-bit з увімкненим пошуком; базові моделі запускаються у f16 через vLLM.
Бенчмарк
Needle 2 (CQ2)
LFM2.5 230M
FunctionGemma 270M
Apple FM
Mobile Actions (961)
63.7
69.1
64.0
57.6
DroidCall (200)
17.0
11.0
17.5
—
Seal-Tools у домені (700)
32.6
26.9
16.3
—
Seal-Tools поза доменом (654)
28.7
17.0
15.6
—
BFCL v4, один хід (3 641), загалом
42.6
60.8
46.1
61.7
Needle 2 очолює обидва розділи Seal-Tools і демонструє точність назв функцій 98,3% у Mobile Actions. Вона поступається в BFCL v4, що Cactus пояснює розподілом даних: її корпус містить дії споживчих пристроїв, а не загальні чи корпоративні API. Частка коректно сформованих результатів у 3 641 рядку BFCL становить 93,4%. Команда заздалегідь зазначає дві асиметрії: базові моделі у f16 мають перевагу, а спеціалізація завдань — Needle.
Розкодувати виклик</button>
<button class="n2-btn" id="n2-reset-g">Скинути</button>
</div>
<div class="n2-term" id="n2-term"><span class="q">> приглушити світло у вітальні до 30</span></div>
<div class="n2-legend">
<span><i class="n2-swatch" style="background:var(--green)"></i>примусово граматикою</span>
<span><i class="n2-swatch" style="background:var(--sand)"></i>значення обрано моделлю</span>
</div>
<div class="n2-meters">
<div class="n2-meter"><span>Оцінено рядків словника · структурні</span><b id="n2-m1">0%</b></div>
<div class="n2-meter"><span>Примусово задані структурні байти</span><b id="n2-m2">0</b></div>
</div>
</section>
<!-- 03 RETRIEVAL -->
<section class="n2-panel" role="tabpanel" data-p="2">
<h3 class="n2-h">Якщо інструментів більше п’яти, до моделі потрапляють лише п’ять</h3>
<p class="n2-p">Кожна схема один раз вбудовується вбудованою контрастивною головою. Кожен хід оцінює запит і
допускає п’ять найкращих; після цього граматика перебудовується для цієї підмножини. Невибраний інструмент недоступний.</p>
<div class="n2-controls" role="group" aria-label="Choose a query">
<button class="n2-btn is-on" data-q="0">"приглушити світло у вітальні"</button>
<button class="n2-btn" data-q="1">"написати Аді, що я запізнююся"</button>
<button class="n2-btn" data-q="2">"встановити 21 і ввімкнути джаз"</button>
</div>
<p class="n2-lanehead">Оголошений каталог — 12 інструментів</p>
<div class="n2-chips" id="n2-chips"></div>
<p class="n2-note">Ілюстративна симуляція описаного механізму top-5, а не результат роботи моделі наживо.</p>
</section>
<!-- 04 CONFIDENCE -->
<section class="n2-panel" role="tabpanel" data-p="3">
<h3 class="n2-h">Результатом збою є передавання запиту далі, а не неправильне виконання</h3>
<p class="n2-p"><code>confidence</code> — це мінімум каліброваної постфактум-голови та ймовірності
декодування токенів виклику. Поріг обираєте ви. Запит не за темою повертає порожній виклик
<code>[]</code> до застосування будь-якої оцінки.</p>
<input class="n2-slider" id="n2-slider" type="range" min="50" max="99" value="80" aria-label="Confidence threshold">
<p class="n2-thresh">Поріг: <b id="n2-tv">0.80</b> — виконувати на цьому рівні або вище, повторно запитувати чи передавати більшій моделі нижче.</p>
<div class="n2-rows" id="n2-rows"></div>
<p class="n2-note">0.94 — значення, показане в об’єкті відповіді README Cactus; решта є ілюстративними.</p>
</section>
<!-- 05 MEMORY -->
<section class="n2-panel" role="tabpanel" data-p="4">
<h3 class="n2-h">Стеля, а не крива</h3>
<p class="n2-p">Механізм уваги використовує ковзне вікно на 256 токенів, а системний хід і декларації інструментів
закріплюються як KV sink — тому єдине, що модель виклику інструментів не повинна забути, не може бути витіснене. Додавайте ходи
й спостерігайте, що відбувається з кожною смугою.</p>
<div class="n2-controls">
<button class="n2-btn is-on" id="n2-turn">+ Додати хід</button>
<button class="n2-btn" id="n2-reset-m">Скинути розмову</button>
<span class="n2-btn" style="cursor:default" id="n2-turns">ходи: 1</span>
</div>
<div class="n2-memgrid">
<div class="n2-membar">
<span class="n2-bar-l" style="color:var(--green)">Needle 2</span>
<div class="n2-memtrack">
<div class="n2-memfill pin" id="n2-pin" style="width:34%"></div>
<div class="n2-memfill win" id="n2-win" style="left:34%;width:24%"></div>
</div>
<span class="n2-bar-v" id="n2-memv">28.0 МБ</span>
</div>
<div class="n2-membar">
<span class="n2-bar-l">Н необмеженого KV</span>
<div class="n2-memtrack"><div class="n2-memfill grow" id="n2-grow" style="width:22%"></div></div>
<span class="n2-bar-v" id="n2-growv">зростає</span>
</div>
</div>
<p class="n2-note">Заштрихований блок = закріплені інструменти та системні факти (KV sink). Суцільний блок = вікно на 256 токенів.</p>
<p class="n2-overflow" id="n2-of"></p>
</section>
<div class="n2-foot">
<span class="n2-brand">Пояснення від <b>Marktechpost</b></span>
<span>
<a href="https://github.com/cactus-compute/needle" target="_blank" rel="noopener">GitHub</a> ·
<a href="https://huggingface.co/Cactus-Compute/needle2" target="_blank" rel="noopener">Hugging Face</a> ·
<a href="https://cactuscompute.com/needle" target="_blank" rel="noopener">Cactus</a> ·
<a href="https://arxiv.org/abs/2607.18363" target="_blank" rel="noopener">arXiv:2607.18363</a>
</span>
</div>
</div>
<script>
(function(){
var root=document.getElementById('n2-root');
var reduce=window.matchMedia&&window.matchMedia('(prefers-reduced-motion:reduce)').matches;
/* ---------- resize ---------- */
function post(){
try{ parent.postMessage({type:'n2-height', height: root.offsetHeight + 40}, '*'); }catch(e){}
}
var ro=window.ResizeObserver?new ResizeObserver(post):null; if(ro) ro.observe(root);
window.addEventListener('load',post); setTimeout(post,120); setTimeout(post,600);
/* ---------- tabs ---------- */
var tabs=[].slice.call(root.querySelectorAll('.n2-tab'));
var panels=[].slice.call(root.querySelectorAll('.n2-panel'));
tabs.forEach(function(t){
t.addEventListener('click',function(){
tabs.forEach(function(x){x.setAttribute('aria-selected','false')});
t.setAttribute('aria-selected','true');
panels.forEach(function(p){p.classList.remove('is-on')});
panels[+t.dataset.t].classList.add('is-on');
if(t.dataset.t==='0') drawBars(metric);
post();
});
});
/* ---------- 01 frontier ---------- */
var DATA={
params:{unit:'M params', note:'Усього параметрів. Apple FM має приблизно 3B. Джерело: таблиця Cactus щодо обчислень на токен.',
rows:[['Needle 2',45,'45M'],['LFM2.5 230M',230,'230M'],['FunctionGemma 270M',270,'270M'],['Apple FM',3000,'~3B']]},
flops:{unit:'MFLOPs/token', note:'2 FLOPs на операцію множення-накопичення для параметрів, активних у матричному множенні; ембеддинги пов’язані, члени уваги виключено. Needle зберігає 8M параметрів як зібрану енграмну пам’ять, яка не потребує арифметичних операцій.',
rows:[['Needle 2',70,'70'],['LFM2.5 230M',460,'460'],['FunctionGemma 270M',540,'540'],['Apple FM',6000,'~6,000']]},
acc:{unit:'%', note:'Оціночний розділ Mobile Actions, 961 рядок, впорядкований строгий точний збіг. Needle 2 у CQ2-bit з увімкненим пошуком; LFM2.5 і FunctionGemma у f16 через vLLM; Apple FM на пристрої.',
rows:[['Needle 2',63.7,'63.7'],['LFM2.5 230M',69.1,'69.1'],['FunctionGemma 270M',64.0,'64.0'],['Apple FM',57.6,'57.6']]}
};
var metric='params', barsEl=document.getElementById('n2-bars'), noteEl=document.getElementById('n2-barnote');
function drawBars(m){
var d=DATA[m], max=Math.max.apply(null,d.rows.map(function(r){return r[1]}));
barsEl.innerHTML=d.rows.map(function(r){
return '<div class="n2-bar'+(r[0]==='Needle 2'?' is-hero':'')+'">'+
'<span class="n2-bar-l">'+r[0]+'</span>'+
'<div class="n2-bar-t"><div class="n2-bar-f"></div></div>'+
'<span class="n2-bar-v">'+r[2]+'</span></div>';
}).join('');
noteEl.textContent=d.note;
var fills=barsEl.querySelectorAll('.n2-bar-f');
d.rows.forEach(function(r,i){
var w=Math.max(2,(r[1]/max)*100);
if(reduce){fills[i].style.width=w+'%';}
else setTimeout(function(){fills[i].style.width=w+'%';},60*i+40);
});
post();
}
root.querySelectorAll('[data-m]').forEach(function(b){
b.addEventListener('click',function(){
root.querySelectorAll('[data-m]').forEach(function(x){x.classList.remove('is-on')});
b.classList.add('is-on'); metric=b.dataset.m; drawBars(metric);
});
});
drawBars('params');
/* ---------- 02 grammar ---------- */
var CALL=[
['{"name":"',1],['set_lights',0],['","arguments":{"room":"',1],['living room',0],
['","on":',1],['true',0],[',"brightness":',1],['30',0],['}}',1]
];
var term=document.getElementById('n2-term'), m1=document.getElementById('n2-m1'), m2=document.getElementById('n2-m2');
var timer=null;
function resetG(){
if(timer){clearInterval(timer);timer=null;}
term.innerHTML='<span class="q">> приглушити світло у вітальні до 30</span>';
m1.textContent='0%'; m2.textContent='0'; post();
}
document.getElementById('n2-reset-g').addEventListener('click',resetG);
document.getElementById('n2-decode').addEventListener('click',function(){
if(timer){clearInterval(timer);timer=null;}
var flat=[]; CALL.forEach(function(seg){
for(var i=0;i<seg[0].length;i++) flat.push([seg[0][i],seg[1]]);
});
term.innerHTML='<span class="q">> приглушити світло у вітальні до 30</span><br>';
var out=document.createElement('span'); term.appendChild(out);
var caret=document.createElement('span'); caret.className='n2-caret'; caret.textContent='_'; term.appendChild(caret);
var i=0, forced=0;
function step(){
if(i>=flat.length){ if(timer){clearInterval(timer);timer=null;} caret.remove();
m1.textContent='2%'; m2.textContent=forced; post(); return; }
var ch=flat[i][0], isF=flat[i][1];
var s=document.createElement('span'); s.className=isF?'n2-forced':'n2-free';
s.textContent=ch; out.appendChild(s);
if(isF) forced++;
var pct=Math.round(100-(forced/(i+1))*98);
m1.textContent=pct+'%'; m2.textContent=forced;
i++;
}
if(reduce){ while(i<flat.length) step(); }
else timer=setInterval(step,26);
});
/* ---------- 03 retrieval ---------- */
var TOOLS=['set_lights','play_music','send_message','set_thermostat','get_weather','set_timer',
'call_contact','navigate_to','take_photo','lock_door','add_calendar_event','control_tv'];
var PICKS=[[0,3,11,1,5],[2,6,10,0,4],[3,1,0,5,4]];
var chipsEl=document.getElementById('n2-chips');
chipsEl.innerHTML=TOOLS.map(function(t,i){return '<span class="n2-chip" data-i="'+i+'">'+t+'</span>'}).join('');
function retrieve(qi){
var sel=PICKS[qi];
[].slice.call(chipsEl.children).forEach(function(c,i){
c.classList.remove('is-sel','is-out');
var d=sel.indexOf(i)>-1?0:180;
setTimeout(function(){ c.classList.add(sel.indexOf(i)>-1?'is-sel':'is-out'); }, reduce?0:d);
});
}
root.querySelectorAll('[data-q]').forEach(function(b){
b.addEventListener('click',function(){
root.querySelectorAll('[data-q]').forEach(function(x){x.classList.remove('is-on')});
b.classList.add('is-on'); retrieve(+b.dataset.q);
});
});
retrieve(0);
/* ---------- 04 confidence ---------- */
var TURNS=[
['приглушити світло у вітальні до 30',0.94,false],
['зробити тут тепліше',0.81,false],
['увімкнути ту річ на іншій речі',0.58,false],
['написати Аді, що я запізнююся',0.88,false],
['яка столиця Перу?',null,true]
];
var rowsEl=document.getElementById('n2-rows'), slider=document.getElementById('n2-slider'), tv=document.getElementById('n2-tv');
function renderRows(){
var th=(+slider.value)/100; tv.textContent=th.toFixed(2);
rowsEl.innerHTML=TURNS.map(function(t){
if(t[2]) return '<div class="n2-row ref"><span class="n2-row-q">'+t[0]+
'</span><span class="n2-row-c">[ ]</span><span class="n2-verdict v-ref">порожній виклик</span></div>';
var ok=t[1]>=th;
return '<div class="n2-row '+(ok?'act':'esc')+'"><span class="n2-row-q">'+t[0]+
'</span><span class="n2-row-c">'+t[1].toFixed(2)+'</span><span class="n2-verdict '+
(ok?'v-act">виконати':'v-esc">передати далі')+'</span></div>';
}).join('');
post();
}
slider.addEventListener('input',renderRows); renderRows();
/* ---------- 05 memory ---------- */
var turns=1, gv=document.getElementById('n2-gv'), gf=document.getElementById('n2-gf');
var win=document.getElementById('n2-win'), grow=document.getElementById('n2-grow');
var memv=document.getElementById('n2-memv'), growv=document.getElementById('n2-growv');
var ofEl=document.getElementById('n2-of'), turnsEl=document.getElementById('n2-turns');
function renderMem(){
turnsEl.textContent='ходи: '+turns;
var w=Math.min(24, 12+turns*2.5);
win.style.width=w+'%';
memv.textContent='28.0 МБ';
gf.style.width='78%'; gv.innerHTML='<b>28.0 МБ</b> / жорстка стеля 28 МБ';
var g=Math.min(100, 18+turns*7);
grow.style.width=g+'%';
growv.textContent=(Math.round((18+turns*7)*0.62*10)/10)+' МБ';
ofEl.textContent = g>=78 ? 'Необмежений KV перевищив стелю 28 МБ. Needle 2 не змінилася.' : '';
post();
}
document.getElementById('n2-turn').addEventListener('click',function(){turns++;renderMem();});
document.getElementById('n2-reset-m').addEventListener('click',function(){turns=1;renderMem();});
renderMem();
})();
</script>
</body>
</html>
">
Основні висновки
Needle 2 — це модель із 45 млн параметрів для виклику інструментів у бінарному файлі розміром 14 МБ, яка запускає сесію приблизно у 28 МБ оперативної пам’яті.
CQ2-bit інтегровано в модель під час попереднього навчання, а не застосовано постфактум, тому розгорнута модель є тією самою моделлю, що й навчалася.
Вона очолює результати Seal-Tools у домені (32,6) і поза доменом (28,7), але поступається за загальним показником BFCL v4 (42,6).
Ковзне вікно на 256 токенів із закріпленими сховищами інструментів робить оперативну пам’ять фіксованою стелею, а не кривою, що зростає.
Оцінки впевненості та відмови у вигляді порожнього виклику перетворюють передавання запитів від периферії до хмари на чітке продуктовe рішення.