Hacker News 熱門精选77

Show HN:Cactus Needle 3:8-29MB 自动化模型可媲美 DeepSeek V4 Flash

Show HN: Cactus Needle 3: 8-29MB automation models can match DeepSeek V4 Flash

· 26天前
跳到正文

精选理由

了解小型端侧自动化模型的性能与部署潜力

AI 摘要

嘿 HN,我是来自 Cactus 的 Henry。 几周前我们在这里提交了 Needle 2,讨论串里的反馈非常有价值,感谢大家!得益于所有这些反馈,我们得以快速发布 Needle 3,我也很想再次听听你们的看法。 关键特性: 1) 自动化(工具调用与结构化 JSON 输出):Needle 仍然在设计上不用于聊天,要把通用能力塞进如此小的模型里相当有挑战,所以我们专注于工具调用和结构化 JSON。如果你声明的工具都不适合该请求,就会返回一个空列表(试玩 demo 时请注意这一点)。 2) 智能阶梯化:每一层(2 到 20 层)都是一个可部署的子网络,因此只需一组权重,2-bit 下为 2500 万到 1.21 亿参数,以 8-29MB 二进制文件发布。在 Raspberry Pi 5 上,它的解码速度最高可达 4k tokens/秒,预填充最高可达 10k。 3) Monarch Hadamard MLP:用三组可学习的、以 Walsh-Hadamard 初始化的 Kronecker(Monarch)因子对取代密集 FFN,中间交错每通道对角缩放、固定置换、SiLU 非线性,以及一个 rank-8 输入条件门控,因此每个 token 都能对其 d_model 通道进行完全混合的非线性变换,参数量和计算量为 O(d√d),而不是密集 4 倍扩展 MLP 所需的 O(d²)。 4) 性能:在 Mobile Actions(手机命令,按精确调用评分)上,20 层模型通过发布的 2-bit 二进制文件取得 86.0;LFM2.5 1.2B 为 82.4,Qwen3.5 0.8B 为 76.0,Apple 的端侧模型为 57.6,全部为 f16。链接中有更多结果,当然我们并不是处处领先。 5) 多语言:Needle 3 现在支持英语、法语、西班牙语、德语、荷兰语、意大利语、波兰语,更多语言即将推出。 6) 微调:你可以只用 4L 在一个窄任务上达到 DeepSeek v4 Flash 级别的性能,重点是“窄任务”;我们发现生产用户通常更倾向于在投产前先进行调优。 7) 触发器:落地到具体上下文是工具调用的常见挑战,至少对 Needle 2 来说如此,因此我们增加了对大小写不敏感正则表达式的支持,可与每个请求匹配以拦截假阴性。 8) 置信度:每个响应还附带一个校准后的置信度分数,取完成调用后的判断与其解码概率二者中的较小值。高于你的阈值就执行;低于阈值则显示调用并询问,或升级到更大的模型。 9) 支持平台:macOS、x86-64 上的 Linux、ARM64、ARMv7、RISC-V 和 MIPS32、Windows x64 和 ARM、Android、iOS、watchOS、tvOS、作为 WebAssembly 的浏览器,以及一个 WASI component。 感谢阅读,和往常一样,欢迎分享想法! 评论 URL: https://news.ycombinator.com/item?id=49748553 得分:144 评论数:71

正文

Tool calls
Given the functions your app exposes, Needle picks the right ones and fills every argument from what the user said. Ask for two things and you get two calls in order; ask for something no tool covers and you get an empty list, not a guess.
Structured extraction
Declare a shape, hand over messy text, get typed fields back: an invoice, a booking, a notification, a form. The decode grammar guarantees the output parses. Extraction generalised well to classification problems too.
Text embedding
The same model returns a vector for a sentence, so an app can search, match and route locally: find the note you mean, pick the tool closest to a request, collapse duplicate alerts.

What that looks like in a product:

Smart home
Go from pressing buttons to talking to the house. "Dim the bedroom and lock up" becomes two calls, executed offline, with no hub round trip.
Robots
Give a vacuum or a small robot nuanced instructions: "clean the kitchen but leave the bedroom", "go back to the dock when you are done". Each becomes a sequence of moves it can execute.
Phones
An assistant that acts on the device instead of answering: make an album from last weekend's photos, open a site, dim the screen, find the lease in your files.
Wearables
Read a notification into structured data on the wrist: a card charge into merchant, amount and date; a message into a reply; a complaint into a sentiment flag.
AR glasses
Navigation and nearby search from a short request, with no phone or network in the loop.
Automotive
Climate, media, navigation and calls from requests in the cabin, with the tool set pinned so it survives a long drive's worth of conversation.
Computers
Plain-English control of the machine in front of you: draft the mail, start the timer, copy the address, open the tab.
Search and matching
Embeddings that never leave the device: semantic search over notes, messages and documents; a query matched to the closest of hundreds of tools; near-duplicate alerts merged on a watch.

Model

