Reason on Data

The Reason stage evaluates the data that the Sense stage collects and determines the appropriate action. The four Reason substages run in order: analyze → plan → govern → authorize.

The Reason stage is optional, and so are each of the Reason substages. A simple agent might only need analyze and plan. Add govern when you want safety rules, and authorize to declare explicit permission boundaries.

Both Reason modes, deterministic rules and large language model (LLM) prompts, read from the same environmentalData.payload object that the Sense stage writes to. The property names inside payload depend on the Sense substage type and on whether a window is configured. See Where the Platform Stores Sensed Data and Reason Stage Inputs and Outputs.

Analyze

The analyze substage produces a structured assessment of what the Sense data means. Use the analyze substage to classify conditions, detect anomalies, or evaluate thresholds.

Option 1: Deterministic (Rule-Based, No LLM)

Write conditions as JavaScript expressions. If a condition evaluates to true, the platform adds the results of the rule (type, severity, message) to the analysis output:

reason:
  substages:
    - type: analyze
      name: threshold-check
      config:
        rules:
          - condition: 'temperature.value > 45'
            type: temperature_critical_high
            severity: critical
            message: 'Temperature {{temperature.value}}°C critically exceeds limit of 35°C'

          - condition: 'temperature.value > 35 && temperature.value <= 45'
            type: temperature_high
            severity: high
            message: 'Temperature {{temperature.value}}°C exceeds normal limit of 35°C'

          - condition: 'humidity.value < 30'
            type: humidity_low
            severity: high
            message: 'Humidity {{humidity.value}}% below minimum of 30%'

Multiple rules can match in the same cycle. Use the deterministic mode for fixed thresholds. The deterministic mode runs with zero LLM cost and minimal latency.

Option 2: LLM-Based (AI Reasoning)

Provide a prompt and define the expected output shape. The LLM analyzes the data and returns structured results:

reason:
  substages:
    - type: analyze
      name: quality-assessment
      prompt: |
        You are an industrial quality monitoring agent.
        Analyze the following quality metrics and identify any anomalies.

        Current readings:
        {{environmentalData | json}}

        Historical patterns:
        {{memory.patterns | json}}

        Identify anomalies, classify severity (low/medium/high/critical),
        and assess overall production health.
      outputSchema:
        type: object
        properties:
          anomalies:
            type: array
            items:
              type: object
              properties:
                metricName:
                  type: string
                severity:
                  type: string
                  enum: [low, medium, high, critical]
                description:
                  type: string
          overallHealth:
            type: string
            enum: [healthy, degraded, at-risk, critical]
          confidence:
            type: number
            minimum: 0
            maximum: 1

The platform validates the LLM output against outputSchema. If the output does not match the schema, the platform marks the analyze result as failed, and logs the error. Results are available in the Plan substage as derivedData.analysis.

Plan

The plan substage decides which actions to execute based on the analysis. Like analyze, the plan substage supports two modes: deterministic rules and LLM prompts.

Deterministic Plan

Write conditions to select actions. If the condition of an action evaluates to true, the plan passes the action to the Actuate stage:

reason:
  substages:
    - type: plan
      name: response-planner
      config:
        objective: 'Alert on threshold violations'
        actions:
          - action: critical-temperature-alert
            type: mqtt_publish
            target: factory/alerts/temperature
            condition: 'temperature.value > 45 || temperature.value < 5'
            priority: 1
            params:
              severity: critical
              message: 'CRITICAL: Temperature {{temperature.value}}°C outside safe range'

          - action: temperature-warning
            type: mqtt_publish
            target: factory/alerts/temperature
            condition: 'temperature.value > 35 && temperature.value <= 45'
            priority: 2
            params:
              severity: warning
              message: 'WARNING: Temperature {{temperature.value}}°C above normal range'

The platform sorts actions by priority (lower number = higher priority) and passes the actions to the Actuate stage.

LLM-Based Plan

reason:
  substages:
    - type: plan
      name: response-planner
      prompt: |
        Based on the quality analysis, create a response plan.

        Analysis:
        {{derivedData.analysis | json}}

        Available actions:
        - publish-alert: Send alert to MQTT (use for notifications)
        - stop-line: Emergency production stop (critical quality failures only)
        - log-observation: Record for trending (low severity)

        Guidelines:
        - critical severity → stop-line + publish-alert
        - high severity → publish-alert
        - medium/low → log-observation
      outputSchema:
        type: object
        properties:
          actions:
            type: array
            items:
              type: object
              properties:
                action:
                  type: string
                  enum: [publish-alert, stop-line, log-observation]
                target:
                  type: string
                params:
                  type: object
                confidence:
                  type: number

Govern

The govern substage applies safety rules to the plan. Each rule has a condition and an action: approve or reject. If the condition of a reject rule evaluates to true, the platform discards the plan and skips the Actuate stage for that cycle.

Use the govern substage to limit the number of actions the agent takes in a short time, or to block specific actions under certain conditions:

