Zum Hauptinhalt springen
connic
Starte kostenlos

Einen WhatsApp-Support-Agenten mit Twilio und connic bauen

WhatsApp über Twilio an einen connic-Agenten anbinden: Verlauf je Kunde behalten, Antworten richtig adressieren, Vorlagen und Sendefehler behandeln.

10. November 20269 Min. LesezeitAutor: connic engineering

Ein WhatsApp-Support-Agent auf connic ist ein bereitgestellter Agent mit zwei Connectors für Twilio Messaging: Ein Inbound-Connector startet für jede Nachricht, die ein Kunde schickt, einen Run, und ein Outbound-Connector sendet die Antwort des Agenten zurück. Im Folgenden geht es um die Einrichtung beider Connectors, den Verlauf pro Kunde, die Wahl des Empfängers und die Fälle, in denen WhatsApp eine Vorlage verlangt oder Twilio eine Nachricht nicht annimmt.

Den Absender vorbereiten

connic sendet und empfängt WhatsApp-Nachrichten über die Region US1 von Twilio. IE1 verarbeitet nur SMS-Text, und AU1 unterstützt Messaging gar nicht. Unter Project Settings → Connections wird dafür eine Twilio-Verbindung mit der Region US1, der Account SID (sie beginnt mit AC) und dem Auth Token dieser Region angelegt. Die Connector-Dateien weiter unten verweisen über den Namen der Verbindung, support-twilio, auf sie, sodass die Zugangsdaten nie im Code stehen. Dieselbe Verbindung kann auch Connectors für Twilio Voice bedienen, also für Telefonanrufe.

Für einen ersten Test ersetzt Twilios WhatsApp Sandbox einen registrierten Absender. Nach der Aktivierung in Twilio tritt das Testtelefon der Sandbox bei, indem es den Beitrittscode an deren Nummer whatsapp:+14155238886 schickt. Nach drei Tagen läuft die Sandbox-Sitzung ab, und das Telefon muss erneut beitreten. Im Produktivbetrieb ist der Absender ein registrierter WhatsApp-Absender im selben Twilio-Konto. Für den Wechsel wird sender in beiden Connector-Dateien geändert.

Telnyx Messaging bindet WhatsApp genauso an, mit getrennten Inbound- und Outbound-Connectors. Dieses Tutorial verwendet Twilio.

Inbound- und Outbound-Connector anlegen

