llmOS — natural language, real world action. Offline AI firmware for the ESP32-S3.

llmOS — intent compiler

A 2.85M-parameter language model that runs on an ESP32-S3 microcontroller and turns a spoken sentence into a hardware automation rule.

Say "if the sensor on pin 1 goes high, turn off pin 2" and the board does that from then on. No code, no recompiling, no internet, no cloud account. The model runs on the chip itself, from a microSD card.

Firmware + installer github.com/AliAkrami1375/llmos-firmware
Try it in the browser the Space
Languages Persian (primary) and English
Hardware ESP32-S3 with 8 MB PSRAM

فارسی — پایین صفحه


What it does

You say What it produces
if pin 1 goes high, turn off pin 2 ON GPIO1=1 DO GPIO2=0
toggle pin 5 every 30 seconds ON EVERY 30s DO GPIO5=T
when the light sensor on pin 4 drops below 800, turn on pin 7 ON ADC4<800 DO GPIO7=1
at 07:00 turn on pin 9 for 30 seconds ON AT 07:00 DO GPIO9=1; WAIT 30s; GPIO9=0
turn on pin 16 now DO GPIO16=1
connect to wifi MyHome with password 1a2b3c CALL wifi.connect "MyHome" "1a2b3c"
read pin 5 CALL pin.read "5"
hello, how are you? ERR — correctly refused

The output is not free text. It is a small formal language a deterministic rule engine executes; the model's only job is the translation.

Persian is the primary language

The training distribution is mostly Persian, and the gap is not cosmetic. Two capabilities are reliable in Persian and are not reached in English:

Persian Result
پایه ۷ رو بذار اتاق علی NAME GPIO7 "اتاق علی"
اتاق علی رو روشن کن DO GPIO7=1
ساعت ۳ بامداد بازر پایه ۹ را ۳۰ ثانیه بزن ON AT 03:00 DO GPIO9=1; WAIT 30s; GPIO9=0

Pin naming is Persian-only in practice, and so are clock times written as words. In English, give the time in figures — at 07:00, not "at 7am", which is read unreliably. Everything in the first table works in both languages.


Why invalid output is impossible, not merely unlikely

This is the part worth reading even if you never touch an ESP32.

A 2.85M-parameter model left to itself produces nonsense some of the time. This one cannot, because the grammar is enforced during decoding rather than learned.

The rule language is compiled into a prefix automaton. At every generation step each candidate token is tested: would accepting this token leave the output a valid prefix of some complete rule? Tokens that fail are removed before the sample. The end-of-output token only becomes reachable once the text is a complete, valid rule.

The same test enforces numeric range, not just syntax. GPIO99 is unreachable because 99 is not an allowed pin; ADC4<9999 is unreachable because the ADC is 12-bit. Pins that carry flash, PSRAM or the SD card are excluded from the automaton entirely, so no sentence — however phrased — can produce a rule that unmounts the card the model is running from.

Quoted values are copied, not spelled

A model this small cannot spell a random WiFi password, and it never has to.

While the decoder is inside a quoted argument it stops asking "is this valid syntax?" and starts asking "does this keep the argument a verbatim substring of what the user typed?" Only tokens preserving that property survive. The model never spells anything — it decides where to start copying and where to stop, which is what attention is naturally good at.

A password full of strange characters therefore comes out byte-exact by construction rather than by luck. Range checking deliberately does not look inside quotes: a network named GPIO99 ADC9999 is a legal SSID and is never read as a pin.


Accuracy

Measured on 5,000 held-out requests, through the same C engine that runs on the board, against these exact quantized weights:

exact match      :  99.92%     4 wrong out of 5,000
syntactically ok : 100.00%
parses + in range: 100.00%
mean confidence  :   1.000

Broken down by intent class, because one number hides which half of the behaviour is weak:

Intent n Accuracy
digital rule 1,496 99.9%
immediate action 530 100%
analog threshold 828 100%
periodic 611 100%
scheduled (at 3am) 580 99.8%
pin naming 527 99.8%
tool call 317 99.7%
out of domain 111 100%

The last row matters as much as the first: the model recognises when a request is not an automation rule and says so, instead of forcing it into one.

The two 100% rows above are by construction, not by training — see the previous section.


Architecture

Type decoder-only transformer, Llama-style
Parameters 2,853,312
Dimension / layers / heads 192 / 6 / 4 (4 KV heads)
Feed-forward SwiGLU, hidden 512
Positional RoPE
Normalisation RMSNorm
Context 128 tokens
Vocabulary 1,024
Quantization int8 symmetric, group size 32
On disk 3,142 KB
Working memory on device 1,166 KB (KV cache + buffers)

