From 92473c894f7106f70d01de9d75fa83ad148986dd Mon Sep 17 00:00:00 2001 From: dctouch Date: Tue, 26 May 2026 00:05:28 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A3=D1=82=D0=BE=D1=87=D0=BD=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20=D1=87=D0=B5=D1=81=D1=82=D0=BD=D0=BE=D1=81=D1=82=D1=8C?= =?UTF-8?q?=20=D1=88=D0=B8=D1=80=D0=BE=D0=BA=D0=BE=D0=B3=D0=BE=20=D0=B1?= =?UTF-8?q?=D0=B8=D0=B7=D0=BD=D0=B5=D1=81-=D0=BE=D0=B1=D0=B7=D0=BE=D1=80?= =?UTF-8?q?=D0=B0=20=D0=B0=D1=81=D1=81=D0=B8=D1=81=D1=82=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ss_profile_evaluation_limits_20260525.json | 96 +++++++++++++++++++ .../assistantMcpDiscoveryResponseCandidate.js | 14 ++- .../assistantMcpDiscoveryResponseCandidate.ts | 18 ++-- ...stantMcpDiscoveryResponseCandidate.test.ts | 10 +- 4 files changed, 118 insertions(+), 20 deletions(-) create mode 100644 docs/orchestration/hm_business_profile_evaluation_limits_20260525.json diff --git a/docs/orchestration/hm_business_profile_evaluation_limits_20260525.json b/docs/orchestration/hm_business_profile_evaluation_limits_20260525.json new file mode 100644 index 0000000..5e17983 --- /dev/null +++ b/docs/orchestration/hm_business_profile_evaluation_limits_20260525.json @@ -0,0 +1,96 @@ +{ + "schema_version": "domain_truth_harness_spec_v1", + "scenario_id": "hm_business_profile_evaluation_limits_20260525", + "domain": "assistant_stage1_business_overview", + "title": "Hm business profile evaluation coverage honesty", + "description": "Focused live replay for broad company evaluation: the assistant must give a useful business reading while clearly presenting cashflow probes as a limited checked 1C slice, not a full audit, clean profit, or complete accounting turnover.", + "bindings": {}, + "steps": [ + { + "step_id": "s01_top_year_establish_company", + "title": "Establish company context through top-year question", + "question": "какой у ООО \"Альтернатива Плюс\" самый доходный год", + "allowed_reply_types": [ + "factual", + "factual_with_explanation", + "partial_coverage" + ], + "required_direct_answer_patterns_any": [ + "2015", + "входящ" + ], + "forbidden_answer_patterns": [ + "Коротко:", + "capability_id", + "mcp_discovery" + ], + "criticality": "critical", + "semantic_tags": [ + "business_overview", + "company_context", + "yearly_ranking" + ] + }, + { + "step_id": "s02_broad_company_evaluation", + "title": "Broad company evaluation must lead with limited-evidence framing", + "question": "Как ты оценишь деятельность компании?", + "allowed_reply_types": [ + "factual_with_explanation", + "partial_coverage" + ], + "required_answer_patterns_all": [ + "ограниченн", + "денежн", + "не аудиторское", + "не подтвержден", + "чист", + "прибыл", + "Комитет государственных услуг", + "зависим" + ], + "forbidden_answer_patterns": [ + "Коротко:", + "capability_id", + "mcp_discovery", + "query_movements", + "business_overview_route_template_v1" + ], + "criticality": "critical", + "semantic_tags": [ + "business_overview", + "broad_business_evaluation", + "coverage_honesty", + "profit_boundary" + ] + }, + { + "step_id": "s03_profitability_boundary", + "title": "Profitability follow-up must not convert net cashflow into profit", + "question": "то есть бизнес прибыльный или это только обороты?", + "allowed_reply_types": [ + "factual_with_explanation", + "partial_coverage" + ], + "required_answer_patterns_all": [ + "не подтверждаю", + "прибыл", + "денежн", + "нетто", + "не полн" + ], + "forbidden_answer_patterns": [ + "Коротко:", + "capability_id", + "mcp_discovery", + "это прибыль" + ], + "criticality": "critical", + "semantic_tags": [ + "business_overview", + "profitability_boundary", + "cashflow_not_profit" + ] + } + ] +} diff --git a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryResponseCandidate.js b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryResponseCandidate.js index 7350070..f0e1442 100644 --- a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryResponseCandidate.js +++ b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryResponseCandidate.js @@ -959,11 +959,7 @@ function buildCompactBusinessOverviewReply(entryPoint, draft) { : "Это учетный финрезультат по найденным строкам закрытия периода в 1С, а не внешний аудит и не юридически подтвержденная отчетность."); return joinBusinessReplyLines(lines); } - const headline = toNonEmptyString(draft.headline); - const cleanHeadline = headline?.replace(/^Коротко:\s*/iu, "").trim(); - lines.push(cleanHeadline - ? `Коротко: ${localizeLine(cleanHeadline)}` - : "Коротко: нельзя точно подтвердить чистую прибыль и маржу по текущему срезу 1С; есть только ограниченный операционный денежный/товарный сигнал, а не полный отчет о прибыли и не бухгалтерский финансовый результат."); + lines.push("Коротко: чистую прибыль и маржу не подтверждаю по текущему срезу 1С; видны только ограниченные денежные обороты и расчетное нетто/операционный сигнал, а не полный отчет о прибыли и не бухгалтерский финансовый результат."); const boundaryLines = userFacingLines([ ...toStringList(draft.confirmed_lines), ...toStringList(draft.inference_lines), @@ -1113,8 +1109,8 @@ function buildCompactBusinessOverviewReply(entryPoint, draft) { (actionFamily === "broad_evaluation" || unsupportedFamily === "broad_business_evaluation")) { const subject = organizationScope ?? "компания"; const periodWithoutPrefix = period.replace(/^за\s+/iu, ""); - lines.push(`Коротко: по доступным данным ${subject} выглядит как бизнес с крупными контрактными денежными потоками и заметной зависимостью от нескольких крупных контрагентов, а не как равномерный поток мелких продаж.`); - lines.push("Что видно:"); + lines.push(`Коротко: по ограниченному проверенному 1С-срезу ${subject} выглядит как бизнес с крупными контрактными денежными потоками и заметной зависимостью от нескольких крупных контрагентов, а не как равномерный поток мелких продаж; это не аудиторское заключение и не подтвержденная чистая прибыль.`); + lines.push("Что видно в ограниченном денежном срезе:"); if (incomingAmount) { lines.push(`- входящие деньги за ${periodWithoutPrefix}: ${incomingAmount};`); } @@ -1146,7 +1142,9 @@ function buildCompactBusinessOverviewReply(entryPoint, draft) { if (debtLine) { lines.push(`- ${localizeLine(debtLine)}`); } - lines.push("Ограничение: это оценка по денежным потокам и найденным срезам 1С, не аудиторское заключение и не подтвержденная чистая прибыль."); + if (!limitLine) { + lines.push("Ограничение: это оценка по денежным потокам и найденным срезам 1С, не аудиторское заключение и не подтвержденная чистая прибыль."); + } const missingOverviewFamilies = []; if (!taxLine) { missingOverviewFamilies.push("НДС/налоговая позиция без отдельного точного расчета"); diff --git a/llm_normalizer/backend/src/services/assistantMcpDiscoveryResponseCandidate.ts b/llm_normalizer/backend/src/services/assistantMcpDiscoveryResponseCandidate.ts index 433d560..f2aeb00 100644 --- a/llm_normalizer/backend/src/services/assistantMcpDiscoveryResponseCandidate.ts +++ b/llm_normalizer/backend/src/services/assistantMcpDiscoveryResponseCandidate.ts @@ -1167,12 +1167,8 @@ function buildCompactBusinessOverviewReply( ); return joinBusinessReplyLines(lines); } - const headline = toNonEmptyString(draft.headline); - const cleanHeadline = headline?.replace(/^Коротко:\s*/iu, "").trim(); lines.push( - cleanHeadline - ? `Коротко: ${localizeLine(cleanHeadline)}` - : "Коротко: нельзя точно подтвердить чистую прибыль и маржу по текущему срезу 1С; есть только ограниченный операционный денежный/товарный сигнал, а не полный отчет о прибыли и не бухгалтерский финансовый результат." + "Коротко: чистую прибыль и маржу не подтверждаю по текущему срезу 1С; видны только ограниченные денежные обороты и расчетное нетто/операционный сигнал, а не полный отчет о прибыли и не бухгалтерский финансовый результат." ); const boundaryLines = userFacingLines([ ...toStringList(draft.confirmed_lines), @@ -1366,9 +1362,9 @@ function buildCompactBusinessOverviewReply( const subject = organizationScope ?? "компания"; const periodWithoutPrefix = period.replace(/^за\s+/iu, ""); lines.push( - `Коротко: по доступным данным ${subject} выглядит как бизнес с крупными контрактными денежными потоками и заметной зависимостью от нескольких крупных контрагентов, а не как равномерный поток мелких продаж.` + `Коротко: по ограниченному проверенному 1С-срезу ${subject} выглядит как бизнес с крупными контрактными денежными потоками и заметной зависимостью от нескольких крупных контрагентов, а не как равномерный поток мелких продаж; это не аудиторское заключение и не подтвержденная чистая прибыль.` ); - lines.push("Что видно:"); + lines.push("Что видно в ограниченном денежном срезе:"); if (incomingAmount) { lines.push(`- входящие деньги за ${periodWithoutPrefix}: ${incomingAmount};`); } @@ -1404,9 +1400,11 @@ function buildCompactBusinessOverviewReply( if (debtLine) { lines.push(`- ${localizeLine(debtLine)}`); } - lines.push( - "Ограничение: это оценка по денежным потокам и найденным срезам 1С, не аудиторское заключение и не подтвержденная чистая прибыль." - ); + if (!limitLine) { + lines.push( + "Ограничение: это оценка по денежным потокам и найденным срезам 1С, не аудиторское заключение и не подтвержденная чистая прибыль." + ); + } const missingOverviewFamilies: string[] = []; if (!taxLine) { missingOverviewFamilies.push("НДС/налоговая позиция без отдельного точного расчета"); diff --git a/llm_normalizer/backend/tests/assistantMcpDiscoveryResponseCandidate.test.ts b/llm_normalizer/backend/tests/assistantMcpDiscoveryResponseCandidate.test.ts index 52f8f14..0ca1fd0 100644 --- a/llm_normalizer/backend/tests/assistantMcpDiscoveryResponseCandidate.test.ts +++ b/llm_normalizer/backend/tests/assistantMcpDiscoveryResponseCandidate.test.ts @@ -202,7 +202,8 @@ describe("assistant MCP discovery response candidate", () => { }) ); - expect(candidate.reply_text).toContain("нельзя точно подтвердить чистую прибыль"); + expect(candidate.reply_text).toContain("чистую прибыль и маржу не подтверждаю"); + expect(candidate.reply_text).toContain("расчетное нетто"); expect(candidate.reply_text).toContain("полный отчет о прибылях и убытках"); expect(candidate.reply_text).toContain("себестоимости"); expect(candidate.reply_text).not.toContain("47 628 853"); @@ -1003,7 +1004,7 @@ describe("assistant MCP discovery response candidate", () => { }) ); - expect(candidate.reply_text).toContain("Что видно:"); + expect(candidate.reply_text).toContain("Что видно в ограниченном денежном срезе:"); expect(candidate.reply_text).toContain("НДС:"); expect(candidate.reply_text).toContain("Долги:"); expect(candidate.reply_text).toContain("Склад:"); @@ -1425,6 +1426,11 @@ describe("assistant MCP discovery response candidate", () => { }) ); + const firstLine = candidate.reply_text?.split("\n")[0] ?? ""; + expect(firstLine).toContain("по ограниченному проверенному 1С-срезу"); + expect(firstLine).toContain("не аудиторское заключение"); + expect(firstLine).toContain("не подтвержденная чистая прибыль"); + expect(candidate.reply_text).toContain("Что видно в ограниченном денежном срезе"); expect(candidate.reply_text).toContain("Что не подтверждено в этом срезе"); expect(candidate.reply_text).toContain("НДС"); expect(candidate.reply_text).toContain("долги");