Zum Hauptinhalt springen
connic

E-Mail-Helpdesk

Von connic
KommunikationFortgeschritten

Der E-Mail-Helpdesk fragt Postfächer per IMAP ab, filtert automatische Antworten und klassifiziert Nachrichten nach Anliegen. Kontextbezogene Antwortentwürfe stammen aus dem konfigurierten Retrieval.

Mit der CLI installieren
$ connic init my-project \
  --templates=email-helpdesk
Publisher
Von connic
Schwierigkeitsgrad
Fortgeschritten
Funktioniert mit
E-Mail
Quelle
GitHub

Was das Template macht

Die Pipeline fragt ein IMAP-Postfach nach neuen E-Mails ab, filtert automatische Antworten und Spam mit StopProcessing Middleware, klassifiziert Nachrichten nach Anliegen, Dringlichkeit und Abteilung und entwirft kontextbezogene Antworten. Dazu können die Retrieval-Tools Inhalte abfragen, speichern und löschen. Antworten werden automatisch über die ausgehende SMTP-Verbindung versendet. Middleware übernimmt Absendermetadaten für personalisierte Antworten in den Context. Die Pipeline unterstützt mehrsprachige Antworten in der Sprache des Absenders.

Architektur
IMAP-Posteingang
helpdesk-pipeline
email-classifier
email-responder
Retrieval
Ausgehendes SMTP
Automatisierung des Support-Postfachs
Support-E-Mails aus einem gemeinsamen Postfach werden automatisch priorisiert und beantwortet; irrelevante Nachrichten werden herausgefiltert.
Pflege des Retrievals
Neue Lösungen aktualisieren das Retrieval automatisch; retrieval_delete entfernt veraltete Einträge.
Mehrsprachiger Support
Die Sprache des Absenders wird erkannt, damit internationale Kunden Antworten in derselben Sprache erhalten.

Jede Datei landet in deinem Projekt

connic init kopiert diese Dateien in deine Codebase. Dort änderst du sie wie den Rest deines Codes.

agents/email-helpdesk/
tools/
middleware/
schemas/
tests/
agents/email-helpdesk/email-classifier.yaml

LLM-Agent mit Modell, Prompt und Tools.

version: "1.0"

name: email-classifier
type: llm
model: connic/gpt-5.6-luna
description: "Classifies incoming emails by intent, urgency, and department"

system_prompt: |
  You classify incoming helpdesk emails.

  Classification rules:
  1. Intent: question, bug_report, feature_request, complaint, praise, spam, auto_reply
  2. Urgency: low (general inquiry), medium (needs response within 24h),
     high (blocking issue), critical (data loss, security, outage)
  3. Department: engineering, billing, sales, hr, legal, general
  4. Language: detect the email language (ISO 639-1 code)

  Auto-reply detection: if the email has out-of-office text, bounce notification
  patterns, or auto-generated content, classify as "auto_reply" intent with
  urgency "low". The middleware will stop processing these.

  For spam classification, set intent to "spam" and urgency to "low".
  The middleware will block these before the responder runs.

  Extract the sender's name from the "from" field. Include the "from_address"
  and "subject" in your output - the responder needs them for the reply.

temperature: 0.2
output_schema: email-classification

guardrails:
  input:
    - type: pii
      mode: redact
      config:
        entities: [phone, credit_card]
agents/email-helpdesk/email-responder.yaml

LLM-Agent mit Modell, Prompt und Tools.

version: "1.0"

name: email-responder
type: llm
model: connic/gpt-5.6-terra
description: "Drafts email replies using Retrieval and classification context"