Tokenizer

Byte-level BPE, 1,024 tokens, with atomic digits — no digit-digit merge exists in the vocabulary.

That last detail was not cosmetic. An earlier tokenizer split 500 as ['500'], 800 as ['8','00'] and 4095 as ['40','9','5']. The model had to learn a different spelling strategy per number, and scheduled rules sat at 89.2% accuracy. Making digits atomic moved that class to 99.8% with no architecture change.

Tokenization is greedy longest-match, implemented identically in C and Python and verified byte-for-byte over 740,000 strings — because a tokenizer that disagrees with itself across the training/inference boundary produces a model that looks trained and behaves broken.


Files

File What it is
model.llm the quantized weights, custom binary format
tokenizer.bin the vocabulary — must match the model
tools.json the shipped HTTP tool definitions, as reference
LICENSE MIT

model.llm and tokenizer.bin are a matched pair. Replacing one without the other gives confident nonsense rather than an error. Always take both from the same revision.

This is not a transformers checkpoint

model.llm is a flat int8 format read by a C99 inference engine with no dependencies, designed to be mmap-friendly on a microcontroller. There is no config.json, no safetensors, and AutoModel.from_pretrained will not work.

To run it you either flash the firmware, or use the Space, which runs that same C engine on a CPU.


How to run it

On hardware

git clone https://github.com/AliAkrami1375/llmos-firmware.git
cd llmos-firmware
./install.sh                 # Linux, macOS
powershell -ExecutionPolicy Bypass -File install.ps1     # Windows

The installer finds the board, flashes it and copies the model to a FAT32 microSD card. You need an ESP32-S3 with 8 MB PSRAM (N8R8 or N16R8) and a USB data cable.

In a browser

huggingface.co/spaces/DibaAi/llmOS — the same C engine, the same weights, on a CPU.


The rule language

rule    := "ON " trigger " DO " actions
         | "DO " actions
         | "CALL " tool arg*
         | "NAME GPIO" pin " " label
         | "ERR"

trigger := "GPIO" pin "=" ("0"|"1")
         | "ADC" pin ("<"|">") value
         | "EVERY " n unit
         | "AT " hh ":" mm

action  := "GPIO" pin "=" ("0"|"1"|"T")     T = toggle
         | "WAIT " n unit
         | "CALL " tool arg*

unit    := "ms" | "s" | "m" | "h"

Allowed pins: 1–9, 14–18, 21, 38–44, 47, 48. ADC: 1–9, threshold 0–4095. Durations up to 24 hours. Everything else is unreachable in the automaton, not merely rejected afterwards.


Tool calls and notifications

Rules control pins. A tool is something the device does that is not a pin rule — joining a WiFi network, reading an API, and most usefully telling you when something happened.

CALL is an action like any other, so a rule may contain one. These are valid, and the parser and engine both have tests for them:

ON GPIO7=1 DO CALL notify.send "door opened"
ON ADC3>2400 DO GPIO5=1; CALL notify.send "too hot"
ON AT 07:00 DO CALL notify.send "good morning"

The model will not write those lines for you. It only ever emits the five tool names it was trained on — wifi.connect, wifi.scan, wifi.status, sys.info, pin.read. notify.send is a user tool, defined in a file on the SD card long after training, so no sentence produces it. Ask "if pin 7 goes high, notify me" and you get a plain pin rule or ERR.

To get a rule like the ones above onto the device, write the line into rules.txt on the card; it is parsed and stored at boot.

notify.send

Every other tool answers a question you asked. notify.send is the one that speaks without being asked — the one that tells you the door opened at 3 a.m. while you were asleep.

It posts to a webhook on your own network, so it needs no internet, no TLS, no API key and no third party that can be blocked or shut down:

{
  "name": "notify.send",
  "kind": "http",
  "parameters": [{ "name": "text", "required": true }],
  "phrases": ["پیام بده", "خبرم کن", "اطلاع بده", "notify me"],
  "execute": {
    "method": "POST",
    "url": "http://192.168.1.50:8123/api/webhook/llmos",
    "body": "{\"message\":\"{text}\"}",
    "timeout_ms": 5000
  },
  "reply": { "fa": "پیام فرستاده شد.", "on_error": "پیام فرستاده نشد." }
}

