From b2fe0460b39674e519a7c66f59270ae1cbe5c3c0 Mon Sep 17 00:00:00 2001 From: dctouch Date: Sat, 23 May 2026 19:46:32 +0300 Subject: [PATCH] =?UTF-8?q?=D0=97=D0=B0=D0=BA=D1=80=D0=B5=D0=BF=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20phase5=20continuity=20replay=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=201=D0=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...e5_company_selection_and_activity_age.json | 87 +++++-------------- .../counterpartyAnalyticsReplyBuilders.js | 7 +- .../backend/dist/services/assistantService.js | 2 +- .../dist/services/capabilitiesRegistry.js | 2 +- .../counterpartyAnalyticsReplyBuilders.ts | 8 +- .../backend/src/services/assistantService.ts | 2 +- .../src/services/capabilitiesRegistry.ts | 2 +- 7 files changed, 37 insertions(+), 73 deletions(-) diff --git a/docs/orchestration/address_truth_harness_phase5_company_selection_and_activity_age.json b/docs/orchestration/address_truth_harness_phase5_company_selection_and_activity_age.json index a2f11b2..97131ea 100644 --- a/docs/orchestration/address_truth_harness_phase5_company_selection_and_activity_age.json +++ b/docs/orchestration/address_truth_harness_phase5_company_selection_and_activity_age.json @@ -10,109 +10,75 @@ "step_id": "step_01_smalltalk", "title": "Casual opening stays human", "question": "привет, как дела?", - "semantic_tags": [ - "meta_smalltalk" - ] + "semantic_tags": ["meta_smalltalk"] }, { "step_id": "step_02_data_scope_meta", "title": "Data-scope question shows available companies", "question": "по какой компании мы сейчас работаем?", - "semantic_tags": [ - "meta_scope" - ] + "semantic_tags": ["meta_scope"] }, { "step_id": "step_03_inventory_root_requires_company", "title": "Inventory root correctly asks to choose a company", "question": "какие остатки на складе на март 2021", - "semantic_tags": [ - "inventory_root", - "company_clarification" - ] + "semantic_tags": ["inventory_root", "company_clarification"] }, { "step_id": "step_04_choose_company", "title": "User selects the company inside the same session", "question": "давай по Альтернативе Плюс", - "semantic_tags": [ - "meta_scope", - "company_selection" - ] + "semantic_tags": ["meta_scope", "company_selection"] }, { "step_id": "step_05_inventory_root_after_company", "title": "Inventory root continues after the company choice", "question": "тогда покажи остатки на март 2021", - "semantic_tags": [ - "inventory_root", - "company_selected" - ] + "semantic_tags": ["inventory_root", "company_selected"] }, { "step_id": "step_06_selected_item_supplier", "title": "Selected-object supplier follow-up survives after company selection", "question": "По выбранному объекту \"Столешница 600*3050*26 альмандин\": кто нам это поставил?", - "semantic_tags": [ - "selected_object", - "selected_object_supplier" - ] + "semantic_tags": ["selected_object", "selected_object_supplier"] }, { "step_id": "step_07_selected_item_documents", "title": "Selected-object documents stay in the same contour", "question": "По выбранному объекту \"Столешница 600*3050*26 альмандин\": покажи документы по этой позиции", - "semantic_tags": [ - "selected_object", - "selected_object_documents" - ] + "semantic_tags": ["selected_object", "selected_object_documents"] }, { "step_id": "step_08_inventory_same_date_restore", "title": "Same-date restore returns to the inventory root within the chosen company", "question": "покажи еще раз остатки на эту же дату", - "semantic_tags": [ - "inventory_root", - "same_date_restore" - ] + "semantic_tags": ["inventory_root", "same_date_restore"] }, { "step_id": "step_09_company_activity_age", "title": "Organization age should be answered through reachable activity evidence or honest boundedness", - "allowed_reply_types": [ - "factual", - "factual_with_explanation", - "partial_coverage" - ], - "expected_intents": [ - "counterparty_activity_lifecycle" - ], + "allowed_reply_types": ["factual", "factual_with_explanation", "partial_coverage"], + "expected_intents": ["counterparty_activity_lifecycle"], "expected_recipe": "address_counterparty_activity_lifecycle_v1", "required_direct_answer_patterns_any": [ - "(?i)РїРѕ активности", - "(?i)первая подтвержденная активность|РЅРµ удается точно определить" + "(?i)по активности", + "(?i)первая подтвержденная активность|не удается точно определить" ], "forbidden_direct_answer_patterns": [ - "(?i)РЅРµ найден контрагент", - "(?i)уточните точное наименование организации" + "(?i)не найден контрагент", + "(?i)уточните точное наименование организации" ], "criticality": "critical", "question": "а по Альтернативе Плюс сколько лет активности в базе 1С?", - "semantic_tags": [ - "organization_activity_age", - "company_selected" - ] + "semantic_tags": ["organization_activity_age", "company_selected"] }, { "step_id": "step_10_capability_meta_interrupt", "title": "Capability meta interrupt does not destroy prior context", - "allowed_reply_types": [ - "factual_with_explanation", - "factual" - ], + "allowed_reply_types": ["factual_with_explanation", "factual"], "required_direct_answer_patterns_any": [ - "(?i)1СЃ", - "(?i)РЅРґСЃ|контрагент|остатк|склад" + "(?i)1с", + "(?i)ндс|контрагент|остатк|склад" ], "forbidden_direct_answer_patterns": [ "(?i)vat_period_snapshot", @@ -124,34 +90,25 @@ ], "criticality": "warning", "question": "что ты умеешь?", - "semantic_tags": [ - "meta_capability" - ] + "semantic_tags": ["meta_capability"] }, { "step_id": "step_11_memory_recap_after_interrupts", "title": "Memory recap still remembers the selected object after capability interrupt", "question": "а ты помнишь, что мы по этой позиции уже выяснили?", - "semantic_tags": [ - "meta_memory" - ] + "semantic_tags": ["meta_memory"] }, { "step_id": "step_12_receivables_march_2020", "title": "Cross-domain pivot keeps the active company", "question": "кто нам должен на март 2020", - "semantic_tags": [ - "settlements_receivables" - ] + "semantic_tags": ["settlements_receivables"] }, { "step_id": "step_13_inventory_same_date_cross_domain", "title": "Inventory same-date pivot should reuse the root date inside the active company", "question": "остатки по складу на эту же дату", - "semantic_tags": [ - "inventory_root", - "same_date_pivot" - ] + "semantic_tags": ["inventory_root", "same_date_pivot"] } ] } diff --git a/llm_normalizer/backend/dist/services/address_runtime/counterpartyAnalyticsReplyBuilders.js b/llm_normalizer/backend/dist/services/address_runtime/counterpartyAnalyticsReplyBuilders.js index 7735b39..1d7256b 100644 --- a/llm_normalizer/backend/dist/services/address_runtime/counterpartyAnalyticsReplyBuilders.js +++ b/llm_normalizer/backend/dist/services/address_runtime/counterpartyAnalyticsReplyBuilders.js @@ -20,6 +20,9 @@ function groupRowsByMarker(rows) { function formatOptionalDate(value, formatDateRu) { return value ? formatDateRu(value) : "дата не указана"; } +function trimTrailingSentenceDot(value) { + return value ? value.replace(/\.+$/u, "") : null; +} function findFocusedCounterpartyValuePoint(profileRows, counterpartyHint, deps) { if (!counterpartyHint) { return null; @@ -252,7 +255,7 @@ function composeCounterpartyAnalyticsReply(intent, rows, options = {}, deps) { : null; const lines = [ observedAgeLabel && firstObservedActivity && lastObservedActivity - ? `По активности в базе 1С контрагент ${focusedCounterparty.name} наблюдается минимум ${observedAgeLabel}.` + ? `По активности в базе 1С контрагент ${focusedCounterparty.name} наблюдается минимум ${trimTrailingSentenceDot(observedAgeLabel)}.` : `По активности в базе 1С контрагент ${focusedCounterparty.name} найден в подтвержденных движениях.` ]; if (firstObservedActivity) { @@ -311,7 +314,7 @@ function composeCounterpartyAnalyticsReply(intent, rows, options = {}, deps) { : null; const lines = [ observedAgeLabel && organizationFirstObservedActivity && organizationLastObservedActivity - ? `По активности организации ${organizationHint} в базе 1С наблюдается минимум ${observedAgeLabel}.` + ? `По активности организации ${organizationHint} в базе 1С наблюдается минимум ${trimTrailingSentenceDot(observedAgeLabel)}.` : `По активности организации ${organizationHint} в базе 1С найдены подтвержденные движения.` ]; if (organizationFirstObservedActivity) { diff --git a/llm_normalizer/backend/dist/services/assistantService.js b/llm_normalizer/backend/dist/services/assistantService.js index 006ad7b..c458632 100644 --- a/llm_normalizer/backend/dist/services/assistantService.js +++ b/llm_normalizer/backend/dist/services/assistantService.js @@ -4090,7 +4090,7 @@ function buildAssistantCapabilityContractReply(userMessage = "") { .replace(/По основным группам:/giu, "Основные направления:") .replace(/Если нужно, подскажу, как лучше сформулировать запрос под вашу задачу\./giu, "Если хотите, можно сразу задать конкретный вопрос по документам, остаткам, НДС, контрагенту или договору.") .replace(/Что не делаю:\s*/giu, "Не делаю только административные действия: ") - .replace(/\s{2,}/g, " ") + .replace(/[^\S\r\n]{2,}/g, " ") .replace(/\n{3,}/g, "\n\n") .trim(); return normalizedReply || "Могу помогать с вопросами по данным 1С: НДС, контрагенты, долги, деньги, договоры и склад."; diff --git a/llm_normalizer/backend/dist/services/capabilitiesRegistry.js b/llm_normalizer/backend/dist/services/capabilitiesRegistry.js index 78f5e19..6e79fb5 100644 --- a/llm_normalizer/backend/dist/services/capabilitiesRegistry.js +++ b/llm_normalizer/backend/dist/services/capabilitiesRegistry.js @@ -199,7 +199,7 @@ function loadCapabilitiesRegistry() { } function buildCapabilityContractReplyFromRegistry() { return [ - "Могу быстро смотреть управленческие вещи по данным 1С в режиме чтения.", + "Могу помочь с быстрым анализом данных 1С:", "", "- кто должен денег и кому должны;", "- какой год или месяц был самым денежным;", diff --git a/llm_normalizer/backend/src/services/address_runtime/counterpartyAnalyticsReplyBuilders.ts b/llm_normalizer/backend/src/services/address_runtime/counterpartyAnalyticsReplyBuilders.ts index 4c685be..2c133cd 100644 --- a/llm_normalizer/backend/src/services/address_runtime/counterpartyAnalyticsReplyBuilders.ts +++ b/llm_normalizer/backend/src/services/address_runtime/counterpartyAnalyticsReplyBuilders.ts @@ -105,6 +105,10 @@ function formatOptionalDate(value: string | null, formatDateRu: (isoDate: string return value ? formatDateRu(value) : "дата не указана"; } +function trimTrailingSentenceDot(value: string | null): string | null { + return value ? value.replace(/\.+$/u, "") : null; +} + function findFocusedCounterpartyValuePoint( profileRows: CounterpartyValuePoint[], counterpartyHint: string | null | undefined, @@ -371,7 +375,7 @@ export function composeCounterpartyAnalyticsReply( : null; const lines: string[] = [ observedAgeLabel && firstObservedActivity && lastObservedActivity - ? `По активности в базе 1С контрагент ${focusedCounterparty.name} наблюдается минимум ${observedAgeLabel}.` + ? `По активности в базе 1С контрагент ${focusedCounterparty.name} наблюдается минимум ${trimTrailingSentenceDot(observedAgeLabel)}.` : `По активности в базе 1С контрагент ${focusedCounterparty.name} найден в подтвержденных движениях.` ]; if (firstObservedActivity) { @@ -433,7 +437,7 @@ export function composeCounterpartyAnalyticsReply( : null; const lines: string[] = [ observedAgeLabel && organizationFirstObservedActivity && organizationLastObservedActivity - ? `По активности организации ${organizationHint} в базе 1С наблюдается минимум ${observedAgeLabel}.` + ? `По активности организации ${organizationHint} в базе 1С наблюдается минимум ${trimTrailingSentenceDot(observedAgeLabel)}.` : `По активности организации ${organizationHint} в базе 1С найдены подтвержденные движения.` ]; if (organizationFirstObservedActivity) { diff --git a/llm_normalizer/backend/src/services/assistantService.ts b/llm_normalizer/backend/src/services/assistantService.ts index ce3ce19..6920f7d 100644 --- a/llm_normalizer/backend/src/services/assistantService.ts +++ b/llm_normalizer/backend/src/services/assistantService.ts @@ -4047,7 +4047,7 @@ function buildAssistantCapabilityContractReply(userMessage = "") { .replace(/По основным группам:/giu, "Основные направления:") .replace(/Если нужно, подскажу, как лучше сформулировать запрос под вашу задачу\./giu, "Если хотите, можно сразу задать конкретный вопрос по документам, остаткам, НДС, контрагенту или договору.") .replace(/Что не делаю:\s*/giu, "Не делаю только административные действия: ") - .replace(/\s{2,}/g, " ") + .replace(/[^\S\r\n]{2,}/g, " ") .replace(/\n{3,}/g, "\n\n") .trim(); return normalizedReply || "Могу помогать с вопросами по данным 1С: НДС, контрагенты, долги, деньги, договоры и склад."; diff --git a/llm_normalizer/backend/src/services/capabilitiesRegistry.ts b/llm_normalizer/backend/src/services/capabilitiesRegistry.ts index 78a3126..70203e1 100644 --- a/llm_normalizer/backend/src/services/capabilitiesRegistry.ts +++ b/llm_normalizer/backend/src/services/capabilitiesRegistry.ts @@ -220,7 +220,7 @@ export function loadCapabilitiesRegistry(): CapabilityRegistry { export function buildCapabilityContractReplyFromRegistry(): string { return [ - "Могу быстро смотреть управленческие вещи по данным 1С в режиме чтения.", + "Могу помочь с быстрым анализом данных 1С:", "", "- кто должен денег и кому должны;", "- какой год или месяц был самым денежным;",