
Artificial Domain Intelligence (ADI) – Embedded Intelligence for Appliances
Artificial Domain Intelligence is an approach to making physical machines
以下正文同步自 Hacker News 最新,版权归原站所有,已转换为易读排版。
Artificial Domain Intelligence is an approach to making physical machines intelligent and capable within the thing they are built to do (their domain). A machine measures what it just produced, determines what was wrong with it, corrects itself, and accepts spoken instruction from the person using it. It acts on the machine directly instead of offering advice for the person to carry out.
It also holds the knowledge of its domain, so a person can ask it questions and receive instruction without consulting anything else. What model it is, why a result came out as it did, what a particular setting changes. It answers from what the manufacturer supplied and from the declaration it already reasons over.
Everything is designed to run on the device. No internet connection, no account, no companion application.
The intelligence is bounded to one domain, meaning the scope of what the machine exists to do. An espresso machine reasons about extraction while a greenhouse controller reasons about growth. That boundary is what keeps the system small enough to run on appliance hardware, specific enough to be useful, and narrow enough that its behaviour can be tested and certified.
This repository contains the protocol that defines the integration, and a working implementation of both sides of it. The first domain is espresso.
Three steps, in order. The first needs a Rust toolchain and a C compiler and nothing else: no account, no API key, no model download. The build fetches crates; after that nothing here reaches a network.
git clone https://github.com/Tem-Degu/adi.git cd adi/sdk
cargo build --release cd c && make example && cd ..
cargo run -p adi-cli -- run --level d0
Type cycle:
This step needs a model, because turning words into a request is the one thing no amount of declaration can do. One command says what to obtain:
cargo run -p adi-cli -- models check --config models/models.example-local.yaml
obtain the files it names, then
cargo run -p adi-cli -- run --config models/models.example-local.yaml
Now say what you want in plain words, or ask it something. A request becomes a goal, the engine works out which mechanism serves it and what value to send, and the appliance checks that value again before acting. Step 3 shows a full exchange.
With a transcriber configured as well, listen <file.wav> takes a recording instead of typing: 16 bit mono at the rate the transcriber asks for. That path has been run end to end against the washing machine firmware, and sdk/MODELS.md shows the transcript it produced and how to make a recording of your own.
Where a network is available, every seam can be hosted instead, which needs no model files at all:
export OPENAI_API_KEY=... cargo run -p adi-cli -- run --config models/models.example-hosted.yaml
sdk/MODELS.md also covers which models were measured and what they scored. The short version: use a 4B class model or better, because the smaller one we measured guessed where it should have asked.
Two further appliances are included as firmware for an emulated microcontroller, on a profile that ships no knowledge of its own, so everything they reason about comes from a file their manufacturer wrote. This needs arm-none-eabi-gcc and qemu-system-arm, both free, and no board.
cd c && make shower && cd ..
prints the pty to use below
qemu-system-arm -M lm3s6965evb -nographic -serial pty -kernel c/build/arm/shower.elf
cargo run -p adi-cli -- run --serial /dev/pts/N
--declaration examples/declarations/shower.yaml
--skill examples/declarations/shower.skill.yaml
--knowledge examples/declarations/shower.knowledge.yaml
--config models/models.example-local.yaml
make it a bit warmer understood Adjust target x.rainline.water_temperature = now +2 C measured 38 C sent x.rainline.mixer.set_temperature celsius = 40 C accepted by the appliance
The machines included
Machine What it is How it runs
Example EX-900 espresso machine The first domain, with a profile behind it that can tell two faults apart A host process, driven over stdin and stdout
Dryline DH-20 food dehydrator One mechanism and one outcome, on base/v1 Firmware on an emulated Cortex-M3, over a UART
Rainline SX-2 shower mixer Two mechanisms and two outcomes, on base/v1 Firmware on an emulated Cortex-M3, over a UART
Northwash NW-8 washing machine A door that must be latched, on base/v1 Firmware on an emulated Cortex-M3, over two UARTs
The last three exist to answer one question: whether a manufacturer can describe a machine nobody wrote a profile for and have it understood, corrected and actuated. Their declarations are in sdk/examples/declarations/ and their firmware in sdk/c/dehydrator/, sdk/c/shower/ and sdk/c/washer/.
The washing machine is the one to run to see a machine refuse. Its second serial port stands in for the door latch, so opening the door is something you can do by hand:
cd c && make washer && cd ..
prints two ptys: the first is the link, the second is the door
qemu-system-arm -M lm3s6965evb -nographic -serial pty -serial pty
-kernel c/build/arm/washer.elf
Send o to the second port to open the door and c to shut it. With it open, every instruction is refused by the appliance itself, and an operation already running is stopped:
An appliance declares what it can physically do. For an espresso machine that might be: set the grind, dose by mass, set the brew temperature, start extraction and stop at a target weight, dispense an additive, eject the puck. Each declared action states its range, its unit, its resolution, and the conditions under which it may be used.
The intelligence acts only by invoking actions from that declaration. It cannot perform an action the manufacturer never built, cannot send a value outside a declared range, and cannot breach a limit the appliance enforces in its own firmware. This is the Safe Action Protocol, and its consequence is that safety is a property of the declared action surface, not of a model behaving correctly.
A language model turns speech into a structured request and phrases the explanation afterwards. Three autonomy levels determine how much of the decision it makes; see Autonomy.
A request is not the only thing a person can say. Asked a question, the machine answers from the declaration it already reasons over and from whatever the manufacturer wrote down.
A tool is one action the machine can perform: setting the grind, dispensing milk, ejecting the puck.
Three things make one available: the mechanism, a handler that runs it, and one entry in a data file. The first two are ordinary appliance engineering and the third is data rather than code:
-
Build the mechanism. A motor, a valve, a heater, driven from a pin.
-
Decide how the firmware knows it finished. A limit switch, an encoder count, a measured mass. An expired timer is not evidence of completion, and the engine treats a reported completion as fact, so an assumed one leaves it reasoning about a state the machine is not in.
-
Write a handler. One function, called when a named instruction arrives. It starts the mechanism and returns; it does not block. Long operations report completion later.
adip_action_t adip_on_invoke(const adip_invocation_t *inv, adip_reason_t *reason, void *user) { adip_num_t setting;
if (adip_tool_index(ctx.dev, inv->tool) == TOOL_GRIND_SET && adip_arg_num(inv, "setting", &setting)) { start_grind_move(setting); return ADIP_ACTION_RUNNING; } return ADIP_ACTION_DONE; }
By the time this is called the library has confirmed the tool exists, every argument is present, in range and in the declared unit, no interlock blocks it, the preconditions hold, and the sequence constraints are satisfied.
- Declare it. One entry in a data file, stating the name, the parameters, their limits, the description, and the conditions under which the tool may run.
tools:
- id: "adi.espresso.grind.set_setting" parameters: setting: type: integer unit: step min: 0 max: 60 resolution: 1 preconditions: ["state == idle"] duration_ms_max: 6000 reversible: true
What the mechanism is, for a model choosing between two tools that
serve the same outcome. Read by no engine logic.
description: > Stepper-driven burr collar. Settles within two seconds, and retains roughly 0.4 g of grounds from the previous setting.
adi generate-c compiles that into constant tables, so a limit exists in one place and nothing can drift from it.
A tool from the registry needs no description, because the profile supplies one: adi.espresso.grind.set_setting means the same on every machine that declares it. Write your own only where your mechanism differs from the usual, as above, and it takes precedence.
A tool of your own must carry one, and adi validate refuses a declaration that omits it. Your identifier means nothing to anyone else, so a model offered x.acme.aux.actuate_3 with a magnitude and a confidence cannot tell what it does.
A description may influence which tool a model picks and never what value is sent, so one that is wrong or out of date cannot reach the actuator. Keep the numbers out of it: how far the mechanism moves an outcome belongs in step 6.
- Declare the outcome it moves, if the profile has no name for it. The engine reasons about outcomes, not tools: a spoken request names one and a correction targets one, so a tool that moves nothing named is unreachable. Skip this where the profile already names it, since a second grinder still moves shot_time and a second name for it would leave the engine treating one outcome as two.
variables: x.acme.tray_state: unit: none description: > How clean the drip tray is, from 0 for soiled to 1 for clean.
- State how far your tool moves it. This is what lets the engine size a correction instead of guessing at one, and it is what makes the tool reachable at all: without this entry the engine will never select it and no spoken request can arrive at it. The tool stays callable from your own panel or app, because the appliance accepts invocations the engine did not originate.
responses:
- tool: "adi.espresso.grind.set_setting" parameter: "setting" variable: "shot_time" per_unit: -1.4 # seconds of shot time per step, signed unit: s confidence: 0.75
- Optionally, say what you know. Free text the machine may answer from.
notes:
- id: "x.acme.note.basket" text: > A 58 mm basket, taking 18 to 20 g. A stepped basket will not seat correctly.
A note may inform an answer and may never determine an action. Every note reaches the model on every utterance, so keeping the total within the context of the smallest model you ship is the author's responsibility.
Steps 5 and 7 are one file, a knowledge extension, which is separate from the skill in step 6. Load it with --knowledge:
adi validate my-machine.yaml --skill my-machine.skill.yaml --knowledge my-machine.knowledge.yaml adi run --declaration my-machine.yaml --skill my-machine.skill.yaml --knowledge my-machine.knowledge.yaml
An extension that fails validation is rejected whole and the machine keeps working on base knowledge, which ADI-P 6.5 requires: a bad extension degrades a machine to standard behaviour and never stops it working.
The manufacturer implements no reasoning and no models. A tool they implement works the day they declare it, without a release from this project.
Three platform functions complete the integration: a millisecond clock, a byte write, and a function that drives the machine to a safe state.
The first domain is espresso. The engine does not know that, and never did: a domain is supplied at runtime and every crate except the profile itself is checked against a rule that forbids domain vocabulary.
A machine says which domain it uses in its declaration. Saying base/v1 selects a profile that ships no knowledge of its own, so everything the machine reasons about comes from one file you write: your outcomes with their units and meanings, what a good result looks like, and what the machine should be ab
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。