Point the URL at Home Assistant, Node-RED, or a five-line Python receiver. Until you do, it correctly reports that the message was not sent — nothing is listening at the placeholder address.

There are exactly two ways to reach it:

What you want How
A message now say "خبرم کن …" / "notify me …" — the phrase router matches it, and whatever follows the phrase becomes the message
A message when something happens write ON GPIO7=1 DO CALL notify.send "door opened" into rules.txt on the card

A rule containing a CALL fires on the edge. One message on the low→high transition, not one every 20 ms tick while the pin stays high; a 30 ms debounce means a bouncing contact still counts as one event. That is the difference between a useful notification and three thousand messages.

A failed call does not stop the rule. The remaining actions still run — a dead webhook must not stop the light from switching on.

The two routes into a tool

your sentence
     │
     ├─► phrase router ──── matched a tool's trigger phrase? ──► run it now
     │   (typo-tolerant)              │ no
     │                                ▼
     └───────────────────────────► the model ──► ON … DO … │ CALL … │ ERR

rules.txt on the card ────────────► parsed at boot ──► stored as a rule

The router normalises Persian text (ی/ي, ک/ك, zero-width non-joiners, Persian, Arabic and Latin digits, diacritics) and tolerates typos. It runs the tool immediately rather than creating a rule.

Adding a tool to tools.json teaches the model nothing. The model was trained on a fixed set of tool names; a tool added afterwards is reached by its own trigger phrases, not by the model writing CALL yourtool. This is not a bug — it is what a closed vocabulary means. It is also exactly why every tool carries its own phrases, and why the third route above exists.

Seven HTTP tools ship in tools.json — notifications, weather, air quality, sunrise/sunset, time, earthquakes, connectivity. Every URL was actually called and every extraction path checked against the real response.

Five more are compiled into the firmware and cannot be deletedwifi.scan, wifi.connect, wifi.status, sys.info, pin.read. Not because of a flag in the file, but because they are not in the file at all; they are rebuilt from a table at every boot. Pull the SD card out and wifi.connect is still there.

Full tool and notification documentation (Persian)


Pin names

You can call pin 7 "Ali's room" and then say "turn on Ali's room".

The model never learns your names. They are created long after training. A name is substituted with its pin number before the sentence reaches the model and substituted back afterwards, entirely outside it. That is why a name you invented one minute ago works immediately, and why 32 names cost the model nothing.


Training

Trained from scratch on a synthetic Persian/English corpus of automation requests paired with their target rules, on a single T4. No pretrained checkpoint; the vocabulary and the task are both narrow enough that pretraining on general text would have spent capacity on nothing useful.

The loss is applied only to the rule span, not to the input sentence, so all capacity goes to the translation rather than to modelling how people write.

What the numbers actually taught us

Overall accuracy said almost nothing useful. Breaking it down by intent class exposed every real bug. Five separate apparent "model weaknesses" turned out to be data or tooling defects:

  • 10% of quoted values were corrupted by the typo-noise augmentation, teaching the model to copy text that was no longer in the input;
  • 4.1% of the test set asked for durations above the 24-hour cap, which the grammar makes unreachable — the model was being scored on impossible targets;
  • the decoder's own range check quietly made ADC thresholds 410–999 unreachable, so it emitted 55 when asked for 550. This one accounted for 123 of the last 127 failures and had misled three training runs before it was found;
  • the output token limit was below what 15% of targets needed, showing up as a 12-point accuracy drop while the training loss reported 0.0001;
  • the tokenizer split numbers inconsistently, as described above.

The dataset generator now refuses to emit an example it can prove is impossible, and the test suite sweeps every legal value — all 4,096 ADC thresholds and every duration — to assert each one is reachable.


Limits, stated plainly

  • Narrow by design. Pins, sensors, timers and the listed tools. "Make the house warm" is refused, and that is intended behaviour, not a gap.
  • No compound conditions. if pin 1 AND pin 2 is not supported yet.
  • 128-token context. Long, rambling sentences are truncated.
  • Scheduling needs the network once. There is no RTC, so nothing time-based fires until the clock has synchronised over SNTP.
  • 24 rules and 32 pin names at a time on the device.
  • Persian first. English works, but the training distribution is mostly Persian and English coverage is correspondingly thinner.
  • No authentication on the device's HTTP API. Anyone on the network can control it.

What is not in this repository

The training code, the dataset generator, the model architecture and the firmware sources are not published here. This repository is the release: weights, tokenizer and documentation. Ready-to-flash binaries and the installer are at github.com/AliAkrami1375/llmos-firmware.

