Укрепить семантику бизнес-ответов адресного контура

This commit is contained in:
2026-05-09 21:48:17 +03:00
parent f86cb8e886
commit dfbfe26501
48 changed files with 2644 additions and 176 deletions
@@ -3281,11 +3281,22 @@ function composeFactualReplyBody(intent, rows, options = {}) {
}
if (intent === "open_items_by_counterparty_or_contract") {
const counterparties = buildCounterpartyRiskAggregate(rows);
const accountLead = typeof options.accountHint === "string" && options.accountHint.trim().length > 0
? `Проверил хвосты по счету ${options.accountHint.trim()}.`
: "Собраны открытые позиции по взаиморасчетам.";
const accountLabel = typeof options.accountHint === "string" && options.accountHint.trim().length > 0
? `по счету ${options.accountHint.trim()}`
: "по взаиморасчетам";
const exactBalanceRequested = options.requestedResultMode === "confirmed_balance";
const periodLabel = options.asOfDate
? `на ${formatDateRu(options.asOfDate)}`
: options.periodFrom || options.periodTo
? `за период ${formatDateRu(options.periodFrom ?? "...")}..${formatDateRu(options.periodTo ?? "...")}`
: null;
const lines = [
accountLead,
exactBalanceRequested
? `Коротко: точный открытый остаток ${accountLabel}${periodLabel ? ` ${periodLabel}` : ""} не подтвержден; ниже только предварительные сигналы по движениям: ${formatNumberWithDots(rows.length)} строк, контрагентов с сигналом: ${formatNumberWithDots(counterparties.length)}.`
: `Коротко: ${accountLabel} найдено ${formatNumberWithDots(rows.length)} строк хвостов/открытых расчетов; контрагентов с сигналом: ${formatNumberWithDots(counterparties.length)}.`,
exactBalanceRequested
? "Это не подтвержденное сальдо и не финальный реестр открытых расчетов: текущий контур видит движения-кандидаты, но не доказывает остаток закрытия."
: "Это shortlist для проверки, а не финальный подтвержденный реестр открытых расчетов.",
`Строк отобрано: ${rows.length}.`,
`Контрагентов с сигналом: ${counterparties.length}.`
];
@@ -3301,7 +3312,12 @@ function composeFactualReplyBody(intent, rows, options = {}) {
}
return {
responseType: "FACTUAL_LIST",
text: lines.join("\n")
text: lines.join("\n"),
semantics: {
result_mode: "heuristic_candidates",
evidence_strength: counterparties.length > 0 || rows.length > 0 ? "medium" : "weak",
balance_confirmed: false
}
};
}
if (intent === "list_contracts_by_counterparty") {
@@ -3366,7 +3382,7 @@ function composeFactualReplyBody(intent, rows, options = {}) {
? `Контрагент: ${counterpartyInline}. Найдено документов: ${rows.length}.`
: `Найдено документов по контрагенту: ${rows.length}.`);
}
if (counterpartyLabel) {
if (counterpartyLabel && itemFlowQuestion) {
lines.push(`Контрагент: ${counterpartyLabel}`);
}
if (itemFlowQuestion) {
@@ -3388,7 +3404,11 @@ function composeFactualReplyBody(intent, rows, options = {}) {
}
}
else {
lines.push(...formatTopRows(rows, rows.length));
const visibleRows = rows.slice(0, 5);
lines.push(...formatTopRows(visibleRows, visibleRows.length));
if (rows.length > visibleRows.length) {
lines.push(`Показаны первые ${visibleRows.length} из ${rows.length} документов; полный список остается в подтвержденном срезе.`);
}
}
return {
responseType: "FACTUAL_LIST",
@@ -165,11 +165,18 @@ const FOLLOWUP_LOW_QUALITY_COUNTERPARTY_TOKENS = new Set([
"сейчас",
"этому",
"этомуже",
"этой",
"этойже",
"тому",
"томуже",
"той",
"тойже",
"нему",
"ней",
"ним",
"цепочка",
"цепочке",
"цепочку",
"неуказанному",
"неуказанный",
"неуказанная",
@@ -93,11 +93,16 @@ function composeInventoryReply(intent, rows, options, deps) {
: `На ${deps.formatDateRu(asOfDate)} подтвержденных товарных остатков по счету 41.01 не найдено.`;
const lines = [directAnswerLine];
if (positions.length > 0) {
(0, inventoryReplyPresentation_1.appendInventorySection)(lines, "Позиции:", positions.slice(0, 20).map((item, index) => (0, inventoryReplyPresentation_1.formatInventorySnapshotPositionLine)(item, index, {
const visiblePositionsLimit = 6;
const visiblePositions = positions.slice(0, visiblePositionsLimit);
(0, inventoryReplyPresentation_1.appendInventorySection)(lines, "Позиции:", visiblePositions.map((item, index) => (0, inventoryReplyPresentation_1.formatInventorySnapshotPositionLine)(item, index, {
formatDateRu: deps.formatDateRu,
formatNumberWithDots: deps.formatNumberWithDots,
formatMoneyRub: deps.formatMoneyRub
})));
if (positions.length > visiblePositions.length) {
lines.push(`Показаны первые ${deps.formatNumberWithDots(visiblePositions.length)} из ${deps.formatNumberWithDots(positions.length)} позиций по сумме; полный список можно раскрыть отдельным запросом.`);
}
}
else {
(0, inventoryReplyPresentation_1.appendInventorySection)(lines, "Позиции:", [