Multi-Step Chain Example

Build a complete customer support automation workflow with sentiment analysis, conditional routing, and personalized responses.

Overview

This example demonstrates a real-world customer support automation chain that:

  • Analyzes customer message sentiment
  • Fetches customer profile and history from an API
  • Routes to different response strategies based on sentiment and customer tier
  • Generates personalized responses
  • Creates support tickets for negative feedback
  • Logs all interactions for analysis

Architecture

The workflow has 8 steps with conditional branching:

Step 0: Analyze sentiment
Step 1: Fetch customer profile
Step 2: Check if negative sentiment
  ├─ TRUE (negative):
  │   Step 3: Create urgent support ticket
  │   Step 4: Generate empathetic response
  │
  └─ FALSE (neutral/positive):
      Step 5: Check if premium customer
        ├─ TRUE (premium):
        │   Step 6: Generate VIP response
        │
        └─ FALSE (standard):
            Step 7: Generate friendly response

Step 8: Log interaction (always runs)

Step 1: Create Required Prompts

First, create three prompts that will be used in the chain:

Sentiment Analysis Prompt

mutation CreateSentimentPrompt {
  createPrompt(input: {
    name: "support-sentiment-analyzer"
    description: "Analyzes customer support messages for sentiment"
    category: ANALYSIS
  }) {
    id
  }
}

mutation CreateSentimentVersion {
  createPromptVersion(input: {
    promptId: "prompt_sentiment_123"
    content: """
Analyze the sentiment of this customer support message.

Message: {{message}}

Return JSON:
{
  "sentiment": "Positive" | "Negative" | "Neutral",
  "confidence": 0.0 to 1.0,
  "urgency": "low" | "medium" | "high"
}
"""
    params: {
      model: "claude-3-5-sonnet-20241022"
      temperature: 0.2
      max_tokens: 200
    }
    inputSchema: {
      type: "object"
      properties: {
        message: { type: "string" }
      }
      required: ["message"]
    }
  }) {
    id
  }
}

Empathetic Response Prompt

mutation CreateEmpatheticPrompt {
  createPrompt(input: {
    name: "empathetic-support-response"
    description: "Generates empathetic responses for upset customers"
    category: GENERATION
  }) {
    id
  }
}

mutation CreateEmpatheticVersion {
  createPromptVersion(input: {
    promptId: "prompt_empathetic_456"
    content: """
Generate a highly empathetic customer support response.

Customer: {{customerName}} ({{customerTier}} tier)
Original Message: {{message}}
Sentiment: {{sentiment}}
Ticket ID: {{ticketId}}

Write a compassionate response that:
1. Acknowledges their frustration
2. Takes ownership of the issue
3. References the ticket number
4. Provides next steps
5. Assures urgent resolution
"""
    params: {
      model: "claude-3-5-sonnet-20241022"
      temperature: 0.7
      max_tokens: 400
    }
    inputSchema: {
      type: "object"
      properties: {
        customerName: { type: "string" }
        customerTier: { type: "string" }
        message: { type: "string" }
        sentiment: { type: "string" }
        ticketId: { type: "string" }
      }
      required: ["customerName", "message"]
    }
  }) {
    id
  }
}

VIP Response Prompt

mutation CreateVIPPrompt {
  createPrompt(input: {
    name: "vip-support-response"
    description: "Generates personalized responses for VIP customers"
    category: GENERATION
  }) {
    id
  }
}

mutation CreateVIPVersion {
  createPromptVersion(input: {
    promptId: "prompt_vip_789"
    content: """
Generate a personalized VIP customer support response.

Customer: {{customerName}} (Premium Member since {{memberSince}})
Message: {{message}}
Purchase History: {{purchaseCount}} orders

Write a warm, personalized response that:
1. Thanks them for their loyalty
2. Addresses their message directly
3. Offers priority assistance
4. Mentions their membership benefits
"""
    params: {
      model: "claude-3-5-sonnet-20241022"
      temperature: 0.6
      max_tokens: 350
    }
    inputSchema: {
      type: "object"
      properties: {
        customerName: { type: "string" }
        memberSince: { type: "string" }
        message: { type: "string" }
        purchaseCount: { type: "number" }
      }
      required: ["customerName", "message"]
    }
  }) {
    id
  }
}

Step 2: Create the Chain

Create Chain
graphql
mutation CreateChain {
  createChain(input: {
    name: "customer-support-automation"
    description: "Automated customer support with sentiment routing"
    isActive: true
    requiredInputs: {
      type: "object"
      properties: {
        message: {
          type: "string"
          description: "Customer support message"
        }
        customerId: {
          type: "string"
          description: "Customer ID"
        }
      }
      required: ["message", "customerId"]
    }
    intentTags: ["support", "customer-service", "help", "assistance"]
  }) {
    id
  }
}
Save the chain ID - we'll use it to add steps in the next section.

Step 3: Add Chain Steps

Step 0: Sentiment Analysis

mutation AddSentimentStep {
  addChainStep(input: {
    chainId: "chain_abc123"
    order: 0
    type: PROMPT
    promptId: "prompt_sentiment_123"
    inputMapping: {
      message: "{{message}}"
    }
    outputKey: "sentiment"
  }) {
    id
  }
}