License

MIT.




llmOS — فرم‌ور هوشمند آفلاین برای ESP32-S3

‏llmOS — کامپایلر نیت

یک مدل زبانی ۲٫۸۵ میلیون پارامتری که روی میکروکنترلر ESP32-S3 اجرا می‌شود و یک جمله‌ی معمولی را به قانون اتوماسیون سخت‌افزاری تبدیل می‌کند.

می‌گویید «اگر سنسور پایه ۱ روشن شد پایه ۲ را خاموش کن» و برد از همان لحظه این کار را می‌کند. بدون کد، بدون کامپایل مجدد، بدون اینترنت و بدون هیچ سرویس ابری. مدل روی خود چیپ اجرا می‌شود، از روی کارت microSD.

فرم‌ور و نصب‌کننده github.com/AliAkrami1375/llmos-firmware
تست در مرورگر اسپیس
زبان فارسی (اصلی) و انگلیسی
سخت‌افزار ESP32-S3 با ۸ مگابایت PSRAM

چه کار می‌کند

می‌گویید تولید می‌کند
اگر سنسور پایه ۱ روشن شد پایه ۲ را خاموش کن ON GPIO1=1 DO GPIO2=0
هر ۳۰ ثانیه پایه ۵ را چشمک بزن ON EVERY 30s DO GPIO5=T
وقتی سنسور نور پایه ۴ کمتر از ۸۰۰ شد چراغ پایه ۷ را روشن کن ON ADC4<800 DO GPIO7=1
ساعت ۳ صبح بازر پایه ۹ را ۳۰ ثانیه بزن ON AT 03:00 DO GPIO9=1; WAIT 30s; GPIO9=0
پایه ۷ را بگذار «اتاق علی» NAME GPIO7 "اتاق علی"
اتاق علی را روشن کن DO GPIO7=1
به وای‌فای MyHome با رمز 1a2b3c وصل شو CALL wifi.connect "MyHome" "1a2b3c"
شبکه‌های وای‌فای رو اسکن کن CALL wifi.scan
سلام خوبی؟ ERR — درست رد شد

خروجی متن آزاد نیست. یک زبان صوری کوچک است که یک موتور قوانین قطعی اجرایش می‌کند؛ تنها کار مدل ترجمه است.


چرا خروجی نامعتبر ناممکن است، نه فقط بعید

این بخش حتی اگر هیچ‌وقت به ESP32 دست نزنید ارزش خواندن دارد.

مدل ۲٫۸۵ میلیون پارامتری اگر رها شود گاهی چرت می‌گوید. این یکی نمی‌تواند، چون گرامر هنگام دیکود کردن اعمال می‌شود نه اینکه یاد گرفته شده باشد.

زبان قانون به یک اتوماتای پیشوندی کامپایل می‌شود. در هر گام تولید، هر توکن کاندید تست می‌شود: اگر این توکن را بپذیریم، خروجی هنوز پیشوند معتبری از یک قانون کامل می‌ماند؟ توکن‌هایی که رد شوند قبل از نمونه‌برداری حذف می‌شوند. توکن پایان تنها وقتی قابل دسترس می‌شود که متن یک قانون کامل و معتبر باشد.

همین تست بازه‌ی عددی را هم اعمال می‌کند، نه فقط نحو را. GPIO99 غیرقابل‌دسترس است چون ۹۹ پایه‌ی مجاز نیست؛ ADC4<9999 غیرقابل‌دسترس است چون ADC دوازده‌بیتی است. پایه‌هایی که فلش، PSRAM یا کارت حافظه رویشان است کلاً از اتوماتا بیرون‌اند، پس هیچ جمله‌ای — با هر عبارتی — نمی‌تواند قانونی بسازد که کارتی را که مدل از رویش اجرا می‌شود unmount کند.

مقدار نقل‌قولی کپی می‌شود، هجی نمی‌شود

مدلی به این کوچکی نمی‌تواند رمز تصادفی وای‌فای را هجی کند، و هیچ‌وقت لازم نیست بکند.

وقتی دیکودر داخل یک آرگومان نقل‌قولی است، از پرسیدن «آیا این نحو معتبری است؟» به پرسیدن «آیا این آرگومان را زیررشته‌ای از متن کاربر نگه می‌دارد؟» سوئیچ می‌کند. فقط توکن‌هایی که این شرط را حفظ کنند زنده می‌مانند. مدل هیچ‌وقت چیزی را هجی نمی‌کند — فقط تصمیم می‌گیرد از کجا شروع به کپی کند و کجا بایستد، و این دقیقاً کاری است که مکانیزم توجه به‌طور طبیعی خوب انجام می‌دهد.

