Tier 2: Rule-Based Routing

chevron-rightRelevant source fileshashtag

Purpose and Scope

Tier 2 implements deterministic, rule-based routing for the Universal Router. It executes after Tier 0 (user overrides, see #9.2) and Tier 1 (cache lookup, see #9.3) fail to produce a routing decision. Tier 2 consists of three sequential sub-strategies that match incoming requests against workspace-configured routing rules, trigger subscriptions, and intent patterns. For LLM-based classification when all rules fail, see Tier 3: LLM Classification.

Tier 2 provides workspace administrators with explicit control over routing behavior through:

  • Tier 2a: Source pattern matching against RoutingRule table entries

  • Tier 2b: Jira trigger subscriptions via TriggerSubscription table (Composio integration)

  • Tier 2c: Keyword-based intent classification with fallback to rules

All three sub-tiers query database tables filtered by workspace_id to enforce multi-tenancy isolation.


Tier 2 Architecture Overview

spinner

Sources: orchestrator/core/routing/engine.py:78-144


Tier 2a: Routing Rules (Source Pattern Matching)

Overview

Tier 2a queries the RoutingRule table to find explicit routing rules configured by workspace administrators. Rules are matched based on the request's source channel and can route to either agents or workflows.

Matching Logic

spinner

Sources: orchestrator/core/routing/engine.py:182-214

Implementation Details

The _tier2a_rules method executes the following query pattern:

  1. Query RoutingRule table filtered by workspace_id and is_active = True

  2. Order by priority (descending) to ensure higher-priority rules are evaluated first

  3. Iterate through rules and check source_pattern match:

    • If rule.source_pattern is None or empty, the rule matches any source

    • Otherwise, rule.source_pattern must exactly match envelope.source.value

  4. Return first match with confidence=0.9 and reasoning that includes rule ID

Sources: orchestrator/core/routing/engine.py:182-214

RoutingRule Table Schema

Field
Type
Description

id

int

Primary key

workspace_id

UUID

Workspace isolation

is_active

bool

Enable/disable rule without deletion

priority

int

Higher values evaluated first

source_pattern

str (optional)

Channel source to match (e.g., "slack", "email"). None = match all

target_agent_id

int (optional)

Route to this agent if matched

target_workflow_id

int (optional)

Route to this workflow if matched

intent_keywords

list[str]

Keywords for Tier 2c intent matching

Sources: orchestrator/core/routing/engine.py:183-214, orchestrator/core/models/routing.py

Example Routing Rule Configurations

Sources: orchestrator/core/routing/engine.py:182-214


Tier 2b: Trigger Subscriptions (Jira Integration)

Overview

Tier 2b handles routing for requests originating from Composio trigger subscriptions, specifically Jira webhooks. This tier only executes when envelope.source == ChannelSource.JIRA_TRIGGER.

Resolution Flow

spinner

Sources: orchestrator/core/routing/engine.py:220-278

Implementation Details

The _tier2b_trigger_subscription method implements a two-step resolution:

Step 1: Resolve Workspace → Entity

  • Query ComposioEntity table to find the entity associated with the workspace

  • Each workspace has a unique Composio entity for OAuth connections and trigger subscriptions

Step 2: Find Active Trigger Subscription

  • Query TriggerSubscription table filtered by entity_id and is_active = True

  • If envelope.metadata["trigger_name"] is present, attempt to find an exact match

  • Falls back to the first active subscription if no exact match

Step 3: Return Routing Decision

  • Uses subscription.agent_id or subscription.workflow_id as the target

  • Returns confidence=0.95 (higher than Tier 2a rules)

  • Reasoning includes subscription ID and trigger name

Sources: orchestrator/core/routing/engine.py:220-278

TriggerSubscription Table Schema

Field
Type
Description

id

int

Primary key

entity_id

int

Foreign key to ComposioEntity

trigger_name

str

Composio trigger identifier (e.g., "JIRA_NEW_ISSUE_CREATED")

agent_id

int (optional)

Route to this agent

workflow_id

int (optional)

Route to this workflow

is_active

bool

Enable/disable subscription

Sources: orchestrator/core/routing/engine.py:236-278, orchestrator/core/models/composio.py


Tier 2c: Intent Classification

Overview

Tier 2c uses keyword-based intent classification as a fallback when explicit source pattern matching fails. The IntentClassifier analyzes the request content to determine its intent category, then matches that category against routing rules configured with intent_keywords.

Intent Classification Flow

spinner

Sources: orchestrator/core/routing/engine.py:284-326

Implementation Details

Step 1: Intent Classification

  • IntentClassifier.classify(envelope.content) returns {category: str, confidence: float}

  • If confidence < 0.4, classification is considered too uncertain and None is returned

Step 2: Match Against Rule Keywords

  • Queries the same RoutingRule table as Tier 2a

  • For each rule, checks if rule.intent_keywords contains the classified category (case-insensitive match)

  • Returns the first matching rule with the classification's confidence (not hardcoded 0.9)

Step 3: Reasoning

  • Reasoning string includes the intent category and matched rule ID

  • Example: "Intent 'bug_report' matched rule #42 keywords"

Sources: orchestrator/core/routing/engine.py:284-326

IntentClassifier Integration

The IntentClassifier performs lightweight, non-LLM intent detection using keyword matching and pattern recognition. It is initialized once per UniversalRouter instance:

Classification returns:

  • category: String identifier (e.g., "bug_report", "feature_request", "support")

  • confidence: Float between 0 and 1

Sources: orchestrator/core/routing/engine.py:72, orchestrator/core/services/intent_classifier.py

Example Intent Keyword Configuration

Sources: orchestrator/core/routing/engine.py:284-326


Tier 2 Confidence Levels

Each Tier 2 sub-strategy returns a different confidence level to reflect the strength of the match:

Tier
Confidence
Reasoning

Tier 2a

0.9

Explicit source pattern match — high confidence but allows for LLM override if needed

Tier 2b

0.95

Trigger subscription is the most explicit form of routing configuration — highest rule-based confidence

Tier 2c

Variable

Uses IntentClassifier confidence (minimum 0.4), reflecting uncertainty in keyword-based classification

These confidence values affect whether the router proceeds to Tier 3 LLM classification or terminates early. See Tier 3: LLM Classification for threshold-based orchestration logic.

Sources: orchestrator/core/routing/engine.py:204-276, orchestrator/core/routing/engine.py:314-324


Execution Order and Fallthrough

spinner

Sources: orchestrator/core/routing/engine.py:108-128


Decision Logging

All successful Tier 2 routing decisions are logged to the RoutingDecisionRecord table for analytics and debugging:

This enables:

  • Analytics: Track which rules are used most frequently

  • Debugging: Understand why specific routing decisions were made

  • Auditing: Compliance and security review of routing behavior

Sources: orchestrator/core/routing/engine.py:561-586


Managing Routing Rules

Creating Routing Rules

Routing rules are created via the Routing API (see /api/routing endpoints):

Sources: orchestrator/api/routing.py

Rule Priority and Ordering

Rules are evaluated in descending priority order (highest first). Best practices:

  • Assign high priorities (90-100) to specific, narrow rules

  • Assign medium priorities (50-70) to category-based intent rules

  • Assign low priorities (10-30) to broad fallback rules

  • Use priority=0 for disabled-but-kept rules

Sources: orchestrator/core/routing/engine.py:191

Activating and Deactivating Rules

Toggle is_active to enable/disable rules without deletion:

This preserves historical routing decisions and allows easy re-activation.

Sources: orchestrator/core/routing/engine.py:188


Unrouted Events

When all Tier 2 sub-strategies (and Tier 3) fail to produce a routing decision, the request is logged as an UnroutedEvent for later analysis:

Workspace admins can review UnroutedEvent records to identify gaps in routing configuration and create new rules accordingly.

Sources: orchestrator/core/routing/engine.py:539-555


Integration with Tier 1 Cache

Tier 2 decisions are cached by Tier 1 for future requests. After a successful Tier 2 match, the decision is stored in RoutingCache (Redis) with a configurable TTL:

This reduces database queries for repeated requests. See Tier 1: Cache Lookup for cache invalidation and TTL configuration.

Sources: orchestrator/core/routing/engine.py:403-409


Configuration Reference

Tier 2 behavior is controlled by environment variables and database configuration:

Variable
Type
Default
Description

ROUTING_CACHE_TTL_HOURS

int

24

Cache TTL for routing decisions (affects Tier 1 caching of Tier 2 results)

ROUTING_LLM_CONFIDENCE_THRESHOLD

float

0.5

Threshold for Tier 3 LLM decisions (not directly used by Tier 2, but affects fallthrough)

Sources: orchestrator/config.py:143-144


Summary

Tier 2 provides deterministic, rule-based routing through three sequential strategies:

  1. Tier 2a: Matches source_pattern against workspace routing rules (confidence 0.9)

  2. Tier 2b: Resolves Jira trigger subscriptions via Composio integration (confidence 0.95)

  3. Tier 2c: Uses intent classification with keyword matching against rules (variable confidence ≥0.4)

When all Tier 2 strategies fail, the router falls through to Tier 3 LLM classification. All decisions are logged to RoutingDecisionRecord for analytics, and successful matches are cached for future Tier 1 hits.

Sources: orchestrator/core/routing/engine.py:78-144


Last updated