Initial commit: VKUS vitrina MVP (frontend + backend).
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
"""Сборка полного пакета ответа и контекста заявки по CJM."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from app.config import settings
|
||||
from app.data.knowledge_base import get_colleague_cases_for_system
|
||||
from app.models import ColleagueCase, ResponsePackage, Source, TicketDraft
|
||||
|
||||
|
||||
def build_why_suggested(
|
||||
*,
|
||||
system: str | None,
|
||||
sources: list[Source],
|
||||
confidence: int,
|
||||
source_title: str | None = None,
|
||||
) -> str:
|
||||
reasons: list[str] = []
|
||||
if system:
|
||||
reasons.append(f"в запросе определена система «{system}»")
|
||||
if sources:
|
||||
reasons.append(f"в инструкциях найден материал «{sources[0].title}»")
|
||||
elif source_title:
|
||||
reasons.append(f"совпадение с документом «{source_title}»")
|
||||
if confidence < 70:
|
||||
reasons.append("уверенность ниже 70% — ответ может быть неполным")
|
||||
if not reasons:
|
||||
return "Ответ сформирован на основе доступных корпоративных инструкций."
|
||||
return "Мы предлагаем этот ответ, потому что " + "; ".join(reasons) + "."
|
||||
|
||||
|
||||
def build_warning(recommendation: str, cases: list[ColleagueCase]) -> str | None:
|
||||
if not cases:
|
||||
return None
|
||||
|
||||
rec_lower = recommendation.lower()
|
||||
divergent_keywords = (
|
||||
"переустанов",
|
||||
"другой браузер",
|
||||
"режим инкогнито",
|
||||
"перезапуск сервера",
|
||||
"администратор",
|
||||
)
|
||||
|
||||
for case in cases:
|
||||
outcome_lower = case.outcome.lower()
|
||||
if any(keyword in outcome_lower for keyword in divergent_keywords):
|
||||
if not any(keyword in rec_lower for keyword in divergent_keywords):
|
||||
return (
|
||||
"В похожих случаях коллеги использовали обходные решения, которые могут "
|
||||
"отличаться от официальной инструкции. Рекомендуем сначала следовать "
|
||||
"регламенту, а при неудаче — оформить обращение."
|
||||
)
|
||||
|
||||
return (
|
||||
"Опыт коллег приведён для ориентира. При расхождении с инструкцией "
|
||||
"приоритет у официального регламента."
|
||||
)
|
||||
|
||||
|
||||
def build_ticket_description(
|
||||
query: str,
|
||||
system: str,
|
||||
*,
|
||||
conversation: list[dict[str, str]] | None = None,
|
||||
sources: list[Source] | None = None,
|
||||
colleague_cases: list[ColleagueCase] | None = None,
|
||||
recommendation: str = "",
|
||||
) -> str:
|
||||
lines = [
|
||||
f"Исходный вопрос: {query}",
|
||||
f"ИТ-система: {system}",
|
||||
]
|
||||
|
||||
if recommendation:
|
||||
lines.extend(["", "Рекомендация ассистента:", recommendation[:800]])
|
||||
|
||||
if conversation:
|
||||
lines.extend(["", "Диалог с ассистентом:"])
|
||||
for turn in conversation[-8:]:
|
||||
role = "Сотрудник" if turn.get("role") == "user" else "Ассистент"
|
||||
text = turn.get("text", "").strip()
|
||||
if text:
|
||||
lines.append(f"- {role}: {text[:300]}")
|
||||
|
||||
if sources:
|
||||
lines.extend(["", "Источники:"])
|
||||
for src in sources[:4]:
|
||||
title = src.title
|
||||
if src.url:
|
||||
lines.append(f"- {title} ({src.url})")
|
||||
else:
|
||||
lines.append(f"- {title}")
|
||||
|
||||
if colleague_cases:
|
||||
lines.extend(["", "Похожие случаи коллег:"])
|
||||
for case in colleague_cases[:3]:
|
||||
lines.append(f"- {case.summary} → {case.outcome}")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def ensure_escalation_draft(
|
||||
package: ResponsePackage,
|
||||
query: str,
|
||||
*,
|
||||
conversation: list[dict[str, str]] | None = None,
|
||||
) -> ResponsePackage:
|
||||
"""CJM §4.2: при уверенности ниже порога — черновик заявки всегда в пакете."""
|
||||
if package.confidence >= settings.confidence_threshold:
|
||||
return package
|
||||
|
||||
system = package.system or "Общее"
|
||||
cases = package.colleague_cases
|
||||
package.ticket_draft = enrich_ticket_draft(
|
||||
query,
|
||||
system,
|
||||
"Высокий",
|
||||
conversation=conversation,
|
||||
sources=package.sources,
|
||||
colleague_cases=cases,
|
||||
recommendation=package.recommendation,
|
||||
)
|
||||
return package
|
||||
|
||||
|
||||
def enrich_ticket_draft(
|
||||
query: str,
|
||||
system: str,
|
||||
priority: str,
|
||||
*,
|
||||
conversation: list[dict[str, str]] | None = None,
|
||||
sources: list[Source] | None = None,
|
||||
colleague_cases: list[ColleagueCase] | None = None,
|
||||
recommendation: str = "",
|
||||
) -> TicketDraft:
|
||||
cases = colleague_cases or get_colleague_cases_for_system(system)
|
||||
return TicketDraft(
|
||||
title=f"Вопрос по системе {system}",
|
||||
description=build_ticket_description(
|
||||
query,
|
||||
system,
|
||||
conversation=conversation,
|
||||
sources=sources,
|
||||
colleague_cases=cases,
|
||||
recommendation=recommendation,
|
||||
),
|
||||
system=system,
|
||||
priority=priority,
|
||||
)
|
||||
Reference in New Issue
Block a user