From 49f291a14b88a277f38a1cd351bbeb7cb589fd6f Mon Sep 17 00:00:00 2001 From: dctouch Date: Sat, 18 Apr 2026 15:36:08 +0300 Subject: [PATCH] =?UTF-8?q?=D0=90=D0=A0=D0=A7=20=D0=90=D0=9F11=20-=20?= =?UTF-8?q?=D0=90=D1=80=D1=85=D0=B8=D1=82=D0=B5=D0=BA=D1=82=D1=83=D1=80?= =?UTF-8?q?=D0=B0=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20=D1=80=D0=B5=D0=B3?= =?UTF-8?q?=D1=80=D0=B5=D1=81=D1=81=D0=B0:=20=D0=90=D1=80=D1=85=D0=B8?= =?UTF-8?q?=D1=82=D0=B5=D0=BA=D1=82=D1=83=D1=80=D0=B0:=20=D1=83=D0=B1?= =?UTF-8?q?=D1=80=D0=B0=D1=82=D1=8C=20lane-=D0=BB=D0=B5=D0=B9=D0=B1=D0=BB?= =?UTF-8?q?=D1=8B=20=D0=B8=D0=B7=20plain=20list=20exact-=D0=BE=D1=82=D0=B2?= =?UTF-8?q?=D0=B5=D1=82=D0=BE=D0=B2=20=D0=B8=20=D1=81=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D0=B0=D1=82=D1=8C=20=D0=B2=D0=B5=D1=80=D1=85=20=D0=BE=D1=82?= =?UTF-8?q?=D0=B2=D0=B5=D1=82=D0=B0=20business-first?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ontinuity_stabilization_plan_2026-04-17.md | 4 ++++ .../services/address_runtime/composeStage.js | 24 ++++++++----------- .../services/address_runtime/composeStage.ts | 24 ++++++++----------- .../tests/addressQueryRuntimeM23.test.ts | 11 +++++---- 4 files changed, 31 insertions(+), 32 deletions(-) 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 44121f0..28abadc 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 @@ -256,6 +256,10 @@ Latest continuity-authority convergence evidence after the current route pass: - `counterparty_activity_lifecycle`, `contract_usage_overview`, `customer_revenue_and_payments`, `supplier_payouts_profile`, and `contract_usage_and_value` now open with business-first wording instead of service-flavored `профиль собран / строк агрегата / строк источника`; - ranking and contract replies now preserve user wording better in the visible heading layer, including `минимальный бюджет` phrasing for low-turnover active contracts; - targeted ranking/profile tests now protect the new top-block shape, so these families are less likely to regress back into report-like wording during later route/domain work; +- the next human-answer-shaping cleanup pass is now applied to plain list replies in the exact lane: + - `list_contracts_by_counterparty`, `list_documents_by_contract`, `bank_operations_by_counterparty`, `bank_operations_by_contract`, and the generic factual-list fallback no longer leak `live address lane / catalog address lane` wording into the user-facing answer; + - these list replies now start with direct business-first leads and keep the selected rows below, which preserves factual usefulness without exposing internal routing labels; + - targeted utf8 header tests now explicitly protect against `lane` leakage in these list families; - this is still not the end of shaping work: some long evidence-heavy replies and residual catalog-style blocks 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. diff --git a/llm_normalizer/backend/dist/services/address_runtime/composeStage.js b/llm_normalizer/backend/dist/services/address_runtime/composeStage.js index f753a0a..36c4669 100644 --- a/llm_normalizer/backend/dist/services/address_runtime/composeStage.js +++ b/llm_normalizer/backend/dist/services/address_runtime/composeStage.js @@ -3301,9 +3301,8 @@ function composeFactualReplyBody(intent, rows, options = {}) { .map((item) => String(item ?? "").trim()) .filter((item) => item.length > 0)); const lines = [ - "Собран список договоров по контрагенту (catalog address lane).", - `Строк отобрано: ${rows.length}.`, - `Уникальных договоров: ${contracts.length}.` + `Коротко: найдено ${contracts.length} договоров по контрагенту.`, + `В выборке ${rows.length} строк для подтверждения списка.` ]; if (counterparties.length === 1) { lines.push(`Контрагент: ${counterparties[0]}.`); @@ -3319,7 +3318,7 @@ function composeFactualReplyBody(intent, rows, options = {}) { } } else { - lines.push("Договоры по указанному якорю в текущем live-срезе не найдены."); + lines.push("По указанному контрагенту договоры в текущем срезе не найдены."); } return { responseType: "FACTUAL_LIST", @@ -3386,9 +3385,8 @@ function composeFactualReplyBody(intent, rows, options = {}) { } if (intent === "list_documents_by_contract") { const lines = [ - `Найдено документов по договору: ${rows.length}.`, - "Собран список документов по договору (live address lane).", - `Строк отобрано: ${rows.length}.`, + `Коротко: найдено документов по договору — ${rows.length}.`, + `Показываю подтвержденные документы из текущего среза.`, ...formatTopRows(rows, rows.length) ]; return { @@ -3398,9 +3396,8 @@ function composeFactualReplyBody(intent, rows, options = {}) { } if (intent === "bank_operations_by_counterparty") { const lines = [ - `Найдено банковских операций по контрагенту: ${rows.length}.`, - "Собран список банковских операций по контрагенту (live address lane).", - `Строк отобрано: ${rows.length}.`, + `Коротко: найдено банковских операций по контрагенту — ${rows.length}.`, + "Показываю подтвержденные банковские операции из текущего среза.", ...formatTopRows(rows, rows.length) ]; return { @@ -3410,9 +3407,8 @@ function composeFactualReplyBody(intent, rows, options = {}) { } if (intent === "bank_operations_by_contract") { const lines = [ - `Найдено банковских операций по договору: ${rows.length}.`, - "Собран список банковских операций по договору (live address lane).", - `Строк отобрано: ${rows.length}.`, + `Коротко: найдено банковских операций по договору — ${rows.length}.`, + "Показываю подтвержденные банковские операции из текущего среза.", ...formatTopRows(rows, rows.length) ]; return { @@ -3420,7 +3416,7 @@ function composeFactualReplyBody(intent, rows, options = {}) { text: lines.join("\n") }; } - const lines = ["Срез адресного запроса собран.", `Строк отобрано: ${rows.length}.`, ...formatTopRows(rows, 6)]; + const lines = ["Коротко: собрал подтвержденный срез по запросу.", `В выборке ${rows.length} строк.`, ...formatTopRows(rows, 6)]; return { responseType: "FACTUAL_LIST", text: lines.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 87da137..87d53fd 100644 --- a/llm_normalizer/backend/src/services/address_runtime/composeStage.ts +++ b/llm_normalizer/backend/src/services/address_runtime/composeStage.ts @@ -4224,9 +4224,8 @@ function composeFactualReplyBody( ); const lines: string[] = [ - "Собран список договоров по контрагенту (catalog address lane).", - `Строк отобрано: ${rows.length}.`, - `Уникальных договоров: ${contracts.length}.` + `Коротко: найдено ${contracts.length} договоров по контрагенту.`, + `В выборке ${rows.length} строк для подтверждения списка.` ]; if (counterparties.length === 1) { @@ -4242,7 +4241,7 @@ function composeFactualReplyBody( lines.push(`Показаны первые ${visible.length} из ${contracts.length} договоров.`); } } else { - lines.push("Договоры по указанному якорю в текущем live-срезе не найдены."); + lines.push("По указанному контрагенту договоры в текущем срезе не найдены."); } return { @@ -4324,9 +4323,8 @@ function composeFactualReplyBody( if (intent === "list_documents_by_contract") { const lines = [ - `Найдено документов по договору: ${rows.length}.`, - "Собран список документов по договору (live address lane).", - `Строк отобрано: ${rows.length}.`, + `Коротко: найдено документов по договору — ${rows.length}.`, + `Показываю подтвержденные документы из текущего среза.`, ...formatTopRows(rows, rows.length) ]; return { @@ -4337,9 +4335,8 @@ function composeFactualReplyBody( if (intent === "bank_operations_by_counterparty") { const lines = [ - `Найдено банковских операций по контрагенту: ${rows.length}.`, - "Собран список банковских операций по контрагенту (live address lane).", - `Строк отобрано: ${rows.length}.`, + `Коротко: найдено банковских операций по контрагенту — ${rows.length}.`, + "Показываю подтвержденные банковские операции из текущего среза.", ...formatTopRows(rows, rows.length) ]; return { @@ -4350,9 +4347,8 @@ function composeFactualReplyBody( if (intent === "bank_operations_by_contract") { const lines = [ - `Найдено банковских операций по договору: ${rows.length}.`, - "Собран список банковских операций по договору (live address lane).", - `Строк отобрано: ${rows.length}.`, + `Коротко: найдено банковских операций по договору — ${rows.length}.`, + "Показываю подтвержденные банковские операции из текущего среза.", ...formatTopRows(rows, rows.length) ]; return { @@ -4361,7 +4357,7 @@ function composeFactualReplyBody( }; } - const lines = ["Срез адресного запроса собран.", `Строк отобрано: ${rows.length}.`, ...formatTopRows(rows, 6)]; + const lines = ["Коротко: собрал подтвержденный срез по запросу.", `В выборке ${rows.length} строк.`, ...formatTopRows(rows, 6)]; return { responseType: "FACTUAL_LIST", text: lines.join("\n") diff --git a/llm_normalizer/backend/tests/addressQueryRuntimeM23.test.ts b/llm_normalizer/backend/tests/addressQueryRuntimeM23.test.ts index a96ce7e..e340454 100644 --- a/llm_normalizer/backend/tests/addressQueryRuntimeM23.test.ts +++ b/llm_normalizer/backend/tests/addressQueryRuntimeM23.test.ts @@ -451,7 +451,8 @@ describe("address compose stage utf8 headers", () => { analytics: [] } ]); - expect(reply.text).toContain("Собран список документов по договору (live address lane)."); + expect(reply.text).toContain("Коротко: найдено документов по договору"); + expect(reply.text).not.toContain("live address lane"); }); it("renders readable russian header for contract bank operations", () => { @@ -465,7 +466,8 @@ describe("address compose stage utf8 headers", () => { analytics: [] } ]); - expect(reply.text).toContain("Собран список банковских операций по договору (live address lane)."); + expect(reply.text).toContain("Коротко: найдено банковских операций по договору"); + expect(reply.text).not.toContain("live address lane"); }); it("renders readable russian header for contracts-by-counterparty list", () => { @@ -479,8 +481,9 @@ describe("address compose stage utf8 headers", () => { analytics: ["Жуковка 51"] } ]); - expect(reply.text).toContain("Собран список договоров по контрагенту (catalog address lane)."); - expect(reply.text).toContain("Уникальных договоров: 1."); + expect(reply.text).toContain("Коротко: найдено 1 договоров по контрагенту."); + expect(reply.text).not.toContain("catalog address lane"); + expect(reply.text).toContain("В выборке 1 строк для подтверждения списка."); expect(reply.text).toContain("Договор №19/15"); });