From 0d3b33578e4927d6834323f3e12d752be40cb329 Mon Sep 17 00:00:00 2001 From: dctouch Date: Thu, 23 Apr 2026 10:53:50 +0300 Subject: [PATCH] =?UTF-8?q?ARCH:=20=D1=81=D0=B4=D0=B5=D0=BB=D0=B0=D1=82?= =?UTF-8?q?=D1=8C=20catalog=20drilldown=20=D0=BE=D1=82=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D1=8C=D0=BD=D1=8B=D0=BC=20user-facing=20metadata=20=D1=88?= =?UTF-8?q?=D0=B0=D0=B3=D0=BE=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ss_phase42_catalog_metadata_drilldown.json | 63 +++++++++++++++++++ .../assistantMcpDiscoveryAnswerAdapter.js | 9 +++ .../assistantMcpDiscoveryAnswerAdapter.ts | 12 ++++ ...assistantMcpDiscoveryAnswerAdapter.test.ts | 36 +++++++++++ 4 files changed, 120 insertions(+) create mode 100644 docs/orchestration/address_truth_harness_phase42_catalog_metadata_drilldown.json diff --git a/docs/orchestration/address_truth_harness_phase42_catalog_metadata_drilldown.json b/docs/orchestration/address_truth_harness_phase42_catalog_metadata_drilldown.json new file mode 100644 index 0000000..5c9cea6 --- /dev/null +++ b/docs/orchestration/address_truth_harness_phase42_catalog_metadata_drilldown.json @@ -0,0 +1,63 @@ +{ + "schema_version": "domain_truth_harness_spec_v1", + "scenario_id": "address_truth_harness_phase42_catalog_metadata_drilldown", + "domain": "address_phase42_catalog_metadata_drilldown", + "title": "Phase 42 catalog metadata drilldown replay", + "description": "Targeted AGENT replay for the new Big Block E seam where a catalog-oriented metadata surface should continue on a neutral follow-up into a deeper catalog metadata probe instead of collapsing back into generic metadata or asking for an unrelated lane choice.", + "bindings": {}, + "steps": [ + { + "step_id": "step_01_catalog_metadata_surface", + "title": "Catalog-oriented metadata surface is surfaced honestly for counterparties", + "question": "какие справочники 1С есть по контрагентам?", + "allowed_reply_types": [ + "partial_coverage", + "factual_with_explanation" + ], + "required_answer_patterns_all": [ + "(?i)metadata|метадан", + "(?i)справоч|catalog|directory", + "(?i)контрагент" + ], + "forbidden_answer_patterns": [ + "(?i)получили", + "(?i)заплатили", + "(?i)нетто", + "(?i)документные строки найдены", + "(?i)строки денежных движений найдены" + ], + "criticality": "critical", + "semantic_tags": [ + "catalog_metadata_surface", + "counterparty_catalog_scope" + ] + }, + { + "step_id": "step_02_neutral_followup_catalog_drilldown", + "title": "Neutral follow-up continues into deeper catalog metadata instead of asking for a documents-vs-movements lane choice", + "question": "давай дальше", + "allowed_reply_types": [ + "partial_coverage", + "factual_with_explanation" + ], + "required_answer_patterns_all": [ + "(?i)metadata|метадан|схем", + "(?i)справоч|catalog|directory", + "(?i)контрагент|counterpart" + ], + "forbidden_answer_patterns": [ + "(?i)документ", + "(?i)движени|регистр", + "(?i)уточн.*контур", + "(?i)получили", + "(?i)заплатили", + "(?i)нетто" + ], + "criticality": "critical", + "semantic_tags": [ + "catalog_drilldown", + "neutral_followup" + ] + } + ] +} diff --git a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryAnswerAdapter.js b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryAnswerAdapter.js index 1f983e2..6ec3f3c 100644 --- a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryAnswerAdapter.js +++ b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryAnswerAdapter.js @@ -84,6 +84,12 @@ function isMovementPilot(pilot) { function isMetadataPilot(pilot) { return pilot.pilot_scope === "metadata_inspection_v1"; } +function isCatalogDrilldownPilot(pilot) { + return (isMetadataPilot(pilot) && + (pilot.reason_codes.includes("planner_selected_catalog_drilldown_from_confirmed_metadata_surface_ref") || + pilot.dry_run.reason_codes.includes("planner_selected_catalog_drilldown_from_confirmed_metadata_surface_ref") || + pilot.reason_codes.includes("pilot_catalog_drilldown_metadata_scope_seeded_from_surface_ref"))); +} function isEntityResolutionPilot(pilot) { return pilot.pilot_scope === "entity_resolution_search_v1"; } @@ -267,6 +273,9 @@ function headlineFor(mode, pilot) { if (isMovementPilot(pilot) && mode === "confirmed_with_bounded_inference") { return `По движениям${documentOrMovementScopeRu(pilot)} в 1С найдены подтвержденные строки; ответ ограничен проверенным окном и найденными строками.`; } + if (isCatalogDrilldownPilot(pilot) && mode === "confirmed_with_bounded_inference") { + return "По метаданным 1С удалось углубиться в контур справочников и связанных объектов; это уже не общий обзор схемы, а следующий безопасный catalog drilldown."; + } if (pilot.derived_metadata_surface && mode === "confirmed_with_bounded_inference") { if (pilot.derived_metadata_surface.ambiguity_detected) { return "По метаданным 1С найдены конкурирующие schema-поверхности; перед следующим шагом нужно удержать неоднозначность явно."; diff --git a/llm_normalizer/backend/src/services/assistantMcpDiscoveryAnswerAdapter.ts b/llm_normalizer/backend/src/services/assistantMcpDiscoveryAnswerAdapter.ts index 1921355..c0bd5de 100644 --- a/llm_normalizer/backend/src/services/assistantMcpDiscoveryAnswerAdapter.ts +++ b/llm_normalizer/backend/src/services/assistantMcpDiscoveryAnswerAdapter.ts @@ -123,6 +123,15 @@ function isMetadataPilot(pilot: AssistantMcpDiscoveryPilotExecutionContract): bo return pilot.pilot_scope === "metadata_inspection_v1"; } +function isCatalogDrilldownPilot(pilot: AssistantMcpDiscoveryPilotExecutionContract): boolean { + return ( + isMetadataPilot(pilot) && + (pilot.reason_codes.includes("planner_selected_catalog_drilldown_from_confirmed_metadata_surface_ref") || + pilot.dry_run.reason_codes.includes("planner_selected_catalog_drilldown_from_confirmed_metadata_surface_ref") || + pilot.reason_codes.includes("pilot_catalog_drilldown_metadata_scope_seeded_from_surface_ref")) + ); +} + function isEntityResolutionPilot(pilot: AssistantMcpDiscoveryPilotExecutionContract): boolean { return pilot.pilot_scope === "entity_resolution_search_v1"; } @@ -353,6 +362,9 @@ function headlineFor(mode: AssistantMcpDiscoveryAnswerMode, pilot: AssistantMcpD if (isMovementPilot(pilot) && mode === "confirmed_with_bounded_inference") { return `По движениям${documentOrMovementScopeRu(pilot)} в 1С найдены подтвержденные строки; ответ ограничен проверенным окном и найденными строками.`; } + if (isCatalogDrilldownPilot(pilot) && mode === "confirmed_with_bounded_inference") { + return "По метаданным 1С удалось углубиться в контур справочников и связанных объектов; это уже не общий обзор схемы, а следующий безопасный catalog drilldown."; + } if (pilot.derived_metadata_surface && mode === "confirmed_with_bounded_inference") { if (pilot.derived_metadata_surface.ambiguity_detected) { return "По метаданным 1С найдены конкурирующие schema-поверхности; перед следующим шагом нужно удержать неоднозначность явно."; diff --git a/llm_normalizer/backend/tests/assistantMcpDiscoveryAnswerAdapter.test.ts b/llm_normalizer/backend/tests/assistantMcpDiscoveryAnswerAdapter.test.ts index bcece6b..0997d97 100644 --- a/llm_normalizer/backend/tests/assistantMcpDiscoveryAnswerAdapter.test.ts +++ b/llm_normalizer/backend/tests/assistantMcpDiscoveryAnswerAdapter.test.ts @@ -486,6 +486,42 @@ describe("assistant MCP discovery answer adapter", () => { expect(draft.must_not_claim).toContain("Do not present the inferred next checked lane as already executed data retrieval."); }); + it("uses a distinct human headline for catalog drilldown instead of repeating a generic metadata overview", async () => { + const planner = planAssistantMcpDiscovery({ + metadataSurface: { + selected_entity_set: "Catalog", + selected_surface_objects: ["Catalog.Counterparties"], + downstream_route_family: "catalog_drilldown", + route_family_selection_basis: "selected_entity_set", + recommended_next_primitive: "drilldown_related_objects", + ambiguity_detected: false, + ambiguity_entity_sets: [] + }, + turnMeaning: { + asked_domain_family: "metadata", + asked_action_family: "inspect_catalog", + unsupported_but_understood_family: "schema_surface" + } + }); + const pilot = await executeAssistantMcpDiscoveryPilot( + planner, + buildMetadataDeps([ + { + FullName: "Catalog.Counterparties", + MetaType: "Catalog", + attributes: [{ Name: "Description" }] + } + ]) + ); + + const draft = buildAssistantMcpDiscoveryAnswerDraft(pilot); + + expect(draft.answer_mode).toBe("confirmed_with_bounded_inference"); + expect(draft.headline).toContain("углубиться"); + expect(draft.headline).toContain("справочников"); + expect(draft.headline).toContain("catalog drilldown"); + }); + it("keeps metadata answer honest when schema surface stays ambiguous", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: {