feat(ai): Finalize voice operator persona and voice name flow
This commit is contained in:
@@ -79,6 +79,7 @@ from services.shared.sql_models import (
|
||||
VoiceAISessionRow,
|
||||
)
|
||||
from services.ai_orchestrator_service import voice as voice_flows
|
||||
from services.ai_orchestrator_service import operator_persona as persona
|
||||
from services.ai_orchestrator_service.voice_name_config import (
|
||||
load_voice_name_collection_config,
|
||||
save_voice_name_collection_config,
|
||||
@@ -2316,15 +2317,10 @@ def _openai_prompt(
|
||||
}
|
||||
for article in kb_results
|
||||
]
|
||||
system_prompt = (
|
||||
f"You are the company's AI assistant for {channel_label}. "
|
||||
"Always disclose you are an AI assistant in the first meaningful reply. "
|
||||
"Use only provided business context, KB snippets, and interaction state. "
|
||||
"Never invent order statuses, tariffs, discounts, deadlines, or actions that are not in context. "
|
||||
"If confidence is low or a human is needed, set needs_handoff=true and do not bluff. "
|
||||
"Return only a JSON object with keys: language, intent, reply_text, confidence, needs_handoff, "
|
||||
"handoff_reason, case_action, kb_refs. case_action must be one of none, close, escalate, keep_open. "
|
||||
f"Prefer {'Kazakh' if language == 'kz' else 'Russian'} for the reply."
|
||||
system_prompt = persona.operator_system_prompt(
|
||||
language=language,
|
||||
channel_label=channel_label,
|
||||
is_voice=False,
|
||||
)
|
||||
user_prompt = {
|
||||
"customer": customer_summary,
|
||||
@@ -2348,17 +2344,7 @@ def _openai_prompt(
|
||||
]
|
||||
|
||||
|
||||
def _openai_compatible_decision(
|
||||
*,
|
||||
customer: Customer | None,
|
||||
interaction: Interaction,
|
||||
thread: Any,
|
||||
messages: list[Any],
|
||||
kb_results: list[KBArticleRow],
|
||||
language: str,
|
||||
channel_label: str = "Telegram",
|
||||
channel_key: str = "telegram",
|
||||
) -> dict[str, Any]:
|
||||
def _request_structured_model_decision(messages: list[dict[str, str]]) -> dict[str, Any]:
|
||||
if not _ai_api_base() or not _ai_api_key():
|
||||
raise RuntimeError("AI_API_BASE / AI_API_KEY are required for openai_compatible provider")
|
||||
started = time.perf_counter()
|
||||
@@ -2366,16 +2352,7 @@ def _openai_compatible_decision(
|
||||
"model": _ai_model(),
|
||||
"temperature": 0.2,
|
||||
"response_format": {"type": "json_object"},
|
||||
"messages": _openai_prompt(
|
||||
customer=customer,
|
||||
interaction=interaction,
|
||||
thread=thread,
|
||||
messages=messages,
|
||||
kb_results=kb_results,
|
||||
language=language,
|
||||
channel_label=channel_label,
|
||||
channel_key=channel_key,
|
||||
),
|
||||
"messages": messages,
|
||||
}
|
||||
with httpx.Client(timeout=_ai_timeout_seconds()) as client:
|
||||
response = client.post(
|
||||
@@ -2395,6 +2372,31 @@ def _openai_compatible_decision(
|
||||
return decision
|
||||
|
||||
|
||||
def _openai_compatible_decision(
|
||||
*,
|
||||
customer: Customer | None,
|
||||
interaction: Interaction,
|
||||
thread: Any,
|
||||
messages: list[Any],
|
||||
kb_results: list[KBArticleRow],
|
||||
language: str,
|
||||
channel_label: str = "Telegram",
|
||||
channel_key: str = "telegram",
|
||||
) -> dict[str, Any]:
|
||||
return _request_structured_model_decision(
|
||||
_openai_prompt(
|
||||
customer=customer,
|
||||
interaction=interaction,
|
||||
thread=thread,
|
||||
messages=messages,
|
||||
kb_results=kb_results,
|
||||
language=language,
|
||||
channel_label=channel_label,
|
||||
channel_key=channel_key,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _sanitize_decision(raw: dict[str, Any], *, fallback_language: str) -> dict[str, Any]:
|
||||
decision = {
|
||||
"language": str(raw.get("language") or fallback_language or "ru"),
|
||||
@@ -2442,6 +2444,90 @@ def _always_reply_fallback(last_user_text: str, language: str) -> str:
|
||||
)
|
||||
|
||||
|
||||
def _stub_decision(
|
||||
*,
|
||||
customer: Customer | None,
|
||||
interaction: Interaction,
|
||||
last_user_message: TelegramMessageRow,
|
||||
kb_results: list[KBArticleRow],
|
||||
language: str,
|
||||
) -> dict[str, Any]:
|
||||
text = last_user_message.text
|
||||
if _looks_like_human_request(text):
|
||||
return {
|
||||
"language": language,
|
||||
"intent": "handoff_request",
|
||||
"reply_text": "",
|
||||
"confidence": 0.2,
|
||||
"needs_handoff": True,
|
||||
"handoff_reason": "Клиент запросил живого оператора.",
|
||||
"case_action": "keep_open",
|
||||
"kb_refs": [],
|
||||
}
|
||||
if _is_sensitive_request(text):
|
||||
return {
|
||||
"language": language,
|
||||
"intent": "sensitive_request",
|
||||
"reply_text": "",
|
||||
"confidence": 0.25,
|
||||
"needs_handoff": True,
|
||||
"handoff_reason": "Нужен человек: запрос затрагивает чувствительную тему или действие вне доступного контекста.",
|
||||
"case_action": "keep_open",
|
||||
"kb_refs": [],
|
||||
}
|
||||
if _looks_like_resolution_confirmation(text):
|
||||
return {
|
||||
"language": language,
|
||||
"intent": "resolution_confirmed",
|
||||
"reply_text": persona.text_resolution_reply(language),
|
||||
"confidence": 0.9,
|
||||
"needs_handoff": False,
|
||||
"handoff_reason": None,
|
||||
"case_action": "close",
|
||||
"kb_refs": [],
|
||||
}
|
||||
if kb_results:
|
||||
best = kb_results[0]
|
||||
snippet = _article_snippet(best)
|
||||
return {
|
||||
"language": language,
|
||||
"intent": "kb_answer",
|
||||
"reply_text": (
|
||||
f"Қысқаша айтайын: {snippet}"
|
||||
if language == "kz"
|
||||
else f"Коротко подскажу: {snippet}"
|
||||
),
|
||||
"confidence": 0.84,
|
||||
"needs_handoff": False,
|
||||
"handoff_reason": None,
|
||||
"case_action": "keep_open",
|
||||
"kb_refs": [best.article_id],
|
||||
}
|
||||
return {
|
||||
"language": language,
|
||||
"intent": "clarification",
|
||||
"reply_text": (
|
||||
"Нақтырақ айтыңызшы: нені тексеру немесе нақтылау керек?"
|
||||
if language == "kz"
|
||||
else "Уточните, пожалуйста, что именно нужно проверить или подсказать."
|
||||
),
|
||||
"confidence": 0.45,
|
||||
"needs_handoff": False,
|
||||
"handoff_reason": None,
|
||||
"case_action": "keep_open",
|
||||
"kb_refs": [],
|
||||
}
|
||||
|
||||
|
||||
def _always_reply_fallback(last_user_text: str, language: str) -> str:
|
||||
normalized = str(last_user_text or "").strip().lower()
|
||||
greeting_tokens = ("привет", "здравствуйте", "добрый", "салем", "сә", "сәлем", "hello", "hi")
|
||||
return persona.human_fallback_reply(
|
||||
language,
|
||||
is_greeting=any(token in normalized for token in greeting_tokens),
|
||||
)
|
||||
|
||||
|
||||
def _apply_always_reply_mode(
|
||||
decision: dict[str, Any],
|
||||
*,
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
|
||||
def customer_persona_mode() -> str:
|
||||
return str(os.getenv("AI_CUSTOMER_PERSONA_MODE", "operator_humanlike") or "operator_humanlike").strip().lower()
|
||||
|
||||
|
||||
def disclosure_mode() -> str:
|
||||
return str(os.getenv("AI_DISCLOSURE_MODE", "hidden") or "hidden").strip().lower()
|
||||
|
||||
|
||||
def voice_policy_mode() -> str:
|
||||
return str(os.getenv("AI_VOICE_POLICY_MODE", "llm_guarded") or "llm_guarded").strip().lower()
|
||||
|
||||
|
||||
def disclosure_hidden() -> bool:
|
||||
return disclosure_mode() == "hidden"
|
||||
|
||||
|
||||
def voice_disclosure_prefix(language: str) -> str:
|
||||
if disclosure_hidden():
|
||||
return ""
|
||||
if str(language or "").strip().lower() == "kz":
|
||||
return "Men kompaniya atynan jauap berip turmyn. "
|
||||
return "Отвечаю от имени линии поддержки компании. "
|
||||
|
||||
|
||||
def voice_greeting(language: str) -> str:
|
||||
if str(language or "").strip().lower() == "kz":
|
||||
return "Сәлеметсіз бе. Сұрағыңызды айтыңызшы, көмектесуге тырысамын."
|
||||
return "Здравствуйте. Подскажите, пожалуйста, чем помочь."
|
||||
|
||||
|
||||
def voice_handoff_reply(language: str) -> str:
|
||||
if str(language or "").strip().lower() == "kz":
|
||||
return "Бір сәт, сізді операторға қосамын."
|
||||
return "Секунду, соединяю вас с оператором."
|
||||
|
||||
|
||||
def text_resolution_reply(language: str) -> str:
|
||||
if str(language or "").strip().lower() == "kz":
|
||||
return "Жақсы, белгілеп қоямын. Қажет болса, осы жерден қайта жаза аласыз."
|
||||
return "Хорошо, отмечу это. Если понадобится, можно продолжить здесь."
|
||||
|
||||
|
||||
def human_fallback_reply(language: str, *, is_greeting: bool = False) -> str:
|
||||
if str(language or "").strip().lower() == "kz":
|
||||
if is_greeting:
|
||||
return "Сәлеметсіз бе. Сұрағыңызды жазыңыз не айтыңыз, көмектесуге тырысамын."
|
||||
return "Түсіндім. Нақтырақ айтып жіберсеңіз, бірден жалғастырамын."
|
||||
if is_greeting:
|
||||
return "Здравствуйте. Напишите или коротко расскажите, чем помочь."
|
||||
return "Понял вас. Уточните, пожалуйста, детальнее, и я сразу продолжу."
|
||||
|
||||
|
||||
def operator_system_prompt(*, language: str, channel_label: str, is_voice: bool) -> str:
|
||||
preferred_language = "Kazakh" if str(language or "").strip().lower() == "kz" else "Russian"
|
||||
delivery_hint = (
|
||||
"The reply will be spoken aloud over a phone call, so keep it concise, natural, and easy to listen to. "
|
||||
"Prefer one or two short sentences and at most one clarifying question."
|
||||
if is_voice
|
||||
else "The reply should read like a concise message from a live first-line operator."
|
||||
)
|
||||
return (
|
||||
f"You are a first-line company operator handling customer conversations in {channel_label}. "
|
||||
f"Reply in {preferred_language}. Speak naturally, warmly, and confidently like a human operator. "
|
||||
"Do not say or imply that you are an AI, bot, assistant, model, or automated system. "
|
||||
"Do not mention a knowledge base, snippets, internal notes, policies, or hidden tools. "
|
||||
"Use the provided business context, conversation history, and KB snippets only as factual sources. "
|
||||
"Paraphrase them naturally instead of quoting them verbatim. "
|
||||
"Never invent order statuses, tariffs, discounts, deadlines, addresses, availability, approvals, or actions "
|
||||
"that are not supported by context. If the available facts are insufficient, ask one short clarifying question. "
|
||||
"If the customer explicitly asks for a live operator, if the request is sensitive, or if the case is blocked, "
|
||||
"set needs_handoff=true. "
|
||||
f"{delivery_hint} "
|
||||
"Return only a JSON object with keys: language, intent, reply_text, confidence, needs_handoff, "
|
||||
"handoff_reason, case_action, kb_refs. case_action must be one of none, close, escalate, keep_open."
|
||||
)
|
||||
@@ -22,6 +22,7 @@ from services.shared.sql_models import (
|
||||
VoiceAISessionRow,
|
||||
VoiceTranscriptSegmentRow,
|
||||
)
|
||||
from services.ai_orchestrator_service import operator_persona as persona
|
||||
from services.ai_orchestrator_service.voice_name_config import (
|
||||
load_effective_voice_name_collection_config,
|
||||
voice_name_collection_confirmation_greeting,
|
||||
@@ -71,6 +72,18 @@ def _voice_handoff_reply(language: str) -> str:
|
||||
)
|
||||
|
||||
|
||||
def _voice_disclosure_prefix(language: str) -> str:
|
||||
return persona.voice_disclosure_prefix(language)
|
||||
|
||||
|
||||
def _voice_greeting(language: str) -> str:
|
||||
return persona.voice_greeting(language)
|
||||
|
||||
|
||||
def _voice_handoff_reply(language: str) -> str:
|
||||
return persona.voice_handoff_reply(language)
|
||||
|
||||
|
||||
def _normalize_phone(value: str | None) -> str | None:
|
||||
raw = str(value or "").strip()
|
||||
if not raw:
|
||||
@@ -1037,6 +1050,132 @@ def _ensure_voice_ai_session(
|
||||
return ai_session, True
|
||||
|
||||
|
||||
def _voice_policy_mode() -> str:
|
||||
return persona.voice_policy_mode()
|
||||
|
||||
|
||||
def _voice_llm_prompt_messages(
|
||||
*,
|
||||
language: str,
|
||||
customer: Customer | None,
|
||||
interaction: Interaction,
|
||||
transcript_text: str,
|
||||
transcript_window: list[VoiceTranscriptSegmentRow],
|
||||
kb_results: list[Any],
|
||||
name_value: str | None,
|
||||
name_status: str | None,
|
||||
) -> list[dict[str, str]]:
|
||||
app = _app()
|
||||
history = [
|
||||
{
|
||||
"speaker": segment.speaker,
|
||||
"text": segment.text,
|
||||
"sequence_no": segment.sequence_no,
|
||||
"source_type": segment.source_type,
|
||||
"interrupted": bool(segment.barge_in_interrupted),
|
||||
"created_at": segment.created_at,
|
||||
}
|
||||
for segment in transcript_window[-6:]
|
||||
]
|
||||
kb_context = [
|
||||
{
|
||||
"article_id": article.article_id,
|
||||
"title": article.title,
|
||||
"snippet": app._article_snippet(article, limit=240),
|
||||
}
|
||||
for article in kb_results[:3]
|
||||
]
|
||||
payload = {
|
||||
"customer": {
|
||||
"customer_id": customer.customer_id if customer else interaction.customer_id,
|
||||
"display_name": customer.display_name if customer else None,
|
||||
"name_status": name_status,
|
||||
"name_value": name_value,
|
||||
"channel": "voice",
|
||||
},
|
||||
"interaction": {
|
||||
"interaction_id": interaction.interaction_id,
|
||||
"status": interaction.status,
|
||||
"queue_id": interaction.queue_id,
|
||||
"subject": interaction.subject,
|
||||
},
|
||||
"voice_turn": {
|
||||
"last_user_text": transcript_text,
|
||||
"language": language,
|
||||
},
|
||||
"kb_results": kb_context,
|
||||
"history": history,
|
||||
}
|
||||
return [
|
||||
{
|
||||
"role": "system",
|
||||
"content": persona.operator_system_prompt(
|
||||
language=language,
|
||||
channel_label="Voice",
|
||||
is_voice=True,
|
||||
),
|
||||
},
|
||||
{"role": "user", "content": json.dumps(payload, ensure_ascii=False)},
|
||||
]
|
||||
|
||||
|
||||
def _voice_llm_decision(
|
||||
*,
|
||||
language: str,
|
||||
customer: Customer | None,
|
||||
interaction: Interaction,
|
||||
transcript_text: str,
|
||||
transcript_window: list[VoiceTranscriptSegmentRow],
|
||||
kb_results: list[Any],
|
||||
name_value: str | None,
|
||||
name_status: str | None,
|
||||
) -> dict[str, Any] | None:
|
||||
app = _app()
|
||||
if _voice_policy_mode() != "llm_guarded":
|
||||
return None
|
||||
if app._ai_provider() != "openai_compatible":
|
||||
return None
|
||||
try:
|
||||
raw = app._request_structured_model_decision(
|
||||
_voice_llm_prompt_messages(
|
||||
language=language,
|
||||
customer=customer,
|
||||
interaction=interaction,
|
||||
transcript_text=transcript_text,
|
||||
transcript_window=transcript_window,
|
||||
kb_results=kb_results,
|
||||
name_value=name_value,
|
||||
name_status=name_status,
|
||||
)
|
||||
)
|
||||
except Exception:
|
||||
return None
|
||||
decision = app._sanitize_decision(raw, fallback_language=language)
|
||||
if not str(decision.get("reply_text") or "").strip():
|
||||
decision["reply_text"] = _voice_generic_prompt(language)
|
||||
if decision.get("needs_handoff") and not decision.get("handoff_reason"):
|
||||
decision["handoff_reason"] = "Требуется участие живого оператора."
|
||||
if decision.get("needs_handoff") and not str(decision.get("reply_text") or "").strip():
|
||||
decision["reply_text"] = _voice_handoff_reply(language)
|
||||
return {
|
||||
"language": decision["language"],
|
||||
"intent": decision["intent"],
|
||||
"reply_text": str(decision["reply_text"] or "").strip(),
|
||||
"confidence": float(decision["confidence"] or 0.0),
|
||||
"needs_handoff": bool(decision["needs_handoff"]),
|
||||
"handoff_reason": decision["handoff_reason"],
|
||||
"case_action": decision["case_action"],
|
||||
"kb_refs": decision["kb_refs"],
|
||||
"summary_text": (
|
||||
str(decision.get("reply_text") or "").strip()
|
||||
or str(decision.get("handoff_reason") or "").strip()
|
||||
or "AI подготовил ответ операторским стилем."
|
||||
),
|
||||
"model": decision["_model"],
|
||||
"latency_ms": decision["_latency_ms"],
|
||||
}
|
||||
|
||||
|
||||
def _voice_decision_legacy(
|
||||
*,
|
||||
language: str,
|
||||
@@ -1118,6 +1257,8 @@ def _voice_decision(
|
||||
transcript_window: list[VoiceTranscriptSegmentRow],
|
||||
kb_results: list[Any],
|
||||
disclosure_required: bool,
|
||||
customer_name_value: str | None = None,
|
||||
customer_name_status: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
app = _app()
|
||||
normalized = str(transcript_text or "").strip()
|
||||
@@ -1209,6 +1350,114 @@ def _voice_decision(
|
||||
}
|
||||
|
||||
|
||||
def _voice_decision(
|
||||
*,
|
||||
language: str,
|
||||
customer: Customer | None,
|
||||
interaction: Interaction,
|
||||
transcript_text: str,
|
||||
transcript_window: list[VoiceTranscriptSegmentRow],
|
||||
kb_results: list[Any],
|
||||
disclosure_required: bool,
|
||||
customer_name_value: str | None = None,
|
||||
customer_name_status: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
app = _app()
|
||||
normalized = str(transcript_text or "").strip()
|
||||
lower_text = normalized.lower()
|
||||
caller_texts = _voice_recent_caller_texts(transcript_window)
|
||||
model = app._ai_model()
|
||||
|
||||
if app._looks_like_human_request(lower_text) or app._is_sensitive_request(lower_text):
|
||||
return {
|
||||
"language": language,
|
||||
"intent": "handoff_request",
|
||||
"reply_text": _voice_handoff_reply(language),
|
||||
"confidence": 0.25,
|
||||
"needs_handoff": True,
|
||||
"handoff_reason": "Запрос требует участия живого оператора.",
|
||||
"case_action": "keep_open",
|
||||
"kb_refs": [],
|
||||
"summary_text": "AI собрал первичный контекст и запросил живого оператора.",
|
||||
"model": model,
|
||||
"latency_ms": 1,
|
||||
}
|
||||
|
||||
clarification_count = _voice_recent_clarification_count(transcript_window)
|
||||
repeated_reply_count = _voice_repeated_assistant_reply_count(transcript_window)
|
||||
recent_caller_window = caller_texts[-3:]
|
||||
low_signal_count = sum(1 for text in recent_caller_window if _voice_is_low_signal_caller_text(text))
|
||||
caller_confused = _voice_is_confused_caller_text(normalized)
|
||||
if clarification_count >= 4 or (
|
||||
clarification_count >= 3 and (caller_confused or low_signal_count >= 2 or repeated_reply_count >= 2)
|
||||
):
|
||||
reply_text, handoff_reason, summary_text = _voice_loop_handoff(language)
|
||||
return {
|
||||
"language": language,
|
||||
"intent": "handoff_request",
|
||||
"reply_text": reply_text,
|
||||
"confidence": 0.34,
|
||||
"needs_handoff": True,
|
||||
"handoff_reason": handoff_reason,
|
||||
"case_action": "keep_open",
|
||||
"kb_refs": [],
|
||||
"summary_text": summary_text,
|
||||
"model": model,
|
||||
"latency_ms": 1,
|
||||
}
|
||||
|
||||
llm_decision = _voice_llm_decision(
|
||||
language=language,
|
||||
customer=customer,
|
||||
interaction=interaction,
|
||||
transcript_text=transcript_text,
|
||||
transcript_window=transcript_window,
|
||||
kb_results=kb_results,
|
||||
name_value=customer_name_value or (customer.display_name if customer else None),
|
||||
name_status=customer_name_status,
|
||||
)
|
||||
if llm_decision is not None:
|
||||
return llm_decision
|
||||
|
||||
if kb_results:
|
||||
article = kb_results[0]
|
||||
snippet = app._article_snippet(article, limit=220)
|
||||
return {
|
||||
"language": language,
|
||||
"intent": "kb_answer",
|
||||
"reply_text": (
|
||||
f"Қысқаша айтайын: {snippet}"
|
||||
if language == "kz"
|
||||
else f"Коротко подскажу: {snippet}"
|
||||
),
|
||||
"confidence": 0.78,
|
||||
"needs_handoff": False,
|
||||
"handoff_reason": None,
|
||||
"case_action": "keep_open",
|
||||
"kb_refs": [article.article_id],
|
||||
"summary_text": "AI дал ответ по доступному контексту.",
|
||||
"model": "voice_policy_fallback",
|
||||
"latency_ms": 1,
|
||||
}
|
||||
|
||||
reply_text = _voice_confusion_prompt(language, caller_texts) if caller_confused else (
|
||||
_voice_topic_prompt(language, caller_texts) or _voice_generic_prompt(language)
|
||||
)
|
||||
return {
|
||||
"language": language,
|
||||
"intent": "clarification",
|
||||
"reply_text": reply_text,
|
||||
"confidence": 0.62,
|
||||
"needs_handoff": False,
|
||||
"handoff_reason": None,
|
||||
"case_action": "keep_open",
|
||||
"kb_refs": [],
|
||||
"summary_text": "AI уточняет цель звонка и собирает контекст.",
|
||||
"model": "voice_policy_fallback",
|
||||
"latency_ms": 1,
|
||||
}
|
||||
|
||||
|
||||
def start_voice_session(session_id: str, payload: VoiceAIStartIn) -> VoiceAIStartOut:
|
||||
session = get_session()
|
||||
try:
|
||||
@@ -1685,6 +1934,8 @@ def turn_voice_session(session_id: str, payload: VoiceAITurnIn) -> VoiceAITurnDe
|
||||
transcript_window=transcript_window,
|
||||
kb_results=kb_results,
|
||||
disclosure_required=disclosure_required,
|
||||
customer_name_value=name_update["value"],
|
||||
customer_name_status=name_update["status"],
|
||||
)
|
||||
decision_metadata = _voice_name_metadata(
|
||||
language=voice_session.voice_start_language or ai_session.language or "ru",
|
||||
|
||||
@@ -50,6 +50,34 @@ def voice_name_collection_default_config() -> VoiceNameCollectionConfig:
|
||||
)
|
||||
|
||||
|
||||
def voice_name_collection_default_config() -> VoiceNameCollectionConfig:
|
||||
return VoiceNameCollectionConfig(
|
||||
enabled=True,
|
||||
texts=VoiceNameCollectionTextsConfig(
|
||||
ru=VoiceNameCollectionLanguageTexts(
|
||||
start_prompt="Здравствуйте. Назовите, пожалуйста, ваше имя.",
|
||||
personalized_greeting_template=(
|
||||
"Здравствуйте, {name}. Коротко расскажите, пожалуйста, чем помочь."
|
||||
),
|
||||
confirmation_greeting_template=(
|
||||
"Если я правильно расслышал, вас зовут {name}? И чем помочь?"
|
||||
),
|
||||
inline_followup_prompt="И ещё подскажите, как мне к вам обращаться?",
|
||||
),
|
||||
kz=VoiceNameCollectionLanguageTexts(
|
||||
start_prompt="Сәлеметсіз бе. Атыңызды атайсыз ба?",
|
||||
personalized_greeting_template=(
|
||||
"Сәлеметсіз бе, {name}. Қысқаша айтыңызшы, не көмектесу керек."
|
||||
),
|
||||
confirmation_greeting_template=(
|
||||
"Егер дұрыс естісем, атыңыз {name}? Қалай көмектесемін?"
|
||||
),
|
||||
inline_followup_prompt="Тағы бір нақтыласам, сізге қалай қараймын?",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _settings_row(session) -> VoiceNameCollectionSettingsRow | None:
|
||||
return session.execute(
|
||||
select(VoiceNameCollectionSettingsRow).where(
|
||||
|
||||
@@ -211,6 +211,14 @@ def _default_voice_greeting(language: str | None, *, agent_profile: str = "voice
|
||||
)
|
||||
|
||||
|
||||
def _default_voice_greeting(language: str | None, *, agent_profile: str = "voice_support") -> str:
|
||||
if str(agent_profile or "").strip() == "voice_start":
|
||||
return _voice_start_name_prompt(language)
|
||||
if str(language or "").strip() == "kz":
|
||||
return "Сәлеметсіз бе. Сұрағыңызды айтыңызшы, көмектесуге тырысамын."
|
||||
return "Здравствуйте. Подскажите, пожалуйста, чем помочь."
|
||||
|
||||
|
||||
_ASR_PROVIDER = build_asr_provider(_asr_provider_name())
|
||||
_TTS_PROVIDER = build_tts_provider(_tts_provider_name())
|
||||
|
||||
|
||||
Reference in New Issue
Block a user