Robot safety and replay
With AEL, the Agent Engineering Language, safety stays deterministic: AI proposes, validators check freshness and limits, and the watchdog and emergency stop stay in charge. This tutorial writes the validator of a robot agent, runs the agent against a simulator first, and records and replays a run to find where its behaviour changed.
Step 1: add the robotics packages
Optional robotics packages provide typed units, coordinate frames, timestamps and sensor and actuator schemas. Add the ones you use:
ael pack add robotics/transforms
ael pack add robotics/simulation
ael pack add robotics/middleware/ros2
Robot agents connect over ROS 2 and DDS, including the DDS-XRCE standard for constrained hardware. Each package version states the message types, settings and transports it covers.
Step 2: let the model propose
A model-driven node plans through typed proposal tools, and its output is a proposal, never a command:
# agents/robot.agent.ael
agent robot(input: Observation) -> RobotStatus {
config {
nodes: [planner = plan_motion, guard = check_command];
edges: [plan_to_guard(planner, guard)];
starts: [planner];
completion: guard.result;
}
}
plan_motion is the model-driven node; check_command is deterministic and calls no model.
Step 3: write the validator
The guard checks every proposal before any command is admitted. Its rules are plain code, not prompt text:
struct Proposal {
speed_mm_s: u32,
age_ms: u32,
sequence: u32,
}
enum Verdict {
Admit(u32),
TooFast(u32),
Stale(u32),
OutOfOrder(u32),
}
fn validate(proposal: Proposal, last_sequence: u32, max_speed: u32, max_age: u32) -> Verdict {
if proposal.sequence <= last_sequence {
return Verdict::OutOfOrder(proposal.sequence);
}
if proposal.age_ms > max_age {
return Verdict::Stale(proposal.age_ms);
}
if proposal.speed_mm_s > max_speed {
return Verdict::TooFast(proposal.speed_mm_s);
}
return Verdict::Admit(proposal.speed_mm_s);
}
- A rejected proposal leads to the local behaviour you declared, such as holding position or a safe state.
- Units and frames are part of the type in the robotics packages, so a pose in one frame does not combine with a pose in another without an explicit conversion.
- The watchdog and the emergency stop do not depend on the model, the network or a parent agent, and no model output can disable a stop path or widen what an actuator may do.
Step 4: run against a simulator first
The same code runs against virtual hardware, a simulator or a real robot. Run the agent against the simulator until its behaviour is what you want. A simulation or a replay never gains access to a physical actuator by accident: moving real hardware needs its own permission.
Step 5: record and replay
You record control and orchestration runs and replay them to find the first point where behaviour diverged:
- Record a run: its inputs, timing, scheduling choices, random seeds and the responses of models, tools and services, with the identity of the build, prompts and configuration.
- Change the prompt or the code.
- Replay the recording. The recorded responses are fed back in, and no live call or real side effect happens.
- Read the divergence report: the first event that differs, the source and prompt involved, and the expected and observed typed values.
An exact replay needs the same build and target; replaying across a changed build is a separate, best-effort mode, and says so. Reporting, logs and replay describes recordings in full.
Honest limits
- None of this is a safety certification. Testing on physical hardware needs your own test rig, interlocks, operator control and a verified stop path, and regulated uses need their own qualification.
- A cancelled run, a lost connection or a model's reply is never taken as proof that a robot stopped.
- Native code alone does not make a control loop hard real-time, and replay does not make a live model's output reproducible.