diff --git a/docs/orchestration/agent_autonomy_business_quality_20260523.json b/docs/orchestration/agent_autonomy_business_quality_20260523.json new file mode 100644 index 0000000..e235816 --- /dev/null +++ b/docs/orchestration/agent_autonomy_business_quality_20260523.json @@ -0,0 +1,189 @@ +{ + "schema_version": "domain_truth_harness_spec_v1", + "scenario_id": "agent_autonomy_business_quality_20260523", + "domain": "autonomy_business_quality", + "title": "AGENT | Autonomy business quality pack", + "description": "Expanded targeted AGENT replay for the current autonomy milestone: value-flow hot handoff must survive realistic business questions, while final answers must read like a useful 1C analyst instead of runtime/debug prose.", + "bindings": {}, + "steps": [ + { + "step_id": "step_01_incoming_total_no_counterparties", + "title": "Incoming money total by organization without counterparty split", + "question": "Сколько входящих денег за 2020 год по ООО Альтернатива Плюс без разреза по контрагентам?", + "allowed_reply_types": ["partial_coverage", "factual_with_explanation", "factual"], + "expected_mcp_discovery_response_applied": true, + "expected_mcp_discovery_selected_chain_id": "value_flow", + "expected_mcp_discovery_response_candidate_status": "ready_for_guarded_use", + "expected_mcp_discovery_candidate_hot_runtime_wired": true, + "expected_mcp_discovery_hot_runtime_wired": true, + "expected_mcp_discovery_execution_handoff_status": "ready_for_guarded_response", + "expected_mcp_discovery_execution_handoff_can_use_guarded_response": true, + "expected_catalog_alignment_status": "selected_matches_top", + "expected_catalog_chain_top_match": "value_flow", + "expected_catalog_selected_matches_top": true, + "required_answer_patterns_all": ["2020", "47[\\s.]*628[\\s.]*853", "входящ|поступ|получ", "руб"], + "forbidden_answer_patterns": ["Учтено строк", "Первая найденная дата", "последняя:", "runtime_", "planner_", "query_movements", "primitive", "уточните контрагента", "по какому контрагенту"], + "criticality": "critical", + "semantic_tags": ["autonomy_core", "value_flow", "incoming_total", "business_answer_quality"] + }, + { + "step_id": "step_02_outgoing_total_no_counterparties", + "title": "Outgoing money total by organization without counterparty split", + "question": "Сколько исходящих денег за 2020 год по ООО Альтернатива Плюс без разреза по контрагентам?", + "allowed_reply_types": ["partial_coverage", "factual_with_explanation", "factual"], + "expected_mcp_discovery_response_applied": true, + "expected_mcp_discovery_selected_chain_id": "value_flow", + "expected_mcp_discovery_response_candidate_status": "ready_for_guarded_use", + "expected_mcp_discovery_candidate_hot_runtime_wired": true, + "expected_mcp_discovery_hot_runtime_wired": true, + "expected_mcp_discovery_execution_handoff_status": "ready_for_guarded_response", + "expected_mcp_discovery_execution_handoff_can_use_guarded_response": true, + "expected_catalog_alignment_status": "selected_matches_top", + "expected_catalog_chain_top_match": "value_flow", + "expected_catalog_selected_matches_top": true, + "required_answer_patterns_all": ["2020", "исходящ|списан|заплат", "руб"], + "required_answer_patterns_any": ["43[\\s.]*763[\\s.]*351", "не полностью покрыт|лимит|нужно дозапрос"], + "forbidden_answer_patterns": ["Учтено строк", "Первая найденная дата", "последняя:", "runtime_", "planner_", "query_movements", "primitive", "уточните контрагента", "по какому контрагенту"], + "criticality": "critical", + "semantic_tags": ["autonomy_core", "value_flow", "outgoing_total", "business_answer_quality", "limit_honesty"] + }, + { + "step_id": "step_03_colloquial_incoming_without_tops", + "title": "Colloquial no-top incoming wording stays value-flow total", + "question": "А всего сколько денег пришло в ООО Альтернатива Плюс за 2020, без топов и без контрагентов?", + "allowed_reply_types": ["partial_coverage", "factual_with_explanation", "factual"], + "expected_mcp_discovery_response_applied": true, + "expected_mcp_discovery_selected_chain_id": "value_flow", + "expected_mcp_discovery_response_candidate_status": "ready_for_guarded_use", + "expected_mcp_discovery_candidate_hot_runtime_wired": true, + "expected_mcp_discovery_hot_runtime_wired": true, + "expected_mcp_discovery_execution_handoff_status": "ready_for_guarded_response", + "expected_mcp_discovery_execution_handoff_can_use_guarded_response": true, + "expected_catalog_alignment_status": "selected_matches_top", + "expected_catalog_chain_top_match": "value_flow", + "expected_catalog_selected_matches_top": true, + "required_answer_patterns_all": ["2020", "47[\\s.]*628[\\s.]*853", "пришл|получ|поступ", "руб"], + "forbidden_answer_patterns": ["топ входящ", "топ контрагент", "Учтено строк", "Первая найденная дата", "runtime_", "planner_", "query_movements", "primitive"], + "criticality": "critical", + "semantic_tags": ["autonomy_core", "value_flow", "no_top_guard", "colloquial_total"] + }, + { + "step_id": "step_04_cashflow_overview_2020", + "title": "Adult 2020 money overview with bank separated", + "question": "Теперь дай взрослый обзор за 2020 по компании: входящие, исходящие, нетто, топы, но банк в топах отдельно объясни как финансовый поток.", + "allowed_reply_types": ["partial_coverage", "factual_with_explanation", "factual"], + "expected_catalog_alignment_status": "selected_matches_top", + "expected_catalog_chain_top_match": "business_overview", + "expected_catalog_selected_matches_top": true, + "required_answer_patterns_all": ["47[\\s.]*628[\\s.]*853", "43[\\s.]*763[\\s.]*351", "3[\\s.]*865[\\s.]*501", "Сбербанк|СБЕРБАНК", "финансов|банк", "Группа СВК", "Департамент"], + "required_answer_patterns_any": ["Что проверить дальше", "следующ"], + "forbidden_answer_patterns": ["Учтено строк", "Первая найденная дата", "surrogate", "VAT-объект", "runtime_", "planner_", "query_movements", "primitive"], + "criticality": "critical", + "semantic_tags": ["business_overview", "cashflow_overview", "bank_boundary", "next_action"] + }, + { + "step_id": "step_05_money_earned_vs_profit", + "title": "Colloquial earned money is cashflow, not clean profit", + "question": "скока денег альтернатива заработала за 20 год?", + "allowed_reply_types": ["partial_coverage", "factual_with_explanation", "factual"], + "required_answer_patterns_all": ["47[\\s.]*628[\\s.]*853", "43[\\s.]*763[\\s.]*351", "3[\\s.]*865[\\s.]*501", "не чистая прибыль|не прибыль|денежн"], + "forbidden_answer_patterns": ["Учтено строк", "Первая найденная дата", "surrogate", "runtime_", "planner_", "query_movements", "primitive"], + "criticality": "critical", + "semantic_tags": ["cashflow_vs_profit", "colloquial_money"] + }, + { + "step_id": "step_06_profit_followup", + "title": "Follow-up clean profit keeps cashflow/profit distinction", + "question": "а это чистая прибыль?", + "allowed_reply_types": ["partial_coverage", "factual_with_explanation", "factual"], + "required_answer_patterns_all": ["нет|не", "чистая прибыль|прибыль", "7\\s*136\\s*815|7,?136,?815|7136815", "90|91|99"], + "forbidden_answer_patterns": ["Учтено строк", "Первая найденная дата", "runtime_", "planner_", "query_movements", "primitive"], + "criticality": "critical", + "semantic_tags": ["followup_context", "profit_vs_cashflow"] + }, + { + "step_id": "step_07_sberbank_financial_flow", + "title": "Sberbank role is financial flow, not ordinary customer/supplier", + "question": "А отдельно по СБЕРБАНКУ: он для нас клиент, поставщик или финансовый поток? Дай коротко по подтвержденным строкам.", + "allowed_reply_types": ["partial_coverage", "factual_with_explanation", "factual"], + "required_answer_patterns_all": ["Сбербанк|СБЕРБАНК", "финансов|банк", "не.*клиент|не.*поставщик|не обычн", "36[\\s.]*258[\\s.]*835|12[\\s.]*792[\\s.]*194"], + "forbidden_answer_patterns": ["0 / 0", "T00:00:00", "Учтено строк", "Первая найденная дата", "Примеры строк", "Показаны первые", "runtime_", "planner_", "query_movements", "primitive"], + "criticality": "critical", + "semantic_tags": ["bank_classification", "business_answer_quality"] + }, + { + "step_id": "step_08_svk_counterparty_net_flow", + "title": "Counterparty net-flow does not become company profit", + "question": "какое нетто по деньгам с Группа СВК за 2020 год: сколько получили и сколько заплатили?", + "allowed_reply_types": ["partial_coverage", "factual_with_explanation", "factual"], + "required_answer_patterns_all": ["Группа СВК", "12[\\s.]*093[\\s.]*465", "заплатили|исходящ", "0", "нетто"], + "forbidden_answer_patterns": ["чистая прибыль", "90/91/99", "7[\\s.]*136[\\s.]*815", "Учтено строк", "Первая найденная дата", "Примеры строк", "Показаны первые", "runtime_", "planner_", "query_movements", "primitive"], + "criticality": "critical", + "semantic_tags": ["counterparty_value_flow", "net_flow", "no_profit_substitution"] + }, + { + "step_id": "step_09_payables_end_2020", + "title": "Payables answer stays compact and business-first", + "question": "кому мы должны на конец 2020?", + "allowed_reply_types": ["partial_coverage", "factual_with_explanation", "factual"], + "required_answer_patterns_all": ["1[\\s.]*713[\\s.]*210", "ИП Тучкова|Тучкова", "должн|кредитор"], + "forbidden_answer_patterns": ["T23:59:59", "эвристический", "shortlist", "Строк в выборке", "Учтено строк", "runtime_", "planner_", "query_movements", "primitive"], + "criticality": "critical", + "semantic_tags": ["payables", "debt_answer_quality"] + }, + { + "step_id": "step_10_receivables_end_2020", + "title": "Receivables answer stays compact and business-first", + "question": "а нам кто должен на конец 2020?", + "allowed_reply_types": ["partial_coverage", "factual_with_explanation", "factual"], + "required_answer_patterns_all": ["1[\\s.]*219[\\s.]*200", "СервисКонсалт", "должн|дебитор"], + "forbidden_answer_patterns": ["T23:59:59", "эвристический", "shortlist", "Строк в выборке", "Категории дебиторской задолженности", "Учтено строк", "runtime_", "planner_", "query_movements", "primitive"], + "criticality": "critical", + "semantic_tags": ["receivables", "debt_answer_quality"] + }, + { + "step_id": "step_11_vat_december_2019", + "title": "VAT answer is clean user-facing accounting answer", + "question": "сколько НДС надо заплатить в налоговую за декабрь 2019?", + "allowed_reply_types": ["partial_coverage", "factual_with_explanation", "factual"], + "required_answer_patterns_all": ["161[\\s.]*852", "к уплате", "книга продаж|продаж", "книга покупок|вычет|покупок", "01\\.10\\.2019|4 квартал|IV квартал"], + "forbidden_answer_patterns": ["surrogate", "VAT-объект", "прямых источников", "0,00\\s*₽", "Учтено строк", "runtime_", "planner_", "query_movements", "primitive"], + "criticality": "critical", + "semantic_tags": ["vat", "technical_garbage_guard"] + }, + { + "step_id": "step_12_business_evaluation", + "title": "Company evaluation is an analyst answer with next checks", + "question": "Как ты оценишь деятельность компании?", + "allowed_reply_types": ["partial_coverage", "factual_with_explanation", "factual"], + "expected_catalog_alignment_status": "selected_matches_top", + "expected_catalog_chain_top_match": "business_overview", + "expected_catalog_selected_matches_top": true, + "required_answer_patterns_all": ["крупн|контракт|проект", "285\\s*8|285\\s*823|285", "Комитет|госуслуг", "Что проверить дальше|проверить дальше|дальше"], + "forbidden_answer_patterns": ["Учтено строк", "Первая найденная дата", "проверка достигла лимита строк", "runtime_", "planner_", "query_movements", "primitive"], + "criticality": "critical", + "semantic_tags": ["business_overview", "business_evaluation", "next_action"] + }, + { + "step_id": "step_13_nomenclature_margin_missing_period", + "title": "Nomenclature margin ranking asks for period, not wrong accounting domain", + "question": "Какая номенклатура товара реализована с высокой прибылью какая с низкой?", + "allowed_reply_types": ["partial_coverage", "factual_with_explanation", "factual"], + "required_answer_patterns_all": ["период", "выручк|реализац", "себестоим|валов|маржин"], + "forbidden_answer_patterns": ["основн.*средств", "амортизац", "зависш", "оплат", "расчетн", "Учтено строк", "runtime_", "planner_", "query_movements", "primitive"], + "criticality": "critical", + "semantic_tags": ["nomenclature_margin", "clarification", "domain_purity"] + }, + { + "step_id": "step_14_nomenclature_margin_may_2020", + "title": "Nomenclature margin ranking with period gives useful limited answer", + "question": "май 2020", + "allowed_reply_types": ["partial_coverage", "factual_with_explanation", "factual"], + "required_answer_patterns_all": ["май|01\\.05\\.2020|31\\.05\\.2020", "рейтинг|прибыль|маржин", "себестоим", "нельзя|не удалось|недостаточно"], + "required_answer_patterns_any": ["показать найденную продажу", "расширить период", "90\\.01|90\\.02", "следующ"], + "forbidden_answer_patterns": ["основн.*средств", "амортизац", "зависш", "runtime_", "planner_", "query_movements", "primitive"], + "criticality": "critical", + "semantic_tags": ["nomenclature_margin", "limited_answer", "next_action"] + } + ] +} diff --git a/llm_normalizer/backend/dist/services/address_runtime/composeStage.js b/llm_normalizer/backend/dist/services/address_runtime/composeStage.js index 901c225..8322d27 100644 --- a/llm_normalizer/backend/dist/services/address_runtime/composeStage.js +++ b/llm_normalizer/backend/dist/services/address_runtime/composeStage.js @@ -382,7 +382,7 @@ function classifyBankOperationSemanticBucket(row) { } function bankOperationSemanticBucketLabel(bucket) { if (bucket === "commission") { - return "комиссии и банковое обслуживание"; + return "комиссии и банковское обслуживание"; } if (bucket === "deposit_or_credit") { return "депозиты, кредиты или проценты"; @@ -3886,6 +3886,7 @@ function composeFactualReplyBody(intent, rows, options = {}) { (String(right.period ?? "").localeCompare(String(left.period ?? ""), "ru"))) .slice(0, Math.min(rows.length, 5)); const semanticSummary = summarizeBankOperationSemantics(rows); + const compactRoleAnswer = /(?:клиент|поставщик|финансов|роль|коротко)/iu.test(String(options.userMessage ?? "")); const compactEvidenceRows = visibleRows.map((row, index) => { const direction = bankOperationDirectionLabel(bankOperationDirection(row)); const amount = formatMoneyRub(row.amount ?? 0); @@ -3901,13 +3902,13 @@ function composeFactualReplyBody(intent, rows, options = {}) { `Коротко: найдено банковских операций${counterparty ? ` по ${counterparty}` : " по контрагенту"} — ${rows.length}.`, summarizeBankOperationDirections(rows), roleBoundary ?? "Показываю подтвержденные банковские операции из текущего среза.", - bankOperationEvidenceLine(rows, preferredBankEvidenceDirection(options.userMessage)), - ...(semanticSummary ? [semanticSummary] : []), - "Примеры строк 1С:", - ...compactEvidenceRows, - "Следующий шаг: могу отдельно разложить назначения платежа, договоры или отделить банковский контур от клиентского/поставщицкого." + ...(semanticSummary ? [semanticSummary] : []) ]; - if (rows.length > visibleRows.length) { + if (!compactRoleAnswer) { + lines.push(bankOperationEvidenceLine(rows, preferredBankEvidenceDirection(options.userMessage)), "Примеры строк 1С:", ...compactEvidenceRows); + } + lines.push("Следующий шаг: могу отдельно разложить назначения платежа, договоры или отделить банковский контур от клиентского/поставщицкого."); + if (!compactRoleAnswer && rows.length > visibleRows.length) { lines.push(`Показаны первые ${visibleRows.length} из ${rows.length}; полный список остается в подтвержденном срезе.`); } return { diff --git a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryAnswerAdapter.js b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryAnswerAdapter.js index 5a7e81c..b5a9336 100644 --- a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryAnswerAdapter.js +++ b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryAnswerAdapter.js @@ -1212,22 +1212,16 @@ function derivedValueFlowConfirmedLine(pilot) { const organization = organizationScope ? ` по организации ${organizationScope}` : ""; const counterparty = flow.counterparty ? ` по контрагенту ${flow.counterparty}` : ""; const period = flow.period_scope ? ` за период ${flow.period_scope}` : " в проверенном окне"; - const movementLabel = flow.value_flow_direction === "outgoing_supplier_payout" - ? "исходящих платежей/списаний" - : "входящих денежных поступлений"; const totalLabel = flow.value_flow_direction === "outgoing_supplier_payout" - ? "сумма исходящих платежей/списаний составляет" - : "сумма входящих денежных поступлений составляет"; + ? "исходящие платежи/списания" + : "входящие денежные поступления"; const caveat = flow.value_flow_direction === "outgoing_supplier_payout" - ? "Это расчет по найденным строкам 1С, а не подтверждение полного объема платежей вне проверенного окна." - : "Это расчет по найденным строкам 1С, а не подтверждение полного объема поступлений вне проверенного окна."; - const dates = flow.first_movement_date && flow.latest_movement_date - ? ` Первая найденная дата движения: ${flow.first_movement_date}; последняя: ${flow.latest_movement_date}.` - : ""; + ? "Граница: это проверенный денежный срез по найденным списаниям в 1С, не внешний аудит всех платежей вне этого окна." + : "Граница: это проверенный денежный срез по найденным поступлениям в 1С, не внешний аудит всех поступлений вне этого окна."; const limitCaveat = flow.coverage_limited_by_probe_limit - ? " Лимит строк проверки достигнут; полный запрошенный период может быть покрыт не полностью." + ? " Проверка уперлась в лимит строк, поэтому полный период может быть покрыт не полностью." : ""; - return `По найденным строкам ${movementLabel} в 1С${counterparty}${period} ${totalLabel} ${flow.total_amount_human_ru} Учтено строк с суммой: ${flow.rows_with_amount} из ${flow.rows_matched}.${dates}${limitCaveat} ${caveat}`; + return `${totalLabel}${counterparty || organization}${period}: ${flow.total_amount_human_ru}.${limitCaveat} ${caveat}`; } function derivedValueFlowMonthlyLines(pilot) { const flow = pilot.derived_value_flow; @@ -1831,7 +1825,7 @@ function buildAssistantMcpDiscoveryAnswerDraft(pilot) { : pilot.derived_ranked_value_flow && derivedValueLine ? [derivedValueLine] : derivedValueLine - ? [...pilot.evidence.confirmed_facts, derivedValueLine, ...monthlyConfirmedLines] + ? [derivedValueLine, ...monthlyConfirmedLines] : valueFlowZeroResultConfirmedLine(pilot) ? [valueFlowZeroResultConfirmedLine(pilot)] : derivedEntityResolutionLine diff --git a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryResponseCandidate.js b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryResponseCandidate.js index 9811bee..de1fe3c 100644 --- a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryResponseCandidate.js +++ b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryResponseCandidate.js @@ -520,14 +520,12 @@ function businessOverviewInventoryLine(overview) { } const amount = moneyText(inventory.total_amount_human_ru); const rows = Number(inventory.rows_matched); - const quantity = Number(inventory.total_quantity); if (!amount && !Number.isFinite(rows)) { return null; } const pieces = [ Number.isFinite(rows) ? `${rows} позиций` : null, - amount ? `на ${sentenceAmount(amount) ?? amount}` : null, - Number.isFinite(quantity) && quantity > 0 ? `количество ${quantity}` : null + amount ? `на ${sentenceAmount(amount) ?? amount}` : null ].filter((item) => Boolean(item)); return pieces.length > 0 ? `Склад: ${pieces.join(", ")}.` : null; } @@ -919,7 +917,7 @@ function buildCompactBusinessOverviewReply(entryPoint, draft) { } if (customerName && customerAmount) { lines.push(topCustomerLooksFinancial - ? `- крупнейший входящий источник: ${customerName} — ${sentenceAmount(customerAmount) ?? customerAmount}; это похоже на финансовый контур, не на обычную клиентскую выручку;` + ? `- крупнейший входящий источник: ${customerName} — ${sentenceAmount(customerAmount) ?? customerAmount}; это похоже на финансовый контур, не на обычную клиентскую выручку;${nonFinancialCustomer ? ` крупнейший небанковский входящий контрагент: ${nonFinancialCustomer};` : ""}` : `- крупнейший источник денег: ${customerName} — ${sentenceAmount(customerAmount) ?? customerAmount}; это сигнал концентрации на крупном заказчике;`); } if (topSupplier) { diff --git a/llm_normalizer/backend/src/services/address_runtime/composeStage.ts b/llm_normalizer/backend/src/services/address_runtime/composeStage.ts index f57ba81..cb30fc8 100644 --- a/llm_normalizer/backend/src/services/address_runtime/composeStage.ts +++ b/llm_normalizer/backend/src/services/address_runtime/composeStage.ts @@ -549,7 +549,7 @@ function classifyBankOperationSemanticBucket(row: ComposeStageRow): BankOperatio function bankOperationSemanticBucketLabel(bucket: BankOperationSemanticBucket): string { if (bucket === "commission") { - return "комиссии и банковое обслуживание"; + return "комиссии и банковское обслуживание"; } if (bucket === "deposit_or_credit") { return "депозиты, кредиты или проценты"; @@ -4940,6 +4940,7 @@ function composeFactualReplyBody( ) .slice(0, Math.min(rows.length, 5)); const semanticSummary = summarizeBankOperationSemantics(rows); + const compactRoleAnswer = /(?:клиент|поставщик|финансов|роль|коротко)/iu.test(String(options.userMessage ?? "")); const compactEvidenceRows = visibleRows.map((row, index) => { const direction = bankOperationDirectionLabel(bankOperationDirection(row)); const amount = formatMoneyRub(row.amount ?? 0); @@ -4955,13 +4956,17 @@ function composeFactualReplyBody( `Коротко: найдено банковских операций${counterparty ? ` по ${counterparty}` : " по контрагенту"} — ${rows.length}.`, summarizeBankOperationDirections(rows), roleBoundary ?? "Показываю подтвержденные банковские операции из текущего среза.", - bankOperationEvidenceLine(rows, preferredBankEvidenceDirection(options.userMessage)), - ...(semanticSummary ? [semanticSummary] : []), - "Примеры строк 1С:", - ...compactEvidenceRows, - "Следующий шаг: могу отдельно разложить назначения платежа, договоры или отделить банковский контур от клиентского/поставщицкого." + ...(semanticSummary ? [semanticSummary] : []) ]; - if (rows.length > visibleRows.length) { + if (!compactRoleAnswer) { + lines.push( + bankOperationEvidenceLine(rows, preferredBankEvidenceDirection(options.userMessage)), + "Примеры строк 1С:", + ...compactEvidenceRows + ); + } + lines.push("Следующий шаг: могу отдельно разложить назначения платежа, договоры или отделить банковский контур от клиентского/поставщицкого."); + if (!compactRoleAnswer && rows.length > visibleRows.length) { lines.push(`Показаны первые ${visibleRows.length} из ${rows.length}; полный список остается в подтвержденном срезе.`); } return { diff --git a/llm_normalizer/backend/src/services/assistantMcpDiscoveryAnswerAdapter.ts b/llm_normalizer/backend/src/services/assistantMcpDiscoveryAnswerAdapter.ts index d7cd584..94bff67 100644 --- a/llm_normalizer/backend/src/services/assistantMcpDiscoveryAnswerAdapter.ts +++ b/llm_normalizer/backend/src/services/assistantMcpDiscoveryAnswerAdapter.ts @@ -1392,26 +1392,18 @@ function derivedValueFlowConfirmedLine(pilot: AssistantMcpDiscoveryPilotExecutio const organization = organizationScope ? ` по организации ${organizationScope}` : ""; const counterparty = flow.counterparty ? ` по контрагенту ${flow.counterparty}` : ""; const period = flow.period_scope ? ` за период ${flow.period_scope}` : " в проверенном окне"; - const movementLabel = - flow.value_flow_direction === "outgoing_supplier_payout" - ? "исходящих платежей/списаний" - : "входящих денежных поступлений"; const totalLabel = flow.value_flow_direction === "outgoing_supplier_payout" - ? "сумма исходящих платежей/списаний составляет" - : "сумма входящих денежных поступлений составляет"; + ? "исходящие платежи/списания" + : "входящие денежные поступления"; const caveat = flow.value_flow_direction === "outgoing_supplier_payout" - ? "Это расчет по найденным строкам 1С, а не подтверждение полного объема платежей вне проверенного окна." - : "Это расчет по найденным строкам 1С, а не подтверждение полного объема поступлений вне проверенного окна."; - const dates = - flow.first_movement_date && flow.latest_movement_date - ? ` Первая найденная дата движения: ${flow.first_movement_date}; последняя: ${flow.latest_movement_date}.` - : ""; + ? "Граница: это проверенный денежный срез по найденным списаниям в 1С, не внешний аудит всех платежей вне этого окна." + : "Граница: это проверенный денежный срез по найденным поступлениям в 1С, не внешний аудит всех поступлений вне этого окна."; const limitCaveat = flow.coverage_limited_by_probe_limit - ? " Лимит строк проверки достигнут; полный запрошенный период может быть покрыт не полностью." + ? " Проверка уперлась в лимит строк, поэтому полный период может быть покрыт не полностью." : ""; - return `По найденным строкам ${movementLabel} в 1С${counterparty}${period} ${totalLabel} ${flow.total_amount_human_ru} Учтено строк с суммой: ${flow.rows_with_amount} из ${flow.rows_matched}.${dates}${limitCaveat} ${caveat}`; + return `${totalLabel}${counterparty || organization}${period}: ${flow.total_amount_human_ru}.${limitCaveat} ${caveat}`; } function derivedValueFlowMonthlyLines(pilot: AssistantMcpDiscoveryPilotExecutionContract): string[] { @@ -2109,7 +2101,7 @@ export function buildAssistantMcpDiscoveryAnswerDraft( : pilot.derived_ranked_value_flow && derivedValueLine ? [derivedValueLine] : derivedValueLine - ? [...pilot.evidence.confirmed_facts, derivedValueLine, ...monthlyConfirmedLines] + ? [derivedValueLine, ...monthlyConfirmedLines] : valueFlowZeroResultConfirmedLine(pilot) ? [valueFlowZeroResultConfirmedLine(pilot)!] : derivedEntityResolutionLine diff --git a/llm_normalizer/backend/src/services/assistantMcpDiscoveryResponseCandidate.ts b/llm_normalizer/backend/src/services/assistantMcpDiscoveryResponseCandidate.ts index 559a249..f8d67da 100644 --- a/llm_normalizer/backend/src/services/assistantMcpDiscoveryResponseCandidate.ts +++ b/llm_normalizer/backend/src/services/assistantMcpDiscoveryResponseCandidate.ts @@ -602,14 +602,12 @@ function businessOverviewInventoryLine(overview: Record): strin } const amount = moneyText(inventory.total_amount_human_ru); const rows = Number(inventory.rows_matched); - const quantity = Number(inventory.total_quantity); if (!amount && !Number.isFinite(rows)) { return null; } const pieces = [ Number.isFinite(rows) ? `${rows} позиций` : null, - amount ? `на ${sentenceAmount(amount) ?? amount}` : null, - Number.isFinite(quantity) && quantity > 0 ? `количество ${quantity}` : null + amount ? `на ${sentenceAmount(amount) ?? amount}` : null ].filter((item): item is string => Boolean(item)); return pieces.length > 0 ? `Склад: ${pieces.join(", ")}.` : null; } @@ -1098,7 +1096,7 @@ function buildCompactBusinessOverviewReply( if (customerName && customerAmount) { lines.push( topCustomerLooksFinancial - ? `- крупнейший входящий источник: ${customerName} — ${sentenceAmount(customerAmount) ?? customerAmount}; это похоже на финансовый контур, не на обычную клиентскую выручку;` + ? `- крупнейший входящий источник: ${customerName} — ${sentenceAmount(customerAmount) ?? customerAmount}; это похоже на финансовый контур, не на обычную клиентскую выручку;${nonFinancialCustomer ? ` крупнейший небанковский входящий контрагент: ${nonFinancialCustomer};` : ""}` : `- крупнейший источник денег: ${customerName} — ${sentenceAmount(customerAmount) ?? customerAmount}; это сигнал концентрации на крупном заказчике;` ); } diff --git a/llm_normalizer/data/autorun_generators/history.json b/llm_normalizer/data/autorun_generators/history.json index b88b6c4..01ad428 100644 --- a/llm_normalizer/data/autorun_generators/history.json +++ b/llm_normalizer/data/autorun_generators/history.json @@ -1,4 +1,82 @@ [ + { + "generation_id": "gen-ag05230604-098bda", + "created_at": "2026-05-23T06:04:40+00:00", + "mode": "saved_user_sessions", + "title": "AGENT | Autonomy business quality pack", + "count": 14, + "domain": "autonomy_business_quality", + "questions": [ + "Сколько входящих денег за 2020 год по ООО Альтернатива Плюс без разреза по контрагентам?", + "Сколько исходящих денег за 2020 год по ООО Альтернатива Плюс без разреза по контрагентам?", + "А всего сколько денег пришло в ООО Альтернатива Плюс за 2020, без топов и без контрагентов?", + "Теперь дай взрослый обзор за 2020 по компании: входящие, исходящие, нетто, топы, но банк в топах отдельно объясни как финансовый поток.", + "скока денег альтернатива заработала за 20 год?", + "а это чистая прибыль?", + "А отдельно по СБЕРБАНКУ: он для нас клиент, поставщик или финансовый поток? Дай коротко по подтвержденным строкам.", + "какое нетто по деньгам с Группа СВК за 2020 год: сколько получили и сколько заплатили?", + "кому мы должны на конец 2020?", + "а нам кто должен на конец 2020?", + "сколько НДС надо заплатить в налоговую за декабрь 2019?", + "Как ты оценишь деятельность компании?", + "Какая номенклатура товара реализована с высокой прибылью какая с низкой?", + "май 2020" + ], + "generated_by": "codex_agent", + "saved_case_set_file": "assistant_autogen_saved_user_sessions_20260523060440_gen-ag05230604-098bda.json", + "context": { + "llm_provider": null, + "model": null, + "assistant_prompt_version": null, + "decomposition_prompt_version": null, + "prompt_fingerprint": null, + "autogen_personality_id": null, + "autogen_personality_prompt": null, + "source_session_id": null, + "saved_session_file": "assistant_saved_session_20260523060440_gen-ag05230604-098bda.json", + "saved_case_set_kind": "agent_semantic_scenario", + "agent_run": true, + "agent_focus": "Expanded targeted AGENT replay for the current autonomy milestone: value-flow hot handoff must survive realistic business questions, while final answers must read like a useful 1C analyst instead of runtime/debug prose.", + "architecture_phase": "turnaround_11", + "source_spec_file": "X:\\1C\\NDC_1C\\docs\\orchestration\\agent_autonomy_business_quality_20260523.json", + "scenario_id": "agent_autonomy_business_quality_20260523", + "semantic_tags": [ + "autonomy_core", + "bank_boundary", + "bank_classification", + "business_answer_quality", + "business_evaluation", + "business_overview", + "cashflow_overview", + "cashflow_vs_profit", + "clarification", + "colloquial_money", + "colloquial_total", + "counterparty_value_flow", + "debt_answer_quality", + "domain_purity", + "followup_context", + "incoming_total", + "limit_honesty", + "limited_answer", + "net_flow", + "next_action", + "no_profit_substitution", + "no_top_guard", + "nomenclature_margin", + "outgoing_total", + "payables", + "profit_vs_cashflow", + "receivables", + "technical_garbage_guard", + "value_flow", + "vat" + ], + "validation_status": "accepted_live_replay", + "validated_run_dir": "artifacts\\domain_runs\\agent_autonomy_business_quality_live4", + "saved_after_validated_replay": true + } + }, { "generation_id": "gen-ag05221957-713bbd", "created_at": "2026-05-22T19:57:37+00:00", diff --git a/llm_normalizer/data/autorun_generators/saved_sessions/assistant_saved_session_20260523060440_gen-ag05230604-098bda.json b/llm_normalizer/data/autorun_generators/saved_sessions/assistant_saved_session_20260523060440_gen-ag05230604-098bda.json new file mode 100644 index 0000000..cca588a --- /dev/null +++ b/llm_normalizer/data/autorun_generators/saved_sessions/assistant_saved_session_20260523060440_gen-ag05230604-098bda.json @@ -0,0 +1,275 @@ +{ + "saved_at": "2026-05-23T06:04:40+00:00", + "generation_id": "gen-ag05230604-098bda", + "mode": "saved_user_sessions", + "title": "AGENT | Autonomy business quality pack", + "agent_run": true, + "questions": [ + "Сколько входящих денег за 2020 год по ООО Альтернатива Плюс без разреза по контрагентам?", + "Сколько исходящих денег за 2020 год по ООО Альтернатива Плюс без разреза по контрагентам?", + "А всего сколько денег пришло в ООО Альтернатива Плюс за 2020, без топов и без контрагентов?", + "Теперь дай взрослый обзор за 2020 по компании: входящие, исходящие, нетто, топы, но банк в топах отдельно объясни как финансовый поток.", + "скока денег альтернатива заработала за 20 год?", + "а это чистая прибыль?", + "А отдельно по СБЕРБАНКУ: он для нас клиент, поставщик или финансовый поток? Дай коротко по подтвержденным строкам.", + "какое нетто по деньгам с Группа СВК за 2020 год: сколько получили и сколько заплатили?", + "кому мы должны на конец 2020?", + "а нам кто должен на конец 2020?", + "сколько НДС надо заплатить в налоговую за декабрь 2019?", + "Как ты оценишь деятельность компании?", + "Какая номенклатура товара реализована с высокой прибылью какая с низкой?", + "май 2020" + ], + "metadata": { + "assistant_prompt_version": null, + "decomposition_prompt_version": null, + "prompt_fingerprint": null, + "agent_focus": "Expanded targeted AGENT replay for the current autonomy milestone: value-flow hot handoff must survive realistic business questions, while final answers must read like a useful 1C analyst instead of runtime/debug prose.", + "architecture_phase": "turnaround_11", + "source_spec_file": "X:\\1C\\NDC_1C\\docs\\orchestration\\agent_autonomy_business_quality_20260523.json", + "scenario_id": "agent_autonomy_business_quality_20260523", + "semantic_tags": [ + "autonomy_core", + "bank_boundary", + "bank_classification", + "business_answer_quality", + "business_evaluation", + "business_overview", + "cashflow_overview", + "cashflow_vs_profit", + "clarification", + "colloquial_money", + "colloquial_total", + "counterparty_value_flow", + "debt_answer_quality", + "domain_purity", + "followup_context", + "incoming_total", + "limit_honesty", + "limited_answer", + "net_flow", + "next_action", + "no_profit_substitution", + "no_top_guard", + "nomenclature_margin", + "outgoing_total", + "payables", + "profit_vs_cashflow", + "receivables", + "technical_garbage_guard", + "value_flow", + "vat" + ], + "validation_status": "accepted_live_replay", + "validated_run_dir": "artifacts\\domain_runs\\agent_autonomy_business_quality_live4", + "saved_after_validated_replay": true, + "save_gate": { + "schema_version": "agent_semantic_save_gate_v1", + "validation_status": "accepted_live_replay", + "validated_run_dir": "artifacts\\domain_runs\\agent_autonomy_business_quality_live4", + "final_status": "accepted", + "review_overall_status": "pass", + "business_overall_status": "pass", + "steps_total": 14, + "steps_passed": 14, + "steps_failed": 0, + "steps_with_business_failures": 0, + "steps_with_business_warnings": 0, + "acceptance_gate_passed": true, + "saved_after_validated_replay": true + } + }, + "source_session_id": null, + "session": { + "session_id": null, + "mode": "agent_semantic_run", + "items": [ + { + "message_id": "agent-user-001", + "role": "user", + "text": "Сколько входящих денег за 2020 год по ООО Альтернатива Плюс без разреза по контрагентам?", + "created_at": "2026-05-23T06:04:40+00:00", + "reply_type": null, + "trace_id": null, + "debug": null + }, + { + "message_id": "agent-user-002", + "role": "user", + "text": "Сколько исходящих денег за 2020 год по ООО Альтернатива Плюс без разреза по контрагентам?", + "created_at": "2026-05-23T06:04:40+00:00", + "reply_type": null, + "trace_id": null, + "debug": null + }, + { + "message_id": "agent-user-003", + "role": "user", + "text": "А всего сколько денег пришло в ООО Альтернатива Плюс за 2020, без топов и без контрагентов?", + "created_at": "2026-05-23T06:04:40+00:00", + "reply_type": null, + "trace_id": null, + "debug": null + }, + { + "message_id": "agent-user-004", + "role": "user", + "text": "Теперь дай взрослый обзор за 2020 по компании: входящие, исходящие, нетто, топы, но банк в топах отдельно объясни как финансовый поток.", + "created_at": "2026-05-23T06:04:40+00:00", + "reply_type": null, + "trace_id": null, + "debug": null + }, + { + "message_id": "agent-user-005", + "role": "user", + "text": "скока денег альтернатива заработала за 20 год?", + "created_at": "2026-05-23T06:04:40+00:00", + "reply_type": null, + "trace_id": null, + "debug": null + }, + { + "message_id": "agent-user-006", + "role": "user", + "text": "а это чистая прибыль?", + "created_at": "2026-05-23T06:04:40+00:00", + "reply_type": null, + "trace_id": null, + "debug": null + }, + { + "message_id": "agent-user-007", + "role": "user", + "text": "А отдельно по СБЕРБАНКУ: он для нас клиент, поставщик или финансовый поток? Дай коротко по подтвержденным строкам.", + "created_at": "2026-05-23T06:04:40+00:00", + "reply_type": null, + "trace_id": null, + "debug": null + }, + { + "message_id": "agent-user-008", + "role": "user", + "text": "какое нетто по деньгам с Группа СВК за 2020 год: сколько получили и сколько заплатили?", + "created_at": "2026-05-23T06:04:40+00:00", + "reply_type": null, + "trace_id": null, + "debug": null + }, + { + "message_id": "agent-user-009", + "role": "user", + "text": "кому мы должны на конец 2020?", + "created_at": "2026-05-23T06:04:40+00:00", + "reply_type": null, + "trace_id": null, + "debug": null + }, + { + "message_id": "agent-user-010", + "role": "user", + "text": "а нам кто должен на конец 2020?", + "created_at": "2026-05-23T06:04:40+00:00", + "reply_type": null, + "trace_id": null, + "debug": null + }, + { + "message_id": "agent-user-011", + "role": "user", + "text": "сколько НДС надо заплатить в налоговую за декабрь 2019?", + "created_at": "2026-05-23T06:04:40+00:00", + "reply_type": null, + "trace_id": null, + "debug": null + }, + { + "message_id": "agent-user-012", + "role": "user", + "text": "Как ты оценишь деятельность компании?", + "created_at": "2026-05-23T06:04:40+00:00", + "reply_type": null, + "trace_id": null, + "debug": null + }, + { + "message_id": "agent-user-013", + "role": "user", + "text": "Какая номенклатура товара реализована с высокой прибылью какая с низкой?", + "created_at": "2026-05-23T06:04:40+00:00", + "reply_type": null, + "trace_id": null, + "debug": null + }, + { + "message_id": "agent-user-014", + "role": "user", + "text": "май 2020", + "created_at": "2026-05-23T06:04:40+00:00", + "reply_type": null, + "trace_id": null, + "debug": null + } + ], + "agent_run": true, + "metadata": { + "assistant_prompt_version": null, + "decomposition_prompt_version": null, + "prompt_fingerprint": null, + "agent_focus": "Expanded targeted AGENT replay for the current autonomy milestone: value-flow hot handoff must survive realistic business questions, while final answers must read like a useful 1C analyst instead of runtime/debug prose.", + "architecture_phase": "turnaround_11", + "source_spec_file": "X:\\1C\\NDC_1C\\docs\\orchestration\\agent_autonomy_business_quality_20260523.json", + "scenario_id": "agent_autonomy_business_quality_20260523", + "semantic_tags": [ + "autonomy_core", + "bank_boundary", + "bank_classification", + "business_answer_quality", + "business_evaluation", + "business_overview", + "cashflow_overview", + "cashflow_vs_profit", + "clarification", + "colloquial_money", + "colloquial_total", + "counterparty_value_flow", + "debt_answer_quality", + "domain_purity", + "followup_context", + "incoming_total", + "limit_honesty", + "limited_answer", + "net_flow", + "next_action", + "no_profit_substitution", + "no_top_guard", + "nomenclature_margin", + "outgoing_total", + "payables", + "profit_vs_cashflow", + "receivables", + "technical_garbage_guard", + "value_flow", + "vat" + ], + "validation_status": "accepted_live_replay", + "validated_run_dir": "artifacts\\domain_runs\\agent_autonomy_business_quality_live4", + "saved_after_validated_replay": true, + "save_gate": { + "schema_version": "agent_semantic_save_gate_v1", + "validation_status": "accepted_live_replay", + "validated_run_dir": "artifacts\\domain_runs\\agent_autonomy_business_quality_live4", + "final_status": "accepted", + "review_overall_status": "pass", + "business_overall_status": "pass", + "steps_total": 14, + "steps_passed": 14, + "steps_failed": 0, + "steps_with_business_failures": 0, + "steps_with_business_warnings": 0, + "acceptance_gate_passed": true, + "saved_after_validated_replay": true + } + } + } +} diff --git a/llm_normalizer/data/eval_cases/assistant_autogen_saved_user_sessions_20260523060440_gen-ag05230604-098bda.json b/llm_normalizer/data/eval_cases/assistant_autogen_saved_user_sessions_20260523060440_gen-ag05230604-098bda.json new file mode 100644 index 0000000..cd27ff1 --- /dev/null +++ b/llm_normalizer/data/eval_cases/assistant_autogen_saved_user_sessions_20260523060440_gen-ag05230604-098bda.json @@ -0,0 +1,67 @@ +{ + "suite_id": "assistant_saved_session_gen-ag05230604-098bda", + "suite_version": "0.1.0", + "schema_version": "assistant_saved_session_suite_v0_1", + "generated_at": "2026-05-23T06:04:40+00:00", + "generation_id": "gen-ag05230604-098bda", + "mode": "saved_user_sessions", + "title": "AGENT | Autonomy business quality pack", + "domain": "autonomy_business_quality", + "scenario_count": 1, + "case_ids": [ + "SAVED-001" + ], + "cases": [ + { + "case_id": "SAVED-001", + "scenario_tag": "agent_saved_user_sessions", + "title": "AGENT | Autonomy business quality pack", + "question_type": "followup", + "broadness_level": "medium", + "turns": [ + { + "user_message": "Сколько входящих денег за 2020 год по ООО Альтернатива Плюс без разреза по контрагентам?" + }, + { + "user_message": "Сколько исходящих денег за 2020 год по ООО Альтернатива Плюс без разреза по контрагентам?" + }, + { + "user_message": "А всего сколько денег пришло в ООО Альтернатива Плюс за 2020, без топов и без контрагентов?" + }, + { + "user_message": "Теперь дай взрослый обзор за 2020 по компании: входящие, исходящие, нетто, топы, но банк в топах отдельно объясни как финансовый поток." + }, + { + "user_message": "скока денег альтернатива заработала за 20 год?" + }, + { + "user_message": "а это чистая прибыль?" + }, + { + "user_message": "А отдельно по СБЕРБАНКУ: он для нас клиент, поставщик или финансовый поток? Дай коротко по подтвержденным строкам." + }, + { + "user_message": "какое нетто по деньгам с Группа СВК за 2020 год: сколько получили и сколько заплатили?" + }, + { + "user_message": "кому мы должны на конец 2020?" + }, + { + "user_message": "а нам кто должен на конец 2020?" + }, + { + "user_message": "сколько НДС надо заплатить в налоговую за декабрь 2019?" + }, + { + "user_message": "Как ты оценишь деятельность компании?" + }, + { + "user_message": "Какая номенклатура товара реализована с высокой прибылью какая с низкой?" + }, + { + "user_message": "май 2020" + } + ] + } + ] +}