رمزی پر از کاراکتر عجیب بنا به ساختار بایت‌به‌بایت درست درمی‌آید، نه به شانس. بررسی بازه‌ها عمداً داخل نقل‌قول نگاه نمی‌کند: شبکه‌ای به نام GPIO99 ADC9999 یک SSID مجاز است و هیچ‌وقت به‌عنوان پایه خوانده نمی‌شود.


دقت

روی ۵۰۰۰ درخواست کنارگذاشته‌شده، از مسیر همان موتور C که روی برد اجرا می‌شود و با همین وزن‌های کوانتیزه:

exact match      :  99.92%     4 wrong out of 5,000
syntactically ok : 100.00%
parses + in range: 100.00%
mean confidence  :   1.000

و به تفکیک نوع نیت، چون یک عدد کلی پنهان می‌کند کدام نیمه‌ی رفتار ضعیف است:

نیت تعداد دقت
قانون دیجیتال ۱۴۹۶ ۹۹٫۹٪
اقدام فوری ۵۳۰ ۱۰۰٪
آستانه‌ی آنالوگ ۸۲۸ ۱۰۰٪
تناوبی ۶۱۱ ۱۰۰٪
زمان‌بندی‌شده (ساعت ۳) ۵۸۰ ۹۹٫۸٪
نام‌گذاری پایه ۵۲۷ ۹۹٫۸٪
فراخوانی ابزار ۳۱۷ ۹۹٫۷٪
خارج از دامنه ۱۱۱ ۱۰۰٪

سطر آخر به اندازه‌ی سطر اول مهم است: مدل تشخیص می‌دهد که یک درخواست قانون اتوماسیون نیست و همین را می‌گوید، به‌جای اینکه به زور در قالبی بچپاندش.

دو سطر ۱۰۰٪ بالا بنا به ساختار است، نه بنا به آموزش — بخش قبل را ببینید.


معماری

نوع ترنسفورمر فقط-دیکودر، سبک Llama
پارامتر ۲٬۸۵۳٬۳۱۲
بعد / لایه / هد ۱۹۲ / ۶ / ۴ (۴ هد KV)
فیدفوروارد SwiGLU، پنهان ۵۱۲
موقعیتی RoPE
نرمال‌سازی RMSNorm
کانتکست ۱۲۸ توکن
واژگان ۱۰۲۴
کوانتیزاسیون int8 متقارن، گروه ۳۲
روی دیسک ۳۱۴۲ کیلوبایت
حافظه‌ی کاری روی دستگاه ۱۱۶۶ کیلوبایت (KV cache و بافرها)

توکنایزر

‏BPE سطح-بایت با ۱۰۲۴ توکن و ارقام اتمی — هیچ ادغام رقم-رقمی در واژگان وجود ندارد.

این جزئیات تزئینی نبود. توکنایزر قبلی 500 را ['500']، 800 را ['8','00'] و 4095 را ['40','9','5'] می‌شکست. مدل مجبور بود برای هر عدد راهبرد هجی متفاوتی یاد بگیرد و قوانین زمان‌بندی‌شده روی ۸۹٫۲٪ گیر کرده بودند. اتمی کردن ارقام همان کلاس را بدون هیچ تغییری در معماری به ۹۹٫۸٪ رساند.

توکنایزیشن حریصانه‌ی طولانی‌ترین-تطبیق است، در C و پایتون یکسان پیاده شده و روی ۷۴۰٬۰۰۰ رشته بایت‌به‌بایت بررسی شده — چون توکنایزری که در مرز آموزش/استنتاج با خودش اختلاف داشته باشد مدلی می‌سازد که آموزش‌دیده به نظر می‌رسد و خراب رفتار می‌کند.


فایل‌ها

فایل چیست
model.llm وزن‌های کوانتیزه، فرمت باینری اختصاصی
tokenizer.bin واژگان — باید با مدل جفت باشد
tools.json تعریف ابزارهای HTTP همراه، به‌عنوان مرجع
LICENSE MIT

model.llm و tokenizer.bin یک جفت هماهنگ‌اند. عوض کردن یکی بدون دیگری به‌جای خطا، جواب اشتباه با اطمینان کامل می‌دهد. همیشه هر دو را از یک نسخه بردارید.

