"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.composeAssistantAnswer = composeAssistantAnswer; function fallbackFromSummary(routeSummary) { if (!routeSummary || routeSummary.mode !== "deterministic_v2") { return "none"; } return routeSummary.fallback.type; } function uniqueStrings(values, limit = 6) { return Array.from(new Set(values.map((item) => item.trim()).filter(Boolean))).slice(0, limit); } function formatList(items) { if (items.length === 0) { return ""; } return items.map((item) => `- ${item}`).join("\n"); } function extractTopFacts(results) { const lines = []; for (const result of results.filter((item) => item.status === "ok").slice(0, 3)) { if (result.result_type === "chain") { const top = result.items.slice(0, 3).map((item) => { const counterparty = String(item.counterparty_id ?? "не указан"); const operations = String(item.operations_count ?? "0"); const docs = String(item.document_refs_count ?? "0"); return `Контрагент ${counterparty}: операций ${operations}, документов в связке ${docs}.`; }); lines.push(...top); continue; } if (result.result_type === "ranking") { const top = result.items .slice(0, 5) .map((item) => `${item.rank ?? "•"}. ${String(item.entity ?? "Сущность")} — ${String(item.records_count ?? 0)}.`); lines.push(...top); continue; } if (result.result_type === "list") { const top = result.items.slice(0, 5).map((item) => { if (item.risk_score !== undefined) { return `${String(item.source_entity ?? "Запись")} (${String(item.source_id ?? "")}) — риск ${String(item.risk_score)}.`; } return `${String(item.source_entity ?? "Запись")} (${String(item.source_id ?? "")}).`; }); lines.push(...top); continue; } const top = result.items .slice(0, 3) .map((item) => `${String(item.source_entity ?? "Запись")} (${String(item.source_id ?? "")}).`); lines.push(...top); } return lines; } function extractWhyIncluded(results) { return uniqueStrings(results.flatMap((item) => item.why_included)); } function extractSelectionReasons(results) { return uniqueStrings(results.flatMap((item) => item.selection_reason)); } function extractRiskFactors(results) { return uniqueStrings(results.flatMap((item) => item.risk_factors)); } function extractBusinessInterpretation(results) { return uniqueStrings(results.flatMap((item) => item.business_interpretation)); } function extractLimitations(results) { return uniqueStrings(results.flatMap((item) => item.limitations)); } function summaryValue(result, key) { const summary = result.summary ?? {}; return Object.prototype.hasOwnProperty.call(summary, key) ? summary[key] : undefined; } function summaryBoolean(result, key) { return summaryValue(result, key) === true; } function summaryString(result, key) { const value = summaryValue(result, key); return typeof value === "string" ? value : null; } function suggestNextStep(requirements, coverage) { const next = []; if (coverage.clarification_needed_for.length > 0) { next.push("Уточните период, счет, документ или контрагента для требований: " + coverage.clarification_needed_for.join(", ") + "."); } if (coverage.requirements_uncovered.length > 0) { next.push("Проверьте непокрытые требования: " + coverage.requirements_uncovered.join(", ") + "."); } if (coverage.out_of_scope_requirements.length > 0) { next.push("Часть запроса вне текущего учетного контура: " + coverage.out_of_scope_requirements.join(", ") + "."); } if (next.length === 0 && requirements.length > 0) { next.push("Следующим шагом можно открыть технический разбор и углубить проверку по выбранным объектам."); } return next; } function flattenEvidence(results) { return results.flatMap((item) => item.evidence); } function buildClaimEvidenceLinks(results) { const byClaim = new Map(); for (const evidence of flattenEvidence(results)) { const claimRef = String(evidence.claim_ref ?? "").trim(); const evidenceId = String(evidence.evidence_id ?? "").trim(); if (!claimRef || !evidenceId) { continue; } const current = byClaim.get(claimRef) ?? []; current.push(evidenceId); byClaim.set(claimRef, current); } return Array.from(byClaim.entries()) .slice(0, 10) .map(([claim_ref, evidenceIds]) => ({ claim_ref, evidence_ids: uniqueStrings(evidenceIds, 10) })); } function aggregatePolicySignals(results) { const broad_query_detected = results.some((item) => summaryBoolean(item, "broad_query_detected")); const broad_result_flag = results.some((item) => summaryBoolean(item, "broad_result_flag")); const minimum_evidence_failed = results.some((item) => summaryBoolean(item, "minimum_evidence_failed")); let degraded_to = null; for (const result of results) { const degraded = summaryString(result, "degraded_to"); if (degraded === "clarification") { degraded_to = "clarification"; break; } if (degraded === "partial") { degraded_to = "partial"; } } const narrowingOrder = { weak: 0, medium: 1, strong: 2 }; let narrowing_strength = null; for (const result of results) { const value = summaryString(result, "narrowing_strength"); if (value !== "weak" && value !== "medium" && value !== "strong") { continue; } if (!narrowing_strength || narrowingOrder[value] < narrowingOrder[narrowing_strength]) { narrowing_strength = value; } } return { broad_query_detected, broad_result_flag, minimum_evidence_failed, degraded_to, narrowing_strength }; } function confidenceToScore(value) { if (value === "high") return 3; if (value === "medium") return 2; return 1; } function aggregateConfidence(results, evidenceItems) { const scores = []; for (const evidence of evidenceItems) { scores.push(confidenceToScore(evidence.confidence)); } for (const result of results) { if (result.status === "error") { continue; } scores.push(confidenceToScore(result.confidence)); } if (scores.length === 0) { return "low"; } const average = scores.reduce((acc, item) => acc + item, 0) / scores.length; if (average >= 2.6) return "high"; if (average >= 1.8) return "medium"; return "low"; } function collectLimitationReasonCodes(evidenceItems) { const codes = evidenceItems .map((item) => item.limitation?.reason_code ?? null) .filter((item) => Boolean(item)); return uniqueStrings(codes, 8); } function limitationReasonToText(code) { if (code === "snapshot_only") return "Evidence is snapshot-only and may lag source-of-record."; if (code === "heuristic_inference") return "Part of the conclusion relies on heuristic inference."; if (code === "missing_mechanism") return "Mechanism is unresolved for part of the evidence."; if (code === "weak_source_mapping") return "Source mapping is weak for part of the evidence."; if (code === "insufficient_detail") return "Evidence lacks detail for a strong factual claim."; return "Some evidence limitations remain unresolved."; } function detectMissingAnchors(userMessage) { const lower = String(userMessage ?? "").toLowerCase(); const hasPeriod = /\b20\d{2}(?:[-./](?:0[1-9]|1[0-2]))?\b/.test(lower); const hasAccount = /(?:\bсчет\b|\baccount\b|\bschet\b|\b\d{2}(?:\.\d{2})?\b)/i.test(lower); const hasDocumentOrObject = /(?:документ|invoice|guid|object|obj|#\d+|\bid\b|\bref\b|dokument|doc)/i.test(lower); const hasCounterparty = /(?:контрагент|supplier|buyer|customer|kontragent|postavsh|pokupatel)/i.test(lower); const hasAnomalyType = /(?:аномал|risk|отклон|разрыв|mismatch|duplicate|tail|цепочк|anomali|hvost)/i.test(lower); return { period: !hasPeriod, account: !hasAccount, documentOrObject: !hasDocumentOrObject, counterparty: !hasCounterparty, anomalyType: !hasAnomalyType }; } function buildClarificationQuestions(input) { const questions = []; const shouldAsk = input.mode === "clarification_required" || input.coverageReport.clarification_needed_for.length > 0; if (!shouldAsk) { return questions; } if (input.missingAnchors.period) { questions.push("Уточните период проверки (например, 2020-06)."); } if (input.missingAnchors.account) { questions.push("Уточните счет или группу счетов (например, 19, 60, 62)."); } if (input.missingAnchors.documentOrObject) { questions.push("Укажите документ/GUID/конкретный объект для трассировки."); } if (input.missingAnchors.counterparty) { questions.push("Укажите контрагента или группу контрагентов."); } if (input.policySignals.broad_query_detected && input.missingAnchors.anomalyType) { questions.push("Уточните тип отклонения: разрыв цепочки, неверный документ или аномальный риск."); } if (input.coverageReport.clarification_needed_for.length > 0) { questions.push(`Закройте уточнения для требований: ${input.coverageReport.clarification_needed_for.join(", ")}.`); } return uniqueStrings(questions, 6); } function buildRecommendedActions(input) { const actions = []; if (input.mode === "focused_grounded") { actions.push("Проверьте 1-2 ключевые записи по source_ref и зафиксируйте итог в рабочем файле проверки."); } if (input.mode === "broad_partial") { actions.push("Сузьте запрос до периода + счета или периода + документа и повторите проверку."); } if (input.mode === "clarification_required") { actions.push("Дайте недостающие якоря (период/счет/объект), иначе сильный factual вывод невозможен."); } if (input.coverageReport.requirements_uncovered.length > 0) { actions.push(`Закройте непокрытые требования: ${input.coverageReport.requirements_uncovered.join(", ")}.`); } if (input.coverageReport.requirements_partially_covered.length > 0) { actions.push(`Доуточните частично покрытые требования: ${input.coverageReport.requirements_partially_covered.join(", ")}.`); } if (input.policySignals.broad_query_detected && input.policySignals.narrowing_strength !== "strong") { actions.push("Добавьте более узкий контекст: тип отклонения, группу документов и бизнес-участок."); } if (input.limitationReasonCodes.includes("snapshot_only")) { actions.push("Сверьте критичные выводы с live source-of-record в 1C."); } if (input.limitationReasonCodes.includes("weak_source_mapping")) { actions.push("Проверьте source mapping для связей document/register по указанным ref."); } if (input.sourceRefs.length > 0) { actions.push(`Начните проверку с source_ref: ${input.sourceRefs.slice(0, 2).join(", ")}.`); } return uniqueStrings(actions, 6); } function firstMeaningfulFact(results) { const facts = extractTopFacts(results); return facts.length > 0 ? facts[0] : null; } function buildPolicyDecision(input) { const hasCoverageGaps = input.coverageReport.requirements_uncovered.length > 0 || input.coverageReport.requirements_partially_covered.length > 0 || input.coverageReport.clarification_needed_for.length > 0 || input.coverageReport.out_of_scope_requirements.length > 0; if (input.fallbackType === "out_of_scope" && input.coverageReport.requirements_covered === 0) { return { mode: "out_of_scope", fallback_type: "out_of_scope", reply_type: "out_of_scope" }; } if (input.groundingCheck.status === "route_mismatch_blocked") { return { mode: "route_mismatch", fallback_type: "partial", reply_type: "route_mismatch_blocked" }; } if ((input.policySignals.degraded_to === "clarification" && input.policySignals.minimum_evidence_failed) || (input.fallbackType === "clarification" && !input.hasSupport) || (input.groundingCheck.status === "no_grounded_answer" && !input.hasSupport)) { return { mode: "clarification_required", fallback_type: "clarification", reply_type: "clarification_required" }; } if (input.errorResults.length > 0 && input.okResults.length === 0 && input.partialResults.length === 0) { return { mode: "backend_error", fallback_type: input.fallbackType, reply_type: "backend_error" }; } if (input.okResults.length === 0 && input.partialResults.length === 0 && input.emptyResults.length > 0) { return { mode: "empty", fallback_type: input.fallbackType, reply_type: "empty_but_valid" }; } if (input.groundingCheck.status === "no_grounded_answer" && input.okResults.length === 0 && input.partialResults.length === 0) { return { mode: "no_grounded", fallback_type: input.fallbackType, reply_type: "no_grounded_answer" }; } if (input.focusedStrong && !input.policySignals.broad_query_detected && !input.policySignals.minimum_evidence_failed && !hasCoverageGaps) { return { mode: "focused_grounded", fallback_type: "none", reply_type: "factual_with_explanation" }; } if (input.okResults.length > 0 || input.partialResults.length > 0 || hasCoverageGaps || input.policySignals.minimum_evidence_failed || input.policySignals.broad_result_flag || input.groundingCheck.status === "partial") { return { mode: "broad_partial", fallback_type: "partial", reply_type: "partial_coverage" }; } return { mode: "backend_error", fallback_type: "unknown", reply_type: "backend_error" }; } function buildAnswerSummary(mode) { if (mode === "focused_grounded") return "Сформирован прямой ответ на основе подтвержденной опоры."; if (mode === "broad_partial") return "Вывод ограничен: есть частичная опора, но не полный coverage."; if (mode === "clarification_required") return "Нужны уточнения: без сужения strong factual вывод ненадежен."; if (mode === "out_of_scope") return "Запрос вне доступного учетного контура."; if (mode === "route_mismatch") return "Результат маршрута не совпал с предметом вопроса."; if (mode === "empty") return "В текущем срезе данных релевантные записи не обнаружены."; if (mode === "no_grounded") return "Недостаточно опоры для обоснованного ответа."; return "Не удалось собрать обоснованный ответ по текущему запросу."; } function buildDirectAnswer(input) { const topFact = firstMeaningfulFact(input.retrievalResults); if (input.mode === "focused_grounded") { return topFact ?? "Подтвержденный результат получен; можно продолжать предметную проверку без деградации."; } if (input.mode === "broad_partial") { if (topFact) { return `Доступен ограниченный подтвержденный фрагмент: ${topFact}`; } return "Есть только ограниченная опора; вывод дан в частичном режиме без ложной точности."; } if (input.mode === "clarification_required") { return "Текущий запрос слишком широкий или недоопределен; надежный factual вывод пока невозможен."; } if (input.mode === "out_of_scope") { return "Могу отвечать только в пределах данных доступного учетного контура."; } if (input.mode === "route_mismatch") { return "Предмет результата не совпал с предметом вопроса; требуется уточнение фокуса."; } if (input.mode === "empty") { return "В текущем срезе данных проблемные записи по заданному условию не найдены."; } if (input.mode === "no_grounded") { return "Недостаточно подтвержденной опоры для ответа в требуемой точности."; } if (input.policySignals.minimum_evidence_failed) { return "Маршрут отработал, но минимальная evidence-опора не пройдена."; } return "Не удалось сформировать обоснованный ответ; нужно уточнение запроса."; } function renderPolicyReply(structure) { const mechanismLines = [`status=${structure.mechanism_block.status}`]; if (structure.mechanism_block.mechanism_notes.length > 0) { mechanismLines.push(...structure.mechanism_block.mechanism_notes.map((item) => `note: ${item}`)); } if (structure.mechanism_block.limitation_reason_codes.length > 0) { mechanismLines.push(`limitation_codes: ${structure.mechanism_block.limitation_reason_codes.join(", ")}`); } if (structure.mechanism_block.status === "unresolved" && structure.mechanism_block.mechanism_notes.length === 0) { mechanismLines.push("mechanism_note is intentionally omitted due to weak or missing mechanism evidence"); } const evidenceLines = [ `coverage=${structure.evidence_block.coverage_note}`, `evidence_ids=${structure.evidence_block.evidence_ids.length > 0 ? structure.evidence_block.evidence_ids.join(", ") : "none"}` ]; if (Array.isArray(structure.evidence_block.source_refs) && structure.evidence_block.source_refs.length > 0) { evidenceLines.push(`source_refs=${structure.evidence_block.source_refs.join(", ")}`); } if (Array.isArray(structure.evidence_block.claim_evidence_links) && structure.evidence_block.claim_evidence_links.length > 0) { const compactLinks = structure.evidence_block.claim_evidence_links .slice(0, 4) .map((item) => `${item.claim_ref}:${item.evidence_ids.join("|")}`); evidenceLines.push(`claim_evidence_links=${compactLinks.join("; ")}`); } const uncertaintyLines = [ ...structure.uncertainty_block.open_uncertainties.map((item) => `open: ${item}`), ...structure.uncertainty_block.limitations.map((item) => `limit: ${item}`) ]; if (uncertaintyLines.length === 0) { uncertaintyLines.push("No material uncertainty detected in current scoped answer."); } const nextStepLines = [ ...structure.next_step_block.recommended_actions.map((item) => `action: ${item}`), ...structure.next_step_block.clarification_questions.map((item) => `clarify: ${item}`) ]; if (nextStepLines.length === 0) { nextStepLines.push("No additional action is required for this scoped answer."); } return [ `Answer summary: ${structure.answer_summary}`, `Direct answer:\n${structure.direct_answer}`, `Mechanism block:\n${formatList(mechanismLines)}`, `Evidence block:\n${formatList(evidenceLines)}`, `Uncertainty block:\n${formatList(uncertaintyLines)}`, `Next step block:\n${formatList(nextStepLines)}` ] .filter(Boolean) .join("\n\n"); } function composeAssistantAnswerV11(input) { const fallbackType = fallbackFromSummary(input.routeSummary); const okResults = input.retrievalResults.filter((item) => item.status === "ok"); const partialResults = input.retrievalResults.filter((item) => item.status === "partial"); const emptyResults = input.retrievalResults.filter((item) => item.status === "empty"); const errorResults = input.retrievalResults.filter((item) => item.status === "error"); const evidenceItems = flattenEvidence(input.retrievalResults); const policySignals = aggregatePolicySignals(input.retrievalResults); const limitationReasonCodes = collectLimitationReasonCodes(evidenceItems); const sourceRefs = uniqueStrings(evidenceItems .map((item) => item.source_ref?.canonical_ref) .filter((item) => typeof item === "string" && item.trim().length > 0), 8); const mechanismNotes = uniqueStrings(evidenceItems .map((item) => item.mechanism_note) .filter((item) => typeof item === "string" && item.trim().length > 0), 6); const claimEvidenceLinks = buildClaimEvidenceLinks(input.retrievalResults); const aggregateEvidenceConfidence = aggregateConfidence(input.retrievalResults, evidenceItems); const hasSupport = okResults.length > 0 || partialResults.length > 0 || evidenceItems.length > 0 || input.retrievalResults.some((item) => item.items.length > 0); const hasCoverageGaps = input.coverageReport.requirements_uncovered.length > 0 || input.coverageReport.requirements_partially_covered.length > 0 || input.coverageReport.clarification_needed_for.length > 0 || input.coverageReport.out_of_scope_requirements.length > 0; const hasCriticalEvidenceLimitation = limitationReasonCodes.includes("weak_source_mapping") || limitationReasonCodes.includes("insufficient_detail"); const hasNonLowRouteConfidence = input.retrievalResults.some((item) => item.status === "ok" && item.confidence !== "low"); const focusedStrong = okResults.length > 0 && input.groundingCheck.status === "grounded" && !hasCoverageGaps && !policySignals.broad_query_detected && !policySignals.broad_result_flag && !policySignals.minimum_evidence_failed && !hasCriticalEvidenceLimitation && (aggregateEvidenceConfidence !== "low" || hasNonLowRouteConfidence); const decision = buildPolicyDecision({ fallbackType, coverageReport: input.coverageReport, groundingCheck: input.groundingCheck, okResults, partialResults, emptyResults, errorResults, hasSupport, focusedStrong, policySignals }); const missingAnchors = detectMissingAnchors(input.userMessage); const clarificationQuestions = buildClarificationQuestions({ mode: decision.mode, missingAnchors, coverageReport: input.coverageReport, policySignals }); const recommendedActions = buildRecommendedActions({ mode: decision.mode, coverageReport: input.coverageReport, policySignals, limitationReasonCodes, sourceRefs }); const limitations = uniqueStrings([ ...limitationReasonCodes.map((code) => limitationReasonToText(code)), ...extractLimitations(input.retrievalResults), ...input.groundingCheck.reasons, ...(policySignals.minimum_evidence_failed ? ["Minimum evidence gate failed for current scope."] : []), ...(policySignals.broad_query_detected && policySignals.narrowing_strength === "weak" ? ["Broad query remains weakly narrowed; precision is intentionally limited."] : []) ], 10); const openUncertainties = uniqueStrings([ ...input.groundingCheck.missing_requirements, ...(decision.mode === "clarification_required" && missingAnchors.period ? ["missing_anchor:period"] : []), ...(decision.mode === "clarification_required" && missingAnchors.account ? ["missing_anchor:account"] : []), ...(decision.mode === "clarification_required" && missingAnchors.documentOrObject ? ["missing_anchor:document_or_object"] : []), ...(decision.mode === "clarification_required" && missingAnchors.counterparty ? ["missing_anchor:counterparty"] : []) ], 8); const mechanismStatus = mechanismNotes.length === 0 ? "unresolved" : limitationReasonCodes.includes("missing_mechanism") || limitationReasonCodes.includes("heuristic_inference") ? "limited" : "grounded"; const answerStructure = { schema_version: "answer_structure_v1_1", answer_summary: buildAnswerSummary(decision.mode), direct_answer: buildDirectAnswer({ mode: decision.mode, retrievalResults: input.retrievalResults, policySignals }), mechanism_block: { status: mechanismStatus, mechanism_notes: mechanismNotes, limitation_reason_codes: limitationReasonCodes }, evidence_block: { evidence_ids: uniqueStrings(evidenceItems.map((item) => item.evidence_id), 10), source_refs: sourceRefs, mechanism_notes: mechanismNotes, coverage_note: input.coverageReport.requirements_total > 0 && input.coverageReport.requirements_total === input.coverageReport.requirements_covered && input.coverageReport.requirements_uncovered.length === 0 && input.coverageReport.requirements_partially_covered.length === 0 ? "coverage_full_or_near_full" : "coverage_partial_or_limited", ...(claimEvidenceLinks.length > 0 ? { claim_evidence_links: claimEvidenceLinks } : {}) }, uncertainty_block: { open_uncertainties: openUncertainties, limitations }, next_step_block: { recommended_actions: recommendedActions, clarification_questions: clarificationQuestions } }; return { assistant_reply: renderPolicyReply(answerStructure), fallback_type: decision.fallback_type, reply_type: decision.reply_type, answer_structure_v11: answerStructure }; } function composeExplainableAnswer(input, scopeLabel) { const facts = extractTopFacts(input.retrievalResults); const whyIncluded = extractWhyIncluded(input.retrievalResults); const selectionReasons = extractSelectionReasons(input.retrievalResults); const riskFactors = extractRiskFactors(input.retrievalResults); const interpretation = extractBusinessInterpretation(input.retrievalResults); const limitations = uniqueStrings([...extractLimitations(input.retrievalResults), ...input.groundingCheck.reasons]); const nextSteps = suggestNextStep(input.requirements, input.coverageReport); const lead = scopeLabel === "full" ? "Итог: запрос обработан по предмету, найденные объекты подтверждены данными контура." : "Итог: запрос обработан частично, ниже подтвержденная часть и ограничения."; return [ lead, facts.length > 0 ? "Подтвержденные результаты:\n" + formatList(facts) : "", whyIncluded.length > 0 ? "Почему это попало в ответ:\n" + formatList(whyIncluded) : "", selectionReasons.length > 0 ? "Основание отбора:\n" + formatList(selectionReasons) : "", riskFactors.length > 0 ? "Подтверждающие признаки:\n" + formatList(riskFactors) : "", interpretation.length > 0 ? "Практический смысл:\n" + formatList(interpretation) : "", limitations.length > 0 ? "Ограничения:\n" + formatList(limitations) : "", nextSteps.length > 0 ? "Что проверить дальше:\n" + formatList(nextSteps) : "" ] .filter(Boolean) .join("\n\n"); } function composeAssistantAnswer(input) { if (input.enableAnswerPolicyV11) { return composeAssistantAnswerV11(input); } const fallbackType = fallbackFromSummary(input.routeSummary); const okResults = input.retrievalResults.filter((item) => item.status === "ok"); const partialResults = input.retrievalResults.filter((item) => item.status === "partial"); const emptyResults = input.retrievalResults.filter((item) => item.status === "empty"); const errorResults = input.retrievalResults.filter((item) => item.status === "error"); const hasBroadMinimumEvidenceSignal = input.retrievalResults.some((item) => summaryBoolean(item, "broad_guard_applied") && summaryBoolean(item, "minimum_evidence_failed")); const hasBroadClarificationSignal = input.retrievalResults.some((item) => summaryBoolean(item, "broad_guard_applied") && summaryBoolean(item, "minimum_evidence_failed") && summaryString(item, "degraded_to") === "clarification"); if (fallbackType === "out_of_scope" && input.coverageReport.requirements_covered === 0) { return { assistant_reply: "Я могу отвечать только по данным вашей учетной базы. Этот запрос выходит за рамки доступного контура.", fallback_type: "out_of_scope", reply_type: "out_of_scope" }; } if (input.groundingCheck.status === "route_mismatch_blocked") { return { assistant_reply: [ "Не отправляю финальный ответ, потому что предмет результата не совпал с предметом вопроса.", "Уточните формулировку (например, нужный счет/участок учета), и я выполню повторный проход." ].join("\n\n"), fallback_type: "partial", reply_type: "route_mismatch_blocked" }; } if (input.groundingCheck.status === "no_grounded_answer" && okResults.length === 0 && !hasBroadMinimumEvidenceSignal) { return { assistant_reply: "Пока не удалось собрать предметно подтвержденный ответ по вашему вопросу. Нужны дополнительные уточнения по периоду или объекту проверки.", fallback_type: fallbackType, reply_type: "no_grounded_answer" }; } if (hasBroadClarificationSignal && okResults.length === 0 && partialResults.length === 0) { return { assistant_reply: "Запрос слишком широкий для надежного вывода по текущей опоре. Уточните период, участок учета или объект проверки, после чего я дам предметный результат.", fallback_type: "clarification", reply_type: "clarification_required" }; } if (fallbackType === "clarification" && okResults.length === 0 && partialResults.length === 0) { return { assistant_reply: "Уточните, пожалуйста, период, счет, документ или контрагента, чтобы закрыть все части вопроса корректно.", fallback_type: "clarification", reply_type: "clarification_required" }; } if (errorResults.length > 0 && okResults.length === 0 && partialResults.length === 0) { return { assistant_reply: "Не удалось получить данные из контура. Попробуйте повторить запрос или уточнить формулировку.", fallback_type: fallbackType, reply_type: "backend_error" }; } if (partialResults.length > 0 && okResults.length === 0) { return { assistant_reply: composeExplainableAnswer(input, "partial"), fallback_type: "partial", reply_type: "partial_coverage" }; } if (okResults.length === 0 && partialResults.length === 0 && emptyResults.length > 0) { return { assistant_reply: "По заданному условию в текущем срезе данных явных проблемных записей не найдено.", fallback_type: fallbackType, reply_type: "empty_but_valid" }; } const hasPartialCoverage = input.coverageReport.requirements_uncovered.length > 0 || input.coverageReport.requirements_partially_covered.length > 0 || input.coverageReport.clarification_needed_for.length > 0 || input.coverageReport.out_of_scope_requirements.length > 0 || input.groundingCheck.status === "partial" || errorResults.length > 0; if (okResults.length > 0 && hasPartialCoverage) { return { assistant_reply: composeExplainableAnswer(input, "partial"), fallback_type: "partial", reply_type: "partial_coverage" }; } if (okResults.length > 0) { return { assistant_reply: composeExplainableAnswer(input, "full"), fallback_type: "none", reply_type: "factual_with_explanation" }; } return { assistant_reply: "По текущему запросу не удалось построить обоснованный ответ. Уточните формулировку и попробуйте снова.", fallback_type: "unknown", reply_type: "backend_error" }; }