merge: bring aimaq host-override files back into git (fix/aimaq-remove-host-override)
deploy / deploy (push) Successful in 32s
deploy / deploy (push) Successful in 32s
This commit is contained in:
@@ -187,8 +187,8 @@ ERROR_TELEGRAM_ALERT_BOT_TOKEN=8734026216:AAEK-xdgVR5al6rfKp_DIb-HSJZXGUG2Tvg
|
|||||||
ERROR_TELEGRAM_ALERT_CHANNEL_ID=t.me/konturaitelecom
|
ERROR_TELEGRAM_ALERT_CHANNEL_ID=t.me/konturaitelecom
|
||||||
AI_OPERATOR_AGENT_NAME=Жанна
|
AI_OPERATOR_AGENT_NAME=Жанна
|
||||||
AI_OPERATOR_COMPANY_NAME=Казакгаз Аймак
|
AI_OPERATOR_COMPANY_NAME=Казакгаз Аймак
|
||||||
AI_OPERATOR_VOICE_GREETING_RU=Добрый день! Меня зовут {agent_name}, я ии-оператор контакт-центра {company_name}. Вам удобнее получить консультацию на казахском или на русском языке?
|
AI_OPERATOR_VOICE_GREETING_RU=Добрый день! Меня зовут Жанна, я ИИ-оператор контакт-центра Казакгаз Аймак. Вам удобнее получить консультацию на казахском или на русском языке?
|
||||||
AI_OPERATOR_VOICE_GREETING_KZ=Қайырлы күн! Менің атым {agent_name}, мен «{company_name}» байланыс орталығында жасанды интеллект операторымын. Кеңесті қазақ немесе орыс тілінде алғыңыз келе ме?
|
AI_OPERATOR_VOICE_GREETING_KZ=Қайырлы күн! Менің атым Жанна, мен «Қазақгаз Аймақ» байланыс орталығында жасанды интеллект операторымын. Кеңесті қазақ немесе орыс тілінде алғыңыз келе ме?
|
||||||
AI_OPERATOR_IDENTITY_REPLY_RU=Я {agent_name}, ии-оператор контакт-центра {company_name}. Чем могу помочь?
|
AI_OPERATOR_IDENTITY_REPLY_RU=Я Жанна, ИИ-оператор контакт-центра Казакгаз Аймак. Чем могу помочь?
|
||||||
AI_OPERATOR_IDENTITY_REPLY_KZ=Мен {agent_name}мын, «{company_name}» байланыс орталығының жасанды интеллект операторымын. Қалай көмектесе аламын?
|
AI_OPERATOR_IDENTITY_REPLY_KZ=Мен Жаннамын, «Қазақгаз Аймақ» байланыс орталығының жасанды интеллект операторымын. Қалай көмектесе аламын?
|
||||||
AI_OPERATOR_BASE_SYSTEM_PROMPT=Ты {agent_name}, ии-оператор контакт-центра {company_name} для звонков, Telegram и других каналов. Всегда сохраняй одну и ту же личность: тебя зовут {agent_name}. Если клиент спрашивает, кто ты или как тебя зовут, отвечай, что ты {agent_name}. В начале разговора, сразу после приветствия, ты уже спросила клиента, на каком языке ему удобнее — на казахском или на русском. Как только клиент ответит, полностью веди остаток разговора на выбранном им языке и больше не спрашивай про язык повторно. После того как язык определён, уточни у клиента, как к нему обращаться, и только затем переходи к сути вопроса. Прежде чем завершить разговор, обязательно спроси, нужна ли клиенту ещё какая-то помощь, и попрощайся («До свидания» или «Қош болыңыз») только после того, как клиент подтвердит, что вопросов больше нет — не завершай диалог самостоятельно. Отвечай естественно, кратко и по делу. Когда говоришь о себе, используй женский род: могла, смогла, сделала, готова, проверила, нашла. Не используй Markdown, URL или таблицы.
|
AI_OPERATOR_BASE_SYSTEM_PROMPT=Ты Жанна, единый ИИ-оператор контакт-центра Казакгаз Аймак для звонков, Telegram и других каналов. Всегда сохраняй одну и ту же личность: тебя зовут Жанна. Если клиент спрашивает, кто ты или как тебя зовут, отвечай, что ты Жанна. В начале разговора, сразу после приветствия, ты уже спросила клиента, на каком языке ему удобнее — на казахском или на русском. Как только клиент ответит, полностью веди остаток разговора на выбранном им языке и больше не спрашивай про язык повторно. После того как язык определён, уточни у клиента, как к нему обращаться, и только затем переходи к сути вопроса. Прежде чем завершить разговор, обязательно спроси, нужна ли клиенту ещё какая-то помощь, и попрощайся («До свидания» или «Қош болыңыз») только после того, как клиент подтвердит, что вопросов больше нет — не завершай диалог самостоятельно. Отвечай естественно, кратко и по делу. Когда говоришь о себе, используй женский род: могла, смогла, сделала, готова, проверила, нашла. Телефонные номера читай по цифрам. Не используй Markdown, URL или таблицы. DOMAIN SCOPE: ты отвечаешь ТОЛЬКО на вопросы, связанные с услугами газоснабжения: оплата газа, счётчики, ваучеры, технические условия, отключение газа, мобильное приложение, квитанции, передача показаний, контакты филиалов, безопасность газа. Если вопрос НЕ связан с газом (погода, спорт, политика, другие компании, личные советы, кулинария, рецепты, анекдоты, новости, фильмы), вежливо откажи БЕЗ поиска в базе знаний и БЕЗ попытки ответить: RU «Я могу ответить только на вопросы, связанные с услугами газоснабжения. Пожалуйста, задайте вопрос по теме газа.» KK «Мен тек газмен қамтамасыз ету қызметтеріне байланысты сұрақтарға жауап беремін. Газ тақырыбында сұрақ қойыңызшы.» Не пытайся ответить на вопрос не по теме, даже если знаешь ответ.
|
||||||
|
|||||||
@@ -604,16 +604,6 @@ def _voice_has_name_correction(text: str | None) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def _voice_reply_already_names_customer(reply_text: str, normalized_short: str) -> bool:
|
def _voice_reply_already_names_customer(reply_text: str, normalized_short: str) -> bool:
|
||||||
"""Whether `reply_text` already addresses the customer by name.
|
|
||||||
|
|
||||||
Russian and Kazakh names decline grammatically (e.g. "Данияр" -> "Данияре",
|
|
||||||
"Данияру", "Данияра"), so the model's own reply often names the customer in
|
|
||||||
an inflected form that never exact-matches `normalized_short`. An exact
|
|
||||||
token match alone misses that and this function then goes on to prepend
|
|
||||||
the name a second time, producing a literal "Данияр, Здравствуйте, Данияре!"
|
|
||||||
duplicate. Matching on a stem prefix instead of the full token catches any
|
|
||||||
declined form of the same name.
|
|
||||||
"""
|
|
||||||
tokens = _voice_text_key(reply_text).split(" ")
|
tokens = _voice_text_key(reply_text).split(" ")
|
||||||
if normalized_short in tokens:
|
if normalized_short in tokens:
|
||||||
return True
|
return True
|
||||||
@@ -635,14 +625,6 @@ def _voice_reply_with_name(
|
|||||||
*,
|
*,
|
||||||
greet: bool = False,
|
greet: bool = False,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Prefix `reply_text` with the customer's name.
|
|
||||||
|
|
||||||
The prefix is always one of exactly two deterministic forms — "{name}, ..."
|
|
||||||
or "Здравствуйте, {name}, ..." (`greet=True`, used the turn their name is
|
|
||||||
first learned) — never left to the model's own free-form phrasing. That is
|
|
||||||
what keeps this from colliding with a self-introduced name inside
|
|
||||||
`reply_text` in the first place (see `_voice_reply_already_names_customer`).
|
|
||||||
"""
|
|
||||||
short_name = _voice_short_name(name)
|
short_name = _voice_short_name(name)
|
||||||
if not short_name:
|
if not short_name:
|
||||||
return reply_text
|
return reply_text
|
||||||
@@ -786,13 +768,7 @@ def _voice_downstream_name_update(
|
|||||||
action = "provide" if not name_value else "confirm"
|
action = "provide" if not name_value else "confirm"
|
||||||
candidate = candidate or name_value
|
candidate = candidate or name_value
|
||||||
elif status == "name_obtained":
|
elif status == "name_obtained":
|
||||||
if (
|
pass
|
||||||
candidate
|
|
||||||
and candidate_key
|
|
||||||
and candidate_key != current_key
|
|
||||||
and _voice_has_name_correction(transcript_text)
|
|
||||||
):
|
|
||||||
action = "correct"
|
|
||||||
else:
|
else:
|
||||||
if explicit_candidate:
|
if explicit_candidate:
|
||||||
action = "provide"
|
action = "provide"
|
||||||
@@ -1079,7 +1055,6 @@ def _voice_has_service_topic(text: str | None) -> bool:
|
|||||||
"ошибк",
|
"ошибк",
|
||||||
"проблем",
|
"проблем",
|
||||||
"сбой",
|
"сбой",
|
||||||
"интернет",
|
|
||||||
"связь",
|
"связь",
|
||||||
"оператор",
|
"оператор",
|
||||||
"менеджер",
|
"менеджер",
|
||||||
@@ -1087,6 +1062,40 @@ def _voice_has_service_topic(text: str | None) -> bool:
|
|||||||
"компан",
|
"компан",
|
||||||
"подключ",
|
"подключ",
|
||||||
"доставк",
|
"доставк",
|
||||||
|
"газ",
|
||||||
|
"счётчик",
|
||||||
|
"счетчик",
|
||||||
|
"ваучер",
|
||||||
|
"отключ",
|
||||||
|
"квитанц",
|
||||||
|
"показан",
|
||||||
|
"приложен",
|
||||||
|
"безопасн",
|
||||||
|
"техническ",
|
||||||
|
"плит",
|
||||||
|
"котл",
|
||||||
|
"труб",
|
||||||
|
"утечк",
|
||||||
|
"запах",
|
||||||
|
"абонент",
|
||||||
|
"договор",
|
||||||
|
"поверк",
|
||||||
|
"монтаж",
|
||||||
|
"счёт",
|
||||||
|
"счет",
|
||||||
|
"долг",
|
||||||
|
"задолжен",
|
||||||
|
"перерасчёт",
|
||||||
|
"перерасчет",
|
||||||
|
"регион",
|
||||||
|
"аимак",
|
||||||
|
"aimaq",
|
||||||
|
"qazaqgaz",
|
||||||
|
"казахгаз",
|
||||||
|
"есептегіш",
|
||||||
|
"төлеу",
|
||||||
|
"өтінім",
|
||||||
|
"шарт",
|
||||||
)
|
)
|
||||||
return any(marker in normalized for marker in service_markers)
|
return any(marker in normalized for marker in service_markers)
|
||||||
|
|
||||||
@@ -1120,6 +1129,37 @@ def _voice_is_off_domain_request(text: str | None) -> bool:
|
|||||||
"java",
|
"java",
|
||||||
"javascript",
|
"javascript",
|
||||||
"погод",
|
"погод",
|
||||||
|
"плов",
|
||||||
|
"приготов",
|
||||||
|
"кулинар",
|
||||||
|
"блюдо",
|
||||||
|
"фильм",
|
||||||
|
"кино",
|
||||||
|
"спорт",
|
||||||
|
"футбол",
|
||||||
|
"хоккей",
|
||||||
|
"теннис",
|
||||||
|
"баскетбол",
|
||||||
|
"президент",
|
||||||
|
"политик",
|
||||||
|
"выборы",
|
||||||
|
"правительств",
|
||||||
|
"курс валют",
|
||||||
|
"криптовалют",
|
||||||
|
"биткоин",
|
||||||
|
"песн",
|
||||||
|
"музык",
|
||||||
|
"танц",
|
||||||
|
"шутк",
|
||||||
|
"загадк",
|
||||||
|
"сериал",
|
||||||
|
"книга",
|
||||||
|
"стихотвор",
|
||||||
|
"чемпионат",
|
||||||
|
"ауа райы",
|
||||||
|
"аспаздық",
|
||||||
|
"кітап",
|
||||||
|
"ән айт",
|
||||||
)
|
)
|
||||||
if any(marker in normalized for marker in broad_markers):
|
if any(marker in normalized for marker in broad_markers):
|
||||||
return True
|
return True
|
||||||
@@ -1129,7 +1169,14 @@ def _voice_is_off_domain_request(text: str | None) -> bool:
|
|||||||
"что такое",
|
"что такое",
|
||||||
"объясни",
|
"объясни",
|
||||||
"расскажи про",
|
"расскажи про",
|
||||||
"почему",
|
"посоветуй",
|
||||||
|
"как приготовить",
|
||||||
|
"какая погода",
|
||||||
|
"кто президент",
|
||||||
|
"кто выиграл",
|
||||||
|
"какая команда",
|
||||||
|
"ауа райы қалай",
|
||||||
|
"әнді айт",
|
||||||
)
|
)
|
||||||
return any(normalized.startswith(prefix) for prefix in broad_openers)
|
return any(normalized.startswith(prefix) for prefix in broad_openers)
|
||||||
|
|
||||||
@@ -1137,13 +1184,12 @@ def _voice_is_off_domain_request(text: str | None) -> bool:
|
|||||||
def _voice_off_domain_reply(language: str) -> tuple[str, str]:
|
def _voice_off_domain_reply(language: str) -> tuple[str, str]:
|
||||||
if language == "kz":
|
if language == "kz":
|
||||||
return (
|
return (
|
||||||
"Men kompaniyamyzdyn qyzmetteri men otinishteri boiynsha komek bere alamyn. "
|
"Кешіріңіз, мен тек газ қызметтері бойынша көмектесемін: төлеу, есептегіш, ваучер. Қалай көмектесе аламын?",
|
||||||
"Eger suraq bizdin qyzmetke qatysty bolsa, qysqasha naqtylaңыз. Qalasaңыз, operatorga qosamyn.",
|
"AI off-topic сұрауды газ қызметтері тақырыбына шектеді.",
|
||||||
"AI qongyraudyn taqyrybyn kompaniya qyzmetteri sheginde naqtylaudy usyndy.",
|
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
"Я помогу по вопросам наших услуг и обращений. Если вопрос связан с нашей компанией, скажите коротко, что именно нужно. Если хотите, сразу соединю с оператором.",
|
"Извините, я помогаю только по вопросам газа: оплата, счётчики, ваучеры. Чем могу помочь?",
|
||||||
"AI мягко вернул разговор к вопросам компании и предложил перевод на оператора.",
|
"AI отклонил off-topic вопрос и ограничил тему газоснабжением.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1856,7 +1902,7 @@ def _voice_decision(
|
|||||||
"latency_ms": 1,
|
"latency_ms": 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
if not kb_results and _voice_is_off_domain_request(normalized):
|
if _voice_is_off_domain_request(normalized):
|
||||||
reply_text, summary_text = _voice_off_domain_reply(language)
|
reply_text, summary_text = _voice_off_domain_reply(language)
|
||||||
decision = {
|
decision = {
|
||||||
"language": language,
|
"language": language,
|
||||||
|
|||||||
Reference in New Issue
Block a user