Skip to content

Compliance Metrics

Compliance measures whether restaurant staff responded to Emilia's real-time AI suggestions (drink opportunities, dessert opportunities).


The Problem Clustering Solves

Emilia lacks context about what's happening at the table. When no one responds to a suggestion, she re-sends the same alert every ~5 minutes. Without clustering:

5 events for the same situation + waiter goes once = ⅕ = 20% compliance

But the waiter DID attend the need → should be ~100%

The hybrid clustering algorithm groups rapid-fire alerts into opportunity moments, so one waiter visit covers the whole burst. But if events happen at genuinely different moments (separated by a waiter visit), they correctly form separate clusters.


Algorithm

flowchart TD
    A["EchoBase Events<br/>from metadata.tables[].echobase_events"] --> B["Filter by alertType<br/>second-drink-opportunity<br/>dessert-opportunity"]
    B --> C["Sort by time per type"]
    C --> D{"Gap > 600s?"}
    D -->|Yes| E["New Cluster"]
    D -->|No| F{"Annotation between<br/>consecutive events?"}
    F -->|Yes| E
    F -->|No| G["Same Cluster"]
    E --> H["Evaluate Cluster"]
    G --> H
    H --> I{"First annotation<br/>in cluster window?"}
    I -->|waiter-touch| J["COMPLIANT"]
    I -->|no-waiter-touch| K["NOT COMPLIANT"]
    I -->|none found| L["EXCLUDED<br/>events not counted"]

Cluster Splitting Rules

A cluster boundary is created when either condition is met:

  1. Time gap > 600 seconds between consecutive events of the same type
  2. Annotation between events — a waiter-touch or no-waiter-touch annotation falls between two consecutive events, meaning the situation was acknowledged

Cluster Evaluation: First Annotation Wins

For each cluster, the algorithm searches its time window [window_start, window_end] for annotations:

  • waiter-touch found first → cluster is COMPLIANT
  • no-waiter-touch found first → cluster is NOT COMPLIANT
  • Neither found → cluster is EXCLUDED (all events omitted from counts)

The result is propagated to every event in the cluster.


Real-World Example

sequenceDiagram
    participant E as Emilia AI
    participant W as Waiter
    participant A as Annotator

    Note over E: Cluster 1 starts
    E->>E: second-drink-opportunity at 11980s
    E->>E: second-drink-opportunity at 12526s
    W->>A: waiter-touch at 12913s
    Note over E: Cluster 1 = COMPLIANT

    Note over E: Cluster 2 starts
    E->>E: second-drink-opportunity at 13795s
    E->>E: second-drink-opportunity at 14116s
    W->>A: waiter-touch at 14128s
    Note over E: Cluster 2 = COMPLIANT

Both clusters are compliant. Result: drink_suggestion_count = 4, drink_suggestion_compliance_pct = 100.0.


Output Fields

Field Type Description
drink_suggestion_count int Count of events (not clusters) for second-drink-opportunity
drink_suggestion_compliance_pct float | null Percentage of compliant drink events. null if count is 0
dessert_suggestion_count int Count of events for dessert-opportunity
dessert_suggestion_compliance_pct float | null Percentage of compliant dessert events

Percentages are rounded to 1 decimal place.


real_time_suggestions Detail

When compliance events are found, a real_time_suggestions array is added to the session (alongside metrics):

{
  "alert_type": "second-drink-opportunity",
  "time_seconds": 11980.0,
  "alert_timestamp_utc": "2026-03-08T18:05:04Z",
  "operator_id": "valeria",
  "compliant": true,
  "cluster_id": 0
}

This array is included in the fingerprint calculation — changes to suggestion matching trigger a new version even if metrics values are identical.