diff --git a/docs/ARCH/11 - architecture_turnaround/11 - continuity_stabilization_plan_2026-04-17.md b/docs/ARCH/11 - architecture_turnaround/11 - continuity_stabilization_plan_2026-04-17.md index fcdf4ef..0b51efd 100644 --- a/docs/ARCH/11 - architecture_turnaround/11 - continuity_stabilization_plan_2026-04-17.md +++ b/docs/ARCH/11 - architecture_turnaround/11 - continuity_stabilization_plan_2026-04-17.md @@ -248,7 +248,11 @@ Latest continuity-authority convergence evidence after the current route pass: - the first human-answer-shaping cleanup pass is now applied to heavy profile/aggregate exact answers: - `period_coverage_profile` and `document_type_and_account_section_profile` now start with a direct business-first lead (`Коротко: ...`) instead of service-flavored intros like `профиль собран` / `строк агрегата`; - the top block now states the business conclusion first and leaves ranked detail blocks below, which reduces the catalog-like feel without hiding the actual data; - - this is still only the first shaping seam: other long exact replies, especially VAT and some ranking families, still need the same treatment; +- the next human-answer-shaping cleanup pass is now applied to VAT exact replies: + - `vat_payable_forecast` and `vat_liability_confirmed_for_tax_period` now open with a business-first `Коротко: ...` lead, while the detailed calculation stays in the secondary block; + - service-flavored top lines like `Собран прогноз...`, `Режим результата...`, and `Строк агрегата...` are removed from the first screen of the reply, which makes VAT answers read like user-facing guidance instead of an engine report; + - VAT reply tests now explicitly protect this top-block shape, so future changes cannot silently reintroduce the same mechanical preamble; + - this is still not the end of shaping work: some ranking families and long evidence-heavy replies still need the same cleanup; - this pass does not yet finish full single-owner continuity, but it narrows one of the remaining seams where route arbitration and scope memory could disagree about whether the session was still grounded. ## Next Execution Slice (2026-04-18) diff --git a/llm_normalizer/backend/dist/services/address_runtime/composeStage.js b/llm_normalizer/backend/dist/services/address_runtime/composeStage.js index 4c2fce9..f753a0a 100644 --- a/llm_normalizer/backend/dist/services/address_runtime/composeStage.js +++ b/llm_normalizer/backend/dist/services/address_runtime/composeStage.js @@ -2480,13 +2480,12 @@ function composeFactualReplyBody(intent, rows, options = {}) { const vatProbe = options.vatDirectSourceProbe ?? null; const periodWindowLabel = options.periodFrom && options.periodTo ? `${formatDateRu(options.periodFrom)}..${formatDateRu(options.periodTo)}` : null; const lines = [ - `Собран прогноз НДС к уплате: ${formatForecastMoney(vatToPay)}.`, - `Потенциальный перенос/переплата: ${formatForecastMoney(carryoverOrOverpayment)}.`, - `Период оценки: ${periodWindowLabel ?? "не задан (использован доступный срез)"}.`, - "Режим результата: предварительная оценка по проводкам 68.02*/19* (не подтвержденная сумма налога по декларации).", + `Коротко: ориентир по НДС к уплате за ${periodWindowLabel ?? "доступный срез"} — ${formatForecastMoney(vatToPay)}.`, + `Если смотреть на возможный перенос или переплату, получается ${formatForecastMoney(carryoverOrOverpayment)}.`, + "Это предварительная оценка по оборотам 68.02*/19*, а не подтвержденная сумма налога по декларации.", "", - "База расчета:", - `- Строк агрегата: ${formatNumberWithDots(rows.length)}.`, + "Что вошло в расчет:", + `- Период оценки: ${periodWindowLabel ?? "не задан (использован доступный срез)"}.`, `- Оборот по кредиту 68*: ${formatForecastMoney(turnover68Credit)}.`, `- Оборот по дебету 68*: ${formatForecastMoney(turnover68Debit)}.`, `- Нетто НДС (68 Кт - 68 Дт): ${formatForecastMoney(netVat)}.`, @@ -2573,13 +2572,12 @@ function composeFactualReplyBody(intent, rows, options = {}) { const formatConfirmedMoney = (value) => (options.useRubCurrency ? formatMoneyRub(value) : formatMoney(value)); const vatProbe = options.vatDirectSourceProbe ?? null; const lines = [ - `Собран подтвержденный расчет НДС к уплате за налоговый период: ${formatConfirmedMoney(vatToPay)}.`, - `Налоговый период расчета: ${periodWindowLabel ?? "не задан (нужен явный период)"}.`, - `Потенциальный перенос/переплата: ${formatConfirmedMoney(carryoverOrOverpayment)}.`, - "Режим результата: подтвержденный расчет по регистрам книг продаж/покупок (tax-period mode, без surrogate-формулы 68/19).", + `Коротко: подтвержденный НДС к уплате за налоговый период — ${formatConfirmedMoney(vatToPay)}.`, + `Если смотреть на возможный перенос или переплату, получается ${formatConfirmedMoney(carryoverOrOverpayment)}.`, + "Это подтвержденный расчет по регистрам книг продаж и покупок, без surrogate-формулы 68/19.", "", - "База расчета:", - `- Строк агрегата: ${formatNumberWithDots(rows.length)}.`, + "Что вошло в расчет:", + `- Налоговый период расчета: ${periodWindowLabel ?? "не задан (нужен явный период)"}.`, `- НДС по книге продаж: ${formatConfirmedMoney(salesVat)}.`, `- НДС по книге покупок (вычеты): ${formatConfirmedMoney(purchaseVat)}.`, `- Нетто НДС (книга продаж - книга покупок): ${formatConfirmedMoney(netVat)}.` diff --git a/llm_normalizer/backend/src/services/address_runtime/composeStage.ts b/llm_normalizer/backend/src/services/address_runtime/composeStage.ts index 5ddafc6..87da137 100644 --- a/llm_normalizer/backend/src/services/address_runtime/composeStage.ts +++ b/llm_normalizer/backend/src/services/address_runtime/composeStage.ts @@ -3166,13 +3166,12 @@ function composeFactualReplyBody( options.periodFrom && options.periodTo ? `${formatDateRu(options.periodFrom)}..${formatDateRu(options.periodTo)}` : null; const lines = [ - `Собран прогноз НДС к уплате: ${formatForecastMoney(vatToPay)}.`, - `Потенциальный перенос/переплата: ${formatForecastMoney(carryoverOrOverpayment)}.`, - `Период оценки: ${periodWindowLabel ?? "не задан (использован доступный срез)"}.`, - "Режим результата: предварительная оценка по проводкам 68.02*/19* (не подтвержденная сумма налога по декларации).", + `Коротко: ориентир по НДС к уплате за ${periodWindowLabel ?? "доступный срез"} — ${formatForecastMoney(vatToPay)}.`, + `Если смотреть на возможный перенос или переплату, получается ${formatForecastMoney(carryoverOrOverpayment)}.`, + "Это предварительная оценка по оборотам 68.02*/19*, а не подтвержденная сумма налога по декларации.", "", - "База расчета:", - `- Строк агрегата: ${formatNumberWithDots(rows.length)}.`, + "Что вошло в расчет:", + `- Период оценки: ${periodWindowLabel ?? "не задан (использован доступный срез)"}.`, `- Оборот по кредиту 68*: ${formatForecastMoney(turnover68Credit)}.`, `- Оборот по дебету 68*: ${formatForecastMoney(turnover68Debit)}.`, `- Нетто НДС (68 Кт - 68 Дт): ${formatForecastMoney(netVat)}.`, @@ -3309,13 +3308,12 @@ function composeFactualReplyBody( const vatProbe = options.vatDirectSourceProbe ?? null; const lines = [ - `Собран подтвержденный расчет НДС к уплате за налоговый период: ${formatConfirmedMoney(vatToPay)}.`, - `Налоговый период расчета: ${periodWindowLabel ?? "не задан (нужен явный период)"}.`, - `Потенциальный перенос/переплата: ${formatConfirmedMoney(carryoverOrOverpayment)}.`, - "Режим результата: подтвержденный расчет по регистрам книг продаж/покупок (tax-period mode, без surrogate-формулы 68/19).", + `Коротко: подтвержденный НДС к уплате за налоговый период — ${formatConfirmedMoney(vatToPay)}.`, + `Если смотреть на возможный перенос или переплату, получается ${formatConfirmedMoney(carryoverOrOverpayment)}.`, + "Это подтвержденный расчет по регистрам книг продаж и покупок, без surrogate-формулы 68/19.", "", - "База расчета:", - `- Строк агрегата: ${formatNumberWithDots(rows.length)}.`, + "Что вошло в расчет:", + `- Налоговый период расчета: ${periodWindowLabel ?? "не задан (нужен явный период)"}.`, `- НДС по книге продаж: ${formatConfirmedMoney(salesVat)}.`, `- НДС по книге покупок (вычеты): ${formatConfirmedMoney(purchaseVat)}.`, `- Нетто НДС (книга продаж - книга покупок): ${formatConfirmedMoney(netVat)}.` diff --git a/llm_normalizer/backend/tests/addressQueryRuntimeM23.test.ts b/llm_normalizer/backend/tests/addressQueryRuntimeM23.test.ts index 20fa911..1baf972 100644 --- a/llm_normalizer/backend/tests/addressQueryRuntimeM23.test.ts +++ b/llm_normalizer/backend/tests/addressQueryRuntimeM23.test.ts @@ -1675,7 +1675,8 @@ describe("address compose stage utf8 headers", () => { expect(reply.responseType).toBe("FACTUAL_SUMMARY"); expect(reply.text).toContain("Почему прогноз к уплате 0"); expect(reply.text).toContain("max(0, 68 Кт - 68 Дт)"); - expect(reply.text).toContain("Собран прогноз НДС к уплате: 0.00."); + expect(reply.text).toContain("Коротко: ориентир по НДС к уплате"); + expect(reply.text).toContain("0.00."); expect(reply.text).toContain("За период 68 Кт = 9126.00, 68 Дт = 115342.00, разница = -106216.00."); expect(reply.text).toContain("Разница неположительная"); expect(reply.text).toContain("оперативный прогноз по оборотам НДС-субсчетов 68.02*/19*"); @@ -1789,7 +1790,8 @@ describe("address compose stage utf8 headers", () => { ); expect(reply.responseType).toBe("FACTUAL_SUMMARY"); - expect(reply.text).toContain("Собран прогноз НДС к уплате: 0.00."); + expect(reply.text).toContain("Коротко: ориентир по НДС к уплате"); + expect(reply.text).toContain("0.00."); expect(reply.text).toContain("не найдено движений по НДС-субсчетам 68.02*/19*"); expect(reply.text).toContain("Чеклист проверки в 1С (почему к уплате 0):"); expect(reply.text).toContain("Проверьте наличие движений в РегистрБухгалтерии.Хозрасчетный"); @@ -1824,7 +1826,8 @@ describe("address compose stage utf8 headers", () => { ); expect(reply.responseType).toBe("FACTUAL_SUMMARY"); - expect(reply.text).toContain("Собран прогноз НДС к уплате: 0.00."); + expect(reply.text).toContain("Коротко: ориентир по НДС к уплате"); + expect(reply.text).toContain("0.00."); expect(reply.text).toContain("обороты по 68* взаимно перекрылись"); expect(reply.text).toContain("Чеклист проверки в 1С (почему к уплате 0):"); }); @@ -1907,7 +1910,7 @@ describe("address compose stage utf8 headers", () => { ); expect(reply.responseType).toBe("FACTUAL_SUMMARY"); - expect(reply.text).toContain("Собран подтвержденный расчет НДС к уплате за налоговый период"); + expect(reply.text).toContain("Коротко: подтвержденный НДС к уплате за налоговый период"); expect(reply.text).toContain("50.000,00 ₽"); expect(reply.semantics?.result_mode).toBe("confirmed_balance"); expect(reply.semantics?.balance_confirmed).toBe(true); @@ -1934,7 +1937,7 @@ describe("address compose stage utf8 headers", () => { ); expect(reply.text).toContain("**1.234.567,89** ₽"); - expect(reply.text).toContain("Собран прогноз НДС к уплате:"); + expect(reply.text).toContain("Коротко: ориентир по НДС к уплате"); }); it("does not split dates and list numbering when numeric emphasis is enabled", () => {