> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Customer Support Reply Drafter

> Draft professional support replies with configurable tone and confidence scoring

# 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.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    Input[📧 Customer Message] --> Analyze[Analyze Intent]
    Analyze --> Draft[Draft Reply]
    Draft --> Score[Confidence Score]
    Score --> Output[📝 Reply + Score]

    classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef process fill:#189AB4,stroke:#7C90A0,color:#fff

    class Input,Output input
    class Analyze,Draft,Score process
```

### Input/Output Contract

| Input | Type | Required | Description |
| - | - | - | - |
| `ticket_id` | string | Yes | Support ticket identifier |
| `message` | string | Yes | Customer message to respond to |
| `tone` | string | No | Response tone (default: `professional`) |

| Output | Type | Description |
| - | - | - |
| `reply` | string | Generated reply draft |
| `confidence` | number | Confidence score (0-1) |
| `ok` | boolean | Success indicator |

## Prerequisites

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
export OPENAI_API_KEY=your_key_here
pip install praisonaiagents
```

## Step-by-Step Build

**1. Create Recipe Directory**

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
mkdir -p ~/.praisonai/templates/customer-support-reply-drafter
cd ~/.praisonai/templates/customer-support-reply-drafter
```

**2. Create TEMPLATE.yaml**

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
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**

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# recipe.py
from praisonaiagents import Agent, Task, AgentTeam

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 = AgentTeam(
            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**

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# 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

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# 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

> [tab] Model 1: Embedded SDK
>
> ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> 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"])
> ```
>
> > [!WARNING]
> >
> > **Safety:** Customer messages may contain PII. Handle securely.

> [tab] Model 2: CLI Invocation
>
> ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> praison recipes run customer-support-reply-drafter \
>   --input '{"ticket_id": "T-123", "message": "Help!"}' \
>   --json | jq '.reply'
> ```

> [tab] Model 3: Plugin Mode
>
> ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> class SupportPlugin:
>     def draft_reply(self, ticket_id, message, tone="professional"):
>         from praisonai import recipe
>         return recipe.run(
>             "customer-support-reply-drafter",
>             input={"ticket_id": ticket_id, "message": message, "tone": tone}
>         )
> ```

> [tab] Model 4: Local HTTP Sidecar
>
> ```javascript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> const response = await fetch('http://localhost:8765/recipes/customer-support-reply-drafter/run', {
>   method: 'POST',
>   body: JSON.stringify({
>     ticket_id: 'T-123',
>     message: 'I need help',
>     tone: 'friendly'
>   })
> });
> ```

> [tab] Model 5: Remote Managed Runner
>
> ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> response = requests.post(
>     "https://api.support-tools.com/draft-reply",
>     headers={"Authorization": f"Bearer {api_key}"},
>     json={"ticket_id": "T-123", "message": "Help needed"}
> )
> ```

> [tab] Model 6: Event-Driven
>
> ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> def on_new_ticket(event):
>     import queue as q
>     job_queue = q.Queue()  # Replace with SQS/RabbitMQ in production
>     job_queue.put({
>         "recipe": "customer-support-reply-drafter",
>         "input": {
>             "ticket_id": event['ticket_id'],
>             "message": event['message']
>         },
>         "callback_url": f"https://api.example.com/tickets/{event['ticket_id']}/draft"
>     })
> ```

## Troubleshooting

> [accordion] Low confidence scores
>
> Complex or ambiguous messages may result in lower confidence. Review these manually.

> [accordion] Tone doesn't match
>
> Ensure the tone parameter matches your brand guidelines. Customize instructions if needed.

## Next Steps

* **[Meeting Minutes Action Items](https://praison.ai/docs/examples/recipe-examples/meeting-minutes-action-items.md)** - Extract action items from support calls
* **[Document Summarizer](https://praison.ai/docs/examples/recipe-examples/document-summarizer-with-citations.md)** - Summarize support documentation


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.