Intelligence laddering. Every layer of Needle 3 is a sub-network with monotonically increasing capacity. Developers can choose the right size from the 2-layer (2L) subnetwork to 20 layers (20L). Each subnetwork is amenable to fine-tuning, such that 4L can match DeepSeek V4 Flash when tuned on downstream tasks for one epoch. Intelligence laddering produces 9 to 29 MB CQ2-bit binaries and supports a wide range of tiny devices.

Inputs
Text prompts, plus tool definitions or an extraction schema
Outputs
Structured JSON with tool calls or extractions
Model
29-121M Laddered Simple Attention Networks, CQ2 quantisation
Training
360B tokens of proprietary structured dataset
Speed
400-4k tokens/s decode and 1-10k tokens/s prefill on a Raspberry Pi 5

Laddered Simple Attention Networks

tokens× 20 layers · 121MEmbedding · 8,192 × 768 · tied unembedSentencePiece BPE, 8,192 piecesmHC lane read · 4 residual lanesu = Σₙ σ(a φₙᵀ x̂ + b) Xₙ, x̂ = rms(lanes)Engram fusion · hashed 2- and 3-gram memoryx ← x + σ(⟨x̂, k̂⟩/√d)·v · sites 3, 7, 11, 15, 19 · 18,432 slots, by gatherQ, K, V projections · 12q : 2kv · 48 qk / 64 v3-tap causal conv per channel: qₜ = Σⱼ wⱼ ⊙ qₜ₋ⱼ, j = 0, 1, 2GQA attention · RoPE · QK-normwindow 1024 · global at layers 4, 9, 14, 19 · o = σ(g) ⊙ softmax(q̂ K̂ᵀ/√d) VMonarch Hadamard FFNy = D₄M₃D₃M₂ silu(D₂c(x)M₁D₁x + b), Mᵢ = Aᵢ ⊗ Bᵢ, 32 × 32mHC lane writeX′ = P X + 2σ(h) ⊗ y, P = Sinkhorn(A) · sandwich ZCRMSNormmean lanes → ZCRMSNorm → tied unembedbyte-level grammar → exact function callladder: any depth 2..20 · blocks 0 and 19 keptmtp t+2 aux head · training only20 × 768 · 121M parameters · 70.8M in engram tables
Figure 1. The architecture spends over 2x fewer MFLOPs per token than a transformer of the same configuration.
Toolsmean of the three tool-calling suitesExtractionsmean of the three extraction suitesMobile Actionsphone commands to Android intents · 961 rows · exact callDroidCallphone requests, some need two calls · 200 rows · exact calls in orderBFCL v4Berkeley function calling, single turn · 3,641 rows · AST match, no call on irrelevantDSTC8dialogue slots into a schema · 1,813 turns · field F1SNIPS goldslot filling with the schema given · 700 rows · field F1SNIPS 7-waypick the schema among seven, then fill it · 700 rows · field F1MFLOPs / tokenparameters and compute per token
50M100M200M500M1B2B4BTotal parameters (log scale)Accuracy (%)
Figure 2. Needle 3 beats models 10x its size on mobile tool calls and matches models 2-3x its size on extraction. Needle 3 subnetworks (20, 16, 8 and 4 layers) run through the shipped CQ2-bit binary, baselines at f16 under vLLM, DeepSeek V4 Flash through its cloud API. The line joins the Needle models.

Get started

Install the Python package. The inference engine is fetched once from Hugging Face and cached; there is nothing else to build.

Needle reads your tool descriptions to decide what to call and how to fill arguments, so describing them well is the whole game.

Simple: decorate a function. The signature gives the argument types, the docstring is the tool description, and run() completes the loop: the model picks the call, Needle executes your function, feeds the result back, and returns the final response with the executed tool results attached as results.

import needle

@needle.tool
def get_weather(city: str):
    "Get the current weather for a city."
    return {"city": city, "temp_c": 27, "sky": "clear"}

agent = needle.Needle(tools=[get_weather])
print(agent.run("what's it like in Lagos right now?")["results"])
# [{'city': 'Lagos', 'temp_c': 27, 'sky': 'clear'}]

Route by pattern: when a description cannot enumerate every phrasing, give a tool triggers, regular expressions matched against each request. A match restricts the decode to the matched tools and requires a call, so the request reaches the tool you named instead of being refused or misrouted, and the call ships even below the confidence floor. A match restricts the whole turn, so a catch-all should exclude the nouns other tools own, e.g. ^(?![\s\S]*\b(lights?|doors?)\b)[\s\S]*\b(turn|switch)\b[\s\S]*\b(on|off)\b; then "switch the fan on and dim the kitchen lights" still reaches both tools.

from typing import Literal

@needle.tool(triggers=[r"\b(turn|switch|power|flip)\b.*\b(on|off)\b", r"\btoggle\b"])
def control_device(device: str, action: Literal["on", "off", "toggle"]):
    "Switch or toggle any named smart-home device."
    return {"device": device, "action": action}

