> ## Documentation Index
> Fetch the complete documentation index at: https://docs.praison.ai/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

<Steps>
  <Step title="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
    ```
  </Step>

  <Step title="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
    ```
  </Step>

  <Step title="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
    ```
  </Step>

  <Step title="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
    ```
  </Step>
</Steps>

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

<Tabs>
  <Tab title="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.</Warning>
  </Tab>

  <Tab title="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>

  <Tab title="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>

  <Tab title="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>

  <Tab title="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>

  <Tab title="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"
        })
    ```
  </Tab>
</Tabs>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Low confidence scores">
    Complex or ambiguous messages may result in lower confidence. Review these manually.
  </Accordion>

  <Accordion title="Tone doesn't match">
    Ensure the tone parameter matches your brand guidelines. Customize instructions if needed.
  </Accordion>
</AccordionGroup>

## Next Steps

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