Aylem LiveKit-Agent (Cartesia-Voice, Streaming-Pipeline)

Worker-Prozess der den Aylem-Voice-Bot ueber LiveKit + Cartesia rendert. Pendant zum ElevenLabs-ConvAI-Stack (elevenlabs.html) — gleiche Persona, gleiche Tools, andere Voice-Pipeline.

Stand 2026-05-12: Emotion-Tag-Adaption ist deaktiviert — der frühere tts_node-Override hat den gesamten LLM-Output gebuffert und damit Cartesias First-Byte-Latenz von ~90 ms ausgehebelt (~1 s Aufschlag pro Antwort). Zusätzlich waren das Cartesia-Attribut und das Tag-Schema in der aktiven api_version inkompatibel — die Steuerung hat de facto nichts gemacht. Re-Add später entweder via Sonic-2-Snapshot model="sonic-2-2025-03-07" + api_version="2024-11-13" (Named Emotion Controls) oder Sonic-3 mit Voice-Switching.

Architektur

Browser /cartesia (LiveKit Web SDK)
   ↓ WebRTC + Token vom Flask-Backend
LiveKit Cloud Room (Region "Germany 2")
   ↑↓ Audio
Python Worker (dieser Code)
   ├─ silero VAD
   ├─ Deepgram nova-3 (de) — STT
   ├─ Bedrock Claude Haiku 4.5 EU (eu-central-1, T 0.0, cache_system) — LLM
   └─ Cartesia Sonic 2 (de Voice) — TTS, Streaming (kein Buffering)

Latenz-Tuning aktiv: preemptive_generation, min_endpointing_delay=0.2,
max_endpointing_delay=2.0, min_consecutive_speech_delay=0.1.

DSGVO-Status: LiveKit-Room in DE-Region, Bedrock-LLM ueber EU-only-Inference-Profile
(`eu.anthropic.*`) — Audio-Ingest + LLM bleiben in EU. Deepgram (STT) und Cartesia
(TTS) sind US-Anbieter, Audio-Bytes gehen dafuer ueber US. Fuer voll-EU spaeter
auf EU-STT (z.B. Speechmatics EU, Bedrock-Transcribe) und EU-TTS (z.B. Polly,
ElevenLabs Enterprise mit EU-Region, oder OpenAI EU) umstellbar.

Setup (einmalig)

1. Accounts anlegen (5–10 Min)

2. .env.local fuellen

cd intern/projekte/telefon-assistent-aws/livekit-agent
cp .env.example .env.local
# Vier Keys eintragen

3. venv + Install

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
 
# Modelle lokal vorbereiten (silero VAD herunterladen)
python agent.py download-files

Run

In einem Terminal — Worker startet und wartet auf Room-Joins:

source .venv/bin/activate
python agent.py dev

Du siehst Logs wie:

INFO: registered worker  worker_id=...  region=...

Im zweiten Terminal — Flask-Webapp starten (gleiches venv-Setup wie bisher):

cd ../webapp
source .venv/bin/activate
python server.py

Browser oeffnen: http://localhost:5050/cartesia

Klick auf “Verbinden”, Mikrofon erlauben — der Browser joined einen LiveKit-Room, der Worker wird automatisch gematcht und antwortet als Aylem-Bot.

Cartesia Voice wechseln

Default ist Viktoria - Phone Conversationalist (b9de4a89-2257-424b-94c2-db18ba68c81a), explizit fuer telefonische Customer-Care designed. Cartesia hat aktuell 6 DE-Voices in der Standard-Library:

VoiceIDProfil
Viktoria - Phone Conversationalistb9de4a89-2257-424b-94c2-db18ba68c81afeminine, Phone-optimiert (Default)
Alina - Engaging Assistant38aabb6a-f52b-4fb0-a3d1-988518f4dc06feminine, Assistant-Vibe
Marlene - Elegant Speaker9b4d08b6-0494-4301-ab92-9150f4ee2718feminine, formeller
Moritz - Modern Communicator4ad22058-7cb6-402c-a115-196cbfc25dcemasculine, modern
Sebastian - Oratorb7187e84-fe22-4344-ba4a-bc013fcb533emasculine, narrator
Henrik - Steady Analystd1cbea67-e4d3-47cd-be2a-2bd4e646b002masculine, ruhig-sachlich

