Skip to main content

Customer Support Reply Drafter

Generate professional customer support replies with tone control and confidence scoring.

Problem Statement

Who: Support teams, customer service managers, help desk operators
Why: Consistent, high-quality replies improve customer satisfaction and reduce response time.

What You’ll Build

A recipe that analyzes customer messages and drafts appropriate, professional replies.

Input/Output Contract

InputTypeRequiredDescription
ticket_idstringYesSupport ticket identifier
messagestringYesCustomer message to respond to
tonestringNoResponse tone (default: professional)
OutputTypeDescription
replystringGenerated reply draft
confidencenumberConfidence score (0-1)
okbooleanSuccess indicator

Prerequisites

export OPENAI_API_KEY=your_key_here
pip install praisonaiagents

Step-by-Step Build

1

Create Recipe Directory

mkdir -p ~/.praison/templates/customer-support-reply-drafter
cd ~/.praison/templates/customer-support-reply-drafter
2

Create TEMPLATE.yaml

name: customer-support-reply-drafter
version: "1.0.0"
description: "Generate professional support reply drafts"
author: "PraisonAI"
license: "MIT"

tags:
  - support
  - customer-service
  - text-generation

requires:
  env:
    - OPENAI_API_KEY
  packages:
    - praisonaiagents

inputs:
  ticket_id:
    type: string
    description: "Support ticket identifier"
    required: true
  message:
    type: string
    description: "Customer message to respond to"
    required: true
  tone:
    type: string
    description: "Response tone"
    default: "professional"
    enum:
      - professional
      - friendly
      - formal

outputs:
  reply:
    type: string
    description: "Generated reply draft"
  confidence:
    type: number
    description: "Confidence score (0-1)"

cli:
  command: "praison recipes run customer-support-reply-drafter"
  examples:
    - 'praison recipes run customer-support-reply-drafter --input ''{"ticket_id": "T-123", "message": "I need help"}'''

safety:
  dry_run_default: false
  requires_consent: false
  overwrites_files: false
  network_access: true
  pii_handling: true
3

Create recipe.py

# recipe.py
from praisonaiagents import Agent, Task, PraisonAIAgents

def run(input_data: dict, config: dict = None) -> dict:
    """Generate a support reply draft."""
    ticket_id = input_data.get("ticket_id")
    message = input_data.get("message")
    tone = input_data.get("tone", "professional")
    
    if not ticket_id:
        return {"ok": False, "error": {"code": "MISSING_INPUT", "message": "ticket_id is required"}}
    
    if not message:
        return {"ok": False, "error": {"code": "MISSING_INPUT", "message": "message is required"}}
    
    try:
        tone_guidelines = {
            "professional": "Clear, courteous, and business-appropriate",
            "friendly": "Warm, approachable, and conversational",
            "formal": "Polished, respectful, and traditional"
        }
        
        # Create support agent
        support_agent = Agent(
            name="Support Specialist",
            role="Customer Support Expert",
            goal=f"Draft a {tone} reply to customer inquiries",
            instructions=f"""
            You are an expert customer support specialist.
            Tone: {tone} - {tone_guidelines[tone]}
            
            Guidelines:
            - Be helpful and empathetic
            - Address the customer's concern directly
            - Provide clear next steps if applicable
            - Keep responses concise but complete
            - Never make promises you can't keep
            - Acknowledge the customer's frustration if present
            """,
        )
        
        # Create quality checker
        quality_agent = Agent(
            name="Quality Reviewer",
            role="Support Quality Analyst",
            goal="Ensure reply quality and assign confidence",
            instructions="""
            You are a support quality analyst.
            - Check for completeness
            - Verify tone consistency
            - Ensure no inappropriate content
            - Assign confidence score (0-1)
            - Flag any concerns
            """,
        )
        
        # Define tasks
        draft_task = Task(
            name="draft_reply",
            description=f"""
            Draft a {tone} reply to this customer message:
            
            Ticket: {ticket_id}
            Message: {message}
            
            Provide a professional, helpful response.
            """,
            expected_output="A well-crafted support reply",
            agent=support_agent,
        )
        
        review_task = Task(
            name="review_reply",
            description="""
            Review the draft reply for quality.
            Assign a confidence score from 0 to 1 where:
            - 0.9-1.0: Excellent, ready to send
            - 0.7-0.9: Good, minor review recommended
            - 0.5-0.7: Acceptable, review before sending
            - Below 0.5: Needs revision
            
            Output format: CONFIDENCE: X.XX
            """,
            expected_output="Confidence score and any notes",
            agent=quality_agent,
            context=[draft_task],
        )
        
        # Execute
        agents = PraisonAIAgents(
            agents=[support_agent, quality_agent],
            tasks=[draft_task, review_task],
        )
        
        result = agents.start()
        
        # Parse confidence
        review_text = result.get("review_reply", "")
        confidence = parse_confidence(review_text)
        
        return {
            "ok": True,
            "reply": result.get("draft_reply", ""),
            "confidence": confidence,
            "artifacts": [],
            "warnings": [] if confidence >= 0.7 else ["Low confidence - review before sending"],
        }
        
    except Exception as e:
        return {"ok": False, "error": {"code": "PROCESSING_ERROR", "message": str(e)}}