system_prompt: |
  You draft replies to helpdesk emails. The previous agent (email-classifier)
  has classified the email with intent, urgency, department, and sender details.

  Response workflow:
  1. Search Retrieval first using search_helpdesk_knowledge with relevant terms.
     Try multiple search terms if the first doesn't return good results.
  2. Draft a reply that:
     - Addresses the sender by name (from classification data)
     - Matches the formality of the original email
     - Answers the question if knowledge is available
     - Acknowledges the issue and sets expectations if escalation is needed
     - Is written in the same language as the original email
  3. If you discover a reusable answer, store it with save_helpdesk_solution.
  4. If knowledge is outdated or wrong, remove it with remove_stale_entry.

  CRITICAL - Output format for the Email outbound connector:
  Your output schema maps directly to SMTP email fields. The outbound
  connector parses your JSON output to send the actual email.

  - "to": use the original sender's from_address
  - "subject": prefix with "RE: " + the original subject
  - "body": plain text email body

  Body guidelines:
  - Keep under 150 words for simple questions, up to 300 for complex ones
  - Always include a closing line inviting follow-up questions
  - Never promise timelines you can't guarantee
  - Sign off as "The [Company] Support Team"

temperature: 0.5
output_schema: email-reply

tools:
  - helpdesk_tools.search_helpdesk_knowledge
  - helpdesk_tools.save_helpdesk_solution
  - helpdesk_tools.remove_stale_entry

guardrails:
  output:
    - type: system_prompt_leakage
      mode: block
    - type: pii_leakage
      mode: block
      config:
        entities: [credit_card, ssn]
    - type: data_exfiltration
      mode: block
agents/email-helpdesk/helpdesk-pipeline.yaml

Führt seine Agenten nacheinander aus, jeden mit der Ausgabe des vorherigen.

version: "1.0"

name: helpdesk-pipeline
type: sequential
description: "Classify incoming email, then draft and send a reply"

agents:
  - email-classifier
  - email-responder
tools/helpdesk_tools.py

Python-Funktionen, die die Agenten als Tools aufrufen.

"""Helpdesk knowledge tools - domain wrappers over retrieval primitives."""

from connic.tools import retrieval_query, retrieval_store, retrieval_delete


async def search_helpdesk_knowledge(query: str) -> list[dict]:
    """Search helpdesk Retrieval for relevant answers.

    Args:
        query: Customer question or keywords from the email.

    Returns:
        List of matching entries with content and relevance score.
    """
    result = await retrieval_query(query, namespace="helpdesk", max_results=3)
    return result.get("results", [])


async def save_helpdesk_solution(
    content: str,
    entry_id: str | None = None,
) -> dict:
    """Store a reusable answer in helpdesk Retrieval.

    Args:
        content: The answer or solution. Include the original question for better retrieval.
        entry_id: Optional stable ID for future updates (e.g. "returns-policy").

    Returns:
        Store result with entry_id.
    """
    return await retrieval_store(content, namespace="helpdesk", entry_id=entry_id)


async def remove_stale_entry(entry_id: str) -> dict:
    """Remove an outdated entry from helpdesk Retrieval.

    Args:
        entry_id: The ID of the entry to remove.

    Returns:
        Deletion result.
    """
    return await retrieval_delete(entry_id=entry_id, namespace="helpdesk")
middleware/email-classifier.py

Läuft vor und nach dem gleichnamigen Agenten.

"""Middleware for email-classifier.

Filters auto-replies and obvious spam before the LLM runs.
Extracts sender metadata into context for the entire pipeline.

The Email inbound connector delivers a JSON payload with fields:
from, from_address, to, subject, body_text, body_html, attachments, _email
"""

import json
import re
from typing import Any

from connic import StopProcessing

AUTO_REPLY_PATTERNS = [
    r"out of office",
    r"automatic reply",
    r"auto-reply",
    r"i am currently out",
    r"on vacation until",
    r"delivery status notification",
    r"undeliverable",
    r"mailer-daemon",
]