Andere wechseln:

# in .env.local
CARTESIA_VOICE_ID=<andere-id>

Worker neustarten.

Emotion-Tags — deaktiviert (2026-05-12)

Frueher hat der LLM-Output jede Antwort mit [friendly]/[empathetic]/… begonnen, ein tts_node-Override hat den Tag geparst und Cartesia-Controls gesetzt. Das Setup hatte zwei Probleme:

  1. Buffering kostete ~1 s Latenz pro Antwort — der Override hat den gesamten LLM-Stream konsumiert bevor Cartesia auch nur das erste Audio-Byte bekommen hat. Cartesias First-Byte von ~90 ms war damit verloren.
  2. Die Controls haben gar nicht gegriffen. Gesetzt wurde tts._opts.experimental_controls — dieses Feld existiert in _TTSOptions nicht. Ausserdem akzeptiert Cartesia das alte positivity:medium-Schema nur unter api_version="2024-11-13" + model="sonic-2-2025-03-07", beide nicht im aktiven Standard.

Re-Add-Pfad wenn dynamische Emotion wieder gewuenscht ist:

  • Saubere Variante A: cartesia.TTS(model="sonic-2-2025-03-07", api_version="2024-11-13", ...) plus EMOTION_MAP mit Named-Cartesia-Emotionen aus models.py (Happy, Apologetic, Sympathetic, …) — setzen via tts._opts.speed und tts._opts.emotion direkt am Plugin (vor Stream-Open).
  • Saubere Variante B: Sonic-3 mit Voice-Switching — pro Stimmung eine vorab eingestellte Voice-ID; LLM tagt nur „warm/empathetic/…”, Worker ruft tts.update_options(voice=...) bevor er den Stream öffnet.
  • Wichtig: jede Variante braucht Streaming-kompatibles Setzen — keine Buffering-Loops um Tags zu strippen. Entweder Tag im LLM-Output rauslassen und Stimmung per Tool-Call oder Metadaten signalisieren, oder Tag im ersten Chunk parsen und ab Chunk 2 durchstreamen.

Tools (Phase 0.7 — MCP-basiert)

Tools werden seit 2026-05-09 nicht mehr inline im Worker-Code definiert, sondern via MCP-Server geladen — siehe ../tools-mcp/ und ADR keine-eigene-plattform.

Der Worker startet tools-mcp/server.py als Subprocess (stdio-MCP) und übergibt die Tool-Liste automatisch ans LLM. Die @function_tool-Decorators in AylemAgent sind weg.

Tools heute:

  • book_reservation(date, time, guests, name, phone, notes?) — Reservierung
  • take_order(items, customer_name, phone, address?, pickup_time?) — Bestellung
  • get_services() — Service-Katalog (heute Aylem-spezifisch)

Calls werden im MCP-Server geloggt und liefern Mock-Bestaetigungs-Strings zurück. Echte Backend-Anbindung (Cal.com etc.) kommt in Phase B.

Voraussetzung beim ersten Run:

cd ../tools-mcp
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt

Worker startet den MCP-Subprocess automatisch — der Pfad wird in agent.py aufgelöst (TOOLS_MCP_DIR).

Run in Production

python agent.py start (statt dev) startet als persistent worker mit Auto-Restart und Multi-Worker-Support (LiveKit verteilt Jobs auf alle verbundenen Worker).

Hosting-Optionen fuer den Worker: AWS Fargate, Hetzner Cloud, Railway, Fly.io — braucht nur Outbound-Internet zum LiveKit-Cloud-WSS-Endpoint.

Cross-Refs