From 93002ab86aab844badd82864e89ed1003e0f75df Mon Sep 17 00:00:00 2001 From: Yera All Date: Sun, 19 Apr 2026 01:30:26 +0500 Subject: [PATCH] Revert "fix(voice): stop repeating failed clarifications" This reverts commit 294d30a7971afb927f8c5b9eaab041b3dad31904. --- services/ai_orchestrator_service/voice.py | 65 +---------------------- tests/test_ai_orchestrator_service.py | 54 ------------------- 2 files changed, 1 insertion(+), 118 deletions(-) diff --git a/services/ai_orchestrator_service/voice.py b/services/ai_orchestrator_service/voice.py index bcb8bf3..6d5a2fa 100644 --- a/services/ai_orchestrator_service/voice.py +++ b/services/ai_orchestrator_service/voice.py @@ -907,37 +907,13 @@ def _voice_is_confused_caller_text(text: str | None) -> bool: return any(marker in normalized for marker in confusion_markers) -def _voice_is_frustrated_caller_text(text: str | None) -> bool: - normalized = _voice_text_key(text) - if not normalized: - return False - frustration_markers = ( - "я же говор", - "я уже говор", - "уже сказал", - "уже сказала", - "сколько раз", - "ты не слыш", - "вы не слыш", - "не слышишь", - "не понимаешь", - "работай", - "давай работай", - ) - return any(marker in normalized for marker in frustration_markers) - - def _voice_recent_clarification_count(transcript_window: list[VoiceTranscriptSegmentRow]) -> int: markers = ( "в двух словах", - "о чем именно", - "чем могу помочь", "график работы какого", "какая услуга", "какой филиал", "назовите пожалуйста", - "назовите один вариант", - "плохо распознал", "опишите пожалуйста", "подскажите", "скажите коротко", @@ -952,12 +928,6 @@ def _voice_recent_clarification_count(transcript_window: list[VoiceTranscriptSeg if segment.speaker != "assistant": continue normalized = _voice_text_key(segment.text) - if normalized.startswith("здравствуйте") and ( - "коротко расскажите" in normalized - or "назовите пожалуйста ваше имя" in normalized - or "подскажите пожалуйста чем помочь" in normalized - ): - continue if any(marker in normalized for marker in markers): count += 1 return count @@ -1146,12 +1116,6 @@ def _voice_confusion_prompt(language: str, caller_texts: list[str]) -> str: return "Подскажите точнее: график работы, статус заявки, тариф или оператор." -def _voice_repair_prompt(language: str) -> str: - if language == "kz": - return "Daуыs nashar tanyldy. Bir нұсқаны ғана айтыңыз: jumys uaqyty, mekenjai, tarif, otinish nemese operator." - return "Плохо распознал запрос. Назовите один вариант: график работы, адрес, тариф, заявка или оператор." - - def _voice_loop_handoff(language: str) -> tuple[str, str, str]: if language == "kz": return ( @@ -1998,9 +1962,8 @@ def _voice_decision( 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) - caller_frustrated = _voice_is_frustrated_caller_text(normalized) if clarification_count >= 4 or ( - clarification_count >= 3 and (caller_confused or caller_frustrated or low_signal_count >= 2 or repeated_reply_count >= 2) + 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 { @@ -2017,32 +1980,6 @@ def _voice_decision( "latency_ms": 1, } - if not kb_results and ( - caller_frustrated - or ( - clarification_count >= 2 - and repeated_reply_count >= 1 - and not _voice_has_service_topic(normalized) - ) - ): - decision = { - "language": language, - "intent": "clarification", - "reply_text": _voice_repair_prompt(language), - "confidence": 0.5, - "needs_handoff": False, - "handoff_reason": None, - "case_action": "keep_open", - "kb_refs": [], - "summary_text": "AI переключился в repair-mode после неудачных уточнений и попросил выбрать один короткий вариант.", - "model": "voice_policy_repair", - "latency_ms": 1, - } - if v2_metadata: - decision["reply_text"] = _voice_compact_reply_text(decision["reply_text"], language=language) - decision["metadata"] = {**v2_metadata, "reply_phase": "final"} - return decision - if not kb_results and _voice_is_off_domain_request(normalized): reply_text, summary_text = _voice_off_domain_reply(language) decision = { diff --git a/tests/test_ai_orchestrator_service.py b/tests/test_ai_orchestrator_service.py index dc488e1..a650869 100644 --- a/tests/test_ai_orchestrator_service.py +++ b/tests/test_ai_orchestrator_service.py @@ -1247,60 +1247,6 @@ def test_voice_decision_handoffs_after_repeated_clarification_loop(): assert "нескольких попыток" in decision["handoff_reason"].lower() -def test_voice_decision_uses_repair_prompt_after_repeated_unknown_request(): - transcript_window = [ - SimpleNamespace(speaker="assistant", text=voice_module._voice_greeting("ru"), sequence_no=1), - SimpleNamespace(speaker="caller", text=_u(r"\u041d\u0443 \u043c\u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u043a \u0432\u0430\u043c \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c\u0441\u044f."), sequence_no=2), - SimpleNamespace(speaker="assistant", text=_u(r"\u0415\u0440\u043d\u0443\u0440. \u0427\u0435\u043c \u043c\u043e\u0433\u0443 \u043f\u043e\u043c\u043e\u0447\u044c?"), sequence_no=3), - SimpleNamespace(speaker="caller", text=_u(r"\u041c\u043d\u0435 \u043d\u0430\u0434\u043e \u0443\u0437\u043d\u0430\u0442\u044c, \u043a\u0430\u043a \u0440\u0430\u0437-"), sequence_no=4), - SimpleNamespace(speaker="assistant", text=_u(r"\u0415\u0440\u043d\u0443\u0440, \u043e \u0447\u0435\u043c \u0438\u043c\u0435\u043d\u043d\u043e \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0443\u0437\u043d\u0430\u0442\u044c?"), sequence_no=5), - SimpleNamespace(speaker="caller", text=_u(r"\u042f \u0436\u0435 \u0433\u043e\u0432\u043e\u0440\u044e, \u043c\u043d\u0435 \u043d\u0430\u0434\u043e \u0443\u0437\u043d\u0430\u0442\u044c, \u0433\u0434\u0435--"), sequence_no=6), - ] - - decision = voice_module._voice_decision( - language="ru", - customer=None, - interaction=SimpleNamespace(interaction_id="int_voice_repair"), - transcript_text=_u(r"\u042f \u0436\u0435 \u0433\u043e\u0432\u043e\u0440\u044e, \u043c\u043d\u0435 \u043d\u0430\u0434\u043e \u0443\u0437\u043d\u0430\u0442\u044c, \u0433\u0434\u0435--"), - transcript_window=transcript_window, - kb_results=[], - disclosure_required=False, - request_metadata={"voice_v2_enabled": True, "response_plan_id": "rsp_repair"}, - ) - - normalized = decision["reply_text"].lower() - assert decision["model"] == "voice_policy_repair" - assert decision["needs_handoff"] is False - assert "назовите один вариант" in normalized - assert "график работы" in normalized - assert "оператор" in normalized - assert "о чем именно" not in normalized - - -def test_voice_decision_handoffs_after_frustrated_repair_loop(): - transcript_window = [ - SimpleNamespace(speaker="assistant", text=voice_module._voice_greeting("ru"), sequence_no=1), - SimpleNamespace(speaker="assistant", text=_u(r"\u0415\u0440\u043d\u0443\u0440. \u0427\u0435\u043c \u043c\u043e\u0433\u0443 \u043f\u043e\u043c\u043e\u0447\u044c?"), sequence_no=2), - SimpleNamespace(speaker="assistant", text=_u(r"\u0415\u0440\u043d\u0443\u0440, \u043e \u0447\u0435\u043c \u0438\u043c\u0435\u043d\u043d\u043e \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0443\u0437\u043d\u0430\u0442\u044c?"), sequence_no=3), - SimpleNamespace(speaker="assistant", text=_u(r"\u041f\u043b\u043e\u0445\u043e \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u043b \u0437\u0430\u043f\u0440\u043e\u0441. \u041d\u0430\u0437\u043e\u0432\u0438\u0442\u0435 \u043e\u0434\u0438\u043d \u0432\u0430\u0440\u0438\u0430\u043d\u0442: \u0433\u0440\u0430\u0444\u0438\u043a \u0440\u0430\u0431\u043e\u0442\u044b, \u0430\u0434\u0440\u0435\u0441, \u0442\u0430\u0440\u0438\u0444, \u0437\u0430\u044f\u0432\u043a\u0430 \u0438\u043b\u0438 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440."), sequence_no=4), - ] - - decision = voice_module._voice_decision( - language="ru", - customer=None, - interaction=SimpleNamespace(interaction_id="int_voice_repair_handoff"), - transcript_text=_u(r"\u0422\u0430\u043a \u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0439."), - transcript_window=transcript_window, - kb_results=[], - disclosure_required=False, - request_metadata={"voice_v2_enabled": True, "response_plan_id": "rsp_repair_handoff"}, - ) - - assert decision["intent"] == "handoff_request" - assert decision["needs_handoff"] is True - assert "оператор" in decision["reply_text"].lower() - - def test_voice_kb_search_matches_relaxed_phrase_and_returns_kb_answer(): query = _u(r"\u0425\u043e\u0447\u0443 \u0443\u0437\u043d\u0430\u0442\u044c \u0442\u0430\u0440\u0438\u0444 relaxbasicx") seed_kb_article(