reason:
  substages:
    - type: govern
      name: safety-governance
      rules:
        - id: max-actions-per-cycle
          condition: 'plan.actions.length <= 5'
          action: approve
          message: 'Action count within limits'

        - id: too-many-actions
          condition: 'plan.actions.length > 5'
          action: reject
          message: 'Too many actions planned ({{plan.actions.length}}). Max is 5 per cycle.'

        - id: alert-rate-limit
          condition: 'memory.recentActions.length < 50'
          action: approve
          message: 'Alert rate within acceptable limits'

        - id: rate-exceeded
          condition: 'memory.recentActions.length >= 50'
          action: reject
          message: 'Alert rate limit exceeded'

The platform evaluates the rules in order. The first reject rule that matches stops the cycle. If no reject matches, the platform approves the plan.

Field Required Description

id

Yes

Unique name for the rule, shown in logs.

condition

Yes

JavaScript expression that returns true or false.

action

Yes

approve or reject.

message

No

Log message for when the rule matches.

Authorize

The authorize substage declares which resources the agent can access. The authorize substage acts as an allowlist that the runtime checks before the Actuate stage runs:

reason:
  substages:
    - type: authorize
      name: permission-check
      type: static
      permissions:
        - mqtt:publish:factory/quality/alerts
        - mqtt:publish:factory/quality/observations
        - mqtt:publish:factory/line-control/stop
        - email:send:alert-email
        - database:write:quality-db

The permission format is <protocol>:<operation>:<resource>. Wildcards (*) are supported at the end of the resource path.

Always set the type field on authorize substages to static. If you omit the field, the platform silently skips the substage.

Complete Deterministic Example

The following example shows a complete Reason stage that uses all four substages and no LLM:

reason:
  substages:
    - type: analyze
      name: threshold-check
      config:
        rules:
          - condition: "temperature.value > 45"
            type: temperature_critical_high
            severity: critical
            message: "Temperature {{temperature.value}}°C critically exceeds maximum of 35°C"
          - condition: "temperature.value > 35 && temperature.value <= 45"
            type: temperature_high
            severity: high
            message: "Temperature {{temperature.value}}°C exceeds maximum of 35°C"
          - condition: "humidity.value > 70"
            type: humidity_high
            severity: high
            message: "Humidity {{humidity.value}}% exceeds maximum of 70%"

    - type: plan
      name: response-planner
      config:
        objective: "Alert on threshold violations for temperature and humidity"
        actions:
          - action: critical-temperature-alert
            type: mqtt_publish
            target: factory/alerts/temperature
            condition: "temperature.value > 45 || temperature.value < 5"
            priority: 1
            params:
              severity: critical
              message: "CRITICAL: Temperature {{temperature.value}}°C outside safe range"

          - action: temperature-warning
            type: mqtt_publish
            target: factory/alerts/temperature
            condition: "temperature.value > 35 && temperature.value <= 45"
            priority: 2
            params:
              severity: warning
              message: "WARNING: Temperature {{temperature.value}}°C above normal range"

    - type: govern
      name: rate-limiter
      rules:
        - id: alert-rate-limit
          condition: "memory.recentActions.length < 10"
          action: approve
        - id: rate-exceeded
          condition: "memory.recentActions.length >= 10"
          action: reject
          message: "Alert rate limit exceeded — max 10 per window"

    - type: authorize
      name: permission-check
      type: static
      permissions:
        - mqtt:publish:factory/alerts/*
        - email:send:alert-email

Template Variables in Prompts

Use {{variable}} syntax in prompts and rule messages:

Template Contents

{{environmentalData | json}}

All Sense data for this cycle (the payload object) as JSON.

{{derivedData.analysis | json}}

Output from the analyze substage.

{{memory.patterns | json}}

Agent memory (patterns, recent actions).

{{memory.feedbackHistory | json}}

History of human feedback responses.

{{temperature.value}}

Individual sensor reading from a simulator.

Always add the | json filter when you reference an object or array in an LLM prompt. Without the filter, the template renders the value as [object Object] and the LLM cannot read the data.

Reason Stage Inputs and Outputs

Reason reads the Sense data from environmentalData.payload (see Where the Platform Stores Sensed Data). Each Reason substage writes its result into a dedicated property on derivedData. Within the same cycle, the following Reason substages, Actuate stage, and Reflect stage read the results from these properties.

Substage Output Property Structure

analyze

derivedData.analysis

{ summary, insights[], anomalies[], confidence }.

plan

derivedData.plan

{ objective, actions[], reasoning }.

govern

derivedData.governance

{ approved, violations[], appliedPolicies[] }.

authorize

derivedData.authorization

{ authorized, deniedActions[], permissions[] }.

The Actuate stage reads derivedData.plan to know which actions to execute. The Reflect stage reads derivedData.analysis and the evaluation results to assess the cycle. Reference any of these properties in a later LLM prompt with {{derivedData.analysis | json}}.

The platform resets derivedData at the start of every cycle. If you need a result from this cycle to influence the next cycle, route the result through memory in the Reflect stage. derivedData itself does not survive a cycle boundary.