> For the complete documentation index, see [llms.txt](https://docs.kiloiot.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kiloiot.io/kilo-docs-de/kilo-iot-server/rules-engine/cel-reference.md).

# CEL-Referenz

CEL-Syntaxreferenz für Kilo IoT-Regeln — Common Expression Language, verwendet in Gateways, Skripten, Alarmen.

Die Rules Engine verwendet [CEL (Common Expression Language)](https://cel.dev) für Ausdrücke im visuellen Workflow-Editor — Gateway-Bedingungen, Berechnungen in Script Tasks, Alarmmeldungen, Enrichment-Lookups und Input-/Output-Definitionen. CEL ist eine schnelle, sichere Ausdruckssprache, die ursprünglich von Google für die Auswertung von Bedingungen in Sicherheitsrichtlinien und Infrastruktursystemen entwickelt wurde. Die vollständige Sprachspezifikation ist verfügbar auf [GitHub](https://github.com/google/cel-spec).

CEL ist keine Programmiersprache für allgemeine Zwecke. Sie wertet Ausdrücke aus und gibt Ergebnisse zurück. Sie kann nicht auf das Dateisystem zugreifen, keine Netzwerkanfragen senden, keine Schleifen erzeugen oder externen Zustand verändern. Dadurch ist es sicher, benutzerdefinierte Ausdrücke auszuführen, ohne die Plattform oder andere Regeln zu gefährden.

Die meisten Kilo-Regeln verwenden nur wenige kurze Ausdrücke. Die Workflow-Struktur bleibt visuell und BPMN-basiert; CEL ist die Präzisionsschicht, die diese Workflows in echten Produktionsszenarien nützlich macht.

***

## Verfügbare Daten

Jeder Ausdruck in der Rules Engine hat über das `vars` Objekt Zugriff auf Prozessvariablen. Welche Daten verfügbar sind, hängt davon ab, an welcher Stelle in der Regel der Ausdruck ausgeführt wird.

Felder in `vars` können mit **Punktnotation** oder **Klammernotation**:

```cel
vars.temperature        // Punktnotation — funktioniert für einfache Bezeichner
vars["sensor_id"]       // Klammernotation — funktioniert für jeden Namen
vars["my-sensor"]       // Klammernotation erforderlich — Bindestrich im Namen
```

Punktnotation ist für die meisten Feldnamen praktisch. Klammernotation ist erforderlich, wenn ein Feldname Bindestriche, Leerzeichen oder andere Sonderzeichen enthält oder wenn der Feldname dynamisch aus einem anderen Ausdruck berechnet wird.

### Verfügbar nach dem Start-Ereignis

**Was das Start-Ereignis bereitstellt, hängt von seiner Startquelle ab**ab, also überprüfe, welche Art von Regel du schreibst, bevor du nach einer Variable greifst.

**Startquelle: Sensorwert**

| Variable         | Typ                            | Beschreibung                                                                                                                                                         |
| ---------------- | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `vars.value`     | Je nach Sensor unterschiedlich | Der Sensorwert, der die Regel ausgelöst hat. Kann eine Zahl (Temperatur, Luftfeuchtigkeit), ein String (Türstatus) oder ein boolescher Wert (Bewegung erkannt) sein. |
| `vars.sensor_id` | string                         | Die eindeutige Kennung des Sensors, der die Regel ausgelöst hat.                                                                                                     |
| `vars.timestamp` | Zeitstempel                    | Die Zeit, zu der der Sensorwert aufgezeichnet wurde.                                                                                                                 |

**Startquelle: Trigger-Bedingung**

| Variable            | Typ    | Beschreibung                                                                                                                                                                                                  |
| ------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `vars.device_name`  | string | Der Name des Geräts, das die Bedingung erfüllt hat. Bei einer [Trigger](/kilo-docs-de/kilo-iot-server/rules-engine/triggers.md) Regel, die mehrere Geräte überwacht, identifiziert dies das auslösende Gerät. |
| `vars.subject_kind` | string | Der Typ der überwachten Ressource. Dies ist derzeit `Gerät`.                                                                                                                                                  |
| `vars.subject_id`   | string | Die Kennung des überwachten Geräts, das die Bedingung erfüllt hat.                                                                                                                                            |
| `vars.sensor_id`    | string | Die Sensor-ID, mit der der Lauf und ein möglicher Alarm dem überwachten Gerät zugeordnet werden.                                                                                                              |
| `vars.detector_id`  | string | Die Kennung des Triggers, der die Regel gestartet hat.                                                                                                                                                        |
| `vars.timestamp`    | int    | Die Zeit des Triggersignals als Unix-Sekunden.                                                                                                                                                                |

> **`vars.value` existiert nicht in einer durch einen Trigger gestarteten Regel.** Ein Trigger meldet einen Zustandsübergang, anstatt der Regel ein normalisiertes Sensorevent zu übergeben. Das gilt sowohl für sofortige als auch für Trigger mit Dauer. Ein Ausdruck, der sich auf `vars.value` bezieht, schlägt bei jedem Triggersignal fehl — prüfe dies zuerst, wenn du eine bestehende Regel von **Sensorwert**.

### Verfügbar nach Script Tasks

Wenn ein Script Task eine Map zurückgibt, wird jeder Schlüssel in `vars`. Zum Beispiel können nach einem Script Task mit dem Ausdruck `{"level": "kritisch", "delta": 15.2}`nachgelagerte Knoten auf `vars.level` und `vars.delta`.

Auch Ausgaben von Script Task, Gateway, Set Alarm, Enrichment und Start Event können benannte Werte in `vars` nachdem dieser Knoten ausgeführt wurde.

### Verfügbar nach Enrichment

Nachdem ein Enrichment-Knoten Daten unter einem Variablennamen speichert (z. B., `outdoor_temp`), sind die angereicherten Daten als verschachteltes Objekt zugänglich:

| Variable                         | Typ             | Beschreibung                                        |
| -------------------------------- | --------------- | --------------------------------------------------- |
| `vars.outdoor_temp.value`        | Unterschiedlich | Der aktuellste Messwert des angereicherten Sensors. |
| `vars.outdoor_temp.sensor_id`    | string          | Die Kennung des angereicherten Sensors.             |
| `vars.outdoor_temp.type`         | string          | Der Datentyp des angereicherten Sensors.            |
| `vars.outdoor_temp.timestamp_ms` | int             | Zeitstempel des Messwerts in Millisekunden.         |

### Benutzerdefinierte Variablen

Input- und Output-Ausdrücke an Start-Ereignissen, exklusiven Gateways, Script Tasks, Set-Alarm-Knoten und Enrichment-Knoten erlauben es dir, einem berechneten Wert einen Namen zu geben. Entscheide anhand der Strecke, die der Wert zurücklegen muss:

* **Ausgaben** fügen den Namen dem Workflow-Kontext hinzu. Er ist zugänglich als `vars.<name>` in jedem Knoten, der danach ausgeführt wird.
* **Eingaben** bleiben auf dem Knoten, auf dem du sie definiert hast. Sie werden berechnet, bevor dieser Knoten ausgeführt wird, und dessen eigene Ausdrücke können sie verwenden — die Eingaben eines Gateways stehen den Flussbedingungen dieses Gateways zur Verfügung — aber spätere Knoten können sie nicht lesen.

Wenn ein auf einem Knoten berechneter Wert weiter in der Regel benötigt wird, mache ihn zu einer Ausgabe. Siehe [Knotenreferenz](/kilo-docs-de/kilo-iot-server/rules-engine/node-reference.md#do-i-need-the-input-and-output-parameters) für ein Beispiel.

***

## Typsystem

CEL unterstützt die folgenden Typen. Jeder Wert in einem Ausdruck wird auf einen dieser Typen aufgelöst.

| Typ           | Beschreibung                      | Beispiel                          |
| ------------- | --------------------------------- | --------------------------------- |
| `bool`        | Boolescher Wert: wahr oder falsch | `true`, `vars.value > 30`         |
| `int`         | Vorzeichenbehaftete Ganzzahl      | `42`, `-7`                        |
| `uint`        | Vorzeichenlose Ganzzahl           | `42u`                             |
| `double`      | Gleitkommazahl                    | `30.5`, `-0.7`                    |
| `string`      | Text                              | `"kritisch"`, `"Temperaturalarm"` |
| `bytes`       | Bytesequenz                       | `b"\x00\xff"`                     |
| `list`        | Geordnete Sammlung                | `[1, 2, 3]`, `["a", "b"]`         |
| `map`         | Schlüssel-Wert-Paare              | `{"level": "hoch", "count": 5}`   |
| `null_type`   | Nullwert                          | `null`                            |
| `Zeitstempel` | Ein Zeitpunkt                     | `vars.timestamp`                  |
| `duration`    | Ein Zeitraum                      | `duration("5m")`                  |

### Typkonvertierungen

Verwende integrierte Konvertierungsfunktionen, um zwischen Typen zu konvertieren:

| Funktion   | Beschreibung                            | Beispiel             |
| ---------- | --------------------------------------- | -------------------- |
| `int()`    | In Ganzzahl konvertieren                | `int(vars.value)`    |
| `uint()`   | In vorzeichenlose Ganzzahl konvertieren | `uint(42)`           |
| `double()` | In Gleitkommazahl konvertieren          | `double(vars.value)` |
| `string()` | In String konvertieren                  | `string(vars.value)` |
| `type()`   | Gibt den Typ eines Werts zurück         | `type(vars.value)`   |

Die String-Konvertierung ist besonders wichtig beim Erstellen von Alarm-Motivationstexten, da CEL für die Verkettung eine explizite Umwandlung von Zahlen in Strings verlangt.

***

## Operatoren

### Vergleichsoperatoren

| Operator | Bedeutung           | Beispiel                   |
| -------- | ------------------- | -------------------------- |
| `==`     | Gleich              | `vars.value == 0`          |
| `!=`     | Ungleich            | `vars.status != "offline"` |
| `>`      | Größer als          | `vars.value > 30.0`        |
| `>=`     | Größer oder gleich  | `vars.value >= 100`        |
| `<`      | Kleiner als         | `vars.value < 5`           |
| `<=`     | Kleiner oder gleich | `vars.value <= 25.0`       |

### Logische Operatoren

| Operator | Bedeutung       | Beispiel                               |
| -------- | --------------- | -------------------------------------- |
| `&&`     | Logisches UND   | `vars.value > 30 && vars.value < 50`   |
| `\|\|`   | Logisches ODER  | `vars.value < 0 \|\| vars.value > 100` |
| `!`      | Logisches NICHT | `!has(vars.humidity)`                  |

### Arithmetische Operatoren

| Operator | Bedeutung      | Beispiel                               |
| -------- | -------------- | -------------------------------------- |
| `+`      | Addition       | `vars.value + 10`                      |
| `-`      | Subtraktion    | `vars.value - vars.outdoor_temp.value` |
| `*`      | Multiplikation | `vars.value * 1.8 + 32`                |
| `/`      | Division       | `vars.value / 100.0`                   |
| `%`      | Modulo         | `vars.value % 10`                      |

### Ternärer Operator

Der ternäre Operator `? :` gibt basierend auf einer Bedingung einen von zwei Werten zurück:

```cel
vars.value > 80 ? "kritisch" : "normal"
```

Ternäre Ausdrücke können für eine mehrstufige Klassifizierung verschachtelt werden:

```cel
vars.value > 80 ? "kritisch" : vars.value > 50 ? "Warnung" : "normal"
```

### String-Operatoren und -Funktionen

| Operation      | Syntax         | Beispiel                                |
| -------------- | -------------- | --------------------------------------- |
| Verkettung     | `+`            | `"Temperatur: " + string(vars.value)`   |
| Länge          | `size()`       | `vars.name.size() > 0`                  |
| Enthält        | `contains()`   | `vars.status.contains("Fehler")`        |
| Beginnt mit    | `startsWith()` | `vars.zone.startsWith("warehouse")`     |
| Endet mit      | `endsWith()`   | `vars.device_id.endsWith("-prod")`      |
| Regex-Abgleich | `matches()`    | `vars.device_id.matches("^WH-[0-9]+$")` |

### Sammlungsfunktionen

| Funktion                   | Beschreibung                                      | Beispiel                                      |
| -------------------------- | ------------------------------------------------- | --------------------------------------------- |
| `x in Liste`               | Mitgliedschaftsprüfung                            | `vars.zone in ["A", "B", "C"]`                |
| `Schlüssel in Map`         | Schlüssel existiert in der Map                    | `"humidity" in vars`                          |
| `list.exists(x, expr)`     | Wahr, wenn irgendein Element den Ausdruck erfüllt | `[10, 25, 40].exists(t, t > 30)`              |
| `list.all(x, expr)`        | Wahr, wenn alle Elemente den Ausdruck erfüllen    | `[10, 25, 40].all(t, t > 0)`                  |
| `list.filter(x, expr)`     | Gibt Elemente zurück, die den Ausdruck erfüllen   | `[10, 25, 40].filter(t, t > 20)` → `[25, 40]` |
| `list.map(x, expr)`        | Transformiert jedes Element                       | `[1, 2, 3].map(x, x * 10)` → `[10, 20, 30]`   |
| `list.exists_one(x, expr)` | Wahr, wenn genau ein Element erfüllt              | `[10, 25, 40].exists_one(t, t > 30)` → `true` |

### Zwischenvariablen mit cel.bind

Verwenden Sie `cel.bind()` um innerhalb eines Ausdrucks eine temporäre Variable zu definieren und redundante Berechnungen zu vermeiden:

```cel
cel.bind(delta, vars.value - vars.baseline.value,
  {"delta": delta, "severity": delta > 20 ? "kritisch" : delta > 10 ? "Warnung" : "normal"}
)
```

Das erste Argument benennt die Variable, das zweite berechnet ihren Wert, und das dritte ist der Ausdruck, der sie verwendet.

### Existenzprüfung

| Funktion | Beschreibung                               | Beispiel             |
| -------- | ------------------------------------------ | -------------------- |
| `has()`  | Gibt zurück `true` wenn das Feld existiert | `has(vars.humidity)` |

Verwenden Sie `has()` bevor du auf eine Variable zugreifst, die möglicherweise nicht existiert. Nach einem Enrichment-Knoten mit einem Boundary Error Event sind die angereicherten Daten beispielsweise nur verfügbar, wenn das Enrichment erfolgreich war.

***

## Häufige Ausdrucksmuster

### Schwellenwertprüfung

Das einfachste und häufigste Muster. Wird in Gateway-Bedingungen verwendet, um anhand eines einzelnen Werts zu verzweigen.

```cel
vars.value > 30.0
```

Gibt zurück `true` wenn der Sensorwert 30 überschreitet. Funktioniert in Bedingungen eines exklusiven Gateways, um den Ablauf zu routen.

### Bereichsprüfung

Prüft, ob ein Messwert innerhalb eines zulässigen Bereichs liegt.

```cel
vars.value >= 20.0 && vars.value <= 25.0
```

Nützlich für HVAC-Konformität, die Überwachung der Kühlkette oder jedes Szenario, in dem sowohl obere als auch untere Grenzwerte wichtig sind.

### Mehrfachbedingungsprüfung

Kombiniere Prüfungen über mehrere Variablen hinweg. Dieses Muster tritt häufig nach Enrichment auf, wenn Daten von mehr als einem Sensor verfügbar sind.

```cel
vars.value > 30.0 && has(vars.humidity) && vars.humidity < 20
```

Immer verwenden `has()` bevor du auf Variablen verweist, die aus optionalen Quellen stammen (Enrichment, vorherige Script Tasks mit bedingten Ausgaben).

### Schweregradklassifizierung (Script Task)

Gib eine Map aus einem Script Task zurück, um einen Messwert in Kategorien einzuteilen. Jeder Schlüssel wird zu einer eigenen Prozessvariable.

```cel
{"level": vars.value > 80 ? "kritisch" : vars.value > 50 ? "Warnung" : "normal"}
```

Nach diesem Script Task `vars.level` steht für Gateway-Bedingungen oder Alarmmeldungen zur Verfügung.

### Dynamische Alarmmeldung (Set Alarm)

Erstelle einen menschenlesbaren String, der Live-Sensordaten enthält. Das Feld für die Motivationsmeldung in einem Set-Alarm-Knoten verwendet dieses Muster.

```cel
"Temperatur " + string(vars.value) + " Grad überschritten den Grenzwert von " + string(vars.threshold)
```

Denke daran, `string()` zur Konvertierung numerischer Werte zu verwenden — CEL wandelt Zahlen bei der Verkettung nicht implizit in Strings um.

### Prüfung angereicherter Daten (nach Enrichment)

Verweise auf einen von einem Enrichment-Knoten abgerufenen Wert. Der in der Enrichment-Konfiguration verwendete Variablenname wird zum Schlüssel unter `vars`.

```cel
vars.outdoor_temp.value > 35.0
```

### Berechnete abgeleitete Werte (Script Task)

Berechne neue Werte aus mehreren Eingaben und speichere sie für die weitere Verwendung.

```cel
{"delta": vars.value - vars.outdoor_temp.value, "needs_alarm": vars.value - vars.outdoor_temp.value > 10}
```

Nach diesem Script Task kann das Gateway `vars.needs_alarm` direkt prüfen, und die Alarmmeldung kann `vars.delta` für den Kontext verwenden.

### Klassifizierung mit berechneten Werten kombinieren

Ein einzelner Script Task kann mehrere Berechnungen auf einmal durchführen.

```cel
{
  "severity": vars.value > 90 ? "kritisch" : vars.value > 70 ? "Warnung" : "normal",
  "deviation": vars.value - vars.baseline.value,
  "message": "Messwert " + string(vars.value) + ", Abweichung " + string(vars.value - vars.baseline.value) + " vom Basiswert"
}
```

***

## Wo CEL verwendet wird

CEL-Ausdrücke erscheinen an mehreren Stellen in der Rules Engine. Der Kontext bestimmt, was der Ausdruck zurückgeben soll.

| Ort                                | Erwarteter Rückgabetyp         | Zweck                                                                                               |
| ---------------------------------- | ------------------------------ | --------------------------------------------------------------------------------------------------- |
| **Start-Ereignis — Eingaben**      | Beliebiger Typ (Map bevorzugt) | Lokale Hilfswerte erstellen, wenn das Sensorereignis in die Regel eintritt.                         |
| **Start-Ereignis — Ausgaben**      | Beliebiger Typ (Map bevorzugt) | Benannte Werte im gemeinsamen Workflow-Kontext veröffentlichen.                                     |
| **Script Task — Skript**           | Beliebiger Typ (Map bevorzugt) | Daten transformieren oder klassifizieren. Map-Schlüssel werden in Prozessvariablen zusammengeführt. |
| **Script Task — Eingaben**         | Beliebiger Typ (Map bevorzugt) | Lokale Hilfswerte erstellen, bevor das Skript ausgeführt wird.                                      |
| **Script Task — Ausgaben**         | Beliebiger Typ (Map bevorzugt) | Zusätzliche benannte Werte nach Ausführung des Tasks veröffentlichen.                               |
| **Exklusives Gateway — Bedingung** | `bool`                         | Steuert die Ausführung. Muss `true` oder `false`.                                                   |
| **Exklusives Gateway — Eingaben**  | Beliebiger Typ (Map bevorzugt) | Lokale Werte vorbereiten, die von den Verzweigungsbedingungen verwendet werden.                     |
| **Exklusives Gateway — Ausgaben**  | Beliebiger Typ (Map bevorzugt) | Werte nach der Routing-Entscheidung veröffentlichen.                                                |
| **Set-Alarm — Motivationsmeldung** | `string`                       | Beschreibt, warum der Alarm ausgelöst wurde. Wird den Einsatzkräften angezeigt.                     |
| **Set-Alarm — Eingaben**           | Beliebiger Typ (Map bevorzugt) | Werte vorbereiten, bevor die Alarmaktion ausgeführt wird.                                           |
| **Set-Alarm — Ausgaben**           | Beliebiger Typ (Map bevorzugt) | Werte nach Ausführung des Alarmknotens veröffentlichen.                                             |
| **Enrichment — Sensor-ID**         | `string`                       | Identifiziert, von welchem Sensor Daten abgerufen werden sollen.                                    |
| **Enrichment — Eingaben**          | Beliebiger Typ (Map bevorzugt) | Werte vorbereiten, bevor die Abfrage ausgeführt wird.                                               |
| **Enrichment — Ausgaben**          | Beliebiger Typ (Map bevorzugt) | Werte veröffentlichen, nachdem das Enrichment-Ergebnis verfügbar ist.                               |

### Bereich für Eingaben und Ausgaben

Eingaben und Ausgaben an einem Knoten dienen unterschiedlichen Zwecken und haben unterschiedliche Sichtbarkeit:

* **Eingaben** erstellen **lokal** Variablen, die nur für den aktuellen Knoten gelten. Sie ändern den gemeinsamen Workflow-Kontext nicht. Eingabeausdrücke werden gegen den aktuellen `vars` Status ausgewertet. Verwenden Sie sie, um Hilfswerte vorzubereiten oder Zwischenergebnisse vorab zu berechnen, bevor die Hauptlogik des Knotens ausgeführt wird.
* **Ausgaben** schreiben Werte in den **gemeinsamen** Workflow-Kontext (`vars`). Ausgabeausdrücke werden gegen den lokalen Geltungsbereich des Knotens ausgewertet — der sowohl den ursprünglichen `vars` als auch alle durch Eingaben definierten lokalen Variablen umfasst. Von Ausgaben veröffentlichte Werte bleiben bestehen und sind für alle nachgelagerten Knoten zugänglich.

**Praktische Konsequenz:** Wenn Sie eine Eingabe mit dem Namen `threshold` an einem Exklusiven Gateway definieren, können nachgelagerte Knoten `vars.threshold` — es existiert nur während der Bedingungsauswertung dieses Gateways. Um einen berechneten Wert nachgelagert verfügbar zu machen, definieren Sie ihn stattdessen als Ausgabe.

***

## Sicherheit und Sandboxing

CEL ist von Haus aus sandboxed. Ausdrücke werden in einer eingeschränkten Umgebung ausgeführt und haben keinen Zugriff auf:

* das Dateisystem
* Netzwerkressourcen
* Systemuhren (außer über bereitgestellte Zeitstempelvariablen)
* externe Dienste
* änderbaren Zustand außerhalb der eigenen Auswertung des Ausdrucks

Ein Ausdruck kann keine unendlichen Schleifen erzeugen, keinen unbegrenzten Speicher belegen und keine anderen Regeln beeinflussen. Wenn ein Ausdruck fehlschlägt (Typfehler, Division durch Null, Verweis auf eine fehlende Variable ohne `has()` Schutz), löst der Knoten, der ihn enthält, einen Fehler aus. Hängen Sie ein Boundary-Fehlerereignis an, um mit diesen Fehlern elegant umzugehen.

***

## Plattformfunktionen

Zusätzlich zur standardmäßigen CEL-Bibliothek stellt die Rules Engine zwei plattformspezifische Funktionen bereit.

### error(message)

Nimmt ein String-Argument und erzeugt immer einen Fehlerwert. Wenn ein Ausdruck einer Script Task zu einem Fehler ausgewertet wird, prüft die Engine, ob ein Boundary Error Event angehängt ist. Wenn ja, wird die Ausführung über den Fehlerpfad geleitet. Wenn nicht, stoppt der Fehler die Regel.

Verwenden Sie `error()` für absichtliches bedingtes Fehlschlagen — Situationen, in denen eine bestimmte Datenbedingung den Fehlerbehandlungspfad auslösen sollte, statt die normale Ausführung fortzusetzen.

```cel
vars.temperature > 200 ? error("kritische Überhitzung erkannt") : {"status": "ok"}
```

In diesem Beispiel lässt eine Temperatur über 200 die Script Task absichtlich fehlschlagen. Wenn ein Boundary Error Event angehängt ist, wird der Fehlerpfad ausgeführt (möglicherweise wird ein Notfallalarm ausgelöst). Unter 200 gibt die Script Task aus `{"status": "ok"}` wie gewohnt.

```cel
has(vars.calibration_date) ? {"calibrated": true} : error("Sensor nicht kalibriert")
```

### random()

Gibt eine pseudorandomisierte Gleitkommazahl im Bereich \[0.0, 1.0) zurück. Nützlich für probabilistisches Sampling, prozentbasierte Weiterleitung oder das Erzeugen zufälliger Kennungen.

```cel
random() < 0.1 ? "ausgewählt" : "übersprungen"
```

```cel
{"random_id": random() * 1000000.0}
```

***

## Praktische Tipps

**Halten Sie Ausdrücke fokussiert.** Ein einzelner Ausdruck sollte nur eine Sache tun. Wenn Sie einen Messwert klassifizieren, eine Differenz berechnen und eine Nachricht erstellen müssen, verwenden Sie mehrere Script Tasks statt einen komplexen Ausdruck. Das macht die Regel leichter lesbar, zu debuggen und zu warten.

**Setzen Sie zuerst auf visuelle Struktur.** Verwenden Sie die Arbeitsfläche, um den Workflow darzustellen, und setzen Sie CEL dann in den relevanten Feldern ein. Ein lesbares BPMN-Diagramm mit einigen klaren Ausdrücken ist leichter zu prüfen als eine Regel, die zu viel Logik in einem einzigen riesigen Ausdruck versteckt.

**Verwenden Sie `has()` vor optionalen Feldern.** Jede Variable, die aus Anreicherung, bedingten Script Tasks oder optionalen Eingaben stammt, existiert möglicherweise nicht. Greifen Sie ohne `has()` auf sie zu, und der Ausdruck löst einen Fehler aus.

```cel
has(vars.outdoor_temp) && vars.outdoor_temp.value > 35.0
```

**Konvertieren Sie Typen explizit.** CEL führt keine implizite Typkonvertierung durch. Beim Erstellen von Alarmmeldungen konvertieren Sie Zahlen mit `string()`. Wenn Sie arithmetische Berechnungen durchführen, stellen Sie sicher, dass beide Operanden denselben numerischen Typ haben — das Mischen `int` und `double` kann unerwartete Ergebnisse erzeugen.

**Verwenden Sie Maps für Ausgaben mit mehreren Werten.** Das Zurückgeben einer Map aus einer Script Task ist der Standardweg, um mehrere berechnete Werte nachgelagert verfügbar zu machen. Jeder Schlüssel wird zu einer eigenständigen Prozessvariable.

**Testen Sie Ausdrücke gegen Grenzfälle.** Überlegen Sie, was passiert, wenn ein Sensor null, einen negativen Wert oder einen unerwartet großen Wert meldet. Gateway-Bedingungen sollten den gesamten Bereich möglicher Eingaben abdecken, ohne zu einem unbeabsichtigten Zweig zu leiten.

**Benennen Sie Variablen aussagekräftig.** Wenn Sie Namen für Anreicherungsvariablen oder Ausgabe-Schlüssel von Script Tasks definieren, verwenden Sie Namen, die die Daten beschreiben — `outdoor_temp`, `humidity_reading`, `severity_level` — nicht `x`, `val2`oder `tmp`. Ihre Kollegen werden diese lesen, wenn sie die Regel prüfen oder ändern.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.kiloiot.io/kilo-docs-de/kilo-iot-server/rules-engine/cel-reference.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
