Introduction

Agent Rules are the decision-making engine of your StateSet agents. They enable sophisticated, context-aware behaviors that adapt to customer needs, business policies, and real-time conditions. This guide will show you how to create rules that make your agents truly intelligent.

What are Agent Rules?

Rules define how your agents respond to specific situations. They work on an “if-then” basis but can incorporate complex logic, multiple conditions, and sophisticated actions. Think of rules as your agent’s decision tree—guiding every interaction toward the best possible outcome.

How Rules Work

Rule Execution Flow

Why Rules Matter

Consistent Responses

Ensure every customer gets the right answer, every time

Business Logic

Embed your policies and procedures directly into agent behavior

Dynamic Adaptation

Adjust responses based on context, customer history, and real-time data

Getting Started

Prerequisites

  1. StateSet account with API access
  2. At least one agent created
  3. Understanding of your business rules and policies

Installation

npm install stateset-node

Creating Rules

Basic Rule Structure

Every rule consists of:

  • Name: Unique identifier
  • Conditions: When the rule should trigger
  • Actions: What should happen
  • Priority: Order of execution
  • Scope: Where the rule applies

Simple Rule Example

Let’s start with a basic rule for handling refund inquiries:

import { StateSetClient } from 'stateset-node';

const client = new StateSetClient({
  apiKey: process.env.STATESET_API_KEY
});

async function createRefundRule(agentId) {
  const rule = await client.rules.create({
    agent_id: agentId,
    name: 'refund_inquiry_handler',
    description: 'Handles customer refund inquiries with appropriate care',
    priority: 100, // Higher priority executes first
    conditions: {
      any: [
        { field: 'message.text', operator: 'contains', value: 'refund' },
        { field: 'message.text', operator: 'contains', value: 'money back' },
        { field: 'message.text', operator: 'matches', value: '/return.*money/i' }
      ]
    },
    actions: [
      {
        type: 'set_attribute',
        attribute: 'conversation_tone',
        value: 'empathetic_professional'
      },
      {
        type: 'add_context',
        context: {
          topic: 'refund',
          sensitivity: 'high',
          require_order_lookup: true
        }
      },
      {
        type: 'send_message',
        message: 'I understand you have questions about a refund. I\'d be happy to help you with that. Could you please provide your order number?'
      }
    ],
    metadata: {
      category: 'customer_service',
      compliance: 'required',
      last_reviewed: new Date().toISOString()
    }
  });
  
  return rule;
}

Complex Rule with Multiple Conditions

Here’s a more sophisticated rule that considers customer history and order value:

async function createVIPCustomerRule(agentId) {
  const vipRule = await client.rules.create({
    agent_id: agentId,
    name: 'vip_customer_treatment',
    description: 'Provides premium service to VIP customers',
    priority: 200, // Executes before standard rules
    conditions: {
      all: [
        {
          any: [
            { field: 'customer.lifetime_value', operator: 'greater_than', value: 5000 },
            { field: 'customer.tier', operator: 'equals', value: 'platinum' },
            { field: 'customer.orders_count', operator: 'greater_than', value: 50 }
          ]
        },
        {
          field: 'conversation.sentiment',
          operator: 'in',
          value: ['negative', 'very_negative']
        }
      ]
    },
    actions: [
      {
        type: 'escalate',
        target: 'vip_support_team',
        priority: 'immediate'
      },
      {
        type: 'set_attribute',
        attribute: 'service_level',
        value: 'white_glove'
      },
      {
        type: 'apply_discount',
        discount_type: 'percentage',
        value: 10,
        code: 'VIP_CARE'
      },
      {
        type: 'send_message',
        message: 'I sincerely apologize for any inconvenience. As one of our valued customers, I\'m immediately escalating this to our VIP support team who will assist you right away.'
      },
      {
        type: 'log_event',
        event: 'vip_escalation',
        data: {
          customer_id: '{{customer.id}}',
          reason: 'negative_sentiment',
          automated: true
        }
      }
    ]
  });
  
  return vipRule;
}

Advanced Rule Patterns

Time-Based Rules

Create rules that adapt based on time of day, business hours, or dates:

