Telefonagent mit Terminbuchung: technische Umsetzung
Wie der Agent Anrufe annimmt, freie Zeiten sucht und bucht, was die KI allein darf und wie Fehler und Sonderfälle abgefangen werden.
- Werkzeuge für den Agenten
- 10
- Schritte bis zur Buchung: Vorschlag, dann Buchung
- 2
- Buchungsfenster im Voraus
- 60 min bis 14 Tage
- Zeitlimit für Anfragen ans Buchungssystem
- 15 s
- Testdateien (pytest)
- 10
So wird aus einem Anruf ein Termin
Jeder Schritt lässt sich aufklappen.
Berechtigungen des Modells
Kein Zugriff
- Bestehende Termine nachschlagen, stornieren oder umbuchen
- Termine bei Personen vergeben, die der Betrieb von der automatischen Vergabe ausnimmt
- Sich als Mensch ausgeben
- Interne Kennungen, Fehlercodes oder Werkzeugnamen nennen
- Anrufe weiterleiten (nicht vorgesehen)
Erlaubt ohne Freigabe
- Fragen zu Leistungen, Preisen, Öffnungszeiten und Anfahrt beantworten
- Freie Zeiten suchen und anbieten
- Einen Buchungsvorschlag anlegen und nach klarem Ja buchen
- Termine für zwei Personen zur gleichen Zeit suchen und buchen
- Eine Nachricht mit Rückrufwunsch ans Team schicken
- Das Gespräch nach der Verabschiedung beenden
Kontrollen
Zwei Schritte bis zur Buchung
Zuerst entsteht mit preview_booking ein Vorschlag mit eigener Kennung, erst book bucht wirklich. Das erzwingt die Buchungs-API: book arbeitet nur mit der Kennung eines Vorschlags und prüft die Pflichtfelder des Betriebs. Ob die Zeit frei ist und die Eingaben stimmen, entscheidet die API.
Datum und Uhrzeit aus dem Code
Einen genannten Wochentag rechnet der Code in ein Datum um, nie in die Vergangenheit. Uhrzeiten zwischen 1 und 8 Uhr liest er als Abendzeiten, weil am Telefon „halb acht“ fast immer 19:30 meint. Beides war vorher eine Fehlerquelle des Sprachmodells.
Rufnummer fertig zum Vorlesen
Die Nummer des Anrufers steht im Prompt bereits in nationaler Form und Ziffer für Ziffer ausgeschrieben. Ob eine Nummer SMS-tauglich ist, liefert die Buchungs-API, nicht das Modell.
Belegte Zeit mit Alternativen
Meldet preview_booking eine belegte Zeit, sucht der Code sofort die nächstgelegenen freien Zeiten und gibt sie mit der Anweisung zurück, nur diese anzubieten. Das spart einen ganzen Durchlauf von Sprachmodell und Suche.
Kein Werkzeug für Stornos
Bestehende Termine nachschlagen, absagen oder verschieben kann der Agent gar nicht, weil er dafür kein Werkzeug hat. Das ist keine Arbeitsregel, sondern fehlt schlicht.
Fehler mit nächster Anweisung
Lehnt die API eine Anfrage wegen unbekannter Kennungen ab, bekommt der Agent den Hinweis, nur Kennungen aus dem Menü zu verwenden, statt denselben Aufruf zu wiederholen. Platzhalter wie „any“, die Sprachmodelle gern als Parameter schicken, entfernt der Code vor dem Aufruf.
Arbeitsregeln im Prompt
Einige Regeln stehen nur im Prompt und sind keine technische Sperre: freie Zeiten nur aus find_times nennen, nach Name und Nummer erst fragen, wenn eine Uhrzeit feststeht, unklare Antworten nicht als Zustimmung werten und nie behaupten, etwas geprüft zu haben, ohne ein Werkzeug aufgerufen zu haben. Unabhängig davon prüft die Buchungs-API jede Buchung.
Sonderfälle
- Wenn der Anruf über einen Trunk kommt, der keinem Betrieb zugeordnet ist, beendet der Agent die Sitzung, statt ihn einem anderen Betrieb zuzuordnen.
- Wenn der Anrufer von einer Festnetznummer anruft, weist der Agent früh darauf hin, dass für die Buchung eine Mobilnummer nötig ist, sucht aber zuerst normal weiter.
- Wenn die genannte Dauer für diese Leistung nicht angeboten wird, nennt der Agent die angebotenen Dauern und sucht erst nach der Wahl des Anrufers.
- Wenn find_times für die gewünschte Leistung nichts liefert, prüft der Agent, ob an einem früheren Tag eine andere Leistung frei wäre, und nennt beides in einem Satz.
- Wenn der Termin zwischen Suche und Vorschlag vergeben wurde, liefert der Code die nächstgelegenen freien Zeiten gleich mit, und der Agent bietet nur diese an.
- Wenn der Vorschlag vor der Buchung abgelaufen ist, meldet die API proposal_expired, und der Agent legt einen neuen Vorschlag an.
- Wenn das Buchungssystem nicht antwortet, bricht die Anfrage nach 15 Sekunden mit transport_error ab. Der Agent nimmt dann eine Nachricht auf.
- Wenn jemand einen Termin absagen oder verschieben will, verweist der Agent auf den Link in der SMS-Bestätigung und nimmt auf Wunsch eine Nachricht auf.
- Wenn ein Termin bei einer Person gewünscht ist, die der Betrieb von der automatischen Vergabe ausnimmt, bucht der Agent nicht, sondern bietet an, das Anliegen ans Team weiterzugeben.
- Wenn zwei Personen zur gleichen Zeit einen Termin möchten, nutzt der Agent eigene Werkzeuge für Termine zu zweit, mit demselben Ablauf aus Vorschlag und Buchung.
- Wenn ein Werkzeug läuft und das Ergebnis noch nicht da ist, spielt ein vorab in der Agentenstimme erzeugter Füller, ohne eigenen Durchlauf des Sprachmodells.
- Wenn jemand eine unangemessene Anfrage stellt, lehnt der Agent höflich, aber bestimmt ab und beendet das Gespräch.
Technische Hürden
- 01
Problem
Die erste Version lief mit Amazon Nova Sonic, einem Sprachmodell, das direkt hört und spricht, über ein eigenes SIP-Gateway in Java. Das Modell hielt sich nicht verlässlich an den Prompt, kündigte Prüfungen an, ohne ein Werkzeug aufzurufen, und blieb danach still.
Lösung
Umstieg auf LiveKit mit getrennter Spracherkennung, Sprachmodell und Sprachausgabe. Die Erfahrungen aus der ersten Version sind in den Code gewandert, etwa dass Datum, Rufnummer und Ausweichzeiten nicht dem Modell überlassen werden.
- 02
Problem
Ein selbst betriebener LiveKit-Server mit Diensten aus der EU erkannte Sprecherwechsel unzuverlässig. Self-hosted steht nur die kleinere Variante des Turn Detectors zur Verfügung, die adaptive Erkennung von Unterbrechungen nur in der Cloud, und die Spracherkennung lieferte fertige Sätze teils mehrere Sekunden zu spät.
Lösung
Betrieb in LiveKit Cloud mit dem vollständigen Turn Detector und adaptiver Unterbrechung. Die Variante mit EU-Diensten bleibt im Code erhalten und lässt sich über eine Umgebungsvariable umschalten.
- 03
Problem
Jede Terminfrage braucht Sprachmodell, Werkzeug und einen zweiten Durchlauf des Modells. In einem Testanruf entstanden so knapp vier Sekunden Stille, bei einer belegten Zeit bis zu 15 Sekunden.
Lösung
Ein vorab erzeugter Füller überbrückt die Wartezeit, das Menü steht schon im Prompt, und bei belegten Zeiten liefert der Code die Alternativen gleich mit. Die Latenz jeder Runde wird geloggt.
- 04
Problem
Die Spracherkennung machte aus Fachbegriffen ähnlich klingende Wörter, aus „Thai“ etwa „drei“ oder „Teil“, und das Modell las Rufnummern falsch vor.
Lösung
Schlüsselwörter für die Spracherkennung, eine Arbeitsregel für die häufigsten Verwechslungen und Rufnummern, die der Code bereits in gesprochener Form in den Prompt schreibt.
Dienste und Datenschutz
| Aufgabe | Anbieter |
|---|---|
| Sprachagent, Medien und Gesprächsverlauf | LiveKit Cloud |
| Spracherkennung | Deepgram (über LiveKit Inference) |
| Sprachmodell | OpenAI GPT-4.1 (über LiveKit Inference) |
| Sprachausgabe | Cartesia (über LiveKit Inference) |
| Telefonie | SIP-Anbieter |
| Buchungssystem | eigene Buchungs-API des Betriebs |
| Nachrichten ans Team | Infomaniak (kChat) |
- Gesprächsverlauf, Traces und Logs in der Observability von LiveKit Cloud, keine Audioaufzeichnung
Deepgram, OpenAI und Cartesia sind US-Anbieter. Eine Variante mit Diensten aus der EU ist im Code vorbereitet. Die Buchungs-API ist mit einem Zugangsschlüssel je Betrieb geschützt.
Betrieb und Tests
Der Agent ist mit LiveKit Agents in Python gebaut und läuft in LiveKit Cloud. Ein warmer Prozess baut die Sprachdienste schon vor dem ersten Anruf auf, damit Konfigurationsfehler beim Start auffallen und nicht erst im Gespräch.
Vorgänger war ein Gateway in Java mit Amazon Nova Sonic. Ein selbst betriebener LiveKit-Server mit Diensten aus der EU wurde erprobt und bleibt als umschaltbare Variante erhalten.
Zehn Testdateien (pytest) prüfen unter anderem die Zuordnung nach Trunk, den Buchungsablauf, die Umrechnung von Wochentagen, den Prompt und die Werkzeugergebnisse.
Mehrere Betriebe können denselben Agenten nutzen. Sie werden über die angerufene Nummer getrennt, jeder mit eigenem Schlüssel für die Buchungs-API und eigenem Team-Chat.