Step 1: Fetch Customer Profile

mutation AddCustomerFetchStep {
  addChainStep(input: {
    chainId: "chain_abc123"
    order: 1
    type: API_CALL
    config: {
      url: "https://api.yourcompany.com/customers/{{customerId}}"
      method: "GET"
      headers: {
        Authorization: "Bearer YOUR_API_KEY"
      }
    }
    outputKey: "customer"
  }) {
    id
  }
}

Step 2: Check Sentiment

mutation AddSentimentCondition {
  addChainStep(input: {
    chainId: "chain_abc123"
    order: 2
    type: CONDITION
    config: {
      condition: "{{sentiment.sentiment}} == 'Negative'"
      trueTag: "negative_path"
      falseTag: "positive_neutral_path"
    }
  }) {
    id
  }
}

Step 3: Create Support Ticket (Negative Path)

mutation AddTicketCreation {
  addChainStep(input: {
    chainId: "chain_abc123"
    order: 3
    type: API_CALL
    tags: ["negative_path"]
    config: {
      url: "https://api.yourcompany.com/tickets"
      method: "POST"
      headers: {
        Authorization: "Bearer YOUR_API_KEY"
        "Content-Type": "application/json"
      }
      body: {
        customerId: "{{customerId}}"
        subject: "Urgent: Negative feedback from {{customer.name}}"
        message: "{{message}}"
        sentiment: "{{sentiment.sentiment}}"
        urgency: "{{sentiment.urgency}}"
        priority: "high"
      }
    }
    outputKey: "ticket"
  }) {
    id
  }
}

Step 4: Generate Empathetic Response (Negative Path)

mutation AddEmpatheticResponse {
  addChainStep(input: {
    chainId: "chain_abc123"
    order: 4
    type: PROMPT
    tags: ["negative_path"]
    promptId: "prompt_empathetic_456"
    inputMapping: {
      customerName: "{{customer.name}}"
      customerTier: "{{customer.tier}}"
      message: "{{message}}"
      sentiment: "{{sentiment.sentiment}}"
      ticketId: "{{ticket.id}}"
    }
    outputKey: "response"
  }) {
    id
  }
}

Step 5: Check Customer Tier (Positive/Neutral Path)

mutation AddTierCondition {
  addChainStep(input: {
    chainId: "chain_abc123"
    order: 5
    type: CONDITION
    tags: ["positive_neutral_path"]
    config: {
      condition: "{{customer.tier}} == 'premium'"
      trueTag: "premium_path"
      falseTag: "standard_path"
    }
  }) {
    id
  }
}

Step 6: VIP Response (Premium Path)

mutation AddVIPResponse {
  addChainStep(input: {
    chainId: "chain_abc123"
    order: 6
    type: PROMPT
    tags: ["premium_path"]
    promptId: "prompt_vip_789"
    inputMapping: {
      customerName: "{{customer.name}}"
      memberSince: "{{customer.memberSince}}"
      message: "{{message}}"
      purchaseCount: "{{customer.purchaseCount}}"
    }
    outputKey: "response"
  }) {
    id
  }
}

Step 7: Friendly Response (Standard Path)

mutation AddStandardResponse {
  addChainStep(input: {
    chainId: "chain_abc123"
    order: 7
    type: PROMPT
    tags: ["standard_path"]
    promptId: "prompt_friendly_123"
    inputMapping: {
      customerName: "{{customer.name}}"
      message: "{{message}}"
    }
    outputKey: "response"
  }) {
    id
  }
}

Step 8: Log Interaction (Always Runs)

mutation AddLogging {
  addChainStep(input: {
    chainId: "chain_abc123"
    order: 8
    type: API_CALL
    config: {
      url: "https://api.yourcompany.com/analytics/log"
      method: "POST"
      headers: {
        Authorization: "Bearer YOUR_API_KEY"
        "Content-Type": "application/json"
      }
      body: {
        customerId: "{{customerId}}"
        sentiment: "{{sentiment.sentiment}}"
        customerTier: "{{customer.tier}}"
        responseGenerated: true
        timestamp: "{{now}}"
      }
    }
    outputKey: "log_result"
  }) {
    id
  }
}

Step 4: Execute the Chain

Example 1: Negative Feedback

