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:
type: twilio_messaging
mode: inbound
connection: support-twilio
config:
channel: whatsapp
sender: "whatsapp:+14155238886" # WhatsApp Sandbox von Twilio
agents:
- whatsapp-supportDer Outbound-Connector verwendet dieselbe Verbindung, denselben Kanal und denselben Absender:
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:
{
"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:
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: queueSessions 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:
Kundin oder Kunde: Kann ich den Mixer, den ich vor drei Wochen gekauft habe, noch zurückschicken?
Agent: Ja, Rücksendungen sind innerhalb von 30 Tagen nach Lieferung möglich. Soll ich dir erklären, wie es geht?
- Zwei Stunden später
Kundin oder Kunde: Ja, bitte
Agent: Öffne in deinem Kundenkonto die Bestellungen, wähle den Mixer und dann Zurücksenden. Das Rücksendeetikett kommt per E-Mail.
Kundin oder Kunde: Kann ich den Mixer, den ich vor drei Wochen gekauft habe, noch zurückschicken?
Agent: Ja, Rücksendungen sind innerhalb von 30 Tagen nach Lieferung möglich. Soll ich dir erklären, wie es geht?
- Zwei Stunden später
Kundin oder Kunde: Ja, bitte
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:
toin der Ausgabe des Agenten, wenn die Ausgabe ein JSON-Objekt ist.- Den Default recipient des Outbound-Connectors, also
toin seinerconfig. - 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:
{
"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:
| Fall | Was connic tut | Nächster Schritt |
|---|---|---|
connic erreicht Twilio nicht, oder Twilio antwortet mit 429 | Versucht es insgesamt dreimal und wartet nach einem 429 so lange, wie Twilios Retry-After verlangt, höchstens fünf Minuten | Keiner, außer auch der letzte Versuch scheitert |
| Twilio lehnt die Nachricht ab | Markiert den Run ohne neuen Versuch als fehlgeschlagen, mit HTTP-Status und dem Fehlercode von Twilio | Absender, Empfänger und Vorlage in Twilio prüfen |
Das Ergebnis ist unklar: Die Anfrage bricht mittendrin ab, oder Twilio antwortet mit einem 5xx-Status | Markiert den Run ohne neuen Versuch als fehlgeschlagen, weil Twilio die Nachricht womöglich schon angenommen hat | Vor 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 Text | Markiert den Run als fehlgeschlagen, bevor Twilio kontaktiert wird | Die 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.