Знакомьтесь: 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-камеры, а также регулируемые сферы, где аудиоданные не могут покидать устройство.
Применения: преобразование голоса в действия на устройствах без экрана, автономное управление бытовой техникой, извлечение полей из чеков и счетов, присвоение значений перечислений и локальная маршрутизация с передачей запроса в облако только при низкой уверенности.
Архитектура: Simple Attention Network
Needle 2 использует архитектуру, которую команда Cactus называет Simple Attention Network. В этой схеме FFN заменён на Hadamard MLP, сохранено внимание GQA, добавлена энграммная память «ключ–значение» на основе хешированных таблиц n-грамм, а также используются гиперсвязи с несколькими каналами. Сеть состоит из 27 слоёв и имеет ширину 512. Исследование, лежащее в её основе, опубликовано на arXiv под названием A Controlled Study of Attention-Only Transformers.
Для предварительного обучения использовался закрытый корпус объёмом 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-стоков. Объём памяти остаётся около 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 ПОИСК -->
<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="Выберите запрос">
<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 УВЕРЕННОСТЬ -->
<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="Порог уверенности">
<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 ПАМЯТЬ -->
<section class="n2-panel" role="tabpanel" data-p="4">
<h3 class="n2-h">Предел, а не кривая роста</h3>
<p class="n2-p">Механизм внимания использует скользящее окно размером 256 токенов, а системный запрос и объявления инструментов
закрепляются как KV-стоки — поэтому единственная вещь, которую модель вызова инструментов не должна забыть, не может быть вытеснена. Добавляйте ходы
и наблюдайте, что происходит с каждой полосой.</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-стоки). Сплошной блок = окно размером 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;
/* ---------- изменение размера ---------- */
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);
/* ---------- вкладки ---------- */
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 передовой уровень ---------- */
var DATA={
params:{unit:'М параметров', note:'Общее число параметров. Apple FM содержит примерно 3 млрд. Источник: таблица Cactus с вычислениями на токен.',
rows:[['Needle 2',45,'45M'],['LFM2.5 230M',230,'230M'],['FunctionGemma 270M',270,'270M'],['Apple FM',3000,'~3B']]},
flops:{unit:'MFLOPs/токен', note:'2 FLOPs на операцию умножения с накоплением для параметров, участвующих в матричных умножениях; эмбеддинги связаны, компоненты внимания исключены. Needle оставляет 8 млн параметров в виде собранной энграммной памяти, не требующей арифметических операций.',
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 грамматика ---------- */
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 поиск ---------- */
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 уверенность ---------- */
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 память ---------- */
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 токенов с закреплёнными стоками инструментов превращает объём оперативной памяти в фиксированный предел, а не в постоянно растущую кривую.
Оценки уверенности и отказы в виде пустого вызова превращают эскалацию с устройства в облако в явное продуктовое решение.