async def before(content: dict[str, Any], context: dict[str, Any]) -> dict[str, Any]:
    """Filter auto-replies and enrich context with sender info."""
    parts = content.get("parts", [])
    full_text = ""
    for part in parts:
        if isinstance(part, dict) and "text" in part:
            full_text += part["text"]

    email_data = None
    try:
        email_data = json.loads(full_text)
    except (json.JSONDecodeError, TypeError):
        pass

    if isinstance(email_data, dict):
        context["from_address"] = email_data.get("from_address", "")
        context["sender_name"] = _extract_name(email_data.get("from", ""))
        context["original_subject"] = email_data.get("subject", "")
        context["message_id"] = email_data.get("message_id", "")

        body = email_data.get("body_text", "") or email_data.get("body_html", "")
        subject = email_data.get("subject", "")
        check_text = f"{subject} {body}".lower()
    else:
        check_text = full_text.lower()

    for pattern in AUTO_REPLY_PATTERNS:
        if re.search(pattern, check_text):
            raise StopProcessing("Auto-reply detected. No response needed.")

    from_addr = (email_data or {}).get("from_address", "").lower()
    if "mailer-daemon" in from_addr or "noreply" in from_addr:
        raise StopProcessing("System email detected. No response needed.")

    return content


async def after(response: str, context: dict[str, Any]) -> str:
    """Pass classification through to the responder."""
    return response


def _extract_name(from_field: str) -> str:
    """Extract display name from 'Name <email>' format."""
    match = re.match(r"^([^<]+)<", from_field)
    if match:
        return match.group(1).strip()
    return from_field.split("@")[0] if "@" in from_field else from_field
schemas/email-classification.json

JSON-Schema für die strukturierte Ausgabe eines Agenten.

{
  "type": "object",
  "description": "Email classification result",
  "properties": {
    "intent": {
      "type": "string",
      "enum": ["question", "bug_report", "feature_request", "complaint", "praise", "spam", "auto_reply"],
      "description": "Primary intent of the email"
    },
    "urgency": {
      "type": "string",
      "enum": ["low", "medium", "high", "critical"]
    },
    "department": {
      "type": "string",
      "enum": ["engineering", "billing", "sales", "hr", "legal", "general"]
    },
    "language": {
      "type": "string",
      "description": "ISO 639-1 language code (e.g. en, de, fr)"
    },
    "sender_name": {
      "type": "string",
      "description": "Extracted sender display name"
    },
    "from_address": {
      "type": "string",
      "description": "Sender email address (from from_address field) - needed for reply routing"
    },
    "original_subject": {
      "type": "string",
      "description": "Original email subject line - needed for reply subject"
    },
    "summary": {
      "type": "string",
      "description": "One-line summary of the email content"
    },
    "key_topics": {
      "type": "array",
      "items": {"type": "string"},
      "description": "Main topics for Retrieval search"
    }
  },
  "required": ["intent", "urgency", "department", "summary", "from_address", "original_subject"]
}
schemas/email-reply.json

JSON-Schema für die strukturierte Ausgabe eines Agenten.

{
  "type": "object",
  "description": "Email reply for the Email outbound connector. Fields map directly to SMTP fields.",
  "properties": {
    "to": {
      "type": "string",
      "description": "Recipient email address (use the sender's from_address from the inbound email)"
    },
    "subject": {
      "type": "string",
      "description": "Email subject line (e.g. RE: Original Subject)"
    },
    "body": {
      "type": "string",
      "description": "Plain text email body"
    },
    "html_body": {
      "type": "string",
      "description": "Optional HTML version of the body"
    },
    "cc": {
      "type": "string",
      "description": "Optional carbon copy recipient"
    },
    "reply_to": {
      "type": "string",
      "description": "Optional reply-to address"
    }
  },
  "required": ["to", "subject", "body"]
}
tests/email-classifier.yaml

2 Testfälle für connic test.

version: "1.0"

# email-classifier has no custom tools. The middleware filters auto-replies and
# system emails with StopProcessing before the LLM runs.
defaults:
  runs: 1
  timeout_s: 60