agent = needle.Needle(tools=[control_device, get_weather])
agent.complete("toggle the garage door")
# function_calls [{"name": "control_device", "arguments": {"device": "garage door", "action": "toggle"}}]

Extraction: to pull structured data out of text, declare the shape and call extract(). Pass a Pydantic model and you get a typed object back.

from pydantic import BaseModel

class Invoice(BaseModel):
    vendor: str
    total: float
    due_date: str

invoice = needle.extract("Invoice from Acme Corp, $1,200.00, due 2026-09-01", Invoice)
print(invoice.vendor, invoice.total)   # -> Acme Corp 1200.0

Every turn returns one JSON object:

{
  "type": "call",
  "success": true,
  "error": null,
  "error_code": null,
  "function_calls": [ { "name": "set_lights", "arguments": { "room": "living room", "on": true, "brightness": 30 } } ],
  "reasoning": "'living room' -> room; 'dim' -> on true, brightness 30",
  "confidence": 0.94,
  "prefill_tps": 4300.0,
  "decode_tps": 850.0,
  "peak_ram_mb": 28.5
}

Confidence gating and routing: every response carries a confidence score from a calibrated head, and the engine already applies a floor of 0.1. Below it, the call is withheld into suppressed_calls and function_calls is empty. Above it, the score is yours to route on: act at once when it is high, show the call and ask when it is middling, and treat an empty result as a refusal. A tool with triggers always produces a call for a matching request, so the score is what tells you whether to run it or confirm it.

r = agent.complete(user_text)
calls = r["function_calls"]
held = r["suppressed_calls"]

if calls and r["confidence"] >= 0.7:
    execute(calls)                                   # sure: act
elif calls or held:
    confirm(calls or held, r["reasoning"])           # unsure: show the call, ask
else:
    say("I can't do that here")                      # nothing to do: refuse

Writing tools: the model reads a schema literally, so a narrow tool with a plain description beats a broad one. One tool per action, described by the actions it covers ("Turn a room's lights on or off") rather than a category. Name enum options after what a user says (action: ["increase", "decrease"]) and keep synonyms in the description. Give a required argument a default when a request may leave it out; a required argument with no default and no evidence in the request is withheld rather than guessed. Put value formats in descriptions ("City, ST", "e.g. T-1042"). Add triggers to intents that must always reach a tool, and keep the toolset per turn small, since every extra tool is a chance to misroute.

Fine-tune: the Python package is the quick path. LoRA on the frozen base at the full 20 layers, then a 4-bit .cact of any subnetwork that runs on the same engine.

needle finetune data.jsonl --epochs 10 --out adapter.safetensors
needle build --lora adapter.safetensors --out tuned.cact
needle build --lora adapter.safetensors --platform linux-arm64 --layers 2 --out ./device

Cactus Platform

Needle was designed to be customised. Its capacity is a ladder, and a subnetwork as small as 2 layers, fine-tuned on one product's tools, runs optimally on devices far smaller than the full model needs. Constraining the capacity to a narrow, well-defined task is what lets it reach frontier-level accuracy there: fine-tuning on DroidCall lifts every subnetwork by 18 to 36 points, and from 4 layers up the tuned subnetwork passes DeepSeek V4 Flash, starting at 29M parameters (Figure 3).

Every subnetwork, fine-tuned on the platform

DroidCall200 rows · exact calls in orderMobile Actions961 rows · exact call
Figure 3. Each Needle 3 subnetwork before and after fine-tuning on the platform, base and tuned both scored with forced calls, against DeepSeek V4 Flash through its cloud API.

The Cactus Platform is the full path: Cactus datasets, the 2-bit quantisation behind the shipped model, evaluation design and tracking, full-depth fine-tunes and dataset management, all on our infrastructure and training pipeline, no need to build your own.

Deploy

Every deployment target ships a prebuilt engine under 1 MB that loads the needle3.cact weights at start. needle build fetches the engine for a platform and puts the weights beside it, at the full 20 layers or any smaller subnetwork:

TargetPlatform folderShips
macOSmacos-arm64needle CLI, libneedle.a, needle.h
Linuxlinux-x86_64, linux-arm64, linux-armv7, linux-riscv64, linux-mipselneedle CLI, libneedle.a, needle.h
Windowswindows-x86_64, windows-arm64needle.exe, libneedle.a, needle.h
Androidandroid-arm64, android-armv7, android-riscv64needle CLI, libneedle.a, needle.h
iOSios-arm64, ios-sim-arm64libneedle.a, needle.h
tvOS, watchOStvos-arm64, watchos-arm64libneedle.a, needle.h
Browserwasmneedle.js, needle.wasm, needle.h
WASI componentwasm-componentneedle.component.wasm, needle.wit
One folder per target; needle build --platform downloads it and places needle3.cact beside the engine.
# engine, header and weights for this Mac
needle build --platform macos-arm64
# an 8-layer subnetwork for a Pi
needle build --platform linux-arm64 --layers 8 --out ./pi
# a tuned archive
needle build --lora adapter.safetensors --out tuned.cact
查看原文(Hacker News 熱門)