diff --git a/llm_normalizer/backend/dist/services/address_runtime/composeStage.js b/llm_normalizer/backend/dist/services/address_runtime/composeStage.js index e5b766f..901c225 100644 --- a/llm_normalizer/backend/dist/services/address_runtime/composeStage.js +++ b/llm_normalizer/backend/dist/services/address_runtime/composeStage.js @@ -3493,44 +3493,27 @@ function composeFactualReplyBody(intent, rows, options = {}) { }; } const lines = [ - `Коротко: подтвержденная дебиторская задолженность на ${formatDateRu(receivablesAsOfDate)} — ${formatMoneyRub(totalOutstandingAmount)}.`, - "Это подтвержденный срез дебиторской задолженности, а не эвристический shortlist." + `Коротко: на ${formatDateRu(receivablesAsOfDate)} нам должны ${formatMoneyRub(totalOutstandingAmount)}.` ]; - lines.push(""); - lines.push("Что учтено"); - lines.push(`- Дата среза: ${formatDateRu(receivablesAsOfDate)}.`); - if (periodScopeLine) { - lines.push(periodScopeLine); - } - lines.push("- Контур: дебиторская задолженность по счетам 62/76."); - if (carryoverLine) { - lines.push(carryoverLine); - } - lines.push(""); - lines.push("Сводка"); - lines.push(`- Строк в выборке: ${formatNumberWithDots(rows.length)}.`); - lines.push(`- Контрагентов с подтвержденным остатком к получению: ${formatNumberWithDots(confirmedBalances.length)}.`); - appendDebtMirrorDisclosure(lines, balanceSnapshot, "receivables"); - lines.push(""); - lines.push("Категории дебиторской задолженности"); - lines.push(`- ${receivablesCategoryLabel("supplier_or_contractor")}: ${formatNumberWithDots(categoryCounts.supplier_or_contractor)}.`); - lines.push(`- ${receivablesCategoryLabel("bank_or_credit")}: ${formatNumberWithDots(categoryCounts.bank_or_credit)}.`); - lines.push(`- ${receivablesCategoryLabel("tax_or_state")}: ${formatNumberWithDots(categoryCounts.tax_or_state)}.`); - lines.push(`- ${receivablesCategoryLabel("other")}: ${formatNumberWithDots(categoryCounts.other)}.`); - lines.push(""); - lines.push("Подтвержденные позиции к получению"); - if (confirmedBalances.length > 0) { - lines.push(...confirmedBalances.slice(0, 10).flatMap((item, index) => [ - `${index + 1}. ${item.name} | категория: ${receivablesCategoryLabel(item.category)} | остаток к получению: ${formatMoneyRub(item.outstandingAmount)} | операций: ${formatNumberWithDots(item.operations)}${item.categoryReasons.length > 0 ? ` | основание: ${item.categoryReasons.join(", ")}` : ""}${formatPayablesEvidenceSuffix(item)}`, - "" - ])); - if (lines[lines.length - 1] === "") { - lines.pop(); + if (periodScopeLine || carryoverLine) { + lines.push(""); + lines.push("Граница ответа:"); + if (periodScopeLine) { + lines.push(periodScopeLine); } + if (carryoverLine) { + lines.push(carryoverLine); + } + } + lines.push(""); + lines.push("К получению:"); + if (confirmedBalances.length > 0) { + lines.push(...confirmedBalances.slice(0, 10).map((item, index) => `${index + 1}. ${item.name} — ${formatMoneyRub(item.outstandingAmount)} (${formatNumberWithDots(item.operations)} опер.).`)); } else { lines.push("- Подтвержденной открытой дебиторской задолженности на дату среза не найдено."); } + appendDebtMirrorDisclosure(lines, balanceSnapshot, "receivables"); return { responseType: confirmedBalances.length > 0 ? "FACTUAL_LIST" : "FACTUAL_SUMMARY", text: joinLines(lines), diff --git a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryResponseCandidate.js b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryResponseCandidate.js index 680bfa1..38e0289 100644 --- a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryResponseCandidate.js +++ b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryResponseCandidate.js @@ -403,9 +403,9 @@ function businessOverviewCoverageLimitLine(overview) { if (outgoing?.coverage_limited_by_probe_limit === true) { limited.push("исходящие"); } - const continuation = "Если нужен полный сквозной ответ, безопасный следующий шаг — выбрать конкретный год или квартал для дозапроса: тогда широкий срез можно собрать частями без выдачи непроверенного итога."; + const continuation = "Для полного сквозного итога лучше разбить проверку по годам или кварталам."; return limited.length > 0 - ? `Важно: по направлению ${limited.join(" и ")} проверка достигла лимита строк; это расширенный проверенный срез найденных строк, но не гарантия полного бухгалтерского оборота без отдельной полной выгрузки. ${continuation}` + ? `Ограничение: широкий денежный срез по направлению ${limited.join(" и ")} не считаю полным бухгалтерским оборотом без отдельной полной выгрузки. ${continuation}` : null; } function joinBusinessReplyLines(lines) { diff --git a/llm_normalizer/backend/dist/services/capabilitiesRegistry.js b/llm_normalizer/backend/dist/services/capabilitiesRegistry.js index 23c7e4f..78f5e19 100644 --- a/llm_normalizer/backend/dist/services/capabilitiesRegistry.js +++ b/llm_normalizer/backend/dist/services/capabilitiesRegistry.js @@ -199,7 +199,8 @@ function loadCapabilitiesRegistry() { } function buildCapabilityContractReplyFromRegistry() { return [ - "Могу быстро смотреть управленческие вещи по данным 1С в режиме чтения:", + "Могу быстро смотреть управленческие вещи по данным 1С в режиме чтения.", + "", "- кто должен денег и кому должны;", "- какой год или месяц был самым денежным;", "- какие контрагенты дают основной поток;", @@ -213,6 +214,7 @@ function buildCapabilityContractReplyFromRegistry() { "- кому мы должны на сегодня", "- какое нетто по СВК за 2020", "- сколько НДС к уплате за 4 квартал 2019", + "", "Что не делаю: не настраиваю 1С, не меняю конфигурацию, не создаю и не провожу документы, не выполняю админ-действия на сервере." ].join("\n"); } diff --git a/llm_normalizer/backend/src/services/address_runtime/composeStage.ts b/llm_normalizer/backend/src/services/address_runtime/composeStage.ts index d8df5f1..f57ba81 100644 --- a/llm_normalizer/backend/src/services/address_runtime/composeStage.ts +++ b/llm_normalizer/backend/src/services/address_runtime/composeStage.ts @@ -4326,7 +4326,6 @@ function composeFactualReplyBody( }, { supplier_or_contractor: 0, bank_or_credit: 0, tax_or_state: 0, other: 0 } ); - if (isDirectBalanceQuestion(options.userMessage)) { const leading = confirmedBalances[0] ?? null; const compactLines: string[] = leading @@ -4472,49 +4471,31 @@ function composeFactualReplyBody( } const lines: string[] = [ - `Коротко: подтвержденная дебиторская задолженность на ${formatDateRu(receivablesAsOfDate)} — ${formatMoneyRub(totalOutstandingAmount)}.`, - "Это подтвержденный срез дебиторской задолженности, а не эвристический shortlist." + `Коротко: на ${formatDateRu(receivablesAsOfDate)} нам должны ${formatMoneyRub(totalOutstandingAmount)}.` ]; - - lines.push(""); - lines.push("Что учтено"); - lines.push(`- Дата среза: ${formatDateRu(receivablesAsOfDate)}.`); - if (periodScopeLine) { - lines.push(periodScopeLine); - } - lines.push("- Контур: дебиторская задолженность по счетам 62/76."); - if (carryoverLine) { - lines.push(carryoverLine); + if (periodScopeLine || carryoverLine) { + lines.push(""); + lines.push("Граница ответа:"); + if (periodScopeLine) { + lines.push(periodScopeLine); + } + if (carryoverLine) { + lines.push(carryoverLine); + } } lines.push(""); - lines.push("Сводка"); - lines.push(`- Строк в выборке: ${formatNumberWithDots(rows.length)}.`); - lines.push(`- Контрагентов с подтвержденным остатком к получению: ${formatNumberWithDots(confirmedBalances.length)}.`); - appendDebtMirrorDisclosure(lines, balanceSnapshot, "receivables"); - - lines.push(""); - lines.push("Категории дебиторской задолженности"); - lines.push(`- ${receivablesCategoryLabel("supplier_or_contractor")}: ${formatNumberWithDots(categoryCounts.supplier_or_contractor)}.`); - lines.push(`- ${receivablesCategoryLabel("bank_or_credit")}: ${formatNumberWithDots(categoryCounts.bank_or_credit)}.`); - lines.push(`- ${receivablesCategoryLabel("tax_or_state")}: ${formatNumberWithDots(categoryCounts.tax_or_state)}.`); - lines.push(`- ${receivablesCategoryLabel("other")}: ${formatNumberWithDots(categoryCounts.other)}.`); - - lines.push(""); - lines.push("Подтвержденные позиции к получению"); + lines.push("К получению:"); if (confirmedBalances.length > 0) { lines.push( - ...confirmedBalances.slice(0, 10).flatMap((item, index) => [ - `${index + 1}. ${item.name} | категория: ${receivablesCategoryLabel(item.category)} | остаток к получению: ${formatMoneyRub(item.outstandingAmount)} | операций: ${formatNumberWithDots(item.operations)}${item.categoryReasons.length > 0 ? ` | основание: ${item.categoryReasons.join(", ")}` : ""}${formatPayablesEvidenceSuffix(item)}`, - "" - ]) + ...confirmedBalances.slice(0, 10).map( + (item, index) => `${index + 1}. ${item.name} — ${formatMoneyRub(item.outstandingAmount)} (${formatNumberWithDots(item.operations)} опер.).` + ) ); - if (lines[lines.length - 1] === "") { - lines.pop(); - } } else { lines.push("- Подтвержденной открытой дебиторской задолженности на дату среза не найдено."); } + appendDebtMirrorDisclosure(lines, balanceSnapshot, "receivables"); return { responseType: confirmedBalances.length > 0 ? "FACTUAL_LIST" : "FACTUAL_SUMMARY", diff --git a/llm_normalizer/backend/src/services/assistantMcpDiscoveryResponseCandidate.ts b/llm_normalizer/backend/src/services/assistantMcpDiscoveryResponseCandidate.ts index 32d115a..24d37c0 100644 --- a/llm_normalizer/backend/src/services/assistantMcpDiscoveryResponseCandidate.ts +++ b/llm_normalizer/backend/src/services/assistantMcpDiscoveryResponseCandidate.ts @@ -472,9 +472,9 @@ function businessOverviewCoverageLimitLine(overview: Record): s limited.push("исходящие"); } const continuation = - "Если нужен полный сквозной ответ, безопасный следующий шаг — выбрать конкретный год или квартал для дозапроса: тогда широкий срез можно собрать частями без выдачи непроверенного итога."; + "Для полного сквозного итога лучше разбить проверку по годам или кварталам."; return limited.length > 0 - ? `Важно: по направлению ${limited.join(" и ")} проверка достигла лимита строк; это расширенный проверенный срез найденных строк, но не гарантия полного бухгалтерского оборота без отдельной полной выгрузки. ${continuation}` + ? `Ограничение: широкий денежный срез по направлению ${limited.join(" и ")} не считаю полным бухгалтерским оборотом без отдельной полной выгрузки. ${continuation}` : null; } diff --git a/llm_normalizer/backend/src/services/capabilitiesRegistry.ts b/llm_normalizer/backend/src/services/capabilitiesRegistry.ts index abc57da..78a3126 100644 --- a/llm_normalizer/backend/src/services/capabilitiesRegistry.ts +++ b/llm_normalizer/backend/src/services/capabilitiesRegistry.ts @@ -220,7 +220,8 @@ export function loadCapabilitiesRegistry(): CapabilityRegistry { export function buildCapabilityContractReplyFromRegistry(): string { return [ - "Могу быстро смотреть управленческие вещи по данным 1С в режиме чтения:", + "Могу быстро смотреть управленческие вещи по данным 1С в режиме чтения.", + "", "- кто должен денег и кому должны;", "- какой год или месяц был самым денежным;", "- какие контрагенты дают основной поток;", @@ -234,6 +235,7 @@ export function buildCapabilityContractReplyFromRegistry(): string { "- кому мы должны на сегодня", "- какое нетто по СВК за 2020", "- сколько НДС к уплате за 4 квартал 2019", + "", "Что не делаю: не настраиваю 1С, не меняю конфигурацию, не создаю и не провожу документы, не выполняю админ-действия на сервере." ].join("\n"); } diff --git a/llm_normalizer/backend/tests/addressQueryRuntimeM23.test.ts b/llm_normalizer/backend/tests/addressQueryRuntimeM23.test.ts index 4d6dafd..1b9133b 100644 --- a/llm_normalizer/backend/tests/addressQueryRuntimeM23.test.ts +++ b/llm_normalizer/backend/tests/addressQueryRuntimeM23.test.ts @@ -796,12 +796,11 @@ describe("address compose stage utf8 headers", () => { ); expect(reply.responseType).toBe("FACTUAL_LIST"); - expect(reply.text).toContain("Коротко: подтвержденная дебиторская задолженность на 31.05.2020"); - expect(reply.text).toContain("Это подтвержденный срез дебиторской задолженности"); - expect(reply.text).toContain("Что учтено"); - expect(reply.text).toContain("Сводка"); - expect(reply.text).toContain("Категории дебиторской задолженности"); - expect(reply.text).toContain("Подтвержденные позиции к получению"); + expect(reply.text).toContain("Коротко: на 31.05.2020 нам должны"); + expect(reply.text).toContain("К получению:"); + expect(reply.text).not.toContain("эвристический shortlist"); + expect(reply.text).not.toContain("Строк в выборке"); + expect(reply.text).not.toContain("Категории дебиторской задолженности"); expect(reply.text).not.toContain("Блок 1"); }); diff --git a/llm_normalizer/backend/tests/assistantMcpDiscoveryResponseCandidate.test.ts b/llm_normalizer/backend/tests/assistantMcpDiscoveryResponseCandidate.test.ts index 6894ca2..c39c0cf 100644 --- a/llm_normalizer/backend/tests/assistantMcpDiscoveryResponseCandidate.test.ts +++ b/llm_normalizer/backend/tests/assistantMcpDiscoveryResponseCandidate.test.ts @@ -446,11 +446,11 @@ describe("assistant MCP discovery response candidate", () => { expect(candidate.reply_text).toContain("не полный бухгалтерский рейтинг доходности"); expect(candidate.reply_text).toContain("не как чистую бухгалтерскую прибыль"); expect(candidate.reply_text).toContain("Годовое операционное нетто в широком срезе не ранжирую"); - expect(candidate.reply_text).toContain("проверка достигла лимита строк"); - expect(candidate.reply_text).toContain("выбрать конкретный год или квартал для дозапроса"); - expect(candidate.reply_text).toContain("без выдачи непроверенного итога"); + expect(candidate.reply_text).toContain("лучше разбить проверку по годам или кварталам"); + expect(candidate.reply_text).toContain("не считаю полным бухгалтерским оборотом"); expect(candidate.reply_text).not.toContain("По расчетному операционному нетто лучший год"); expect(candidate.reply_text).not.toContain("По годам:"); + expect(candidate.reply_text).not.toContain("проверка достигла лимита строк"); expect(candidate.reply_text).not.toContain("лимит выборки MCP"); expect(candidate.reply_text).not.toContain("MCP-срез"); expect(candidate.reply_text).not.toContain("Что подтверждено:");