GitHub Trending に「14MB のモデル」が来ていました。
Cactus Compute の Needle 2 です。45M パラメータで、ツール呼び出しに特化しています。週で +3,627 スター伸びていました。
14MB というのは、画像1枚くらいのサイズです。しかも「モデルファイル」ではなく、エンジンごと入って14MBだといいます。
The whole model is a single 14MB binary that runs a full session in about 28MB of RAM.
(モデル全体が単一の 14MB バイナリであり、セッション全体を約 28MB の RAM で実行する)
本当なのか、Windows で動かして測りました。
動きました。 DLL 1個、RAM 37MB、1回 0.5 秒です。
ただし、日本語は5問中5問外しました。しかも確信度 0.89 で。
検証したもの: https://github.com/cactus-compute/needle (Apache-2.0)
環境: Windows 10 Home 19045 / AMD Ryzen(16スレッド)/ Python 3.12.3
まず結果
| 項目 | 実測 |
|---|---|
| エンジンの実体 |
libneedle.dll 1個だけ(14,301,696 バイト) |
| 依存パッケージ |
ゼロで動く(import needle のみ) |
| 1回の推論 | 0.42〜0.66 秒 |
| decode | 155〜177 tok/s |
| ピークRAM | 36〜43MB |
| ツール選択(英語・5問) | 4/5 |
| ツール選択(日本語・5問) | 0/5 |
(難点)pip install
|
venv が 570MB に膨らみ、Windows では失敗する |
14MB のバイナリ1個で本当に動きます。 最後の行だけが厄介ですが、これは回避できます。
DLL 1個で完結している
まず、実体がどこにあるのかを追いました。needle/agent/fetch.py に書いてありました。
HF_REPO = "Cactus-Compute/needle2"
ENGINE_VERSION = "2.0.2"
PLATFORMS = ("macos-arm64", "linux-x86_64", ..., "windows-x86_64", "windows-arm64",
"android-arm64", "ios-arm64", ..., "wasm")
Hugging Face から、プラットフォームごとのバイナリを取ってきます。 windows-x86_64 が入っています。Windows 対応は本物でした。
| 項目 | 中身 |
|---|---|
| 取ってくるもの |
cactus_needle-2.0.2-py3-none-win_amd64.whl(13MB) |
| 取り出すファイル | その中の libneedle.dll
|
| 保存先 | ~/.cache/cactus-needle/2.0.2/libneedle.dll |
| 上書き方法 | 環境変数 NEEDLE_LIB_PATH
|
落ちてきたファイルを測りました。
14301696 bytes
14,301,696 バイト。約 14MB です。 .gguf のような別のモデルファイルはありません。これ1個に重みまで入っています。
そして、この DLL さえあれば Python 側の依存はゼロでした。cactus-needle と pip しか入っていない venv で動きます。
Package Version
------------- -------
cactus-needle 2.0.6
pip 24.0
import : 0.024 s
Needle() init : 0.158 s
first run : 0.533 s
huggingface_hub すら入っていません。ソースを読むと、キャッシュに DLL があればそのまま ctypes.CDLL() に渡していました。そこから先はネットワークにも触りません。
ただし、「ダウンロード不要」と読むと間違えます。 公式が書いているのは inference does no network(推論はネットワークを使わない)です。初回だけはエンジンを取りに行きます。差は時間に出ました。
| 状態 |
Needle() の初期化 |
|---|---|
| 初回(DLL取得あり) | 5.250 秒 |
| 2回目以降(キャッシュあり) | 0.158〜0.170 秒 |
速度とメモリ
呼び出すと、出力に計測値が入ってきます。自分で測る必要がありませんでした。
{
"type": "respond",
"reasoning": "Alarm set; confirm.",
"confidence": 0.6804,
"decode_tps": 167.1,
"peak_ram_mb": 38.3,
"results": [{"tool": "set_alarm", "at": "7:30"}]
}
| 項目 | 実測 |
|---|---|
| 1回の推論 | 0.42〜0.66 秒 |
| decode | 155〜177 tok/s |
| ピークRAM | 36〜43MB |
公称 28MB は少し超えました。 ただしこれは Windows の x86_64 で Python 経由の数字です。公称値が想定しているのは組み込み側なので、そのまま比較はできません。
ここで1つ面白いことが起きました。
依存を全部入れた状態で同じコードを走らせると、ピークRAM が 94.6MB になりました。依存なしだと 37.8MB です。
| 構成 | ピークRAM |
|---|---|
依存あり(huggingface_hub 等を import) |
94.6MB |
依存なし(cactus-needle のみ) |
37.8MB |
2.5倍違います。読み込んでいる DLL は同じものです。差は Python 側に import したライブラリの分でした。peak_ram_mb はエンジン内部だけでなく、プロセス全体を見ているようです。
「28MB で動く」という設計を活かしたいなら、同じプロセスに何を import しているかのほうが効いてきます。
ツールは、ちゃんと選べている
5つのツールを登録して、英語で5問投げました。定義はデコレータ1つです。
@needle.tool
def get_weather(city: str):
"Get the current weather for a city."
return {"tool": "get_weather"}
| プロンプト | 期待 | 結果 |
|---|---|---|
| wake me up at 7:30 tomorrow | set_alarm | OK |
| what is the weather in Osaka? | get_weather | OK |
| play some Miles Davis | play_music | OK |
| how much is 100 dollars in yen? | convert_currency | MISS(get_weather を選んだ) |
| email bob@example.com about the meeting delay | send_email | OK |
4/5、平均 0.58 秒でした。45M パラメータでこれなら十分だと思います。
外したのは通貨換算で、get_weather を呼びました。
日本語は通りません
ここが一番知りたかったところです。
同じ構成で日本語を投げました。
| プロンプト | 期待 | 結果 |
|---|---|---|
| 大阪の天気を教えて | get_weather | MISS |
| 明日の7時に目覚ましをセットして | set_alarm | MISS |
0/2 でした。中身を見ると、こうなっていました。
{
"type": "call",
"reasoning": "No tool available for sending messages or sharing content.",
"confidence": 0.0313,
"results": []
}
「大阪の天気を教えて」を、メッセージ送信の依頼だと解釈しています。
「ツールの説明が英語だからでは」と思ったので、説明も日本語にして測り直しました。
@needle.tool
def get_weather(city: str):
"指定された都市の現在の天気を取得する。"
return {"tool": "get_weather", "city": city}
README にこう書かれているので、説明文は効くはずです。
Needle reads your tool descriptions to decide what to call and how to fill arguments, so describing them well is the whole game.
(Needle は、何を呼ぶか・引数をどう埋めるかを決めるために、あなたのツール説明を読む。だから、うまく説明することがすべてである)
結果がこれです。
| プロンプト | confidence | モデルの判断 |
|---|---|---|
| 大阪の天気を教えて | 0.8914 | No tool available for sending messages or sharing content. |
| 東京の天気は? | 0.836 | Social media interaction not supported by any tool. |
| 7時30分にアラームをセットして | 0.2302 | No tool available for sharing content. |
0/3。しかも confidence が上がりました。
説明が英語のときは 0.03 でした。日本語にしたら 0.89 です。 判断は同じように間違っているのに、自信だけが上がっています。
これは困ります。Needle には信頼度を返す仕組みがあって、README でも「低いスコアで弾く」使い方が想定されています。日本語ではその門番が働きません。
45M パラメータのモデルに多言語を期待するほうが無理だと思います。ただ、「日本語だと精度が落ちる」ではなく「自信満々で間違える」なので、扱いは別です。
日本語で使うなら、Needle に渡す前に英語へ寄せるしかありません。そこに翻訳を挟むなら、14MB で完結する意味は薄れます。
難点は、入れるところ
ここまで「DLL 1個で動く」と書いてきましたが、素直に pip install すると、そうなりません。
pip install cactus-needle
venv が 17MB から 570MB になりました。しかもエラーで失敗します。
ERROR: Could not install packages due to an OSError: [Errno 2] No such file or directory:
'C:\nd\venv\Lib\site-packages\orbax\checkpoint\experimental\v1\_src\testing\compatibility\
checkpoints\v0_checkpoints\composite_checkpoint\checkpoint_metadata_missing\
pytree_checkpointable_missing_metadata\state\ocdbt.process_0\d\60c4009561fe85efe8a0afa099989378'
このパスを数えました。
failing path length: 260
LongPathsEnabled = 0
ちょうど 260 文字です。Windows の MAX_PATH は 260 で、終端を含むと実質 259 文字までしか使えません。1文字足りずに落ちています。
最初は自分の作業ディレクトリが深いせいかと思って、C:\nd という極限まで短い場所に作り直しました。それでも同じ場所で落ちました。 犯人はインストール先ではなく、パッケージの中身のほうです。
膨らんだ中身を測りました。
| パッケージ | サイズ |
|---|---|
| jaxlib | 242MB |
| scipy | 115MB |
| tensorstore | 35MB |
| numpy | 34MB |
| jax | 30MB |
jaxlib と scipy だけで 357MB です。宣言されている依存に jax / flax / optax が並んでいました。機械学習の学習側のスタックです。
理由はパッケージの中身を見て分かりました。
needle/model/finetune.py ← 学習
needle/model/quantize.py ← 量子化
needle/model/export.py ← 書き出し
ファインチューニングと量子化のコードが同梱されています。 そちらが JAX を使います。推論だけしたい人にも、まとめて付いてきます。
解決は簡単でした。
pip install --no-deps cactus-needle
Successfully installed cactus-needle-2.0.6
パッケージ本体は 479KB。これで import needle が通り、推論も動きます(本記事の計測はすべてこの構成です)。ファインチューニングをしないなら、JAX は要りません。
使いどころ
自分の結論です。
| 場面 | どうするか |
|---|---|
| 英語で、決まったツールを呼ぶ | 向いている。 0.5秒・37MB は魅力 |
| 組み込み・オフライン端末 | 向いている。 DLL 1個で完結する |
| 日本語の入力を直接受ける | 向かない。 0/5 |
小さい LLM ではなく、ツールを選ぶ部品。そう見ると納得できます。ChatGPT の代わりにはなりません。
なお公式のベンチマークでも、勝ち負けは分かれています。BFCL v4 single-turn は 42.6% で、LFM2.5 230M の 60.8% に負けています。サイズが 5〜70 分の1という条件付きの勝負であって、「小さいのに全部勝つ」ではありません。
まとめ
-
14MB は本当。 エンジンの実体は
libneedle.dll1個(14,301,696 バイト) で、重みごと入っている - Python の依存はゼロで動く。 DLL さえキャッシュにあれば、推論でネットワークにも触らない
- 実測は 0.42〜0.66秒 / 155〜177 tok/s / ピークRAM 36〜43MB
- 同じプロセスに何を import するかでRAMが2.5倍変わる(37.8MB → 94.6MB)。小ささを活かすなら周りも軽くする
- ツール選択は英語で 4/5、平均 0.58 秒
- 日本語は 0/5。 しかも説明文を日本語にすると confidence が 0.03 から 0.89 に上がる。 間違ったまま自信だけ上がるので、信頼度で弾けない
-
pip installは venv を 570MB にし、Windows では失敗する(MAX_PATH260文字)。--no-depsで回避でき、推論はそれで足りる
画像1枚のサイズで、ツールを選ぶモデルが動く。 ここは素直に驚きました。日本語が通らないので用途は限られますが、英語のコマンドを受けて機器を操作するような使い方なら、今すぐ試せます。
参考
- cactus-compute/needle(Apache-2.0)
- 公式ページ — ベンチマークの数値はこちら
- Cactus-Compute/needle2 — エンジンの配布元
- cactus-needle · PyPI — 検証したのは 2.0.6(2026-08-17)
※ 引用は原文と日本語訳を併記しています。訳は読みやすさを優先しているので、正確な表現は原典をご確認ください。
関連記事
- Mermaidで書いた過去記事の図を、話題の図解スキルに食わせてみた — 同じく、日本語が通るかを実際に測った話
- プロンプトの次は何を学べばいい? AIとの付き合い方を4段階で整理する — ツール呼び出しは「ハーネス」の層にあたる
JQITのエンジニアの95%以上は未経験からの採用です。
よければコーポレートサイトにも遊びに来てください。
エンジニア採用も行っています。もしご興味あれば覗いてみてください。
▶ 採用サイト