feat(voice): inject datetime into prompt, respond in DE/EN

- Add VOICE_TIMEZONE env var (default: Europe/Berlin) for local time
- Bot knows exact date/time at call start via _build_voice_prompt()
- Respond in user language (DE or EN) instead of always German

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Christian Gick
2026-02-22 11:02:56 +02:00
parent 92ab906a21
commit e3c1ded328

View File

@@ -8,6 +8,8 @@ import hashlib
import logging import logging
import os import os
import zoneinfo
import aiohttp import aiohttp
from livekit import rtc, api as lkapi from livekit import rtc, api as lkapi
from livekit.agents import Agent, AgentSession, room_io from livekit.agents import Agent, AgentSession, room_io
@@ -23,16 +25,29 @@ LK_API_SECRET = os.environ.get("LIVEKIT_API_SECRET", "")
ELEVENLABS_KEY = os.environ.get("ELEVENLABS_API_KEY", "") ELEVENLABS_KEY = os.environ.get("ELEVENLABS_API_KEY", "")
DEFAULT_VOICE_ID = "JBFqnCBsd6RMkjVDRZzb" # George - warm, British male, multilingual DEFAULT_VOICE_ID = "JBFqnCBsd6RMkjVDRZzb" # George - warm, British male, multilingual
VOICE_PROMPT = """Du bist ein hilfreicher Sprachassistent in einem Matrix-Anruf. _VOICE_PROMPT_TEMPLATE = """Du bist ein hilfreicher Sprachassistent in einem Matrix-Anruf.
Aktuelle Zeit: {datetime}
STRIKTE Regeln: STRIKTE Regeln:
- Antworte IMMER auf Deutsch - Antworte in der Sprache des Nutzers (Deutsch oder Englisch)
- Halte JEDE Antwort auf MAXIMAL 1-2 kurze Saetze - Halte JEDE Antwort auf MAXIMAL 1-2 kurze Saetze
- Sei direkt und praezise, keine Fuellwoerter - Sei direkt und praezise, keine Fuellwoerter
- Erfinde NICHTS - keine Geschichten, keine Musik, keine Fantasie - Erfinde NICHTS - keine Geschichten, keine Musik, keine Fantasie
- Beantworte nur was gefragt wird - Beantworte nur was gefragt wird
- Wenn niemand etwas fragt, sage nur kurz Hallo""" - Wenn niemand etwas fragt, sage nur kurz Hallo"""
def _build_voice_prompt() -> str:
tz_name = os.environ.get("VOICE_TIMEZONE", "Europe/Berlin")
try:
tz = zoneinfo.ZoneInfo(tz_name)
except Exception:
tz = datetime.timezone.utc
now = datetime.datetime.now(tz)
return _VOICE_PROMPT_TEMPLATE.format(
datetime=now.strftime("%A, %d. %B %Y %H:%M %Z")
)
_vad = None _vad = None
def _get_vad(): def _get_vad():
global _vad global _vad
@@ -327,7 +342,7 @@ class VoiceSession:
if remote_identity: if remote_identity:
logger.info("Linking to remote participant: %s", remote_identity) logger.info("Linking to remote participant: %s", remote_identity)
# Voice pipeline — German male voice (Daniel) # Voice pipeline — George (British male, multilingual DE/EN)
self._http_session = aiohttp.ClientSession() self._http_session = aiohttp.ClientSession()
voice_id = os.environ.get("ELEVENLABS_VOICE_ID", DEFAULT_VOICE_ID) voice_id = os.environ.get("ELEVENLABS_VOICE_ID", DEFAULT_VOICE_ID)
self.session = AgentSession( self.session = AgentSession(
@@ -347,7 +362,7 @@ class VoiceSession:
def _on_agent_speech(msg): def _on_agent_speech(msg):
logger.info("AGENT_SPEECH: %s", msg.text_content) logger.info("AGENT_SPEECH: %s", msg.text_content)
agent = Agent(instructions=VOICE_PROMPT) agent = Agent(instructions=_build_voice_prompt())
io_opts = room_io.RoomOptions( io_opts = room_io.RoomOptions(
participant_identity=remote_identity, participant_identity=remote_identity,
close_on_disconnect=False, close_on_disconnect=False,