862e6b7b25
Co-authored-by: Cursor <cursoragent@cursor.com>
150 lines
5.3 KiB
Python
150 lines
5.3 KiB
Python
"""Сборка полного пакета ответа и контекста заявки по 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,
|
|
)
|