端侧推理 · 工具调用 · 结构化输出 · 微型语言模型 —— cactus-compute/needle(Needle 2)是一个 45M 参数的开放模型, 把一句自然语言直接变成合规的 JSON 工具调用。 整个模型打包成单个 14MB 引擎二进制,一次完整会话只占约 28MB 内存, 目标设备是手机、可穿戴、智能家居与机器人。
端侧 AI 的三大痛点
智能设备想拥有「AI 助手」能力,通常要撞上三堵墙:
• 模型体积:通用大模型动辄数 GB,手机 App 尚且吃力,手表、音箱、门锁根本没有空间
• 网络依赖:云端推理有往返延迟,断网即失效;喊一句「开空调」还要等云端应答,体验割裂
• 输出不可控:通用模型返回自由文本,还要再解析成结构化指令,格式错误、字段缺失全靠兜底
隐私是第四重隐性成本:设备把指令、位置、语音上传云端, 医疗、家居、工业等场景根本不允许数据出境。
Needle 2 的选择是把这几堵墙一起拆掉:45M 参数、CQ2 量化后整体 14MB, 推理全程离线,输出被字节级文法约束成永远合规的 JSON。 它不追求「什么都会说」,只专注一件事——把自然语言变成一次正确的工具调用。 对智能硬件团队、嵌入式开发者、想做本地语音助手的个人开发者而言, 这是把「AI 能力」塞进低成本设备的最小可行路径。
这个体积档位此前几乎是空白:单片机跑不动任何模型, 手机虽有算力却要忍受几百 MB 的本地模型或依赖云端的延迟与隐私风险, 14MB 恰好落在两者之间,让「AI 助手」第一次成为固件级组件—— 跟 Wi-Fi 模块、蓝牙协议栈一起烧进设备,随 OTA 一起升级。
声明工具即可本地运行
安装只有一条命令,引擎首次使用时自动下载并缓存,之后完全离线:
pip install cactus-needle
样例一:声明工具,跑完整循环。
用装饰器把 Python 函数变成工具——签名即参数类型、docstring 即描述,
run() 自动完成「选工具 -> 执行 -> 回填结果」:
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"])
输入一句查询,返回结构化结果;每次调用还附带 confidence 置信度,
以及 prefill_tps、decode_tps 两个速度指标。
样例二:文本结构化抽取。 抽取与工具调用是同一件事——把 Pydantic 模型声明为唯一工具,传入文本即可:
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
发票、简历、邮件、单据的字段抽取,不需要写任何解析规则。
样例三:参数级约束。 用 needle.Field 给参数加上范围、正则、长度限制,
这些约束会被编译进解码文法,模型只能产出满足条件的值,
连「越界」的可能性都不存在:
from typing import Annotated
@needle.tool
def send_money(
amount: Annotated[float, needle.Field(gt=0, le=10000)],
to: Annotated[str, needle.Field(pattern=r"^@[a-z0-9_]+$")],
):
"Send money to a handle."
return {"sent": amount, "to": to}
样例四:大工具目录自动检索。 工具超过 5 个时,内置检索头对查询与每个工具做嵌入, 每轮只把得分最高的 5 个放进上下文,文法只约束这 5 个; 未被选中的工具是「不可达」,而非「不太可能被选」。 几十个工具的智能家居目录也能稳定工作。
需要定制行为时,官方流程是「合成数据 -> LoRA 微调 -> 导出单个 .cact」, 微调产物仍是单文件,任意设备直接加载:
needle finetune data.jsonl --epochs 10
needle build checkpoints/needle2.pkl --lora checkpoints/needle_lora.pkl --out my.cact
离线部署用 needle fetch 拿到引擎文件拷进设备缓存,
再设 NEEDLE_LIB_PATH 指向它即可,完全断网的设备也能装。
浏览器里还能起 needle playground,改工具、跑对话、一键微调都在网页上完成。
置信度门控是官方推荐的用法:给产品设一个阈值,
得分在阈值之上就执行,之下就重新询问或转人工,避免误操作。
环境事实通过 system 参数注入,如 date、locale、battery,
模型据此把「明天早上 7 点」解析成绝对时间,不注入就不会乱猜。
三个常见的坑要留意:
• 微调权重不会更新置信度头,confidence 会返回 None,别再按阈值做门控
• 非英语文本 token 数约膨胀 1.7 倍(实测西班牙语),256 token 窗口会被挤压
• 无关请求返回空调用 [],没有自由文本兜底,交互设计要留好「听不懂」的分支
装下 45M 参数的秘密
14MB 不是单一技巧的功劳,而是「架构瘦身 + 激进量化 + 单文件打包」三层叠加。
架构层:Simple Attention Network。 项目论文 arXiv:2607.18363 给出的小模型配方:27 层、GQA 注意力, 关键替换有两处——
• Hadamard MLP 取代 FFN:用固定的 Walsh-Hadamard 变换做非线性混合, 变换矩阵无权重、nlogn 复杂度,只需学习三个对角缩放,省掉了最大的参数块
• Engram 键值记忆:哈希 n-gram 表(2/3 阶)在第 2、15 层注入, 把「记忆」压缩进查找表而非堆参数
量化层:Cactus Quants。 权重先做 Hadamard 旋转,再映射到 Lloyd-Max 单位球码本,每组只存一个范数; 导出 .cact 时 matmul 权重压到 2–4 bit(激活 8 bit),嵌入与投影整体仅 14MB。 对比同赛道:FunctionGemma 270M、LFM2.5 230M、Apple FM 都是几十到数百 MB, Needle 以 5–70 倍更小的体积,在工具调用基准上与它们互有胜负。
运行时:自包含单文件引擎。 .cact 用 120 字节头记录全部几何参数,张量按层序排列、64 字节对齐, 引擎 mmap 直读、无需解析,分词器也烘焙在文件里。 Python 包只是 ctypes 薄壳,真正的推理在 C++ 单内核引擎中执行。 内存侧,KV 缓存预算约 11.5MB,256 token 滑动窗口 + 工具作为 KV sink 固定驻留,会话再长,峰值内存都被压在 28MB 附近, 官方示例给出的实测指标约为每秒 4300 token 预填充、850 token 解码。
真正的新意在于受约束解码:不是「生成 JSON 再校验」, 而是把 schema 编译成字节级文法,每一步解码都在合法范围内选 token, 结构错误在机制上不可能出现。 再叠加双信号置信度(校准头打分与解码概率取小者), 失败模式是「拒绝执行或升级」,而不是「执行了错误的调用」。
训练侧也有配套:LoRA 只动每层五个注意力投影(rank 16),
几百条干净样本就能明显改善工具选择;训练可在 CPU 上跑,
数据用 JSONL 一行一条,answers 标注期望调用、reasoning 标注取值依据。
代价同样清晰:词表只有 8192、几乎不支持自由文本(只输出调用与抽取)、 跨语言能力弱。它是专用工具调用模型,不是通用对话模型,选型时要认清边界。
从智能家居到级联推理
两个立即可落地的场景:
• 智能家居中控:把灯光、恒温器、窗帘声明成工具,语音或文本指令本地解析执行; 断网可用、数据不出门;system facts 还能注入日期、电量等环境事实, 让「明天早上 7 点」这类相对表达正确落地
• 端侧文档流水线:发票、简历、工单字段抽取跑在本地, 零 API 成本、零数据外泄,天然适合医疗与财务场景
更远的想象空间在「级联」:官方契约本身就是阈值门控—— 置信度达标就本地执行,不达标就升级给云端大模型。 这意味着 14MB 的 Needle 可以当第一道闸,过滤掉多数简单指令, 只有疑难请求才走昂贵的大模型。 再配合 LoRA,每个行业都能训练自己的「单文件工具模型」, 随固件一起分发到任何设备。 从智能门锁的「本地声控开锁」到工业设备的「语音点检」再到机器人的 「动作指令解析」,凡是需要「听懂一句话、做对一件事」的地方, 这个 14MB 的引擎都能把成本压到几乎为零。
夜雨聆风