این یک چک‌پوینت transformers نیست

model.llm یک فرمت تخت int8 است که یک موتور استنتاج C99 بدون هیچ وابستگی می‌خواندش، و طوری طراحی شده که روی میکروکنترلر mmap-پذیر باشد. نه config.json دارد، نه safetensors، و AutoModel.from_pretrained کار نمی‌کند.

برای اجرا یا فرم‌ور را فلش کنید، یا از اسپیس استفاده کنید که همان موتور C را روی CPU اجرا می‌کند.


چطور اجرایش کنیم

روی سخت‌افزار

git clone https://github.com/AliAkrami1375/llmos-firmware.git
cd llmos-firmware
./install.sh                 # لینوکس، مک
powershell -ExecutionPolicy Bypass -File install.ps1     # ویندوز

نصب‌کننده برد را پیدا می‌کند، فلش می‌کند و مدل را روی کارت microSD با فرمت FAT32 کپی می‌کند. به یک ESP32-S3 با ۸ مگابایت PSRAM (مدل N8R8 یا N16R8) و یک کابل USB دیتا نیاز دارید.

در مرورگر

huggingface.co/spaces/DibaAi/llmOS — همان موتور C، همان وزن‌ها، روی CPU.


زبان قانون‌ها

rule    := "ON " trigger " DO " actions
         | "DO " actions
         | "CALL " tool arg*
         | "NAME GPIO" pin " " label
         | "ERR"

trigger := "GPIO" pin "=" ("0"|"1")
         | "ADC" pin ("<"|">") value
         | "EVERY " n unit
         | "AT " hh ":" mm

action  := "GPIO" pin "=" ("0"|"1"|"T")     T = toggle
         | "WAIT " n unit
         | "CALL " tool arg*

unit    := "ms" | "s" | "m" | "h"

پایه‌های مجاز: ۱ تا ۹، ۱۴ تا ۱۸، ۲۱، ۳۸ تا ۴۴، ۴۷، ۴۸. ‏ADC: ۱ تا ۹، آستانه ۰ تا ۴۰۹۵. مدت‌ها تا ۲۴ ساعت. هر چیز دیگری در اتوماتا غیرقابل‌دسترس است، نه اینکه بعداً رد شود.


فراخوانی ابزار و اطلاع‌رسانی

قانون‌ها پایه‌ها را کنترل می‌کنند. ابزار کاری است که دستگاه انجام می‌دهد ولی قانون GPIO نیست — وصل شدن به وای‌فای، خواندن یک API، و از همه کاربردی‌تر خبر دادن وقتی اتفاقی افتاد.

CALL هم یک اکشن است مثل بقیه، پس قانون می‌تواند شاملش شود. این‌ها معتبرند و پارسر و موتور هر دو برایشان تست دارند:

ON GPIO7=1 DO CALL notify.send "درب باز شد"
ON ADC3>2400 DO GPIO5=1; CALL notify.send "خیلی گرم شد"
ON AT 07:00 DO CALL notify.send "صبح بخیر"

ولی مدل این خط‌ها را برایتان نمی‌نویسد. مدل فقط پنج نام ابزاری را تولید می‌کند که رویشان آموزش دیده — wifi.connect، wifi.scan، wifi.status، sys.info، pin.read. ‏notify.send یک ابزار کاربر است که خیلی بعد از آموزش در فایلی روی کارت حافظه تعریف می‌شود، پس هیچ جمله‌ای تولیدش نمی‌کند. اگر بگویید «اگر پایه ۷ روشن شد بهم خبر بده»، یک قانون پایه‌ی ساده یا ERR می‌گیرید.

برای رساندن چنین قانونی به دستگاه، خط را در rules.txt روی کارت بنویسید؛ هنگام بوت پارس و ذخیره می‌شود.

notify.send

بقیه‌ی ابزارها به سؤالی که پرسیده‌اید جواب می‌دهند. notify.send تنها ابزاری است که بدون پرسیده شدن حرف می‌زند — همان که می‌گوید ساعت ۳ بامداد که خواب بودید درب باز شد.

روی یک وب‌هوک در شبکه‌ی خودتان POST می‌کند، پس نه اینترنت می‌خواهد، نه TLS، نه کلید API و نه هیچ طرف سومی که بشود فیلترش کرد یا تعطیل شود:

