今天我们发布 Needle 3:一个面向移动设备、可穿戴设备、机器人、智能家居、汽车和微控制器的基础模型。整个模型是一个单一的 8-29 MB 二进制文件,构建在我们的 Simple Attention Network 之上,我们牺牲了通用聊天能力,以在移动端工具调用上击败其 10 倍大小的模型,并在提取任务上匹配 2-3 倍大的模型。
- 工具调用
- 给定你的应用所暴露的函数,Needle 会挑选正确的函数,并根据用户所说填充每个参数。要求两件事,你会按顺序得到两个调用;要求某个没有工具覆盖的内容,你会得到一个空列表,而不是猜测。
- 结构化提取
- 声明一个形状,交给它杂乱文本,返回带类型的字段:发票、预订、通知、表单。解码语法保证输出可解析。提取也很好地泛化到了分类问题。
- 文本嵌入
- 同一个模型为句子返回一个向量,因此应用可以在本地进行搜索、匹配和路由:找到你想要的笔记,挑选最接近请求的工具,合并重复警报。
这在产品中是什么样子:
- 智能家居
- 从按按钮到与房子对话。“调暗卧室并锁好”变成两个调用,离线执行,无需中枢往返。
- 机器人
- 给吸尘器或小型机器人细微的指令:“打扫厨房但不要进卧室”,“完成后回到充电座”。每个都变成它可以执行的移动序列。
- 手机
- 一个在设备上执行操作而非回答问题的助手:用上周末的照片制作相册,打开网站,调暗屏幕,在文件中找到租约。
- 可穿戴设备
- 在手腕上将通知读入结构化数据:将卡消费读入商户、金额和日期;将消息读入回复;将投诉读入情感标记。
- AR 眼镜
- 从简短请求进行导航和附近搜索,无需手机或网络参与。
- 汽车
- 在车厢内通过请求控制气候、媒体、导航和通话,工具集固定,因此能经受长途驾驶的对话。
- 计算机
- 用平实的英语控制你面前的机器:起草邮件,启动计时器,复制地址,打开标签页。
- 搜索和匹配
- 从不离开设备的嵌入:对笔记、消息和文档进行语义搜索;将查询匹配到数百个工具中最接近的一个;在手表上合并近乎重复的警报。
模型
智能阶梯。 Needle 3 的每一层都是一个容量单调递增的子网络。开发者可以从 2 层(2L)子网络到 20 层(20L)中选择合适的大小。每个子网络都适合微调,因此 4L 在下游任务上微调一个 epoch 后可以匹配 DeepSeek V4 Flash。智能阶梯产生 9 到 29 MB 的 CQ2 位二进制文件,并支持各种微型设备。
- 输入
- 文本提示,加上工具定义或提取模式
- 输出
- 带有工具调用或提取的结构化 JSON
- 模型
- 29-121M 阶梯式简单注意力网络,CQ2 量化
- 训练
- 360B 令牌的专有结构化数据集
- 速度
- 在 Raspberry Pi 5 上解码 400-4k 令牌/秒,预填充 1-10k 令牌/秒
阶梯式简单注意力网络
图 1。该架构每令牌的 MFLOPs 消耗比相同配置的 transformer 少 2 倍以上。
图 2. Needle 3 在移动端工具调用上击败了规模为其 10 倍的模型,在抽取任务上与规模为其 2-3 倍的模型相当。Needle 3 子网络(20、16、8 和 4 层)通过已发布的 CQ2 位二进制文件运行,基线在 vLLM 下以 f16 运行,DeepSeek V4 Flash 通过其云 API 运行。该连线连接了 Needle 模型。
快速开始
安装 Python 包。推理引擎从 Hugging Face 获取一次并缓存;无需构建其他任何内容。
Needle 会读取你的工具描述来决定调用什么以及如何填充参数,因此把描述写好就是全部关键。
简单:装饰一个函数。函数签名给出参数类型,文档字符串就是工具描述,而 run()
完成整个循环:模型选择调用,Needle 执行你的函数,将结果反馈回去,并返回最终响应,其中已执行的工具结果作为 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'}]
按模式路由:当描述无法枚举所有措辞时,给工具提供 triggers
,即与每个请求匹配的正则表达式。匹配会将解码限制在匹配的工具上并要求调用,因此请求会到达你指定的工具,而不是被拒绝或错误路由,并且即使低于置信度下限,调用也会发出。匹配会限制整个回合,因此一个兜底模式应排除其他工具拥有的名词,例如 ^(?![\s\S]*\b(lights?|doors?)\b)[\s\S]*\b(turn|switch)\b[\s\S]*\b(on|off)\b
;这样 "switch the fan on and dim the kitchen lights" 仍然会到达两个工具。
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"}}]
抽取:要从文本中提取结构化数据,声明其形状并调用 extract()
。传入一个 Pydantic 模型,你会得到一个类型化对象。
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
每一回合返回一个 JSON 对象:
{ "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
分数,引擎已经应用了 0.1 的下限。低于该下限时,调用会被扣留到 suppressed_calls
中,且 function_calls
为空。高于该下限时,分数由你决定如何路由:分数高时立即执行,分数中等时显示调用并询问,将空结果视为拒绝。带有 triggers 的工具
总是会为匹配的请求产生一次调用,因此分数才是告诉你该运行它还是确认它的依据。
r = agent.complete(user_text) calls = r["function_calls"] held = r["suppressed_calls"] if calls and r["confidence"] >= 0.7: execute(calls) # 确定:执行 elif calls or held: confirm(calls or held, r["reasoning"]) # 不确定:展示调用,询问 else: say("I can't do that here") # 无事可做:拒绝
编写工具:模型会逐字读取 schema,因此一个描述平实的窄工具胜过宽泛的工具。每个动作一个工具,按其覆盖的动作来描述(“打开或关闭房间的灯”),而不是按类别。用用户会说的词来命名枚举选项(action: ["increase", "decrease"]
),并把同义词保留在描述中。当请求可能省略某个必需参数时,给它一个 default
;一个没有默认值、请求中也没有证据的必需参数会被保留而不是猜测。把值格式写进描述("City, ST"
、"e.g. T-1042"
)。给必须始终到达工具的意图添加 triggers
,并让每轮的工具集保持精简,因为每多一个工具就多一次误路由的机会。
微调:Python 包是快捷路径。在冻结的基座上进行 LoRA,使用完整的 20 层,然后生成一个 4 位的 .cact
,用于任何在同一引擎上运行的子网络。
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
这些指南讲得更深入:设计工具、置信度、提取、微调、Python 参考、支持的设备、.cact 格式 以及 移植 Needle。源码在 GitHub 上。
Cactus 平台
Needle 的设计初衷就是可定制。它的容量是一架梯子,一个小到 2 层的子网络,在某个产品的工具上微调后,可以在比完整模型所需小得多的设备上以最优状态运行。把容量约束到一个狭窄、定义明确的任务上,正是它能在该任务上达到前沿级准确率的原因:在 DroidCall 上微调能让每个子网络提升 18 到 36 个点,而从 4 层起,微调后的子网络就超过了 DeepSeek V4 Flash,起点是 29M 参数(图 3)。
每个子网络,都在平台上微调
图 3.每个 Needle 3 子网络在平台上微调前后的对比,基座和微调后都用强制调用评分,对照通过其云 API 访问的 DeepSeek V4 Flash。
Cactus 平台 是完整路径:Cactus 数据集、已发布模型背后的 2 位量化、评估设计与跟踪、全深度微调和数据集管理,全部在我们的基础设施和训练流水线上完成,无需自己搭建。
部署
每个部署目标都附带一个小于 1 MB 的预构建引擎,启动时加载 needle3.cact
权重。needle build
会为某个平台获取引擎,并把权重放在它旁边,可以是完整的 20 层或任何更小的子网络:
| 目标 | 平台文件夹 | 附带文件 |
|---|---|---|
| macOS | macos-arm64 | needle CLI, libneedle.a, needle.h |
| Linux | linux-x86_64, linux-arm64, linux-armv7, linux-riscv64, linux-mipsel | needle CLI, libneedle.a, needle.h |
| Windows | windows-x86_64, windows-arm64 | needle.exe, libneedle.a, needle.h |
| Android | android-arm64, android-armv7, android-riscv64 | needle CLI, libneedle.a, needle.h |
| iOS | ios-arm64, ios-sim-arm64 | libneedle.a, needle.h |
| tvOS, watchOS | tvos-arm64, watchos-arm64 | libneedle.a, needle.h |
| 浏览器 | wasm | needle.js, needle.wasm, needle.h |
| WASI 组件 | wasm-component | needle.component.wasm, needle.wit |
needle build --platform
下载它并将 needle3.cact 放置在引擎旁边。# 此 Mac 的引擎、头文件和权重 needle build --platform macos-arm64 # 用于 Pi 的 8 层子网络 needle build --platform linux-arm64 --layers 8 --out ./pi # 一个调优后的存档 needle build --lora adapter.safetensors --out tuned.cact