Execute Chain
graphql
mutation ExecuteChain {
  executeChain(
    chainId: "chain_abc123"
    input: {
      message: "I'm extremely disappointed. My order arrived damaged and I've been trying to get a refund for 2 weeks with no response!"
      customerId: "cust_456"
    }
  ) {
    output
    steps {
      order
      type
      status
      latency
      cost
    }
    totalLatency
    totalCost
  }
}
Response
json
{
  "data": {
    "executeChain": {
      "output": "Dear Sarah, I sincerely apologize for this extremely frustrating experience. You absolutely should not have had to wait 2 weeks for a refund, and I take full ownership of this failure on our part.\n\nI've immediately escalated your case to our senior support team under ticket #TKT-45678. Your refund will be processed within the next 24 hours, and you'll receive a confirmation email directly from our finance team.\n\nAdditionally, I'm applying a 20% discount to your next order as a gesture of our commitment to making this right. Your trust matters deeply to us, and we're implementing process improvements to ensure this never happens again.\n\nYou should expect an update within 4 hours. Thank you for bringing this to our attention.",
      "steps": [
        {
          "order": 0,
          "type": "PROMPT",
          "status": "success",
          "latency": 1200,
          "cost": 0.0015
        },
        {
          "order": 1,
          "type": "API_CALL",
          "status": "success",
          "latency": 450,
          "cost": 0
        },
        {
          "order": 2,
          "type": "CONDITION",
          "status": "success",
          "latency": 5,
          "cost": 0
        },
        {
          "order": 3,
          "type": "API_CALL",
          "status": "success",
          "latency": 380,
          "cost": 0
        },
        {
          "order": 4,
          "type": "PROMPT",
          "status": "success",
          "latency": 2100,
          "cost": 0.0025
        },
        {
          "order": 5,
          "type": "CONDITION",
          "status": "skipped",
          "latency": 0,
          "cost": 0
        },
        {
          "order": 6,
          "type": "PROMPT",
          "status": "skipped",
          "latency": 0,
          "cost": 0
        },
        {
          "order": 7,
          "type": "PROMPT",
          "status": "skipped",
          "latency": 0,
          "cost": 0
        },
        {
          "order": 8,
          "type": "API_CALL",
          "status": "success",
          "latency": 200,
          "cost": 0
        }
      ],
      "totalLatency": 4335,
      "totalCost": 0.0040
    }
  }
}
Steps 5, 6, and 7 were skipped because the negative path was taken. A support ticket was created and an empathetic response was generated.

Example 2: Premium Customer Query

Execute Chain
graphql
mutation ExecuteChain {
  executeChain(
    chainId: "chain_abc123"
    input: {
      message: "Hi! I'm interested in upgrading my subscription. What options do you have?"
      customerId: "cust_premium_789"
    }
  ) {
    output
    totalLatency
    totalCost
  }
}
Response
json
{
  "data": {
    "executeChain": {
      "output": "Hi Michael! It's wonderful to hear from you, and thank you for being a valued Premium member since 2022. We truly appreciate your continued loyalty!\n\nI'm excited to help you explore upgrade options. As a Premium member, you already enjoy priority support and exclusive features, but our Enterprise plan offers even more:\n\n• Dedicated account manager\n• Custom integrations\n• Advanced analytics dashboard\n• 24/7 white-glove support\n• Volume discounts (perfect given your 47 orders with us!)\n\nI'd love to schedule a personalized demo to show you exactly how these features could benefit your use case. Would you prefer a call this week or next?\n\nAs always, you have priority access to our team - feel free to reach out anytime!",
      "totalLatency": 3890,
      "totalCost": 0.0035
    }
  }
}

In this execution, the chain took the positive/neutral path, then the premium path, generating a personalized VIP response.

Step 5: Monitor and Optimize

Query Chain Statistics
graphql
query GetChainStats {
  chain(id: "chain_abc123") {
    name
    executions {
      id
      status
      totalLatency
      totalCost
      createdAt
      steps {
        type
        status
        latency
        cost
      }
    }
  }
}

Analyze execution patterns to optimize:

// Calculate path distribution
const executions = data.chain.executions

const pathCounts = {
  negative: 0,
  premium: 0,
  standard: 0,
}

executions.forEach(exec => {
  const steps = exec.steps

  // Check which response step executed
  if (steps.find(s => s.order === 4 && s.status === 'success')) {
    pathCounts.negative++
  } else if (steps.find(s => s.order === 6 && s.status === 'success')) {
    pathCounts.premium++
  } else if (steps.find(s => s.order === 7 && s.status === 'success')) {
    pathCounts.standard++
  }
})

console.log('Path Distribution:')
console.log(`Negative path: ${pathCounts.negative} (${(pathCounts.negative / executions.length * 100).toFixed(1)}%)`)
console.log(`Premium path: ${pathCounts.premium} (${(pathCounts.premium / executions.length * 100).toFixed(1)}%)`)
console.log(`Standard path: ${pathCounts.standard} (${(pathCounts.standard / executions.length * 100).toFixed(1)}%)`)

Key Patterns

  • Conditional Routing - Use CONDITION steps to route execution based on data
  • Shared Output Keys - Multiple conditional branches can write to the same output key
  • API Integration - Fetch external data and create records via API calls
  • Always-Run Steps - Steps without tags execute regardless of conditions
  • Nested Conditions - Tag CONDITION steps to create multi-level decision trees

Production Considerations

Error Handling - Add error handling for API calls with fallback responses
Timeout Configuration - Set appropriate timeouts for external API calls
Rate Limiting - Monitor execution volume and implement rate limiting
Cost Monitoring - This workflow costs ~$0.004 per execution. Monitor daily volumes to control spending.

Next Steps

Intent Routing

Automatically route messages to chains

Routing Example →

Workflow Concepts

Deep dive into workflow patterns

Workflow Steps →