tests:
  - name: classifies_question
    payload: '{"from": "Jane Doe <jane@acme.com>", "from_address": "jane@acme.com", "to": "support@example.com", "subject": "How do I reset my password?", "body_text": "I forgot my password and cannot log in. How do I reset it?"}'
    expected_result: output.intent == "question"

  - name: drops_auto_reply
    payload: '{"from": "Bob <bob@acme.com>", "from_address": "bob@acme.com", "to": "support@example.com", "subject": "Out of Office", "body_text": "I am currently out of office until next Monday and will reply on my return."}'
    expected_result: status == "completed"
tests/email-responder.yaml

2 Testfälle für connic test.

version: "1.0"

# The responder only calls custom helpdesk tools, so strict_mocks guarantees it
# never reached for anything unstubbed. Retrieval is mocked so nothing
# is written or deleted.
defaults:
  runs: 1
  timeout_s: 90
  mocks: helpdesk_mocks
  strict_mocks: true

tests:
  - name: drafts_reply_from_knowledge
    payload: '{"intent": "question", "urgency": "low", "department": "general", "language": "en", "sender_name": "Jane", "from_address": "jane@acme.com", "original_subject": "How do I reset my password?", "summary": "Customer cannot reset their password."}'
    expected_result: output.body != "" and output.to != ""
    expected_tool_calls:
      - helpdesk_tools.search_helpdesk_knowledge

  - name: removes_outdated_entry
    payload: '{"intent": "question", "urgency": "low", "department": "general", "language": "en", "sender_name": "Jane", "from_address": "jane@acme.com", "original_subject": "Password reset steps", "summary": "Customer reports the Retrieval entry hd_old_1 about password reset is outdated and wrong. Remove the stale entry hd_old_1 and reply explaining the current steps."}'
    expected_result: output.body != "" and output.to != ""
    expected_tool_calls:
      - helpdesk_tools.remove_stale_entry

Installieren und bereitstellen

Installiere das Template, erstelle ein connic-Projekt und starte das Deployment über ein verbundenes Git-Repository oder die CLI.

Voraussetzungen
  • Python 3.10+
  • Ein connic-Konto mit einem angelegten Projekt
  • Projektguthaben für ein connic/* KI-Modell oder Zugangsdaten für einen BYOK Provider
Deployment mit Git
Verbinde das Repository unter Projekt Settings → Git & Environments, ordne einen Branch einem Environment zu und pushe.
Deployment mit der CLI
Erstelle unter Projekt Settings → API Keys & MCP Auth einen API Key, führe im Projektordner connic login aus und danach connic deploy. Änderungen probierst du vorher mit connic dev und connic test aus.
Verbinden und konfigurieren
Füge eine eingehende E-Mail-Verbindung (IMAP) zum Abfragen des Postfachs und eine ausgehende E-Mail-Verbindung (SMTP) zum Versenden von Antworten hinzu. Hinterlege für beide die Zugangsdaten des Mailservers. Ein connic/* KI-Modell nutzen oder BYOK-Zugangsdaten unter Projekt Settings → LLM Provider hinterlegen.
~/my-project
SDK
$ pip install connic-composer-sdk
Projekt
$ connic init my-project --templates=email-helpdesk
connic Init
──────────────────────────────
→ Created directory: my-project
→ Creating project structure...
✓ agents/, tools/, middleware/, schemas/
→ Fetching templates from connic-awesome-agents...
✓ Fetched
→ Merging templates...
✓ Added template: email-helpdesk
→ Next steps:
1. Run `connic lint` to validate your project
2. Run `connic dev` to choose a dev environment with hot reload
3. Run `connic deploy` when ready
✓ Initialized with templates: email-helpdesk
Deploy
$ connic deploy
→ Validating project files...
✓ Lint passed: 3 agent(s) validated (email-classifier, email-responder, helpdesk-pipeline)
✓ Lint passed
✓ Deployment created.

Weitere Agent Templates

Zum Marketplace