{
  "name": "notify.send",
  "kind": "http",
  "parameters": [{ "name": "text", "required": true }],
  "phrases": ["پیام بده", "خبرم کن", "اطلاع بده", "notify me"],
  "execute": {
    "method": "POST",
    "url": "http://192.168.1.50:8123/api/webhook/llmos",
    "body": "{\"message\":\"{text}\"}",
    "timeout_ms": 5000
  },
  "reply": { "fa": "پیام فرستاده شد.", "on_error": "پیام فرستاده نشد." }
}

آدرس را به Home Assistant، Node-RED یا یک گیرنده‌ی پنج‌خطی پایتون وصل کنید. تا وقتی این کار را نکرده‌اید درست می‌گوید که پیام فرستاده نشد — چیزی روی آدرس نمونه گوش نمی‌دهد.

دقیقاً دو راه برای رسیدن به آن هست:

چه می‌خواهید چطور
همین حالا پیام بگویید «خبرم کن …» — مسیریاب عبارت تطبیقش می‌دهد و هر چه بعد از عبارت بیاید متن پیام می‌شود
وقتی اتفاقی افتاد پیام خط ON GPIO7=1 DO CALL notify.send "درب باز شد" را در rules.txt روی کارت بنویسید

قانونی که CALL داشته باشد روی لبه شلیک می‌کند. یک پیام روی گذار صفر→یک، نه یک پیام در هر تیک ۲۰ میلی‌ثانیه‌ای تا وقتی پایه روشن مانده؛ و ۳۰ میلی‌ثانیه debounce باعث می‌شود کنتاکت لرزان هم یک رویداد شمرده شود. تفاوت یک اطلاع‌رسانی مفید با سه هزار پیام همین است.

شکست یک فراخوانی، قانون را متوقف نمی‌کند. اکشن‌های بعدی همچنان اجرا می‌شوند — یک وب‌هوک مرده نباید جلوی روشن شدن چراغ را بگیرد.

دو مسیری که به ابزار می‌رسند

جمله‌ی شما
     │
     ├─► مسیریاب عبارت ──── با عبارت تریگر ابزاری خواند؟ ──► همین حالا اجرا
     │   (با تحمل غلط املایی)          │ نه
     │                                 ▼
     └───────────────────────────► مدل ──► ON … DO … │ CALL … │ ERR

rules.txt روی کارت ───────────────► پارس در بوت ──► ذخیره به‌عنوان قانون

مسیریاب متن فارسی را نرمال می‌کند (ی/ي، ک/ك، نیم‌فاصله، ارقام فارسی و عربی و لاتین، اعراب) و غلط تایپی را تحمل می‌کند. ابزار را بلافاصله اجرا می‌کند، نه اینکه قانون بسازد.

اضافه کردن ابزار به tools.json به مدل چیزی یاد نمی‌دهد. مدل روی مجموعه‌ی ثابتی از نام ابزارها آموزش دیده؛ ابزاری که بعداً اضافه شود از راه عبارت‌های تریگر خودش صدا زده می‌شود، نه با اینکه مدل CALL yourtool بنویسد. این باگ نیست — معنای «واژگان بسته» همین است. و دقیقاً به همین دلیل هر ابزار عبارت‌های خودش را حمل می‌کند.

هفت ابزار HTTP در tools.json آماده‌اند — اطلاع‌رسانی، آب و هوا، کیفیت هوا، طلوع و غروب، ساعت، زلزله، وضعیت اینترنت. هر آدرس واقعاً صدا زده شده و هر مسیر استخراج با پاسخ واقعی بررسی شده.

پنج ابزار دیگر داخل فرم‌ور کامپایل شده‌اند و حذف نمی‌شوندwifi.scan، wifi.connect، wifi.status، sys.info، pin.read. نه به خاطر فلگی در فایل، بلکه چون اصلاً داخل فایل نیستند؛ در هر بوت از یک جدول ساخته می‌شوند. کارت را در بیاورید، wifi.connect سر جایش است.

مستندات کامل ابزار و اطلاع‌رسانی (انگلیسی)


نام‌گذاری پایه‌ها

می‌توانید پایه ۷ را «اتاق علی» بنامید و بعد بگویید «اتاق علی را روشن کن».

مدل هیچ‌وقت اسم‌های شما را یاد نمی‌گیرد. این اسم‌ها خیلی بعد از آموزش ساخته می‌شوند. اسم قبل از رسیدن جمله به مدل با شماره‌ی پایه جایگزین می‌شود و بعد از آن برمی‌گردد، کاملاً بیرون از مدل. به همین دلیل اسمی که یک دقیقه پیش ساخته‌اید بلافاصله کار می‌کند، و ۳۲ اسم هیچ هزینه‌ای به مدل تحمیل نمی‌کند.


