Закрепить ответы ассистента по бизнес-зависимостям

This commit is contained in:
2026-05-26 00:52:02 +03:00
parent 92473c894f
commit e3e17ac7db
27 changed files with 663 additions and 28 deletions
@@ -406,7 +406,11 @@ function composeCounterpartyAnalyticsReply(intent, rows, options = {}, deps) {
const focus = deps.detectValueRankingFocus(options.userMessage);
const limit = deps.detectRankingLimit(options.userMessage, 20);
const minOpsForAvgCheck = deps.detectMinOpsForAvgCheck(options.userMessage);
const normalizedQuestion = deps.normalizeQuestionText(options.userMessage);
const questionSignalText = [options.rawUserMessage, options.userMessage]
.map((value) => String(value ?? "").trim())
.filter(Boolean)
.join(" ");
const normalizedQuestion = deps.normalizeQuestionText(questionSignalText || options.userMessage);
const asksSingleBestCounterparty = focus === "top_by_total" &&
/(?:какой|кто|which|who|какой|кто)/iu.test(normalizedQuestion) &&
/(?:больше\s+всего|сам(?:ый|ая|ое|ые)|наибольш|прин[её]с|highest|most|больше\s+всего|сам(?:ый|ая|РѕРµ|ые)|наибол|РїСЂРёРЅ[её]СЃ)/iu.test(normalizedQuestion) &&
@@ -418,6 +422,7 @@ function composeCounterpartyAnalyticsReply(intent, rows, options = {}, deps) {
const effectiveLimit = asksSingleBestCounterparty || semanticSingleBestCounterparty ? 1 : limit;
const cashflowBoundaryLine = buildCashflowBoundaryLine(isSupplier);
const cashflowNextStepLine = buildCashflowNextStepLine(isSupplier, normalizedQuestion);
const dependencyCue = /(?:зависим|завязан|концентрац|ключев|основн|критич|dependency|concentration|key|critical)/iu.test(normalizedQuestion);
const byCounterparty = new Map();
const byYear = new Map();
const deals = [];
@@ -653,13 +658,20 @@ function composeCounterpartyAnalyticsReply(intent, rows, options = {}, deps) {
? `В выбранном срезе найден один поставщик: ${leadingCounterparty.name} (${deps.formatMoneyRub(leadingCounterparty.total)} по ${leadingCounterparty.ops} операциям). Это не полноценный сравнительный рейтинг.`
: `В выбранном срезе найден один клиент: ${leadingCounterparty.name} (${deps.formatMoneyRub(leadingCounterparty.total)} по ${leadingCounterparty.ops} операциям). Это не полноценный сравнительный рейтинг.`
: isSupplier
? `Крупнейший поставщик по подтвержденным выплатам ${rankingPeriodLabel}: ${leadingCounterparty.name} (${deps.formatMoneyRub(leadingCounterparty.total)} по ${leadingCounterparty.ops} операциям).`
: `Самый доходный клиент ${rankingPeriodLabel} по подтвержденным поступлениям: ${leadingCounterparty.name} (${deps.formatMoneyRub(leadingCounterparty.total)} по ${leadingCounterparty.ops} операциям).`;
? dependencyCue
? `Главный сигнал зависимости по исходящим платежам ${rankingPeriodLabel}: ${leadingCounterparty.name} (${deps.formatMoneyRub(leadingCounterparty.total)} по ${leadingCounterparty.ops} операциям).`
: `Крупнейший поставщик по подтвержденным выплатам ${rankingPeriodLabel}: ${leadingCounterparty.name} (${deps.formatMoneyRub(leadingCounterparty.total)} по ${leadingCounterparty.ops} операциям).`
: dependencyCue
? `Главный сигнал зависимости по входящим деньгам ${rankingPeriodLabel}: ${leadingCounterparty.name} (${deps.formatMoneyRub(leadingCounterparty.total)} по ${leadingCounterparty.ops} операциям).`
: `Самый доходный клиент ${rankingPeriodLabel} по подтвержденным поступлениям: ${leadingCounterparty.name} (${deps.formatMoneyRub(leadingCounterparty.total)} по ${leadingCounterparty.ops} операциям).`;
lines.unshift(directAnswerLine);
if (dependencyCue) {
lines.splice(1, 0, "Это сигнал концентрации по найденным денежным строкам 1С, а не подтверждение договорной зависимости, надежности контрагента или чистой прибыли.");
}
if (!isSupplier) {
lines.splice(1, 0, cashflowBoundaryLine);
lines.splice(dependencyCue ? 2 : 1, 0, cashflowBoundaryLine);
if (cashflowNextStepLine) {
lines.splice(2, 0, cashflowNextStepLine);
lines.splice(dependencyCue ? 3 : 2, 0, cashflowNextStepLine);
}
}
}