Der Inbound-Connector empfängt Nachrichten und startet einen Run jedes verknüpften Agenten, hier whatsapp-support. Twilio bekommt von ihm eine leere Antwort, selbst sendet er nichts. Antworten verschickt ein eigener Outbound-Connector. Beide werden in connectors/*.yaml deklariert und mit dem Agenten bereitgestellt. Telefonnummern brauchen Anführungszeichen, weil YAML eine Nummer wie +14155238886 ohne sie als Ganzzahl liest und das Deployment fehlschlägt:

connectors/support-whatsapp.yaml
type: twilio_messaging
mode: inbound
connection: support-twilio
config:
  channel: whatsapp
  sender: "whatsapp:+14155238886"   # WhatsApp Sandbox von Twilio
agents:
  - whatsapp-support

Der Outbound-Connector verwendet dieselbe Verbindung, denselben Kanal und denselben Absender:

connectors/support-whatsapp-replies.yaml
type: twilio_messaging
mode: outbound
connection: support-twilio
config:
  channel: whatsapp
  sender: "whatsapp:+14155238886"
agents:
  - name: whatsapp-support
    outbound: { usage: automatic, source: [support-whatsapp] }

usage: automatic sendet die finale Ausgabe des Agenten, sobald sein Run abgeschlossen ist, und source beschränkt das auf Runs, die die Inbound-Datei gestartet hat. Ohne source würde der Connector auch nach Runs aus anderen Triggern senden, hinter denen kein WhatsApp-Kunde steht. Im Dashboard entspricht das einem Automatic outbound connector mit Only selected inputs und ausgewähltem Inbound-Connector.

Bei einem registrierten Absender trägt connic den Webhook für eingehende Nachrichten in Twilio ein, sobald es den Inbound-Connector anlegt. Dabei ersetzt connic die bisherige URL und schreibt den alten Wert in die Logs des Connectors und in das Audit Log des Projekts. Gehört der Absender zu einem Messaging Service, muss dieser vorher auf Defer to sender's webhook stehen. Für den Webhook der Sandbox bietet Twilio keine dokumentierte API, deshalb ist dieser Schritt Handarbeit: den Incoming-message webhook aus dem Detailbereich des Connectors kopieren, in den Sandbox-Einstellungen unter When a Message Comes in einfügen, HTTP POST wählen und speichern.

Was ein Run bekommt

Jede eingehende Nachricht startet pro verknüpftem Agenten einen Run mit dieser Eingabe, hier gekürzt:

input.json
{
  "message_sid": "SM00000000000000000000000000000001",
  "channel": "whatsapp",
  "from": "whatsapp:+14155550124",
  "to": "whatsapp:+14155238886",
  "text": "Kann ich den Mixer, den ich vor drei Wochen gekauft habe, noch zurückschicken?",
  "conversation_id": "twilio_messaging:9e0f77d9-2fd8-5a8e-8d47-59fbf5b8a091",
  "raw": {
    "MessageSid": "SM00000000000000000000000000000001",
    "From": "whatsapp:+14155550124",
    "To": "whatsapp:+14155238886",
    "NumMedia": "0"
  }
}

from ist der Kunde, to der Absender, beide mit dem Präfix whatsapp:. message_sid ist die ID, die Twilio der Nachricht gibt; schickt Twilio dieselbe Nachricht zweimal, startet kein zweiter Run. raw enthält alle Felder, die Twilio gesendet hat.

Statt einer Beschreibung schicken Kunden oft ein Foto. connic lädt eingehende Medien in files herunter, bis zu 50 Dateien und 32 MiB pro Nachricht, und führt jede Datei, die es auslassen musste, in file_issues auf. Der Agent sollte beides prüfen. Das Modell muss verstehen, was Kunden schicken: connic/gemini-3.8-flash versteht Fotos, PDFs und Sprachnachrichten. Eine Sprachnachricht beantwortet der Agent also wie eine getippte Nachricht.

Das Gespräch jedes Kunden behalten

Jede Nachricht startet einen eigenen Run, und ohne Session sieht der Agent nur die aktuelle Nachricht. conversation_id bleibt für jede Kombination aus Inbound-Connector, Twilio-Konto, Region, Kanal, Absender und Kunde gleich und bezeichnet damit das Gespräch eines Kunden mit einer Nummer. Als Key einer persistenten Session gibt sie jedem Kunden einen eigenen Verlauf:

agents/whatsapp-support.yaml
version: "1.0"

name: whatsapp-support
type: llm
model: connic/gemini-3.8-flash
description: "Beantwortet Kundenfragen auf WhatsApp"
system_prompt: |
  Du beantwortest Kundenfragen für den Acme-Shop auf WhatsApp.
  Rücksendungen: innerhalb von 30 Tagen nach Lieferung, gestartet unter
  Bestellungen im Kundenkonto; das Rücksendeetikett kommt per E-Mail.
  Antworte kurz und in der Sprache der Kundschaft,
  und gib die Antwort als reinen Text zurück.

max_concurrent_runs: 10

session:
  key: input.conversation_id
  ttl: 604800   # Verlauf läuft nach 7 Tagen ohne Nachricht ab

concurrency:
  key: conversation_id
  on_conflict: queue

Sessions sind aus, bis die Agentendatei session setzt, und die Änderung greift mit dem nächsten Deployment. ttl beendet einen Verlauf nach sieben Tagen ohne Nachricht; die nächste Nachricht des Kunden beginnt dann ohne Vorgeschichte. Ohne ttl läuft der Verlauf nie ab. Dieselben zwei Nachrichten mit und ohne Session-Key:

Mit session.key
  1. Kundin oder Kunde: Kann ich den Mixer, den ich vor drei Wochen gekauft habe, noch zurückschicken?

  2. Agent: Ja, Rücksendungen sind innerhalb von 30 Tagen nach Lieferung möglich. Soll ich dir erklären, wie es geht?

  3. Zwei Stunden später
  4. Kundin oder Kunde: Ja, bitte

  5. Agent: Öffne in deinem Kundenkonto die Bestellungen, wähle den Mixer und dann Zurücksenden. Das Rücksendeetikett kommt per E-Mail.

Ohne session.key
  1. Kundin oder Kunde: Kann ich den Mixer, den ich vor drei Wochen gekauft habe, noch zurückschicken?

  2. Agent: Ja, Rücksendungen sind innerhalb von 30 Tagen nach Lieferung möglich. Soll ich dir erklären, wie es geht?

  3. Zwei Stunden später
  4. Kundin oder Kunde: Ja, bitte

  5. Agent: Gern. Wobei kann ich dir helfen?

Ohne Session weiß der zweite Run nicht, wozu der Kunde Ja gesagt hat. Fotos aus einem langen Gespräch würden das Modell mit jeder Nachricht erneut erreichen; files: false im session-Block ersetzt frühere Dateien durch einen kurzen Hinweis und gibt die Dateien der aktuellen Nachricht weiterhin mit.

Mehrere Nachrichten kurz hintereinander

Auf WhatsApp verteilen viele Menschen einen Gedanken auf mehrere Nachrichten. Jede startet Sekunden nach der vorigen einen Run. Runs derselben Session warten nicht aufeinander: Überschneiden sich zwei, schlägt der Run fehl, der seinen Verlauf als zweiter schreibt, und diese Nachricht bleibt unbeantwortet. Der concurrency-Block verhindert das. Mit key: conversation_id und on_conflict: queue wartet der Run für eine zweite Nachricht desselben Kunden, bis der erste Run fertig ist, und startet dann mit diesem Austausch im Verlauf. Jede Nachricht bekommt weiterhin ihre eigene Antwort, und die Runs bearbeiten die Nachrichten in der Reihenfolge, in der sie ankamen. on_conflict: coalesce passt hier nicht, weil es einen wartenden Run abbricht, sobald eine neuere Nachricht kommt. Die abgebrochene Nachricht landet dann nie im Verlauf.

Die Concurrency-Regel wirkt pro Gespräch. max_concurrent_runs begrenzt den Agenten insgesamt und steht standardmäßig auf 1, sodass jeder Kunde auf alle anderen warten müsste. Das Beispiel erhöht den Wert auf 10 und setzt damit mindestens den Developer-Tarif voraus. Basic erlaubt 1, und ein Deployment über der Grenze des Tarifs schlägt fehl. Runs, die auf ihr Gespräch warten, belegen keinen dieser Plätze.

Die Antwort an die richtige Nummer senden

Der Outbound-Connector braucht für jede Nachricht einen Empfänger und nimmt den ersten, den er findet:

  1. to in der Ausgabe des Agenten, wenn die Ausgabe ein JSON-Objekt ist.
  2. Den Default recipient des Outbound-Connectors, also to in seiner config.
  3. Die Person, die geschrieben hat, bei einem Run, den ein Inbound-Connector für Twilio Messaging gestartet hat. Beide Connectors müssen dieselbe Verbindung, denselben Kanal und denselben Absender verwenden.

Die dritte Regel deckt den Normalfall ab, deshalb setzt das Beispiel keinen Default recipient. Ein Default recipient hat Vorrang: Bleibt einer aus dem Test übrig, etwa das Telefon einer Teamkollegin, bekommt dieses Telefon die Antworten an alle Kunden.

Außerdem verlangt die dritte Regel eine exakte Übereinstimmung. Verwendet der Outbound-Connector einen anderen Absender, etwa weil nur die Inbound-Datei von der Sandbox auf den registrierten Absender umgestellt wurde, hat die Antwort keinen Empfänger, und der Connector-Run schlägt mit „Set a recipient, provide 'to' in the output, or reply through the same Twilio connection and sender“ fehl.

Kunden nach Ablauf des 24-Stunden-Fensters erreichen

WhatsApp erlaubt frei formulierte Nachrichten nur innerhalb von 24 Stunden nach der letzten Nachricht des Kunden. Danach kann ein Unternehmen nur noch eine von WhatsApp genehmigte Vorlage senden, etwa eine Versandbenachrichtigung mit Platzhaltern für Bestellnummer und Liefertag. Die Vorlage wird in Twilio angelegt und zur Genehmigung eingereicht, und Twilio vergibt dafür eine Content SID, die mit HX beginnt. Twilios Anleitung zu Vorlagen beschreibt die Schritte. Eine Outbound-Nachricht nennt die Vorlage und füllt ihre Variablen:

template-payload.json
{
  "to": "whatsapp:+14155550124",
  "content_sid": "HX00000000000000000000000000000000",
  "content_variables": { "1": "12345", "2": "Freitag" }
}

Weil seit einem Tag niemand geschrieben hat, ist eine solche Nachricht meist keine Antwort und setzt to selbst. Der Agent übergibt sie als JSON-Ausgabe bei einer automatischen Verknüpfung oder als Payload, wenn er den Connector als Tool aufruft. Variablenwerte müssen Strings sein, und eine Nachricht mit content_sid darf weder Text noch media_urls enthalten. Eigene Vorlagen brauchen einen registrierten Absender; die Sandbox bietet nur die vorab genehmigten Testvorlagen von Twilio.

Der Connector kann content_sid und content_variables als Standardwerte setzen, die der Payload überschreibt. Am Antwort-Connector macht eine Standardvorlage jede Antwort zu dieser Vorlage. Reiner Text des Agenten wird dann ignoriert, und Medien-URLs werden abgelehnt. Ein zweiter Outbound-Connector für Vorlagen hält den Antwort-Connector für normale Antworten frei.

Wenn der Versand scheitert

Jeder Versand ist ein Run des Outbound-Connectors. Ein abgeschlossener Run bedeutet, dass Twilio die Nachricht angenommen hat, nicht, dass sie das Telefon erreicht hat. Seine Ausgabe enthält die Message SID und den Status, den Twilio zurückgegeben hat. In den Messaging Logs von Twilio stehen für diese SID der Zustellstatus und spätere Fehler des Kanals. Ein fehlgeschlagener Run fällt unter einen dieser Fälle:

FallWas connic tutNächster Schritt
connic erreicht Twilio nicht, oder Twilio antwortet mit 429Versucht es insgesamt dreimal und wartet nach einem 429 so lange, wie Twilios Retry-After verlangt, höchstens fünf MinutenKeiner, außer auch der letzte Versuch scheitert
Twilio lehnt die Nachricht abMarkiert den Run ohne neuen Versuch als fehlgeschlagen, mit HTTP-Status und dem Fehlercode von TwilioAbsender, Empfänger und Vorlage in Twilio prüfen
Das Ergebnis ist unklar: Die Anfrage bricht mittendrin ab, oder Twilio antwortet mit einem 5xx-StatusMarkiert den Run ohne neuen Versuch als fehlgeschlagen, weil Twilio die Nachricht womöglich schon angenommen hatVor dem erneuten Senden in den Messaging Logs nach der Nachricht suchen
Die Nachricht ist ungültig: kein Empfänger, Text über 1.600 Zeichen, mehr als eine Medien-URL oder eine content_sid in der Ausgabe zusammen mit TextMarkiert den Run als fehlgeschlagen, bevor Twilio kontaktiert wirdDie Ausgabe des Agenten oder den Connector korrigieren

Für den unklaren Fall gibt es keinen automatischen neuen Versuch, weil Twilio keinen dokumentierten Weg bietet, eine wiederholte Sendeanfrage zu erkennen, und der Kunde die Nachricht sonst womöglich doppelt bekäme. Schlägt der Run des Agenten selbst fehl, wird nichts gesendet, und der Kunde bekommt keine Antwort. Der Trace des Runs zeigt, warum.

Auch bei Medien setzt WhatsApp Grenzen. Eine Nachricht kann nur eine Medien-URL enthalten, und WhatsApp verwirft Text, der zusammen mit Audio, Video, Dokumenten oder anderen Medien außer Bildern gesendet wird. Text zu einem PDF muss deshalb als eigene Nachricht verschickt werden, etwa über einen zweiten Outbound-Connector, den der Agent als Tool aufruft. Die Referenz zu Twilio Messaging listet alle Felder, Grenzen und Statuscodes.

Beantworte Kundenfragen auf WhatsApp

Stelle einen Agenten auf connic bereit, verbinde deinen Twilio-Absender und antworte jedem Kunden mit dem bisherigen Gespräch im Kontext.

Häufig gestellte Fragen

Ja. Twilios WhatsApp Sandbox dient als Absender. Dafür werden beide Connectors mit der Sandbox-Nummer whatsapp:+14155238886 angelegt, und die Webhook-URL des Inbound-Connectors wird von Hand in die Sandbox-Einstellungen eingetragen, weil connic sie dort nicht setzen kann. Das Testtelefon muss der Sandbox vorher beitreten.

Nur mit einer persistenten Session. Jede Nachricht startet einen eigenen Run. Mit session.key auf input.conversation_id bekommt jeder Kunde einen Verlauf pro Absender, und eine ttl beendet diesen Verlauf nach einer Zeit ohne Nachrichten. Eine Concurrency-Regel auf conversation_id verhindert, dass sich kurz hintereinander gesendete Nachrichten überschneiden.

Ein Default recipient am Outbound-Connector hat Vorrang vor der Antwort an die Person, die geschrieben hat. Er sollte leer bleiben. Antworten gehen dann an die Person, die geschrieben hat, sofern Inbound- und Outbound-Connector dieselbe Verbindung, denselben Kanal und denselben Absender verwenden; andernfalls scheitert der Versand ohne Empfänger.

Nein. Er bedeutet, dass Twilio die Nachricht angenommen hat. Die Ausgabe des Runs enthält die Message SID, und die Messaging Logs in Twilio zeigen dazu den Zustellstatus und spätere Fehler des Kanals.

Twilio bekommt eine leere Antwort, und es startet kein Run. connic speichert diese Nachrichten nicht und spielt sie nicht erneut ab, sodass sie auch nach dem Fortsetzen des Connectors unbeantwortet bleiben.

Mehr aus dem Blog

Tutorial

KI-Token-Kosten nach Kunde oder Use Case in connic auswerten

Middleware versieht jeden Agenten-Run in connic mit Kunde oder Use Case, und Dashboards sowie die Seite Token Usage ordnen Token-Kosten nach diesem Wert.

15. Oktober 20267 Min. Lesezeit
Tutorial

Invoice Processor als MCP-Tool bereitstellen

connics Rechnungsvorlage als MCP-Tool bereitstellen, per Python aufrufen und die extrahierten Felder prüfen: vollständiges Client-Beispiel mit Testrechnung.

11. September 202610 Min. Lesezeit
Tutorial

Python-KI-Agenten ohne Kubernetes bereitstellen

Ein Python-KI-Agent lässt sich mit YAML, reinem Python, Tests als Deployment-Gate, Git und einer Managed Runtime in der EU ohne Kubernetes bereitstellen. Der Artikel enthält funktionsfähigen Code.

12. August 202612 Min. Lesezeit
Tutorial

So integrieren kleine Engineering-Teams einen KI-Agenten in SaaS

Ein praktikabler Weg zum ersten KI-Agenten im Produktivbetrieb: Aufgabe eingrenzen, per Konfiguration definieren, vorhandene Systeme anbinden und die Runtime verwalten lassen.

12. Juni 20269 Min. Lesezeit
Tutorial

KI-Agenten automatisch mit LLM Judges bewerten

Ein LLM Judge bewertet ausgewählte oder alle passenden Agenten-Runs nach festgelegten Kriterien. Score-Trends und Alerts machen Regressionen sichtbar.

29. März 202610 Min. Lesezeit
Tutorial

Datenbank, Retrieval oder Sessions: Speicher für Agenten im Vergleich

connics Datenbank, Retrieval und persistente Sessions im Vergleich: passende Einsatzbereiche, gespeicherte Gesprächsverläufe, TTL und zugehörige Runs.

4. März 202612 Min. Lesezeit
Tutorial

KI-Agenten: Vom Prototyp zum Produktivbetrieb

Eine Demo funktioniert hervorragend, bis 1.000 Nutzer gleichzeitig darauf zugreifen. Dieser Leitfaden beschreibt die oft zu spät berücksichtigten Anforderungen des Produktivbetriebs.

10. Januar 202610 Min. Lesezeit
Tutorial

Versteckte Kosten beim Self-Hosting von KI-Agenten

Ein Kubernetes-Deployment wirkt zunächst einfach. Der Artikel vergleicht die tatsächlichen Kosten selbst gehosteter KI-Agenten mit einer verwalteten Plattform.

18. Dezember 20257 Min. Lesezeit
Tutorial

connic RAG: Treffer nach Relevanz und Links zu Originaldateien

Ein RAG-Agent für den Produktivbetrieb sucht nach Bedeutung und exakten Begriffen, sortiert Treffer nach Relevanz, nennt Quellen und verlinkt Originaldateien.

15. November 202512 Min. Lesezeit