Add architecture longread and remove dead code found during review
- docs: longread.md — deep architecture/flow review of the whole platform (services, event bus reality vs docs, AI/ML stack honesty check, tech debt inventory) - ai_orchestrator_service/voice.py: drop _voice_decision_legacy (unreferenced) and the shadowed first _voice_decision definition (silently overwritten by the real one, dead code) - ui/analyst/app.js: drop duplicate dead definitions of loadSavedAnalyticsViews/saveAnalyticsView/deleteAnalyticsView and the first loadAnalyticsTrend implementation, all shadowed by later declarations in the same file; kept the intentional AI-mode drilldown wrapper layer (openAnalyticsDrilldown/exportAnalyticsDrilldownCsv/etc.) since that duplication is deliberate delegation, not dead code - ui/operator/vendor/sip-0.21.2.min.js: remove byte-identical orphaned duplicate of ui/operator/sip-0.21.2.min.js (unreferenced anywhere) Verified via full pytest run: identical set of 97 pre-existing failures before and after (sales_* test-isolation ordering issue and one known persona-prompt test), no new regressions. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
09a4ddac51
commit
cdabe61bc2
@@ -1694,212 +1694,6 @@ def _voice_llm_decision(
|
||||
}
|
||||
|
||||
|
||||
def _voice_decision_legacy(
|
||||
*,
|
||||
language: str,
|
||||
customer: Customer | None,
|
||||
interaction: Interaction,
|
||||
transcript_text: str,
|
||||
transcript_window: list[VoiceTranscriptSegmentRow],
|
||||
kb_results: list[Any],
|
||||
disclosure_required: bool,
|
||||
operator_config: Any | None = None,
|
||||
) -> dict[str, Any]:
|
||||
app = _app()
|
||||
normalized = str(transcript_text or "").strip()
|
||||
lower_text = normalized.lower()
|
||||
if persona.is_identity_request(normalized):
|
||||
reply_text = persona.identity_reply(language, operator_config)
|
||||
return {
|
||||
"language": language,
|
||||
"intent": "identity_question",
|
||||
"reply_text": reply_text,
|
||||
"confidence": 0.98,
|
||||
"needs_handoff": False,
|
||||
"handoff_reason": None,
|
||||
"case_action": "keep_open",
|
||||
"kb_refs": [],
|
||||
"summary_text": "AI ответил на вопрос о своей личности.",
|
||||
"model": "operator_identity_policy",
|
||||
"latency_ms": 1,
|
||||
}
|
||||
needs_handoff = app._looks_like_human_request(lower_text) or app._is_sensitive_request(lower_text)
|
||||
model = app._ai_model()
|
||||
if needs_handoff:
|
||||
reply_text = _voice_handoff_reply(language)
|
||||
if disclosure_required and not reply_text.startswith(_voice_disclosure_prefix(language)):
|
||||
reply_text = f"{_voice_disclosure_prefix(language)}{reply_text}"
|
||||
return {
|
||||
"language": language,
|
||||
"intent": "handoff_request",
|
||||
"reply_text": reply_text,
|
||||
"confidence": 0.25,
|
||||
"needs_handoff": True,
|
||||
"handoff_reason": "Запрос требует участия живого оператора.",
|
||||
"case_action": "keep_open",
|
||||
"kb_refs": [],
|
||||
"summary_text": "AI собрал первичный контекст и запросил живого оператора.",
|
||||
"model": model,
|
||||
"latency_ms": 1,
|
||||
}
|
||||
|
||||
customer_name = customer.display_name if customer else "клиент"
|
||||
if kb_results:
|
||||
article = kb_results[0]
|
||||
snippet = app._article_snippet(article, limit=220)
|
||||
reply_text = f"По базе знаний вижу следующее: {snippet}"
|
||||
summary_text = f"AI дал первичный ответ по базе знаний для {customer_name}."
|
||||
kb_refs = [article.article_id]
|
||||
confidence = 0.82
|
||||
intent = "kb_answer"
|
||||
else:
|
||||
transcript_context = " ".join(segment.text for segment in transcript_window[-3:] if segment.speaker == "caller")
|
||||
reply_text = (
|
||||
"Я услышал запрос и уже собрал основной контекст. "
|
||||
"Пожалуйста, уточните самый важный результат, который вы хотите получить."
|
||||
)
|
||||
if transcript_context and transcript_context != normalized:
|
||||
reply_text += " Если правильно понял, речь идет об этом вопросе из разговора."
|
||||
summary_text = f"AI уточняет цель звонка и собирает контекст для {customer_name}."
|
||||
kb_refs = []
|
||||
confidence = 0.68
|
||||
intent = "clarification"
|
||||
|
||||
if disclosure_required and not reply_text.startswith(_voice_disclosure_prefix(language)):
|
||||
reply_text = f"{_voice_disclosure_prefix(language)}{reply_text}"
|
||||
return {
|
||||
"language": language,
|
||||
"intent": intent,
|
||||
"reply_text": reply_text,
|
||||
"confidence": confidence,
|
||||
"needs_handoff": False,
|
||||
"handoff_reason": None,
|
||||
"case_action": "keep_open",
|
||||
"kb_refs": kb_refs,
|
||||
"summary_text": summary_text,
|
||||
"model": model,
|
||||
"latency_ms": 1,
|
||||
}
|
||||
|
||||
|
||||
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,
|
||||
operator_config: Any | 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)
|
||||
if persona.is_identity_request(normalized):
|
||||
reply_text = persona.identity_reply(language, operator_config)
|
||||
return {
|
||||
"language": language,
|
||||
"intent": "identity_question",
|
||||
"reply_text": reply_text,
|
||||
"confidence": 0.98,
|
||||
"needs_handoff": False,
|
||||
"handoff_reason": None,
|
||||
"case_action": "keep_open",
|
||||
"kb_refs": [],
|
||||
"summary_text": "AI ответил на вопрос о своей личности.",
|
||||
"model": "operator_identity_policy",
|
||||
"latency_ms": 1,
|
||||
}
|
||||
needs_handoff = app._looks_like_human_request(lower_text) or app._is_sensitive_request(lower_text)
|
||||
model = app._ai_model()
|
||||
if needs_handoff:
|
||||
reply_text = _voice_handoff_reply(language)
|
||||
if disclosure_required and not reply_text.startswith(_voice_disclosure_prefix(language)):
|
||||
reply_text = f"{_voice_disclosure_prefix(language)}{reply_text}"
|
||||
return {
|
||||
"language": language,
|
||||
"intent": "handoff_request",
|
||||
"reply_text": reply_text,
|
||||
"confidence": 0.25,
|
||||
"needs_handoff": True,
|
||||
"handoff_reason": "Запрос требует участия живого оператора.",
|
||||
"case_action": "keep_open",
|
||||
"kb_refs": [],
|
||||
"summary_text": "AI собрал первичный контекст и запросил живого оператора.",
|
||||
"model": model,
|
||||
"latency_ms": 1,
|
||||
}
|
||||
|
||||
customer_name = customer.display_name if customer else "клиент"
|
||||
if kb_results:
|
||||
article = kb_results[0]
|
||||
snippet = app._article_snippet(article, limit=220)
|
||||
reply_text = f"По базе знаний вижу следующее: {snippet}"
|
||||
summary_text = f"AI дал первичный ответ по базе знаний для {customer_name}."
|
||||
kb_refs = [article.article_id]
|
||||
confidence = 0.82
|
||||
intent = "kb_answer"
|
||||
else:
|
||||
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)
|
||||
if disclosure_required and not reply_text.startswith(_voice_disclosure_prefix(language)):
|
||||
reply_text = f"{_voice_disclosure_prefix(language)}{reply_text}"
|
||||
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,
|
||||
}
|
||||
|
||||
if caller_confused:
|
||||
reply_text = _voice_confusion_prompt(language, caller_texts)
|
||||
else:
|
||||
topic_prompt = _voice_topic_prompt(language, caller_texts)
|
||||
if clarification_count >= 2 and not topic_prompt:
|
||||
reply_text = _voice_confusion_prompt(language, caller_texts)
|
||||
else:
|
||||
reply_text = topic_prompt or _voice_generic_prompt(language)
|
||||
summary_text = f"AI уточняет цель звонка и собирает контекст для {customer_name}."
|
||||
kb_refs = []
|
||||
confidence = 0.68
|
||||
intent = "clarification"
|
||||
|
||||
if disclosure_required and not reply_text.startswith(_voice_disclosure_prefix(language)):
|
||||
reply_text = f"{_voice_disclosure_prefix(language)}{reply_text}"
|
||||
return {
|
||||
"language": language,
|
||||
"intent": intent,
|
||||
"reply_text": reply_text,
|
||||
"confidence": confidence,
|
||||
"needs_handoff": False,
|
||||
"handoff_reason": None,
|
||||
"case_action": "keep_open",
|
||||
"kb_refs": kb_refs,
|
||||
"summary_text": summary_text,
|
||||
"model": model,
|
||||
"latency_ms": 1,
|
||||
}
|
||||
|
||||
|
||||
def _voice_decision(
|
||||
*,
|
||||
language: str,
|
||||
|
||||
Reference in New Issue
Block a user