const afterHoursRule = await client.rules.create({
  agent_id: agentId,
  name: 'after_hours_support',
  conditions: {
    any: [
      {
        field: 'current_time',
        operator: 'outside_range',
        value: { start: '09:00', end: '17:00', timezone: 'America/New_York' }
      },
      {
        field: 'current_day',
        operator: 'in',
        value: ['Saturday', 'Sunday']
      },
      {
        field: 'current_date',
        operator: 'in',
        value: ['2024-12-25', '2024-01-01'] // Holidays
      }
    ]
  },
  actions: [
    {
      type: 'set_expectation',
      message: 'Thanks for reaching out! Our team is currently offline, but I can still help with many requests.'
    },
    {
      type: 'limit_functions',
      allowed: ['order_status', 'track_package', 'faq_lookup'],
      message_on_restricted: 'For this request, our human team will need to assist you. They\'ll respond within one business day.'
    }
  ]
});

Dynamic Routing Rules

Route conversations based on content and context:

const routingRule = await client.rules.create({
  agent_id: agentId,
  name: 'intelligent_routing',
  priority: 300,
  conditions: {
    evaluate: 'routing_logic'
  },
  routing_logic: `
    // Technical issues go to tech support
    if (message.text.match(/error|bug|crash|not working/i)) {
      return 'technical_support';
    }
    
    // Billing issues to finance team
    if (message.text.match(/charge|bill|payment|invoice/i)) {
      return 'billing_team';
    }
    
    // Sales inquiries to sales team
    if (message.text.match(/pricing|discount|upgrade|plan/i) && !customer.is_existing) {
      return 'sales_team';
    }
    
    // VIP customers always get premium support
    if (customer.tier === 'vip' || customer.lifetime_value > 10000) {
      return 'vip_support';
    }
    
    // Default to general support
    return 'general_support';
  `,
  actions: [
    {
      type: 'route_to_team',
      team: '{{routing_result}}',
      transfer_context: true,
      message: 'I\'m connecting you with our {{routing_result.friendly_name}} team who can best assist with your request.'
    }
  ]
});

Compliance and Security Rules

Ensure sensitive data is handled appropriately:

const complianceRule = await client.rules.create({
  agent_id: agentId,
  name: 'pii_protection',
  priority: 500, // Highest priority
  conditions: {
    any: [
      {
        field: 'message.text',
        operator: 'matches',
        value: '/\\b\\d{3}-\\d{2}-\\d{4}\\b/' // SSN pattern
      },
      {
        field: 'message.text',
        operator: 'matches',
        value: '/\\b(?:\\d{4}[\\s-]?){3}\\d{4}\\b/' // Credit card pattern
      }
    ]
  },
  actions: [
    {
      type: 'mask_sensitive_data',
      patterns: ['ssn', 'credit_card'],
      replacement: '[REDACTED]'
    },
    {
      type: 'send_message',
      message: 'For your security, please don\'t share sensitive information like SSN or credit card numbers in chat. I can help you without this information.'
    },
    {
      type: 'log_security_event',
      event_type: 'pii_attempted',
      severity: 'medium'
    }
  ]
});

Rule Conditions

Condition Operators

StateSet supports a comprehensive set of operators:

// Comparison operators
{ field: 'order.total', operator: 'greater_than', value: 100 }
{ field: 'customer.age', operator: 'between', value: { min: 18, max: 65 } }

// String operators
{ field: 'message.text', operator: 'contains', value: 'urgent' }
{ field: 'customer.email', operator: 'ends_with', value: '@company.com' }
{ field: 'product.sku', operator: 'matches', value: '/^PROD-\\d{6}$/' }

// Array operators
{ field: 'customer.tags', operator: 'includes', value: 'vip' }
{ field: 'order.items', operator: 'includes_any', value: ['electronics', 'computers'] }

// Date operators
{ field: 'customer.created_at', operator: 'before', value: '2024-01-01' }
{ field: 'order.date', operator: 'within_last', value: { amount: 30, unit: 'days' } }

// Null checks
{ field: 'customer.phone', operator: 'is_null', value: true }
{ field: 'order.discount_code', operator: 'is_not_null', value: true }

Complex Condition Logic

Combine conditions with AND/OR logic:

