fix: reduce awkward fillers and handle call endings
This commit is contained in:
+4
-4
@@ -9,7 +9,7 @@ LLM_PROVIDER=openai
|
|||||||
OPENAI_API_KEY=
|
OPENAI_API_KEY=
|
||||||
OPENAI_BASE_URL=
|
OPENAI_BASE_URL=
|
||||||
OPENAI_LLM_MODEL=gpt-4o-mini
|
OPENAI_LLM_MODEL=gpt-4o-mini
|
||||||
OPENAI_LLM_SYSTEM_PROMPT=You are Ainur, a concise Russian-speaking voice agent for the DigiOps contact center. Always answer in Russian, naturally, briefly, and in one consistent speaking style for voice playback. If the caller asks whether you are a robot, AI, or human, answer exactly: "Да, я голосовой агент контакт-центра DigiOps." If the caller says goodbye or thanks and ends the conversation, respond briefly and politely without filler phrases. Read phone numbers digit by digit. Prefer clear stress-friendly wording. Do not use markdown, URLs, or tables.
|
OPENAI_LLM_SYSTEM_PROMPT=You are Ainur, a concise Russian-speaking voice agent for the DigiOps contact center. Always answer in Russian, naturally, briefly, and in one calm consistent speaking style for voice playback. Never say waiting fillers like "минуту" or "секунду". If a short holding phrase is needed, the voice service handles it separately, so answer directly and do not add your own waiting phrase. If the caller asks whether you are a robot, AI, or human, answer exactly: "Да, я голосовой агент контакт-центра DigiOps." If the caller says goodbye, thanks, says nothing else is needed, or ends the conversation, respond briefly and politely without filler phrases. Read phone numbers digit by digit. Prefer clear stress-friendly wording and avoid ambiguous stress-heavy wording. Do not use markdown, URLs, or tables.
|
||||||
OPENAI_TIMEOUT_SECONDS=30
|
OPENAI_TIMEOUT_SECONDS=30
|
||||||
OPENAI_STT_MODEL=whisper-1
|
OPENAI_STT_MODEL=whisper-1
|
||||||
OPENAI_STT_LANGUAGE=ru
|
OPENAI_STT_LANGUAGE=ru
|
||||||
@@ -26,7 +26,7 @@ OLLAMA_LLM_NUM_CTX=1024
|
|||||||
|
|
||||||
STT_PROVIDER=openai
|
STT_PROVIDER=openai
|
||||||
STT_FALLBACK_PROVIDER=elevenlabs
|
STT_FALLBACK_PROVIDER=elevenlabs
|
||||||
STT_PROMPT=Это русская речь. Точно распознавай имена, фамилии, слова благодарности и прощания. Возможны имена: Айнур, Айгерим, Алия, Данияр, Азамат, Мадина, Нурсултан. Номера телефонов записывай цифрами.
|
STT_PROMPT=Это русская речь по телефону. Точно распознавай короткие ответы, имена, фамилии, слова благодарности и прощания. Особое внимание именам: Айнур, Айгерим, Алия, Данияр, Азамат, Арман, Мадина, Нурсултан, Руслан, Асель. Если человек представился одним словом, сохраняй его как имя. Номера телефонов записывай цифрами.
|
||||||
TTS_PROVIDER=elevenlabs
|
TTS_PROVIDER=elevenlabs
|
||||||
|
|
||||||
ENABLE_AUDIO_DUMP=false
|
ENABLE_AUDIO_DUMP=false
|
||||||
@@ -55,11 +55,11 @@ YANDEX_STT_TOPIC=general
|
|||||||
VAD_THRESHOLD=0.5
|
VAD_THRESHOLD=0.5
|
||||||
VAD_NEGATIVE_THRESHOLD=
|
VAD_NEGATIVE_THRESHOLD=
|
||||||
VAD_SILENCE_TIMEOUT_MS=420
|
VAD_SILENCE_TIMEOUT_MS=420
|
||||||
VAD_SPEECH_PAD_MS=120
|
VAD_SPEECH_PAD_MS=180
|
||||||
VAD_MIN_SPEECH_DURATION_MS=0
|
VAD_MIN_SPEECH_DURATION_MS=0
|
||||||
VAD_USE_ONNX=false
|
VAD_USE_ONNX=false
|
||||||
REALTIME_VOICE_INITIAL_GREETING_TEXT=Здравствуйте! Меня зовут Айнур, я эй-ай-ассистент компании ДиджиОпс. Как я могу к вам обращаться?
|
REALTIME_VOICE_INITIAL_GREETING_TEXT=Здравствуйте! Меня зовут Айнур, я эй-ай-ассистент компании ДиджиОпс. Как я могу к вам обращаться?
|
||||||
SEMANTIC_ENDPOINTING_HOLD_MS=600
|
SEMANTIC_ENDPOINTING_HOLD_MS=600
|
||||||
REALTIME_VOICE_FILLER_DELAY_MS=200
|
REALTIME_VOICE_FILLER_DELAY_MS=300
|
||||||
REALTIME_VOICE_TTS_CHUNK_SOFT_MIN_CHARS=10
|
REALTIME_VOICE_TTS_CHUNK_SOFT_MIN_CHARS=10
|
||||||
REALTIME_VOICE_TTS_CHUNK_SOFT_MIN_WORDS=2
|
REALTIME_VOICE_TTS_CHUNK_SOFT_MIN_WORDS=2
|
||||||
|
|||||||
@@ -14,18 +14,20 @@ from realtime_voice_service.providers.base import BaseTTS
|
|||||||
|
|
||||||
LOGGER = logging.getLogger("uvicorn.error")
|
LOGGER = logging.getLogger("uvicorn.error")
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_FILLER_TEXTS: dict[str, tuple[str, ...]] = {
|
DEFAULT_FILLER_TEXTS: dict[str, tuple[str, ...]] = {
|
||||||
"serper": (
|
"serper": (
|
||||||
"Сейчас уточню.",
|
|
||||||
"Проверяю информацию.",
|
"Проверяю информацию.",
|
||||||
|
"Сейчас подскажу.",
|
||||||
),
|
),
|
||||||
"generic": (
|
"generic": (
|
||||||
"Сейчас отвечу.",
|
"Сейчас отвечу.",
|
||||||
"Уточняю.",
|
"Уже отвечаю.",
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class FillerAudioLibrary:
|
class FillerAudioLibrary:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
|||||||
+53
-3
@@ -52,6 +52,7 @@ SEMANTIC_ENDPOINTING_HOLD_MS = _semantic_hold_ms()
|
|||||||
FILLER_AUDIO_DELAY_MS = _filler_delay_ms()
|
FILLER_AUDIO_DELAY_MS = _filler_delay_ms()
|
||||||
TTS_CHUNK_SOFT_MIN_CHARS = _tts_chunk_soft_min_chars()
|
TTS_CHUNK_SOFT_MIN_CHARS = _tts_chunk_soft_min_chars()
|
||||||
TTS_CHUNK_SOFT_MIN_WORDS = _tts_chunk_soft_min_words()
|
TTS_CHUNK_SOFT_MIN_WORDS = _tts_chunk_soft_min_words()
|
||||||
|
|
||||||
_FAREWELL_MARKERS: frozenset[str] = frozenset({
|
_FAREWELL_MARKERS: frozenset[str] = frozenset({
|
||||||
"спасибо",
|
"спасибо",
|
||||||
"благодарю",
|
"благодарю",
|
||||||
@@ -60,9 +61,22 @@ _FAREWELL_MARKERS: frozenset[str] = frozenset({
|
|||||||
"пока",
|
"пока",
|
||||||
"до встречи",
|
"до встречи",
|
||||||
"хорошего дня",
|
"хорошего дня",
|
||||||
|
"ничего не нужно",
|
||||||
|
"ничего не надо",
|
||||||
|
"мне ничего не нужно",
|
||||||
|
"мне ничего не надо",
|
||||||
|
"больше ничего",
|
||||||
|
"это все",
|
||||||
|
"это всё",
|
||||||
|
"все понятно",
|
||||||
|
"всё понятно",
|
||||||
|
"вопросов нет",
|
||||||
|
"не надо",
|
||||||
|
"не нужно",
|
||||||
"goodbye",
|
"goodbye",
|
||||||
"bye",
|
"bye",
|
||||||
})
|
})
|
||||||
|
|
||||||
_FAREWELL_QUERY_MARKERS: frozenset[str] = frozenset({
|
_FAREWELL_QUERY_MARKERS: frozenset[str] = frozenset({
|
||||||
"вопрос",
|
"вопрос",
|
||||||
"подскаж",
|
"подскаж",
|
||||||
@@ -73,9 +87,25 @@ _FAREWELL_QUERY_MARKERS: frozenset[str] = frozenset({
|
|||||||
"где",
|
"где",
|
||||||
"когда",
|
"когда",
|
||||||
"можно",
|
"можно",
|
||||||
"нужно",
|
"нужно ли",
|
||||||
|
"нужно еще",
|
||||||
|
"нужно ещё",
|
||||||
"хочу",
|
"хочу",
|
||||||
"мне",
|
"еще",
|
||||||
|
"ещё",
|
||||||
|
"теперь",
|
||||||
|
"тогда",
|
||||||
|
"адрес",
|
||||||
|
"тариф",
|
||||||
|
"оператор",
|
||||||
|
"статус",
|
||||||
|
"проблем",
|
||||||
|
"помощ",
|
||||||
|
"помочь",
|
||||||
|
"узнать",
|
||||||
|
"нужен",
|
||||||
|
"нужна",
|
||||||
|
"нужны",
|
||||||
})
|
})
|
||||||
FAREWELL_RESPONSE_TEXT = "Спасибо за обращение в DigiOps. Всего доброго, до свидания!"
|
FAREWELL_RESPONSE_TEXT = "Спасибо за обращение в DigiOps. Всего доброго, до свидания!"
|
||||||
|
|
||||||
@@ -1082,6 +1112,26 @@ class CallSession:
|
|||||||
self.session_id,
|
self.session_id,
|
||||||
len(self._conversation),
|
len(self._conversation),
|
||||||
)
|
)
|
||||||
|
if _is_terminal_farewell(transcript):
|
||||||
|
LOGGER.info(
|
||||||
|
"realtime session %s farewell detected before assistant response: epoch=%s transcript=%r",
|
||||||
|
self.session_id,
|
||||||
|
epoch,
|
||||||
|
_preview_text(transcript),
|
||||||
|
)
|
||||||
|
await self._play_prepared_response(
|
||||||
|
epoch=epoch,
|
||||||
|
response_text=FAREWELL_RESPONSE_TEXT,
|
||||||
|
actually_spoken_chunks=actually_spoken_chunks,
|
||||||
|
)
|
||||||
|
self._ensure_generation(epoch)
|
||||||
|
self._commit_assistant_context(
|
||||||
|
llm_generated_text=FAREWELL_RESPONSE_TEXT,
|
||||||
|
actually_spoken_chunks=actually_spoken_chunks,
|
||||||
|
interrupted=False,
|
||||||
|
)
|
||||||
|
await self._close_after_farewell()
|
||||||
|
return
|
||||||
name_collection_response = self._build_name_collection_response(transcript)
|
name_collection_response = self._build_name_collection_response(transcript)
|
||||||
if name_collection_response is not None:
|
if name_collection_response is not None:
|
||||||
LOGGER.info(
|
LOGGER.info(
|
||||||
@@ -1143,7 +1193,7 @@ class CallSession:
|
|||||||
epoch=epoch,
|
epoch=epoch,
|
||||||
tool_name=None,
|
tool_name=None,
|
||||||
started_monotonic=llm_started_monotonic,
|
started_monotonic=llm_started_monotonic,
|
||||||
suppress=_is_farewell_transcript(transcript),
|
suppress=_is_terminal_farewell(transcript) or _is_farewell_transcript(transcript),
|
||||||
),
|
),
|
||||||
name=f"{self.session_id}-filler-delay-{epoch}",
|
name=f"{self.session_id}-filler-delay-{epoch}",
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user