def parse_confidence(text: str) -> float:
    """Extract confidence score from review text."""
    import re
    match = re.search(r'CONFIDENCE:\s*([\d.]+)', text, re.IGNORECASE)
    if match:
        try:
            return min(1.0, max(0.0, float(match.group(1))))
        except ValueError:
            pass
    
    # Default confidence based on text analysis
    if "excellent" in text.lower() or "ready" in text.lower():
        return 0.9
    elif "good" in text.lower():
        return 0.8
    elif "acceptable" in text.lower():
        return 0.6
    return 0.7
4

Create test_recipe.py

# test_recipe.py
import pytest
from recipe import run, parse_confidence

def test_missing_ticket_id():
    result = run({"message": "Help me"})
    assert result["ok"] is False
    assert result["error"]["code"] == "MISSING_INPUT"

def test_missing_message():
    result = run({"ticket_id": "T-123"})
    assert result["ok"] is False
    assert result["error"]["code"] == "MISSING_INPUT"

def test_parse_confidence():
    assert parse_confidence("CONFIDENCE: 0.85") == 0.85
    assert parse_confidence("The reply is excellent") == 0.9
    assert parse_confidence("This is good") == 0.8

def test_confidence_bounds():
    assert parse_confidence("CONFIDENCE: 1.5") == 1.0
    assert parse_confidence("CONFIDENCE: -0.5") == 0.0

@pytest.mark.integration
def test_end_to_end():
    result = run({
        "ticket_id": "T-123",
        "message": "I can't log into my account",
        "tone": "professional"
    })
    
    assert result["ok"] is True
    assert len(result["reply"]) > 50
    assert 0 <= result["confidence"] <= 1

@pytest.mark.integration
def test_different_tones():
    tones = ["professional", "friendly", "formal"]
    for tone in tones:
        result = run({
            "ticket_id": "T-124",
            "message": "How do I reset my password?",
            "tone": tone
        })
        assert result["ok"] is True

Run Locally

# Basic usage
praison recipes run customer-support-reply-drafter \
  --input '{"ticket_id": "T-123", "message": "My order hasnt arrived"}'

# With tone
praison recipes run customer-support-reply-drafter \
  --input '{"ticket_id": "T-456", "message": "This is unacceptable!", "tone": "formal"}'

Deploy & Integrate: 6 Integration Models

from praisonai import recipe

result = recipe.run(
    "customer-support-reply-drafter",
    input={
        "ticket_id": "T-123",
        "message": "I need a refund",
        "tone": "professional"
    }
)

if result.ok:
    if result.output["confidence"] >= 0.8:
        send_reply(result.output["reply"])
    else:
        queue_for_review(result.output["reply"])
Safety: Customer messages may contain PII. Handle securely.

Troubleshooting

Complex or ambiguous messages may result in lower confidence. Review these manually.
Ensure the tone parameter matches your brand guidelines. Customize instructions if needed.

Next Steps