const complexRule = await client.rules.create({
  conditions: {
    all: [ // AND logic
      {
        any: [ // OR logic
          { field: 'customer.status', operator: 'equals', value: 'gold' },
          { field: 'customer.lifetime_value', operator: 'greater_than', value: 1000 }
        ]
      },
      {
        field: 'current_promotion.active',
        operator: 'equals',
        value: true
      },
      {
        not: { // NOT logic
          field: 'customer.flags',
          operator: 'includes',
          value: 'promotional_opt_out'
        }
      }
    ]
  }
});

Rule Actions

Available Action Types

// Conversation Control
{ type: 'set_attribute', attribute: 'tone', value: 'formal' }
{ type: 'add_tag', tag: 'urgent_issue' }
{ type: 'set_priority', level: 'high' }

// Message Actions
{ type: 'send_message', message: 'Your custom message here' }
{ type: 'send_template', template_id: 'welcome_back_vip' }
{ type: 'suggest_responses', options: ['Yes', 'No', 'Tell me more'] }

// Function Execution
{ type: 'execute_function', function: 'check_inventory', params: { sku: '{{product.sku}}' } }
{ type: 'disable_function', function: 'process_payment', reason: 'after_hours' }

// Routing and Escalation
{ type: 'route_to_team', team: 'technical_support' }
{ type: 'escalate_to_human', priority: 'immediate', reason: '{{escalation_reason}}' }

// Data Operations
{ type: 'update_customer', fields: { last_contact: '{{current_timestamp}}' } }
{ type: 'create_ticket', priority: 'medium', category: 'billing' }
{ type: 'log_event', event: 'rule_triggered', data: { rule_id: '{{rule.id}}' } }

Action Sequencing

Control the flow of actions:

const sequencedRule = await client.rules.create({
  actions: [
    {
      type: 'execute_function',
      function: 'check_order_status',
      store_result: 'order_status'
    },
    {
      type: 'conditional_action',
      condition: 'order_status.status === "shipped"',
      then: {
        type: 'send_message',
        message: 'Great news! Your order has shipped and is on its way.'
      },
      else: {
        type: 'send_message',
        message: 'Your order is being prepared and will ship soon.'
      }
    }
  ]
});

Testing Rules

Rule Testing Framework

async function testRule(ruleId, testCases) {
  const results = await client.rules.test({
    rule_id: ruleId,
    test_cases: testCases
  });
  
  results.forEach(result => {
    console.log(`Test Case: ${result.name}`);
    console.log(`Expected: ${result.expected}`);
    console.log(`Actual: ${result.actual}`);
    console.log(`Passed: ${result.passed}`);
  });
  
  return results;
}

// Define test cases
const testCases = [
  {
    name: 'VIP customer with complaint',
    input: {
      message: { text: 'This service is terrible!' },
      customer: { tier: 'vip', lifetime_value: 15000 },
      conversation: { sentiment: 'very_negative' }
    },
    expected_actions: ['escalate', 'apply_discount']
  },
  {
    name: 'Regular customer inquiry',
    input: {
      message: { text: 'What are your business hours?' },
      customer: { tier: 'standard' },
      conversation: { sentiment: 'neutral' }
    },
    expected_actions: ['send_message']
  }
];

await testRule(vipRule.id, testCases);

Rule Management

Rule Versioning

Keep track of rule changes:

const ruleWithVersion = await client.rules.create({
  agent_id: agentId,
  name: 'return_policy_v2',
  version: '2.0.0',
  changelog: 'Updated return window from 30 to 60 days',
  replaces: 'rule_abc123', // ID of previous version
  effective_date: '2024-02-01',
  actions: [
    {
      type: 'send_message',
      message: 'Our return policy allows returns within 60 days of purchase.'
    }
  ]
});

Bulk Rule Operations

Manage multiple rules efficiently:

// Import rules from configuration
async function importRules(agentId, rulesConfig) {
  const imported = await client.rules.bulkCreate({
    agent_id: agentId,
    rules: rulesConfig,
    conflict_resolution: 'skip', // or 'overwrite'
    validate_before_import: true
  });
  
  console.log(`Imported ${imported.success.length} rules`);
  if (imported.failures.length > 0) {
    console.error('Failed imports:', imported.failures);
  }
}

