From 631391f47a114285af8e3e3f779821b23e61fd1a Mon Sep 17 00:00:00 2001 From: dctouch Date: Mon, 15 Jun 2026 20:48:39 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9D=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20phase106=20=D0=B8=20=D0=B4=D0=B5=D1=82=D0=B5?= =?UTF-8?q?=D0=BA=D1=82=D0=BE=D1=80=D1=8B=20=D0=B0=D0=B3=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=BB=D1=83=D0=BF=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../orchestration/active_domain_contract.json | 193 ++++++++++-------- docs/orchestration/detector_registry.json | 3 + scripts/test_agent_detector_runner.py | 14 +- 3 files changed, 118 insertions(+), 92 deletions(-) diff --git a/docs/orchestration/active_domain_contract.json b/docs/orchestration/active_domain_contract.json index 42f4a92..dc32c66 100644 --- a/docs/orchestration/active_domain_contract.json +++ b/docs/orchestration/active_domain_contract.json @@ -1,10 +1,10 @@ { "schema_version": "domain_scenario_pack_v1", - "pack_id": "active_phase104_generic_role_tail_anchor_hygiene_20260615", - "domain": "address_phase104_generic_role_tail_anchor_hygiene", - "runtime_domain": "business_overview_role_tail_anchor_hygiene", - "title": "Generic role-tail anchor hygiene", - "description": "Active slice for the autonomous agent loop: broad company overview wording may mention customer/supplier roles, but generic role tails must not become counterparty anchors or poison later bank/counterparty drilldowns.", + "pack_id": "active_phase106_profit_cashflow_margin_next_step_20260615", + "domain": "address_phase106_profit_cashflow_margin_next_step", + "runtime_domain": "business_overview_profit_cashflow_margin_boundary", + "title": "Profit, cashflow, margin and next-check boundary", + "description": "Active slice for the autonomous agent loop: colloquial earnings wording must be answered as checked cashflow/accounting evidence with honest profit boundaries, then pivot safely into товарная маржа and concrete next checks.", "status": "active", "source_of_truth_policy": { "purpose": "single mutable domain source for the current orchestration target", @@ -21,46 +21,54 @@ }, "analysis_context": { "as_of_date": "2026-06-15", - "source": "phase104_generic_role_tail_anchor_hygiene_after_value_flow_p06" + "source": "phase106_after_phase104_role_tail_autorun_persistence", + "known_previous_anchors": [ + "hm_business_overview_cashflow_limits_20260602_p4", + "hm_business_profile_evaluation_limits_p3", + "agent_margin_profitability_reliability_20260615_p12_detector_scope", + "phase104_generic_role_tail_anchor_hygiene_20260615_p04_loop" + ] }, "target_score": 88, "bindings": { "main_organization": "ООО Альтернатива Плюс", - "control_year": "2020", - "bank_counterparty": "СБЕРБАНК", - "svk_counterparty": "Группа СВК" + "control_year": "2020" }, "issue_codes_under_test": [ - "bank_counterparty_misclassified_as_business_partner", - "technical_garbage_in_answer", - "business_next_step_missing" + "cashflow_misreported_as_clean_profit", + "margin_domain_leak_accounting_route", + "business_next_step_missing", + "technical_garbage_in_answer" ], "detectors_under_test": [ - "bank_counterparty_boundary_required", "runtime_tokens_in_user_answer", "capability_ids_in_user_answer", - "limited_answer_without_next_action" + "limited_answer_without_next_action", + "margin_domain_leak_accounting_route", + "margin_required_fields_missing", + "margin_next_action_missing" ], "agent_audit_expectations": { "semantic_answer_review_first": true, "direct_answer_first": true, "business_utility_required": true, - "generic_role_tail_must_not_be_anchor": true, - "bank_role_boundary_required": true, + "cashflow_not_clean_profit": true, + "margin_domain_purity_required": true, + "next_action_required_when_limited": true, "technical_garbage_forbidden": true, "do_not_accept_if": [ - "generic role wording such as 'client or supplier' becomes extracted_filters.counterparty", - "the Sberbank follow-up is answered for a stale role-tail instead of Sberbank", - "Sberbank is presented as an ordinary customer/supplier without a bank/financial-role caveat", - "the real supplier prefix for Группа СВК is erased by the role-tail guard", + "colloquial earnings wording is answered as confirmed clean profit without evidence", + "cashflow/net operating flow is silently presented as accounting profit or margin", + "margin follow-up leaks into bank payments, fixed assets, amortization, or generic accounting route", + "next-check answer is generic advice without concrete 1C evidence contours", "final answer leaks route ids, capability ids, runtime enums, or debug payload" ] }, "scenarios": [ { - "scenario_id": "generic_role_tail_anchor_hygiene", - "title": "Role-tail overview, bank drilldown, and real supplier canary", - "description": "Validate that business overview role words stay semantic-only, Sberbank remains a bank-role drilldown, and an explicit supplier prefix still preserves a real counterparty.", + "scenario_id": "profit_cashflow_margin_next_step_boundary", + "title": "Colloquial earnings, clean-profit boundary, margin pivot, and next checks", + "description": "Validate that the assistant handles colloquial earnings wording, refuses to overclaim clean profit, pivots into товарная маржа without wrong-domain leakage, and proposes concrete next 1C checks.", "steps": [ { "step_id": "step_01_choose_company_scope", @@ -86,104 +94,109 @@ ] }, { - "step_id": "step_02_business_overview_role_tail_not_counterparty", - "title": "Business overview role words do not become counterparty anchors", - "question": "Теперь дай взрослый обзор за {{bindings.control_year}} по компании: входящие, исходящие, нетто, топы, но банк в топах отдельно объясни как финансовый поток, если по назначению он не обычный клиент или поставщик.", + "step_id": "step_02_colloquial_earned_money_2020", + "title": "Colloquial earned-money wording stays bounded", + "question": "скока денег альтернатива заработала за 20 год?", "depends_on": [ "step_01_choose_company_scope" ], - "expected_intents": [ - "business_overview" - ], "semantic_tags": [ "business_overview", - "generic_role_tail_anchor_hygiene", - "financial_role_purpose" + "colloquial_cashflow", + "cashflow_not_profit" ], "required_answer_shape": "direct_answer_first", - "forbidden_filter_keys": [ - "counterparty" - ], - "forbidden_filter_values": { - "counterparty": [ - "или поставщик", - "поставщик", - "клиент", - "обычный клиент", - "обычный клиент или поставщик" - ] - }, "required_answer_patterns_all": [ - "(?i)2020|компан|организац", - "(?i)вход|поступ|исход|списан|нетто", - "(?i)банк|сбербанк|финансов", - "(?i)не.*обычн.*клиент|не.*обычн.*поставщик|не.*выручк|не.*закупк|назначен" + "(?i)2020|20\\s*год", + "(?i)поступ|входящ|денег|заработ", + "(?i)не.*чист.*прибыл|не.*прибыл|прибыл.*не подтвержд|это.*не.*прибыл" ], "forbidden_answer_patterns": [ - "(?i)сбербанк.*обычный поставщик", - "(?i)сбербанк.*обычный клиент", - "(?i)чистая прибыль.*точно", - "(?i)route_candidate|primitive|planner_|catalog_|snapshot_items|answer_object" - ] + "(?i)^\\s*Коротко:", + "(?i)это\\s+чистая\\s+прибыль", + "(?i)точно.*прибыл", + "(?i)route_candidate|primitive|planner_|catalog_|snapshot_items|answer_object|capability_id|mcp_discovery" + ], + "criticality": "critical" }, { - "step_id": "step_03_sberbank_role_after_role_tail_overview", - "title": "Bank-role drilldown still resolves Sberbank after role-tail overview", - "question": "А отдельно по {{bindings.bank_counterparty}}У: он для нас клиент, поставщик или финансовый поток? Дай коротко по подтвержденным строкам.", + "step_id": "step_03_clean_profit_followup_boundary", + "title": "Clean-profit follow-up must not convert cashflow into profit", + "question": "а это чистая прибыль?", "depends_on": [ - "step_02_business_overview_role_tail_not_counterparty" + "step_02_colloquial_earned_money_2020" ], "semantic_tags": [ - "bank_like_counterparty", - "financial_role_purpose", - "post_overview_anchor_integrity" + "profitability_boundary", + "cashflow_not_profit", + "followup_boundary" ], "required_answer_shape": "direct_answer_first", - "required_filters": { - "counterparty": "{{bindings.bank_counterparty}}" - }, "required_answer_patterns_all": [ - "(?i)сбербанк|банк", - "(?i)финансов|банковск|кредит|комисс|возврат|назначен|вид операц", - "(?i)не.*обычн.*клиент|не.*обычн.*поставщик|не.*клиентск.*выручк|нельзя.*читать" + "(?i)не.*чист.*прибыл|не.*является.*прибыл|не подтвержд.*прибыл|это.*не.*прибыл", + "(?i)денежн|поступ|нетто|оборот|cash|себестоим|расход|закрыва" ], "forbidden_answer_patterns": [ - "(?i)или поставщик", - "(?i)сбербанк.*обычный поставщик", - "(?i)сбербанк.*обычный клиент", - "(?i)route_candidate|primitive|planner_|catalog_|snapshot_items|answer_object" - ] + "(?i)^\\s*Коротко:", + "(?i)^\\s*да[,\\s]+это.*прибыл", + "(?i)точно.*чист.*прибыл", + "(?i)route_candidate|primitive|planner_|catalog_|snapshot_items|answer_object|capability_id|mcp_discovery" + ], + "criticality": "critical" }, { - "step_id": "step_04_real_supplier_prefix_still_keeps_counterparty", - "title": "Explicit supplier prefix still preserves a real counterparty", - "question": "А теперь по поставщику {{bindings.svk_counterparty}} за {{bindings.control_year}}: сколько мы ему заплатили и какой общий денежный смысл?", + "step_id": "step_04_margin_pivot_keeps_inventory_domain", + "title": "Margin pivot uses inventory margin contour, not payments or fixed assets", + "question": "тогда какую прибыль или маржу можно проверить по товарам за 2020?", "depends_on": [ - "step_03_sberbank_role_after_role_tail_overview" + "step_03_clean_profit_followup_boundary" ], "expected_intents": [ - "value_flow" + "inventory_margin_ranking_for_nomenclature" ], + "expected_capability": "inventory_inventory_margin_ranking_for_nomenclature", + "expected_recipe": "address_inventory_margin_ranking_for_nomenclature_v1", "semantic_tags": [ - "counterparty_net_cash_flow", - "supplier_prefix_canary", - "stale_scope_guard" + "margin_profitability", + "inventory", + "wrong_domain_trap", + "cashflow_to_margin_pivot" ], "required_answer_shape": "direct_answer_first", - "required_filters": { - "counterparty": "{{bindings.svk_counterparty}}" - }, - "required_answer_patterns_all": [ - "(?i)свк|группа", - "(?i)2020", - "(?i)заплат|исход|списан|получ|поступ|нетто|смысл" + "required_answer_patterns_any": [ + "(?i)марж|валов|выруч|себестоим|номенклатур|товар|не могу подтвердить|не хватает" ], "forbidden_answer_patterns": [ - "(?i)или поставщик", - "(?i)сбербанк", - "(?i)уточните организац|какую компанию", - "(?i)route_candidate|primitive|planner_|catalog_|snapshot_items|answer_object" - ] + "(?i)амортизац|основн(ые|ых)? средств|объект ОС|банк|оплат[аы]|payment_document|settlement", + "(?i)route_candidate|primitive|planner_|catalog_|snapshot_items|answer_object|capability_id|mcp_discovery" + ], + "criticality": "critical" + }, + { + "step_id": "step_05_next_checks_are_concrete_1c_contours", + "title": "Next checks must be concrete 1C evidence contours", + "question": "что дальше проверить, чтобы понять здоровье бизнеса, без гаданий?", + "depends_on": [ + "step_04_margin_pivot_keeps_inventory_domain" + ], + "semantic_tags": [ + "business_next_step", + "evidence_plan", + "no_generic_advice" + ], + "required_answer_shape": "direct_answer_first", + "required_answer_patterns_all": [ + "(?i)прибыл|марж|себестоим|финрезульт", + "(?i)ндс|налог", + "(?i)дебитор|кредитор|долг|расчет", + "(?i)склад|остат|оборач|товар" + ], + "forbidden_answer_patterns": [ + "(?i)^\\s*Коротко:", + "(?i)просто.*посмотр|общ.*рекоменд|улучшить маркетинг|нанять|стратег", + "(?i)route_candidate|primitive|planner_|catalog_|snapshot_items|answer_object|capability_id|mcp_discovery" + ], + "criticality": "important" } ] } diff --git a/docs/orchestration/detector_registry.json b/docs/orchestration/detector_registry.json index 553a11c..845694a 100644 --- a/docs/orchestration/detector_registry.json +++ b/docs/orchestration/detector_registry.json @@ -66,6 +66,9 @@ "required_patterns_any": [ "(?i)(выруч|себестоим|валов|марж|не хватает|не могу подтвердить|не подтвержден|unknown)" ], + "artifact_path_include_patterns": [ + "(?i)steps[\\\\/][^\\\\/]*(?:margin|profitability|марж)" + ], "artifact_path_exclude_patterns": [ "(?i)selected_item", "(?i)supplier", diff --git a/scripts/test_agent_detector_runner.py b/scripts/test_agent_detector_runner.py index b21c785..7fdcb80 100644 --- a/scripts/test_agent_detector_runner.py +++ b/scripts/test_agent_detector_runner.py @@ -228,7 +228,7 @@ class AgentDetectorRunnerTests(unittest.TestCase): root = Path(tmp) artifact_dir = root / "run" write_text( - artifact_dir / "scenarios" / "margin_root" / "steps" / "step_01" / "output.md", + artifact_dir / "scenarios" / "profit_cashflow_boundary" / "steps" / "step_01_margin_root" / "output.md", "Top margin item: revenue 100, cogs 60, gross profit 40, margin 40%.", ) write_text( @@ -240,6 +240,15 @@ class AgentDetectorRunnerTests(unittest.TestCase): / "output.md", "Supplier for the selected item: Example LLC.", ) + write_text( + artifact_dir + / "scenarios" + / "business_health" + / "steps" + / "step_05_next_checks" + / "output.md", + "Check profit, debt, inventory, taxes, and customer concentration next.", + ) registry_path = root / "detector_registry.json" issue_catalog_path = root / "issue_catalog.json" write_json( @@ -255,6 +264,7 @@ class AgentDetectorRunnerTests(unittest.TestCase): "inputs": ["output.md"], "check": { "required_patterns_any": ["(?i)(revenue|cogs|gross profit|margin)"], + "artifact_path_include_patterns": ["(?i)steps[\\\\/][^\\\\/]*(?:margin|profitability)"], "artifact_path_exclude_patterns": ["(?i)selected_item"], }, } @@ -275,7 +285,7 @@ class AgentDetectorRunnerTests(unittest.TestCase): self.assertEqual(results["results"][0]["status"], "pass") evidence_paths = [item["path"] for item in results["results"][0]["evidence"]] self.assertEqual(len(evidence_paths), 1) - self.assertIn("margin_root", evidence_paths[0]) + self.assertIn("step_01_margin_root", evidence_paths[0]) def test_composite_detector_fails_after_child_detector_fails(self) -> None: with tempfile.TemporaryDirectory() as tmp: