JapaneseTinyAgentLM Action 3M
日本語の短い依頼文を、ロボットの動作呼び出し(JSON)に変える、パラメータ数 3,148,608 の小さな言語モデルです。M5Stack のスタックチャン(CoreS3、ESP32-S3)の本体だけで動かすために、一から学習しました。NPU、外付けのモジュール、ネットワークは使いません。
入力: 左を見て、真ん中に戻ってきて。
出力: [{"name":"look","arguments":{"direction":"left","amount":"normal"}},
{"name":"look","arguments":{"direction":"center","amount":"normal"}}]
コード(学習、評価、C の runtime、firmware): GitHub(Apache-2.0)
ブラウザで試す: デモ(Hugging Face Space)。インストール不要で、実機と同じ C の runtime(WebAssembly)と INT4 の重みがブラウザの中で動きます。
English summary at the end.
できること
出力は、次の3種類の動作を最大2個まで並べた JSON の配列です(action_schema_v0.json)。動作の依頼でない文、否定された依頼、このロボットにはできない依頼には [](何もしない)を返します。
| 動作 | 引数 |
|---|---|
look(首を向ける) |
direction: left / right / up / down / center、amount: slight / normal / large |
set_expression(表情) |
expression: happy / sad / surprised / neutral |
nod(うなずく) |
count: 1〜3 |
評価セットでの実際の出力の例です(いずれも正解)。
| 入力 | 出力 |
|---|---|
| ほら 笑顔を見せて | set_expression(happy) |
| ほんの少し左を向いてくれる? | look(left, slight) |
| 右向いて、一回うなずきな。 | look(right, normal), nod(1) |
| いや、下は見なくて大丈夫。上を向いてみて! | look(up, normal) |
| 右むかないで、ちょっとやめといてくれる? | [] |
| まっすぐ行って、右に曲がってください。 | [] |
すぐに試す(Python)
必要なのは Python 3.10 以上と、PyTorch、NumPy、sentencepiece、safetensors だけです。GPU は要りません(CPU で1文あたり約 0.03 秒)。
1. インストール
pip install torch numpy sentencepiece safetensors huggingface_hub
2. ダウンロードして実行
hf download ayousanz/JapaneseTinyAgentLM-Action-3M --local-dir JapaneseTinyAgentLM-Action-3M
python JapaneseTinyAgentLM-Action-3M/inference.py 右を向いて 笑わないでね
{"input": "右を向いて", "actions": [{"name": "look", "arguments": {"direction": "right", "amount": "normal"}}], "confidence": 0.9989, "raw": "..."}
{"input": "笑わないでね", "actions": [], "confidence": 0.9999, "raw": "[]"}
引数を付けずに python JapaneseTinyAgentLM-Action-3M/inference.py と実行すると、標準入力から1行に1文ずつ読みます。
Python から使う
import sys
from huggingface_hub import snapshot_download
folder = snapshot_download("ayousanz/JapaneseTinyAgentLM-Action-3M")
sys.path.insert(0, folder)
from inference import ActionModel
model = ActionModel(folder)
print(model("少し上を向いてから、二回うなずいて"))
# [{'name': 'look', 'arguments': {'direction': 'up', 'amount': 'slight'}},
# {'name': 'nod', 'arguments': {'count': 2}}]
print(model.predict("こんにちは")) # {'actions': [], 'confidence': ..., 'raw': '[]'}
uv を使う場合は、インストールせずに uv run --with torch --with numpy --with sentencepiece --with safetensors JapaneseTinyAgentLM-Action-3M/inference.py 右を向いて でも動きます。
| 出力の項目 | 意味 |
|---|---|
actions |
実行する動作のリスト。[] なら何もしない |
confidence |
生成した token の確率の最小値。0.86808 未満のときは、actions を [] にしています |
raw |
gate をかける前のモデルの出力 |
inference.py は、評価に使ったコードと同じ計算をする単体のスクリプト(約 270 行、Apache-2.0)です。評価セット全 4,794 文で、評価したときの出力と完全に一致することを確かめています。
ロボットにつなぐ
actions を順に実行します。スタックチャン(K151)では、次の角度を使いました。ほかのロボットでは、可動域に合わせて変えてください。
| 動作 | 角度 |
|---|---|
look left / right |
左右に slight 10°、normal 20°、large 30° |
look up / down |
上下に slight 5°、normal 10°、large 15° |
look center |
左右・上下とも 0°(正面) |
nod |
下へ 14° 振って戻す動きを count 回 |
set_expression |
画面の顔を変える(首は動かさない) |
- 出力は必ず
action_schema_v0.jsonに合いますが、ロボットを動かす前に、角度の上限で必ず制限してください。 - 文字の入力を前提にしています。音声で使う場合は、音声認識の結果を入力してください(音声認識の誤りへの強さは評価していません)。
スタックチャンで動かす
ビルド済みの firmware で、M5Stack のスタックチャン(K151。CoreS3 と SCS0009 の servo ×2)の本体だけで動きます。Wi-Fi は使いません。
書き込むと、今入っている firmware(公式のスタックチャンの firmware など)は消えます。 元に戻したい場合は、先に手順 1 でバックアップを取ってください。
0. インストール(上の「すぐに試す」でダウンロード済みのフォルダを使います)
pip install esptool pyserial
USB-C でパソコンにつなぎ、port の名前を確かめます(Windows は COM3 など、Linux は /dev/ttyACM0、macOS は /dev/cu.usbmodem…)。以下の COM3 は自分の port に置き換えてください。
1. バックアップ(任意。16MB、数分かかります)
esptool --chip esp32s3 -p COM3 -b 921600 read-flash 0 0x1000000 backup_k151.bin
# 元に戻すとき: esptool --chip esp32s3 -p COM3 -b 921600 write-flash 0x0 backup_k151.bin
2. 書き込み(firmware とモデルが1つのファイルになっています。4,068,608 bytes)
esptool --chip esp32s3 -p COM3 -b 921600 write-flash 0x0 JapaneseTinyAgentLM-Action-3M/firmware/stackchan_k151_jtalm_action.bin
つながらないときは、本体の横のリセットボタンを緑の LED が点くまで約3秒押し続けて、書き込みモードにしてからやり直してください。書き込んだ後は、リセットボタンを1回押します。
3. 話しかける
python JapaneseTinyAgentLM-Action-3M/firmware/stackchan_chat.py COM3
準備ができました。依頼を入力してください(終了は Ctrl+C)。
右を向いて
→ [{"name":"look","arguments":{"direction":"right","amount":"normal"}}] 963 ms
にっこりして
→ [{"name":"set_expression","arguments":{"expression":"happy"}}] 908 ms
笑わないでね
→ [] 384 ms
画面に顔が出て、表情の依頼で顔が変わります。起動したときは servo が off で、首は動きません(動きの計画だけを作ります)。
4. 首を動かす
python JapaneseTinyAgentLM-Action-3M/firmware/stackchan_chat.py COM3 --servo
servo の電源が入り、首がゆっくり正面に戻ってから、依頼に合わせて首が動きます。首のまわりに指やケーブルを近づけないでください。画面に触れる、Ctrl+C を押す、!stop を送る、のどれかで、すぐに止まり servo の電源が切れます。 角度は firmware が制限します(左右 ±30°、上下 −10〜+15°)。
! で始まる command |
動き |
|---|---|
!servo on / !servo off |
servo の電源を入れる(首が動く)/ 切る |
!stop |
動きを止めて servo の電源を切る |
!center |
正面を向く |
!gate 0.9 |
確信度の gate の閾値を変える(既定 0.868。0 で off) |
!info |
モデルと firmware の情報 |
- 1文の応答時間の中央値は約 1.3 秒でした(CoreS3、2コア)。300文で、PC の PyTorch と出力が完全に一致しました。
- firmware は ESP-IDF v5.5.5 で build しました(app の sha256
7ada159fff9494eb6dd71022207c1863d207f698d7ae80c73e39f44461a1d749)。flash の配置は、bootloader 0x0、partition table 0x8000、app 0x10000、モデル(jtalm_action_3m_q4_g64.jtlm)0x200000 です。モデルだけを替えるときは、0x200000 に.jtlmを書き込みます。 - firmware は Apache-2.0 です。同梱した第三者のコード(ESP-IDF、newlib、FreeRTOS、M5Unified、M5GFX など)のライセンスは
firmware/licenses/にあります。firmware の source と build の手順は GitHub にあります。
ファイル
| ファイル | 内容 |
|---|---|
inference.py |
単体の推論スクリプト(上の「すぐに試す」) |
firmware/stackchan_k151_jtalm_action.bin |
スタックチャン用の firmware とモデルを1つにした書き込み用のイメージ(0x0 に書く) |
firmware/stackchan_chat.py |
スタックチャンと USB serial で話すスクリプト |
firmware/licenses/ |
firmware に含まれる第三者のコードのライセンス |
model.safetensors |
評価した重み。INT4(group 64)で量子化した値を fp32 で保存したもので、ESP32 が計算する値と同じです |
model_fp32.safetensors |
量子化する前の fp32 の重み(追加学習用) |
jtalm_action_3m_q4_g64.jtlm |
スタックチャンの firmware が読む形式(1,971,456 bytes、sha256 b90d0066075d92a2a803b40eb62112cd227480b1fdd24a4f6797a388a6ee7b5d) |
tokenizer.model |
SentencePiece(unigram、2,048語)。JSON の部品を1語として持ちます |
config.json |
構造、tokenizer の hash、確信度の閾値(gate) |
action_schema_v0.json |
出力の JSON Schema |
eval/ |
評価の表と、誤差の範囲 |
SHA256SUMS |
各ファイルの sha256(sha256sum -c SHA256SUMS で確認できます) |
推論の仕組み
ほかの言語や環境に移すときは、inference.py と同じ次の手順にしてください。本モデルの評価の数値は、この手順で出したものです。
- 入力:
<s><act>文の token 列<out>。文はtokenizer.modelで分割します(<act>と<out>は tokenizer にある記号です)。 - 文法による制約:
<out>の後を</s>まで greedy に生成します(最大 24 token)。各 step で、schema に合う token だけから最も確率の高いものを選ぶので、出力は必ず schema に合う JSON になります。 - 確信度の gate: 生成した token(
</s>を含む)の、制約をかける前の確率の最小値が 0.86808 未満なら、出力を[]にします。閾値は validation だけで決めました。
評価
INT4、文法による制約、gate 0.86808 での結果です(%)。exact は出力の完全一致、requests exact は動作を求める文の完全一致、false actions は何もしないのが正解の文で動いてしまった割合です。
gate threshold 0.86808
| set | n | exact | requests exact | false actions | critical |
|---|---|---|---|---|---|
| v0 eval (LLM) | 1189 | 93.8 | 87.8 | 0.5 | 0.3 |
| human v1 | 1159 | 99.6 | 91.9 | 0.0 | 0.0 |
| v2/amount_words | 278 | 94.6 | 94.6 | — | 0.0 |
| v2/center_phrasing | 223 | 93.7 | 93.7 | — | 0.0 |
| v2/correction | 44 | 84.1 | 84.1 | — | 0.0 |
| v2/english | 70 | 45.7 | 5.3 | 6.2 | 2.9 |
| v2/fragments | 182 | 100.0 | — | 0.0 | 0.0 |
| v2/long_preface | 247 | 90.3 | 90.3 | — | 0.0 |
| v2/negation_forms | 168 | 100.0 | — | 0.0 | 0.0 |
| v2/numbers | 167 | 93.4 | 85.7 | 1.9 | 1.2 |
| v2/order_words | 296 | 95.3 | 95.3 | — | 0.0 |
| v2/orthography | 282 | 82.6 | 80.8 | 0.0 | 0.0 |
| v2/question_forms | 203 | 90.1 | 90.1 | — | 0.0 |
| v2/unexecutable | 286 | 99.7 | — | 0.3 | 0.3 |
| 評価セット | 書いたもの |
|---|---|
| v0 eval (LLM) | llm-jp-3.1-13b-instruct4(学習データを書いたモデルとは別)。Qwen3 が正解を確かめた |
| human v1 | 人が書いた公開コーパスの文(JESC、Tatoeba、YJ AmbigDialogue、J-CRe3、対話システムライブコンペ 3、MASSIVE)。依頼はルールで確実に判定できる文だけを選び、Qwen3 と答えが一致したものを残した(依頼 62件、依頼でない文 1,097件)。学習には使っていない |
| v2/* | 苦手になりやすい12の型(言い回し、量、数、否定、言い直し、順序、断片、できない依頼、表記、疑問形、前置き、英語)。llm-jp-3.1-13b-instruct4 が書き、Qwen3 が確かめた |
誤差の範囲
数値のぶれには2つの原因があります。
- 評価セットの大きさ: 下の1つ目の表は、公開したモデルについて、評価の文を復元抽出し直して求めた 95% の区間です(2,000回)。人が書いた依頼文は 62件しかないので、区間が広くなります。
- 学習の seed: 同じデータと設定で seed だけを変えて5回学習し、それぞれを同じ方法(INT4、文法、validation で決めた gate)で評価しました。下の2つ目の表は、5回の平均 ± 標準偏差です。
公開したのは seed 0 です。 seed 0 は、ほかの seed を学習する前に実機への搭載と実機での検証(300文で PC と完全一致)を済ませていたモデルです。人が書いた依頼文では、5つの seed の中で最も高い値でした(seed ごとに 91.9 / 75.8 / 83.9 / 90.3 / 83.9%)。そのため、この評価セットでの実力は、5回の平均(約 85%)で見るのが妥当です。LLM が書いた評価セット(v0 eval)の依頼文は 88.1 ± 1.2% で、seed による差は小さいです。
Released model (seed 0): rate and 95% bootstrap interval (%, 2,000 resamples)
| set | exact | requests exact | false actions |
|---|---|---|---|
| human_v1 | 99.6 [99.1, 99.9] | 91.9 [85.5, 98.4] | 0.0 [0.0, 0.0] |
| v0_eval_LLM | 93.8 [92.3, 95.2] | 87.8 [85.1, 90.4] | 0.5 [0.0, 1.2] |
| v2_amount_words | 94.6 [91.7, 97.1] | 94.6 [92.1, 97.1] | — |
| v2_center_phrasing | 93.7 [90.6, 96.9] | 93.7 [90.6, 96.9] | — |
| v2_correction | 84.1 [72.7, 95.5] | 84.1 [72.7, 95.5] | — |
| v2_english | 45.7 [34.3, 57.1] | 5.3 [0.0, 13.2] | 6.2 [0.0, 15.6] |
| v2_fragments | 100.0 [100.0, 100.0] | — | 0.0 [0.0, 0.0] |
| v2_long_preface | 90.3 [86.2, 93.9] | 90.3 [86.6, 93.5] | — |
| v2_negation_forms | 100.0 [100.0, 100.0] | — | 0.0 [0.0, 0.0] |
| v2_numbers | 93.4 [89.2, 97.0] | 85.7 [76.2, 93.7] | 1.9 [0.0, 4.8] |
| v2_order_words | 95.3 [92.6, 97.6] | 95.3 [92.9, 97.6] | — |
| v2_orthography | 82.6 [78.0, 86.9] | 80.8 [75.7, 85.5] | 0.0 [0.0, 0.0] |
| v2_question_forms | 90.1 [86.2, 94.1] | 90.1 [85.7, 94.1] | — |
| v2_unexecutable | 99.7 [99.0, 100.0] | — | 0.3 [0.0, 1.0] |
5 seeds (0–4), mean ± standard deviation of the per-seed rates (%)
| set | exact | requests exact | false actions |
|---|---|---|---|
| human_v1 | 99.2 ± 0.4 | 85.2 ± 6.4 | 0.0 ± 0.0 |
| v0_eval_LLM | 94.0 ± 0.6 | 88.1 ± 1.2 | 0.4 ± 0.2 |
| v2_amount_words | 94.1 ± 0.6 | 94.1 ± 0.6 | — |
| v2_center_phrasing | 92.8 ± 1.8 | 92.8 ± 1.8 | — |
| v2_correction | 85.5 ± 3.4 | 85.5 ± 3.4 | — |
| v2_english | 46.6 ± 1.6 | 5.8 ± 2.9 | 5.0 ± 6.5 |
| v2_fragments | 99.9 ± 0.2 | — | 0.1 ± 0.2 |
| v2_long_preface | 91.3 ± 1.1 | 91.3 ± 1.1 | — |
| v2_negation_forms | 99.9 ± 0.3 | — | 0.1 ± 0.3 |
| v2_numbers | 93.4 ± 0.4 | 84.4 ± 1.3 | 1.2 ± 0.4 |
| v2_order_words | 95.8 ± 0.7 | 95.8 ± 0.7 | — |
| v2_orthography | 83.3 ± 0.7 | 81.5 ± 0.8 | 0.0 ± 0.0 |
| v2_question_forms | 91.9 ± 2.9 | 91.9 ± 2.9 | — |
| v2_unexecutable | 99.7 ± 0.3 | — | 0.3 ± 0.3 |
学習
- 構造: decoder-only の Transformer(d_model 192、7層、GQA 6/2 head、SwiGLU 512、RoPE、RMSNorm、入出力の埋め込みを共有)。最大 128 token。
- 学習: 66,809文(action v0.5.1)、12 epoch、lr 1e-3、1 GPU で約12分。validation の完全一致が最も高い epoch を採用しました。
- 量子化: 2次元の重みを INT4(group 64、scale は fp16)。評価の数値は量子化した後のものです。
学習データ
| 出典 | 件数 | ライセンス |
|---|---|---|
| オープンモデルが書いた合成文(正解を先に決め、Qwen3-30B-A3B-Instruct-2507 が温度 0 で確かめたものだけを残した) | 約 6.1万 | 書いたモデルはすべて Apache-2.0 または MIT |
| Tatoeba の日本語文(何もしない例) | 1,524 | CC BY 2.0 FR |
| JESC(映画・ドラマの字幕、何もしない例) | 3,518 | CC BY-SA 4.0 |
| Amazon MASSIVE(ja-JP、何もしない例) | 1,169 | CC BY 4.0 |
- 合成文を書いたモデル: Qwen/Qwen3-30B-A3B-Instruct-2507、cyberagent/calm3-22b-chat、abeja/ABEJA-Qwen2.5-32b-Japanese-v1.0、cyberagent/Mistral-Nemo-Japanese-Instruct-2408、elyza/ELYZA-Shortcut-1.0-Qwen-32B、ibm-granite/granite-3.3-8b-instruct(以上 Apache-2.0)、sbintuitions/sarashina2.2-3b-instruct-v0.1(MIT)。
- 人が書いた文のうち「何もしない」例の一部は、前の版のモデルが誤って動いた文を集め、Qwen3 が「何もしない」と確かめたものです。
- Tatoeba と JESC は、hash で固定した約2割と評価セットの文を評価用に取り分け、学習には使っていません。評価セットの文と重なる文は学習データから除きました。
- 合成データの最初の版は japanese-data-analyze/JapaneseTinyAgentLM-Action-Synth で公開しています。
限界と用途
- 英語は扱えません。 英語の依頼の正解率は約5%です。
- ひらがなだけの文や、言い直しの文は弱めです(表記 約83%、言い直し 約84%)。
- 学習の乱数(seed)による差があります。 同じデータと設定でも、人が書いた依頼文の正解率は seed によって 75.8〜91.9% と変わりました(上の「誤差の範囲」)。
- 決まった3種類の動作しか選べません。会話や質問への答えはしません。
- 文字の入力を前提にしています。音声認識の誤りへの強さは評価していません。
- 用途: 小型ロボットや玩具で、日本語の短い指示から安全な範囲の動作を選ぶこと。人の安全にかかわる機械の制御、医療、監視などには使わないでください。出力は必ず schema と角度の上限で検証してから動かしてください(firmware はそうしています)。
先行例との関係
マイコンで動く言語モデルや、マイコンで動く tool calling のモデル(英語と欧州の言語)、外付けの NPU で動くスタックチャンの function calling には先行例があります。日本語の発話からロボットの動作呼び出し(JSON)を決める言語モデルを、ESP32-S3 単体(NPU・外部モジュール・ネットワークなし)で動かした公開事例は、2026年10月1日時点の私たちの調査では見つかりませんでした。調べた範囲と先行例の一覧は docs/prior_art.md にあります。
ライセンスと帰属
- 重み: CC BY-SA 4.0。コード: Apache-2.0(
inference.py、firmware/。firmware の第三者のコードはfirmware/licenses/。学習、評価、firmware の source は GitHub)。 - 学習データに次のものを含みます: Tatoeba(CC BY 2.0 FR)、JESC(Pryzant et al., 2018、CC BY-SA 4.0)、Amazon MASSIVE(FitzGerald et al., 2022、CC BY 4.0)。
- 実装、データの生成と検査、学習、評価、firmware は Claude Code(Anthropic)が行いました。学習データと評価データの文章と正解は、上記のオープンモデル、人が書いた公開コーパス、プログラムによるもので、Claude の出力は含みません。
English summary
A 3,148,608-parameter decoder-only Transformer, trained from scratch, that maps short Japanese requests to robot action calls (JSON: look, set_expression, nod; up to two per request) or [] for non-requests, negated requests and requests the robot cannot perform. It runs entirely on an ESP32-S3 (M5Stack Stack-chan, CoreS3) in INT4 with a median latency of about 1.3 s, bit-exact with the PyTorch reference on 300 prompts. Decoding uses a schema grammar plus a confidence gate (0.86808); the reported numbers use both. Japanese only (English requests are about 5% correct). Results vary across training seeds: on the 62 human-written requests the five seeds scored 75.8–91.9% (mean 85.2%); the released seed 0 was deployed and verified on the device before the other seeds were trained, and is the highest of the five on that set. See the error-bar tables above. Weights are CC BY-SA 4.0; training data includes Tatoeba (CC BY 2.0 FR), JESC (CC BY-SA 4.0) and MASSIVE (CC BY 4.0) plus sentences written by Apache-2.0/MIT open models. Built by Claude Code.
Quick start (CPU is enough):
pip install torch numpy sentencepiece safetensors huggingface_hub
hf download ayousanz/JapaneseTinyAgentLM-Action-3M --local-dir JapaneseTinyAgentLM-Action-3M
python JapaneseTinyAgentLM-Action-3M/inference.py 右を向いて
inference.py is a self-contained script (Apache-2.0) that reproduces the evaluated outputs exactly (4,794 of 4,794 evaluation prompts). Training, evaluation and firmware source: GitHub. Try it in the browser: demo (Hugging Face Space), no install; the same C runtime as the device (WebAssembly) with the INT4 weights runs in the page.
- Downloads last month
- 175