// Export rules for backup
async function exportRules(agentId) {
  const rules = await client.rules.export({
    agent_id: agentId,
    format: 'json',
    include_disabled: false
  });
  
  // Save to file or version control
  fs.writeFileSync('rules-backup.json', JSON.stringify(rules, null, 2));
}

Monitoring & Analytics

Rule Performance Tracking

// Get rule analytics
const analytics = await client.rules.getAnalytics({
  rule_id: ruleId,
  timeframe: '30d',
  metrics: ['trigger_count', 'success_rate', 'avg_execution_time']
});

console.log(`Rule Performance:
  Triggered: ${analytics.trigger_count} times
  Success Rate: ${analytics.success_rate}%
  Avg Execution: ${analytics.avg_execution_time}ms
  Most Common Trigger: ${analytics.top_trigger_condition}
`);

// Set up alerts
await client.rules.createAlert({
  rule_id: ruleId,
  alert_conditions: [
    {
      metric: 'error_rate',
      threshold: 5,
      window: '1h',
      notification: 'email'
    }
  ]
});

Best Practices

1. Rule Priority and Order

// Good: Clear priority hierarchy
const rules = [
  { name: 'security_check', priority: 1000 },      // Always first
  { name: 'compliance_filter', priority: 900 },    // Before business logic
  { name: 'vip_treatment', priority: 500 },        // Special handling
  { name: 'standard_routing', priority: 100 },     // Normal flow
  { name: 'fallback_handler', priority: 1 }        // Catch-all
];

2. Performance Optimization

// Optimize condition checking
const efficientRule = await client.rules.create({
  conditions: {
    // Check simple conditions first
    all: [
      { field: 'message.length', operator: 'less_than', value: 10 }, // Fast
      { field: 'customer.tier', operator: 'equals', value: 'vip' },  // Fast
      { field: 'message.text', operator: 'matches', value: '/complex.*regex/i' } // Slower
    ]
  },
  // Cache results for repeated checks
  cache_ttl: 300 // 5 minutes
});

3. Error Handling

const robustRule = await client.rules.create({
  error_handling: {
    on_condition_error: 'skip_rule',
    on_action_error: 'continue_next_action',
    fallback_message: 'I encountered an issue, but I\'m still here to help!',
    log_errors: true
  }
});

Common Patterns

Customer Segmentation Rules

const segmentationRules = [
  {
    name: 'new_customer_welcome',
    conditions: { field: 'customer.orders_count', operator: 'equals', value: 0 },
    actions: [{ type: 'send_template', template: 'first_time_buyer_welcome' }]
  },
  {
    name: 'loyal_customer_perks',
    conditions: { field: 'customer.orders_count', operator: 'greater_than', value: 10 },
    actions: [{ type: 'apply_loyalty_discount', percentage: 15 }]
  },
  {
    name: 'win_back_inactive',
    conditions: { field: 'customer.last_order_date', operator: 'before', value: '90_days_ago' },
    actions: [{ type: 'send_template', template: 'we_miss_you_offer' }]
  }
];

Contextual Help Rules

const contextualHelpRule = await client.rules.create({
  name: 'smart_help_suggestions',
  conditions: {
    evaluate: `
      const keywords = message.text.toLowerCase();
      const page = context.current_page;
      
      if (page === 'checkout' && keywords.includes('coupon')) {
        return 'show_coupon_help';
      } else if (page === 'product' && keywords.includes('size')) {
        return 'show_size_guide';
      } else if (keywords.includes('ship')) {
        return 'show_shipping_info';
      }
      return null;
    `
  },
  actions: [
    {
      type: 'dynamic_action',
      action: '{{evaluation_result}}'
    }
  ]
});

Troubleshooting

Common Issues

  1. Rules Not Triggering

    • Check rule priority and order
    • Verify conditions are correctly formatted
    • Ensure rule is enabled and not expired
  2. Conflicting Rules

    • Use priority levels to control execution order
    • Implement exclusive rule groups
    • Add conflict detection in conditions
  3. Performance Issues

    • Optimize complex regex patterns
    • Use caching for expensive operations
    • Limit the number of active rules

Next Steps


Pro Tip: Start with simple rules and gradually add complexity. Use the testing framework to validate rules before deploying to production.

For support and examples, visit our GitHub repository or contact support@stateset.com.