آموزش

از صفر روی یک پیکره‌ی مصنوعی فارسی/انگلیسی از درخواست‌های اتوماسیون همراه با قانون هدفشان، روی یک T4 آموزش دیده. بدون چک‌پوینت از پیش آموزش‌دیده؛ واژگان و خود مسئله آن‌قدر باریک‌اند که پیش‌آموزش روی متن عمومی ظرفیت را صرف چیز بی‌فایده می‌کرد.

خطا فقط روی بازه‌ی قانون اعمال می‌شود نه روی جمله‌ی ورودی، پس تمام ظرفیت صرف ترجمه می‌شود نه مدل کردن اینکه آدم‌ها چطور می‌نویسند.

چیزی که اعداد واقعاً یادمان دادند

دقت کلی تقریباً هیچ چیز مفیدی نمی‌گفت. تفکیک آن به کلاس‌های نیت، هر باگ واقعی را بیرون کشید. پنج «ضعف مدل» ظاهری در واقع نقص داده یا ابزار بودند:

  • ۱۰٪ از مقادیر نقل‌قولی توسط نویز غلط تایپی خراب می‌شدند و به مدل یاد می‌دادند متنی را کپی کند که دیگر در ورودی نبود؛
  • ۴٫۱٪ از مجموعه‌ی تست مدت‌هایی بالاتر از سقف ۲۴ ساعت می‌خواستند که گرامر غیرقابل‌دسترسشان می‌کند — مدل داشت روی هدف‌های ناممکن نمره می‌گرفت؛
  • بررسی بازه‌ی خود دیکودر بی‌سروصدا آستانه‌های ADC از ۴۱۰ تا ۹۹۹ را غیرقابل‌دسترس کرده بود، پس وقتی ۵۵۰ خواسته می‌شد 55 تولید می‌کرد. همین یکی مسئول ۱۲۳ تا از ۱۲۷ شکست باقی‌مانده بود و سه اجرای آموزش را گمراه کرده بود؛
  • سقف توکن خروجی کمتر از چیزی بود که ۱۵٪ از هدف‌ها لازم داشتند، و به شکل ۱۲ واحد افت دقت ظاهر می‌شد در حالی که خطای آموزش ۰٫۰۰۰۱ گزارش می‌کرد؛
  • توکنایزر اعداد را ناهماهنگ می‌شکست، همان‌طور که بالا توضیح داده شد.

حالا مولد دیتاست از تولید نمونه‌ای که بتواند ناممکن بودنش را ثابت کند خودداری می‌کند، و مجموعه‌ی تست همه‌ی مقادیر مجاز را جارو می‌کند — هر ۴۰۹۶ آستانه‌ی ADC و هر مدت — تا مطمئن شود همه قابل دسترس‌اند.


محدودیت‌ها، صادقانه

  • دامنه عمداً باریک است. پایه‌ها، سنسورها، تایمرها و ابزارهای فهرست‌شده. «خونه رو گرم کن» رد می‌شود و این رفتار درست است نه کمبود.
  • بدون شرط ترکیبی. اگر پایه ۱ و پایه ۲ هنوز پشتیبانی نمی‌شود.
  • کانتکست ۱۲۸ توکن. جمله‌های خیلی طولانی بریده می‌شوند.
  • زمان‌بندی یک بار به شبکه نیاز دارد. ‏RTC روی برد نیست، پس تا وقتی ساعت با SNTP همگام نشده هیچ قانون زمانی شلیک نمی‌کند.
  • ۲۴ قانون و ۳۲ نام پایه در هر لحظه روی دستگاه.
  • اول فارسی. انگلیسی کار می‌کند، ولی توزیع آموزش عمدتاً فارسی است و پوشش انگلیسی به همان نسبت نازک‌تر.
  • بدون احراز هویت روی API دستگاه. هر کسی روی شبکه می‌تواند کنترلش کند.

چه چیزی در این مخزن نیست

کد آموزش، مولد دیتاست، معماری مدل و سورس فرم‌ور اینجا منتشر نشده‌اند. این مخزن ریلیز است: وزن‌ها، توکنایزر و مستندات. باینری‌های آماده‌ی فلش و نصب‌کننده در github.com/AliAkrami1375/llmos-firmware هستند.

مجوز

‏MIT

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Space using DibaAi/llmOS 1