This guide explains all included examples, how to run them, and the commands to test each feature.
DALI2 now supports DALI-compatible syntax — the same operators (:>, :<, ~/, </, ?/) and suffixes (E, I, A, N, P) as the original DALI framework. Each agent runs as a separate OS process.
- Running Examples
- 1. Smart Agriculture (
agriculture.pl) - 2. Emergency Response (
emergency.pl) - 3. Feature Showcase (
showcase.pl) - 4. Distributed Emergency (
emergency_sensors.pl+emergency_responders.pl) - API Quick Reference
Docker Compose starts Redis automatically — no separate install needed.
Linux / macOS:
# Default (agriculture example)
docker compose up --build
# Choose agent file
AGENT_FILE=examples/emergency.pl docker compose up --build
# Distributed (two nodes)
docker compose -f docker-compose.distributed.yml up --buildPowerShell (Windows):
# Default (agriculture example)
docker compose up --build
# Choose agent file
$env:AGENT_FILE="examples/emergency.pl"; docker compose up --build
# Distributed (two nodes)
docker compose -f docker-compose.distributed.yml up --buildRedis must be running before starting DALI2 (see Prerequisites in README).
# Step 1: Start Redis (if not already running)
redis-server # local install
# or
docker run -d --name dali2-redis -p 6379:6379 redis:7-alpine # via Docker
# Step 2: Start DALI2
swipl -l src/server.pl -g main -- 8080 examples/agriculture.plrun.bat
After starting, open http://localhost:8080 for the web UI.
You can send events to agents via the Web UI or the REST API.
- Open http://localhost:8080 in your browser
- In the Send Event panel (top-right area):
- To: select the target agent from the dropdown (e.g.
sensor) - Content: type the event term (e.g.
read_temp(85)) - Click Send
- To: select the target agent from the dropdown (e.g.
- Watch the Event Log panel (center) for real-time results
- Click any agent name in the Agents panel (left) to inspect its beliefs, past events, and goals
Tip: The "Send Event" panel uses the
/api/sendendpoint. For direct injection (bypasses normal message routing), use the REST API with/api/inject.
PowerShell (Windows):
# Send to a specific agent
curl.exe -X POST http://localhost:8080/api/send -H "Content-Type: application/json" -d "{""to"":""agent_name"",""content"":""event(args)""}"
# Inject directly into an agent's event queue
curl.exe -X POST http://localhost:8080/api/inject -H "Content-Type: application/json" -d "{""agent"":""agent_name"",""event"":""event(args)""}"Note: On Windows, use
curl.exe(notcurl, which is a PowerShell alias). Use""to escape double quotes inside double-quoted strings.
bash (Linux/Mac):
curl -X POST http://localhost:8080/api/send \
-H "Content-Type: application/json" \
-d '{"to":"agent_name","content":"event(args)"}'File: examples/agriculture.pl — Ported from the original DALI case study (dalia/case_study_smart_agriculture).
A precision agriculture system with 6 agents. Sensors validate readings via internal events (only abnormal readings are forwarded), the crop advisor decides actions (irrigate, reduce water, advisory), and the farmer receives notifications.
| Agent | Role |
|---|---|
soil_sensor |
Receives soil readings, validates via internal events (alert vs normal) |
weather_monitor |
Receives weather data, validates via internal events (risk vs normal) |
crop_advisor |
Analyzes reports with AI, decides: irrigate / reduce_water / advisory |
irrigation_controller |
Activates irrigation or reduces water supply |
farmer_agent |
Receives advisories and status updates |
logger |
Logs all events centrally |
- Internal events — sensors validate readings (soil_alert_check, soil_normal_check, weather_risk_check, weather_normal_check)
- Reactive rules (
Esuffix +:>) — all agents react to incoming events - Belief management — irrigation controller tracks active/reduced state per field
- Multi-agent communication — message chains across 4+ agents
- AI Oracle — crop_advisor uses AI for soil/weather analysis (if API key configured)
- Conditional logic — crop_advisor branches on moisture/pH/temperature thresholds
# Start
swipl -l src/server.pl -g main -- 8080 examples/agriculture.plOpen http://localhost:8080 and use the Send Event panel:
| Step | To | Content | Expected |
|---|---|---|---|
| 1 | soil_sensor |
read_soil(25, 6.5, north_field) |
Low moisture → irrigate north_field |
| 2 | soil_sensor |
read_soil(85, 6.5, south_field) |
High moisture → reduce_water south_field |
| 3 | soil_sensor |
read_soil(50, 6.8, east_field) |
Normal soil → "SOIL NORMAL", no action |
| 4 | weather_monitor |
weather_update(40, 15, sunny) |
Drought risk → irrigate all_fields |
| 5 | weather_monitor |
weather_update(0, 60, clear) |
Frost warning → advisory to farmer |
Click any agent in the Agents panel to inspect beliefs and past events.
# 1. Low moisture (25 < 30) → soil alert → irrigate
curl.exe -X POST http://localhost:8080/api/send -H "Content-Type: application/json" -d "{""to"":""soil_sensor"",""content"":""read_soil(25, 6.5, north_field)""}"Expected flow:
soil_sensor: stores soil_state → internal soil_alert_check fires (25 < 30)
soil_sensor → crop_advisor: soil_report(25, 6.5, north_field)
crop_advisor: low moisture → irrigate
crop_advisor → irrigation_controller: irrigate(north_field)
crop_advisor → farmer_agent: advisory(irrigate, north_field)
irrigation_controller → farmer_agent: status(irrigating, north_field)
# 2. High moisture (85 > 80) → reduce water
curl.exe -X POST http://localhost:8080/api/send -H "Content-Type: application/json" -d "{""to"":""soil_sensor"",""content"":""read_soil(85, 6.5, south_field)""}"Expected: soil alert → crop_advisor sends reduce_water(south_field) to irrigation controller.
# 3. Normal soil (50, 6.8) → no action
curl.exe -X POST http://localhost:8080/api/send -H "Content-Type: application/json" -d "{""to"":""soil_sensor"",""content"":""read_soil(50, 6.8, east_field)""}"Expected: internal soil_normal_check fires — "SOIL NORMAL" logged, no report sent.
# 4. Drought risk (temp > 38) → emergency irrigation
curl.exe -X POST http://localhost:8080/api/send -H "Content-Type: application/json" -d "{""to"":""weather_monitor"",""content"":""weather_update(40, 15, sunny)""}"Expected: weather risk → crop_advisor sends irrigate(all_fields) + advisory(drought_risk).
# 5. Frost warning (temp < 2)
curl.exe -X POST http://localhost:8080/api/send -H "Content-Type: application/json" -d "{""to"":""weather_monitor"",""content"":""weather_update(0, 60, clear)""}"Expected: weather risk → advisory(frost_warning, all_fields) to farmer.
# 6. Check state
curl.exe http://localhost:8080/api/beliefs?agent=irrigation_controller
curl.exe http://localhost:8080/api/beliefs?agent=farmer_agentFile: examples/emergency.pl — Ported from the original DALI emergency example (dalia/example).
A 9-agent emergency response system. The sensor validates alarms via internal events (real vs false alarm). The coordinator uses multi-step coordination with internal events: it waits for equipment from the manager before dispatching the responder. The communicator notifies person agents (mary, john).
| Agent | Role |
|---|---|
sensor |
Detects events, validates alarms via internal events (real vs false) |
coordinator |
Multi-step dispatch: AI analysis, waits for equipment, tracks done |
manager |
Determines equipment (firetruck/bulldozer/respirator) based on type |
evacuator |
Handles evacuation, reports back |
responder |
Responds with equipment, reports back |
communicator |
Notifies civilians (mary, john) |
mary, john |
Person agents — receive evacuation messages |
logger |
Logs all events |
- Internal events — sensor: alarm validation (check_alarm, check_false_alarm); coordinator: dispatch_response (waits for equipment + location), check_done (waits for evacuated + responded)
- Reactive rules (
Esuffix +:>) — full chain from detection to resolution - Belief management — coordinator tracks pending_location, equipment_ready, evacuated, responded
- Multi-step coordination — responder is only dispatched after manager provides equipment
- AI Oracle — coordinator analyzes emergency (if API key configured)
# Start
swipl -l src/server.pl -g main -- 8080 examples/emergency.plOpen http://localhost:8080 and use the Send Event panel:
| Step | To | Content | Expected |
|---|---|---|---|
| 1 | sensor |
sense(fire, building_a) |
Fire emergency → full multi-step response chain |
| 2 | sensor |
sense(wind, park) |
False alarm → "FALSE ALARM: wind at park" |
| 3 | sensor |
sense(earthquake, downtown) |
Earthquake → bulldozer selected, same chain |
Click coordinator in the Agents panel to inspect beliefs (pending_location, equipment_ready, etc.).
# 1. Fire emergency — full multi-step flow
curl.exe -X POST http://localhost:8080/api/send -H "Content-Type: application/json" -d "{""to"":""sensor"",""content"":""sense(fire, building_a)""}"Expected flow:
sensor: stores detected(fire, building_a) → internal check_alarm fires (fire ∈ alarm list)
sensor → coordinator: alarm(fire, building_a)
coordinator → evacuator + communicator + manager (dispatches all three)
manager: fire → firetruck → coordinator: equipped(firetruck)
communicator → mary + john: message(fire, building_a)
evacuator → coordinator: evacuated(building_a)
coordinator internal dispatch_response: location + equipment ready → responder: respond(firetruck, building_a)
responder → coordinator: responded(building_a)
coordinator internal check_done: evacuated + responded → "EMERGENCY RESOLVED"
# 2. False alarm — wind is not in [smoke, fire, earthquake]
curl.exe -X POST http://localhost:8080/api/send -H "Content-Type: application/json" -d "{""to"":""sensor"",""content"":""sense(wind, park)""}"Expected: internal check_false_alarm fires — "FALSE ALARM: wind at park". No alarm sent to coordinator.
# 3. Earthquake (different equipment)
curl.exe -X POST http://localhost:8080/api/send -H "Content-Type: application/json" -d "{""to"":""sensor"",""content"":""sense(earthquake, downtown)""}"Expected: manager selects bulldozer, same multi-step flow as fire.
# 4. Check state
curl.exe http://localhost:8080/api/beliefs?agent=coordinator
curl.exe http://localhost:8080/api/past?agent=coordinatorFile: examples/showcase.pl
Demonstrates all 32 DALI2 features in a single file using DALI syntax (:>, :<, ~/, </, ?/, :~ operators and E/I/A suffixes). This is the comprehensive reference example that covers every rule type, DSL predicate, and advanced feature.
| Agent | Role | Features Demonstrated |
|---|---|---|
thermostat |
Temperature control | Internal events (interval, change, trigger, between), constraints, on_change |
sensor |
Sensor readings | Periodic tasks, present events, learning, blackboard, past lifetime/remember |
coordinator |
Central coordination | Tell/told (priority queue), FIPA messages, multi-events, goals, residue goals, export past rules, proposal sending, AI oracle |
logger |
Semantic logging | Ontology (inline + external file), helpers, condition monitor |
worker |
Task execution | Action proposals (on_proposal), FIPA responses, export past rules, told rules |
| # | Feature | Agent | How to Trigger |
|---|---|---|---|
| 1 | Reactive rules (E + :>) |
all | Send events to agents |
| 2 | Internal event interval | thermostat | Automatic — temp_check fires every 5s (not every cycle) |
| 3 | Internal event change | thermostat | Send update_temp — startup_diagnostic counter resets |
| 4 | Internal event trigger | thermostat | cooling_monitor fires only when mode(cooling) |
| 5 | Internal event between | thermostat | work_hours_check fires in time window |
| 6 | Periodic tasks | sensor | Automatic — heartbeat every 15 seconds |
| 7 | Condition monitors (when) |
logger | Warns when log volume > 10 |
| 8 | Condition-action (:<) |
thermostat | Edge-triggered when cooling mode activates |
| 9 | Present events | sensor | Blackboard data triggers environment observation |
| 10 | Multi-events (, + :>) |
coordinator | Both sensor_data + alert → fires |
| 11 | Constraints | thermostat | Temperature > 50 triggers violation |
| 12 | Goals (achieve) | sensor | Calibration goal keeps trying until achieved |
| 13 | Goals (test) | coordinator | Tests if alerts received |
| 14 | Tell/told filtering | coordinator | Only accepts specific patterns; rejects others |
| 15 | Priority queue | coordinator | Messages sorted by told priority (200→10) |
| 16 | FIPA confirm | coordinator→worker | Inject send_confirm(system_ok) into coordinator |
| 17 | FIPA query_ref | coordinator→worker | Inject query_worker(status(_)) — auto-response |
| 18 | FIPA propose/accept | coordinator→worker | Inject request_analysis(data) |
| 19 | FIPA propose/reject | coordinator→worker | Inject test_reject |
| 20 | FIPA inform | worker→coordinator | Worker sends analysis results |
| 21 | Action proposals (on_proposal) |
worker | Accepts/rejects proposals from coordinator |
| 22 | Past lifetime + remember | sensor | sensor_data expires after 30s, remembered 5min |
| 23 | Export past (~/) |
coordinator | Alert + sensor_data consumed together |
| 24 | Export past NOT done (</) |
coordinator | Fires if backup NOT done |
| 25 | Residue goals | coordinator | Inject start_residue_test then residue_resolved |
| 26 | External ontology file | logger | Loads test_ontology.pl on startup |
| 27 | Inline ontology | logger | log_event matches log_entry via same_as |
| 28 | Learning | sensor | read_temp(85) → learns overheating pattern |
| 29 | Actions (A suffix) |
worker | analyze(Data) action definition |
| 30 | Helpers | logger | count_logs helper |
| 31 | Blackboard | sensor | Writes environment data |
| 32 | AI Oracle | coordinator | Emergency analysis (if API key configured) |
# Start the showcase
swipl -l src/server.pl -g main -- 8080 examples/showcase.plOpen http://localhost:8080 and use the Send Event panel. Steps 1–2 use "Send" (message routing); steps 3–8 require the REST API with /api/inject (direct injection).
| Step | To | Content | Expected |
|---|---|---|---|
| 1 | sensor |
read_temp(85) |
Learning, blackboard, present event, cooling mode, constraint violation |
| 2 | sensor |
read_temp(90) |
Learned pattern warning, multi-event fires |
| 10 | thermostat |
update_temp(20) |
Constraint resolves, mode → idle |
Steps 3–9 inject events directly into the coordinator (FIPA, export past, residue goals). Use curl or the REST API for these — see below.
Automatic behavior on startup:
- thermostat:
temp_checkfires every 5s (interval),startup_diagnosticfires 3 times (change resets on temp change),work_hours_checkfires (between),cooling_monitordoes NOT fire (mode=idle) - sensor: periodic heartbeat, achieve goal sends calibration requests
- coordinator: calibrates sensor, test goal checks for alerts
- logger: loads
test_ontology.pl(external ontology) - After ~4 seconds: sensor calibration achieved
# STEP 1: Send first temperature reading
# Triggers: learning, blackboard, present event, on_change, triggered internal,
# constraint, export past (on_past), change condition reset, priority queue
curl.exe -X POST http://localhost:8080/api/send -H "Content-Type: application/json" -d "{""to"":""sensor"",""content"":""read_temp(85)""}"Expected:
- Sensor reads 85, writes to blackboard, sends
sensor_data(85)to coordinator - Learning: learns overheating pattern
- Present event: blackboard → thermostat gets
update_temp(85) - On_change: "Cooling mode just activated" (edge-triggered, fires once)
- Triggered internal:
cooling_monitorstarts firing (mode=cooling) - Constraint violated: 85 > 50 → emergency sent to coordinator
- Change condition: thermostat's
startup_diagnosticcounter resets (current_temp changed) - Priority queue: coordinator processes
emergency(200)beforesensor_data(30) - Logger receives log_event → ontology matching works
# STEP 2: Send second reading (triggers learned pattern + multi-event + export past)
curl.exe -X POST http://localhost:8080/api/send -H "Content-Type: application/json" -d "{""to"":""sensor"",""content"":""read_temp(90)""}"Expected:
- Learned knowledge: "WARNING: Previously learned overheating pattern!"
- Multi-event:
sensor_data+alertboth in past → fires - Export past (on_past):
alert+sensor_dataconsumed from past memory
# STEP 3: Test FIPA confirm — coordinator sends confirm to worker
curl.exe -X POST http://localhost:8080/api/inject -H "Content-Type: application/json" -d "{""agent"":""coordinator"",""event"":""send_confirm(system_ok)""}"Expected: Worker receives confirm(system_ok) → "Fact confirmed: system_ok" + "FIPA CONFIRM received"
# STEP 4: Test FIPA query_ref — coordinator queries worker's beliefs
curl.exe -X POST http://localhost:8080/api/inject -H "Content-Type: application/json" -d "{""agent"":""coordinator"",""event"":""query_worker(status(_))""}"Expected: Worker auto-responds with inform(query_ref(status(_)), values([status(ready)])) → coordinator logs "FIPA QUERY_REF response"
# STEP 5: Test FIPA proposals — coordinator proposes to worker
curl.exe -X POST http://localhost:8080/api/inject -H "Content-Type: application/json" -d "{""agent"":""coordinator"",""event"":""request_analysis(sample_data)""}"Expected: Worker accepts → executes analyze(sample_data) → sends inform(analysis_result, complete) back → coordinator logs "FIPA PROPOSAL ACCEPTED"
# STEP 6: Test rejected proposal
curl.exe -X POST http://localhost:8080/api/inject -H "Content-Type: application/json" -d "{""agent"":""coordinator"",""event"":""test_reject""}"Expected: Worker rejects impossible_task → coordinator logs "FIPA PROPOSAL REJECTED"
# STEP 7: Test export past not_done
curl.exe -X POST http://localhost:8080/api/inject -H "Content-Type: application/json" -d "{""agent"":""coordinator"",""event"":""critical_data(important_backup)""}"Expected: "EXPORT PAST NOT_DONE: backup NOT done! critical_data(important_backup) needs attention!"
# STEP 8: Test residue goals
curl.exe -X POST http://localhost:8080/api/inject -H "Content-Type: application/json" -d "{""agent"":""coordinator"",""event"":""start_residue_test""}"
# Wait 2 seconds, then inject the resolution:
curl.exe -X POST http://localhost:8080/api/inject -H "Content-Type: application/json" -d "{""agent"":""coordinator"",""event"":""residue_resolved""}"Expected: "Goal queued as residue: has_past(residue_resolved)" → then "Residue goal achieved" after injection
# STEP 9: Test tell/told filtering — send an unaccepted message
curl.exe -X POST http://localhost:8080/api/send -H "Content-Type: application/json" -d "{""to"":""coordinator"",""content"":""unknown_message(test)""}"Expected: Message rejected by told rule (coordinator only accepts defined patterns)
# STEP 10: Lower temperature — constraint resolves, change condition resets diagnostic
curl.exe -X POST http://localhost:8080/api/send -H "Content-Type: application/json" -d "{""to"":""thermostat"",""content"":""update_temp(20)""}"Expected: Temperature drops to 20, constraint no longer violated, mode goes to idle, startup_diagnostic counter resets (change detected)
# STEP 11: Check all state via APIs
curl.exe http://localhost:8080/api/agents
curl.exe http://localhost:8080/api/beliefs?agent=thermostat
curl.exe http://localhost:8080/api/beliefs?agent=coordinator
curl.exe http://localhost:8080/api/beliefs?agent=worker
curl.exe http://localhost:8080/api/past?agent=coordinator
curl.exe http://localhost:8080/api/learned?agent=sensor
curl.exe http://localhost:8080/api/goals?agent=sensor
curl.exe http://localhost:8080/api/blackboardFiles: examples/emergency_sensors.pl + examples/emergency_responders.pl
Two separate DALI2 nodes communicating via a shared Redis instance (star topology).
| Agent | Role |
|---|---|
sensor |
Detects emergencies |
logger |
Logs events |
| Agent | Role |
|---|---|
coordinator |
Dispatches responders |
evacuator |
Handles evacuation |
responder |
First response |
communicator |
Public notification |
With Docker Compose (recommended):
docker compose -f docker-compose.distributed.yml up --buildThis starts a shared Redis, sensors on port 8081, and responders on port 8082.
Manually (three terminals — Redis + two nodes):
# Terminal 1 — Start Redis
docker run -d --name dali2-redis -p 6379:6379 redis:7-alpine
# Terminal 2 — Sensor node on port 8080
swipl -l src/server.pl -g main -- 8080 examples/emergency_sensors.pl --name sensors
# Terminal 3 — Responder node on port 8081
swipl -l src/server.pl -g main -- 8081 examples/emergency_responders.pl --name respondersBoth nodes connect to localhost:6379 automatically. No peer registration needed.
Test via Web UI:
Open http://localhost:8081 (sensors node) and use the Send Event panel:
| To | Content | Expected |
|---|---|---|
sensor |
detect(fire, building_a) |
Alarm crosses to node 2 → full response chain |
Open http://localhost:8082 (responders node) to see coordinator, evacuator, and responder activity.
Test via REST API:
# Send emergency to sensor on node 1 (port 8081)
curl.exe -X POST http://localhost:8081/api/send -H "Content-Type: application/json" -d "{""to"":""sensor"",""content"":""detect(fire, building_a)""}"Expected: sensor on node 1 sends alarm to coordinator on node 2 via Redis. Coordinator dispatches to evacuator, responder, communicator (all on node 2). Logger messages go back to node 1 via Redis.
# Send to a specific agent
curl.exe -X POST http://localhost:8080/api/send -H "Content-Type: application/json" -d "{""to"":""AGENT"",""content"":""EVENT(ARGS)""}"
# Inject directly into an agent's event queue
curl.exe -X POST http://localhost:8080/api/inject -H "Content-Type: application/json" -d "{""agent"":""AGENT"",""event"":""EVENT(ARGS)""}"# List all agents
curl.exe http://localhost:8080/api/agents
# Agent beliefs
curl.exe http://localhost:8080/api/beliefs?agent=AGENT
# Past events
curl.exe http://localhost:8080/api/past?agent=AGENT
# Learned patterns
curl.exe http://localhost:8080/api/learned?agent=AGENT
# Goal statuses
curl.exe http://localhost:8080/api/goals?agent=AGENT
# Blackboard contents
curl.exe http://localhost:8080/api/blackboard
# System logs
curl.exe http://localhost:8080/api/logs?agent=AGENT
# Cluster view (all agents across all nodes)
curl.exe http://localhost:8080/api/cluster# Start/stop individual agents
curl.exe -X POST http://localhost:8080/api/start -H "Content-Type: application/json" -d "{""agent"":""AGENT""}"
curl.exe -X POST http://localhost:8080/api/stop -H "Content-Type: application/json" -d "{""agent"":""AGENT""}"
# Reload agent file
curl.exe -X POST http://localhost:8080/api/reload -H "Content-Type: application/json" -d "{""file"":""examples/showcase.pl""}"