Open-World: направить прибыль и маржу компании в бизнес-обзор
This commit is contained in:
parent
0c527e5582
commit
c6024f52eb
|
|
@ -32,8 +32,9 @@ If another document says `78%`, `87%`, `92%`, or `85%` for a module that is now
|
|||
- Completed active slice: `Business Overview Supplier Concentration Proxy Bridge`: business overview now derives top suppliers/recipients from confirmed outgoing payment rows and surfaces procurement concentration without claiming vendor risk, procurement quality, or full expense structure.
|
||||
- Completed active slice: `Business Overview Yearly Operating-Flow Proxy Bridge`: business overview now derives annual incoming/outgoing/net buckets from confirmed money-flow rows and can name the strongest incoming year and best operating-net year without claiming profit or P&L.
|
||||
- Completed active slice: `Business Overview Earnings Wording Arbitration Bridge`: organization-level earnings/best-year/overall-turnover wording now routes to `business_overview` instead of the exact customer-value lane, while explicit customer/counterparty wording remains in `customer_revenue_and_payments`.
|
||||
- Completed active slice: `Business Overview Profit/Margin Wording Boundary Bridge`: organization-level profit, margin, financial-result, and P&L wording now routes to `business_overview` with clean-profit boundary wording, while explicit customer/item/contract routes still use exact recipes.
|
||||
- Next active slice: continue breadth into exact company-wide accounting profit/margin, real due-date debt aging, confirmed inventory reserve/write-off/liquidation evidence, and broader unfamiliar 1C route families only where reviewed evidence routes exist.
|
||||
- Active module progress: `~92% (Open-World Bounded Autonomy Breadth)`.
|
||||
- Active module progress: `~93% (Open-World Bounded Autonomy Breadth)`.
|
||||
|
||||
## Reporting Rule
|
||||
|
||||
|
|
@ -70,7 +71,7 @@ The project is not yet a universal arbitrary-1C agent.
|
|||
|
||||
Remaining work belongs to the next breadth module:
|
||||
|
||||
- extend `business_overview` beyond money-flow/activity, customer and supplier concentration, yearly operating-flow dynamics, explicit-period VAT/tax, as-of-date debt position, open-settlement concentration, contract-date debt age, debt staleness-risk proxy, as-of-date inventory position, trading-margin proxy, sales-to-stock inventory proxy, and warehouse staleness-risk proxy into separately proven exact accounting profit/margin, due-date debt aging/overdue, and confirmed reserve/write-off/liquidation inventory evidence families;
|
||||
- extend `business_overview` beyond money-flow/activity, customer and supplier concentration, yearly operating-flow dynamics, explicit profit/margin wording boundaries, explicit-period VAT/tax, as-of-date debt position, open-settlement concentration, contract-date debt age, debt staleness-risk proxy, as-of-date inventory position, trading-margin proxy, sales-to-stock inventory proxy, and warehouse staleness-risk proxy into separately proven exact accounting profit/margin, due-date debt aging/overdue, and confirmed reserve/write-off/liquidation inventory evidence families;
|
||||
- broader dynamic schema traversal for unfamiliar 1C asks;
|
||||
- more primitive descriptors where live evidence proves a real gap;
|
||||
- more replay-backed domain packs that start from user business meaning, not from route convenience;
|
||||
|
|
|
|||
|
|
@ -553,3 +553,27 @@ Local validation is accepted for this slice:
|
|||
- `npm.cmd run build`: passed.
|
||||
|
||||
Graphify rebuild after Slice 18 code/doc sync: `6052 nodes`, `13187 edges`, `138 communities`.
|
||||
|
||||
## Slice 19 - Business Overview Profit/Margin Wording Boundary Bridge
|
||||
|
||||
This slice closes the next semantic edge after Slice 18: direct company-level wording about "чистая прибыль", "маржа", "финрезультат", `P&L`, `profit`, and `margin`.
|
||||
|
||||
It does not implement exact accounting profit. The goal is to route these broad organization-level questions to `business_overview`, where the assistant can answer with the bounded evidence it actually has and explicitly say what remains unproved.
|
||||
|
||||
Implemented now:
|
||||
|
||||
- turn meaning policy treats organization-level profit/margin/financial-result wording as `broad_business_evaluation` when no explicit customer/supplier/contract/item/deal object is present;
|
||||
- the discovery turn-input adapter promotes the same wording into `business overview evidence with bounded analyst interpretation` before value-flow or exact address recipes can steal it;
|
||||
- the address intent resolver defers organization-level profit/margin wording with `unicode_business_overview_earnings_deferred_to_discovery`, preserving exact routes for explicit customer, counterparty, supplier, contract, item, product, and deal wording;
|
||||
- the business-overview next-step generator now reads the actual runtime keys `profit_margin` and `accounting_profit_margin`, while keeping backward compatibility with older `profit_or_*` keys;
|
||||
- answer drafting now names the right next check: generic profit/margin when no proxy exists, or clean profit through sales cost, expenses, and period closing after a trading-margin proxy exists.
|
||||
|
||||
This is a semantic-boundary bridge, not a new P&L engine. It makes profit/margin questions safe and useful by answering from proven business-overview evidence and by refusing to convert cash-flow, operating-flow, or trading-margin proxy into clean accounting profit.
|
||||
|
||||
Local validation is accepted for this slice:
|
||||
|
||||
- `npm.cmd test -- assistantTurnMeaningPolicy.test.ts assistantMcpDiscoveryTurnInputAdapter.test.ts assistantMcpDiscoveryAnswerAdapter.test.ts`: passed `120` with `7` skipped.
|
||||
- `npm.cmd test -- addressQueryRuntimeM23.test.ts`: passed `413`.
|
||||
- `npm.cmd run build`: passed.
|
||||
|
||||
Graphify rebuild after Slice 19 code/doc sync: `6052 nodes`, `13187 edges`, `138 communities`.
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ Status canon for planning:
|
|||
- The current completed breadth slice is `Business Overview Supplier Concentration Proxy Bridge`: company analysis now ranks confirmed outgoing payment counterparties and surfaces supplier/procurement concentration as a bounded proxy, not as vendor risk or full expense structure.
|
||||
- The current completed breadth slice is `Business Overview Yearly Operating-Flow Proxy Bridge`: company analysis now builds annual incoming/outgoing/net buckets from confirmed money-flow rows and names strongest years as operating-flow proxy, not profit or full P&L.
|
||||
- The current completed breadth slice is `Business Overview Earnings Wording Arbitration Bridge`: organization-level earnings, best-year, and overall-turnover wording now reaches `business_overview` instead of the exact customer-value route, while explicit customer/counterparty ranking remains unchanged.
|
||||
- The current completed breadth slice is `Business Overview Profit/Margin Wording Boundary Bridge`: organization-level profit, margin, financial-result, and P&L wording now reaches `business_overview` with explicit clean-profit boundaries, while explicit customer/item/contract routes remain unchanged.
|
||||
- The next active breadth slice continues breadth into exact company-wide accounting profit/margin, real due-date debt aging, confirmed reserve/write-off/liquidation inventory evidence, and broader unfamiliar 1C route families without relaxing truth boundaries.
|
||||
- The short source of truth for status wording is [21 - current_status_canon_2026-05-01.md](./21%20-%20current_status_canon_2026-05-01.md).
|
||||
|
||||
|
|
@ -132,7 +133,7 @@ Current honest status:
|
|||
- pre-multidomain readiness: `~90%`
|
||||
- bounded-autonomy foundation readiness: `~89%`
|
||||
- open-world bounded-autonomy readiness: `~87%`
|
||||
- active Open-World Bounded Autonomy Breadth progress: `~92%`, with business-overview evidence fusion, the reviewed `business_overview` catalog/data-need/planner route-fabric slice, the fresh multi-probe runtime bridge, the explicit-period VAT/tax fact-family bridge, the explicit-period debt-position bridge, the explicit-date inventory-position bridge, the open-settlement quality bridge accepted by live semantic replay, selected-item profitability bridged by local semantic/runtime regression tests, contract-date debt age bridged locally, debt staleness-risk proxy bridged locally, supplier concentration proxy bridged locally, yearly operating-flow proxy bridged locally, earnings/best-year wording arbitration bridged locally, analyst synthesis added to business-overview answer drafting, company-period trading margin proxy bridged locally, inventory sales-to-stock proxy bridged locally, inventory staleness-risk proxy bridged locally, and gap-specific answer shaping bridged locally; exact accounting profit/margin, true due-date debt aging/overdue, confirmed vendor-risk/procurement-quality analysis, and confirmed reserve/write-off/liquidation inventory evidence are still pending
|
||||
- active Open-World Bounded Autonomy Breadth progress: `~93%`, with business-overview evidence fusion, the reviewed `business_overview` catalog/data-need/planner route-fabric slice, the fresh multi-probe runtime bridge, the explicit-period VAT/tax fact-family bridge, the explicit-period debt-position bridge, the explicit-date inventory-position bridge, the open-settlement quality bridge accepted by live semantic replay, selected-item profitability bridged by local semantic/runtime regression tests, contract-date debt age bridged locally, debt staleness-risk proxy bridged locally, supplier concentration proxy bridged locally, yearly operating-flow proxy bridged locally, earnings/best-year wording arbitration bridged locally, profit/margin wording boundary arbitration bridged locally, analyst synthesis added to business-overview answer drafting, company-period trading margin proxy bridged locally, inventory sales-to-stock proxy bridged locally, inventory staleness-risk proxy bridged locally, and gap-specific answer shaping bridged locally; exact accounting profit/margin, true due-date debt aging/overdue, confirmed vendor-risk/procurement-quality analysis, and confirmed reserve/write-off/liquidation inventory evidence are still pending
|
||||
- Post-F semantic integrity module progress: `~99%` operationally closed, with remaining risk now treated as next-slice discovery rather than an open blocker inside the closed slice
|
||||
- active inventory-stock breadth slice progress: `100%` for the declared scenario pack, not for arbitrary inventory questions
|
||||
- Planner Autonomy Consolidation progress: `100%` for the declared module, with catalog-fabric, value-flow arbitration, lifecycle bounded inference, broad-evaluation bridge, inventory catalog templates, inventory runtime-boundary honesty, exact inventory recipe bridging, unambiguous metadata-surface lane inference, catalog chain-template scoring, structured chain-match contract exposure, runtime/debug propagation, subject-aware bidirectional comparison arbitration, structured catalog-alignment verdicts, representative alignment regression guard, catalog-alignment reason-code telemetry, explicit `alignment_status` propagation, truth-harness/acceptance-matrix surfacing, soft divergence warning, `catalog_alignment_ok` acceptance invariant, step-level expected catalog-alignment assertions, phase66 and phase32 spec alignment expectations, AGENT source-catalog surfacing, generated phase83 mixed planner-brain replay spec, checked-source user-facing error sanitation, surface-grounded catalog promotion, and guarded live phase83 acceptance validated. Broader unfamiliar 1C asks are now next-module breadth work rather than an open blocker inside this declared slice
|
||||
|
|
|
|||
|
|
@ -1489,7 +1489,9 @@ function hasOrganizationLevelEarningsOverviewBridgeSignal(text) {
|
|||
}
|
||||
const hasYearRankingCue = /(?:(?:\u043a\u0430\u043a\u043e\u0439|\u043a\u0430\u043a\u0438\u0435|\u043a\u0430\u043a\u0430\u044f|what|which)\s+(?:\u0443\s+\u043d\u0430\u0441\s+)?(?:[\p{L}0-9._-]+\s+){0,4}(?:\u0441\u0430\u043c|\u0441\u0430\u043c\u044b\u0435|best|top|most)[\s\S]{0,80}(?:\u0434\u043e\u0445\u043e\u0434\u043d|\u043f\u0440\u0438\u0431\u044b\u043b|\u0432\u044b\u0440\u0443\u0447\u043a|\u043e\u0431\u043e\u0440\u043e\u0442|revenue|profit|turnover)[\s\S]{0,40}(?:\u0433\u043e\u0434|year)|(?:\u0434\u043e\u0445\u043e\u0434\u043d|\u043f\u0440\u0438\u0431\u044b\u043b|\u0432\u044b\u0440\u0443\u0447\u043a|\u043e\u0431\u043e\u0440\u043e\u0442|revenue|profit|turnover)[\s\S]{0,60}(?:\u0441\u0430\u043c|\u0441\u0430\u043c\u044b\u0435|best|top|most)[\s\S]{0,40}(?:\u0433\u043e\u0434|year))/iu.test(normalized);
|
||||
const hasCompanyEarningsCue = /(?:(?:\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*|how\s+much)[\s\S]{0,120}(?:\u0437\u0430\u0440\u0430\u0431\u043e\u0442|\u0432\u044b\u0440\u0443\u0447)|(?:\u0437\u0430\u0440\u0430\u0431\u043e\u0442|\u0432\u044b\u0440\u0443\u0447)[\s\S]{0,80}(?:\u0437\u0430\s+(?:\u0432\u0441\u0435\s+\u0432\u0440\u0435\u043c\u044f|\d{2,4}\s*\u0433\u043e\u0434|(?:19|20)\d{2})|all\s+time|\b(?:19|20)\d{2}\b)|(?:\u043e\u0431\u0449\w*\s+(?:\u043e\u0431\u043e\u0440\u043e\u0442|\u0432\u044b\u0440\u0443\u0447\u043a)|(?:\u043e\u0431\u043e\u0440\u043e\u0442|\u0432\u044b\u0440\u0443\u0447\u043a)[\s\S]{0,40}\u0437\u0430\s+\u0432\u0441\u0435\s+\u0432\u0440\u0435\u043c\u044f))/iu.test(normalized);
|
||||
return hasYearRankingCue || hasCompanyEarningsCue;
|
||||
const hasCompanyProfitMarginCue = /(?:\u043f\u0440\u0438\u0431\u044b\u043b\w*|\u043c\u0430\u0440\u0436\w*|\u0440\u0435\u043d\u0442\u0430\u0431\w*|\u0444\u0438\u043d(?:\u0430\u043d\u0441\w*)?\s*[- ]?\s*\u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442|p\s*&\s*l|profit(?:ability)?|margin|financial\s+result)/iu.test(normalized) &&
|
||||
/(?:\u043a\u0430\u043a\w*|\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*|\u043f\u043e\u043a\u0430\u0436|\u0434\u0430\u0439|\u0443\s+\u043d\u0430\u0441|\u043d\u0430\u0448\w*|\u043c\u044b\b|\u043a\u043e\u043c\u043f\u0430\u043d|\u0431\u0438\u0437\u043d\u0435\u0441|\u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446|\u0432\s+\u0446\u0435\u043b\u043e\u043c|\u0432\u043e\u043e\u0431\u0449\u0435|(?:19|20)\d{2}|all\s+time|what|which|how\s+much|show|give|company|business|organization|our|we|us)/iu.test(normalized);
|
||||
return hasYearRankingCue || hasCompanyEarningsCue || hasCompanyProfitMarginCue;
|
||||
}
|
||||
function hasSpecificCounterpartyRevenueBridgeSignal(text) {
|
||||
const normalized = String(text ?? "").trim().toLowerCase();
|
||||
|
|
|
|||
|
|
@ -336,10 +336,10 @@ function businessOverviewInventoryUnknownLabel(overview) {
|
|||
function businessOverviewNextStepLine(overview) {
|
||||
const missing = new Set(overview.missing_signal_families);
|
||||
const checks = [];
|
||||
if (missing.has("profit_or_clean_margin")) {
|
||||
if (missing.has("accounting_profit_margin") || missing.has("profit_or_clean_margin")) {
|
||||
checks.push("чистую прибыль через себестоимость продаж, расходы и закрытие периода");
|
||||
}
|
||||
else if (missing.has("profit_or_margin")) {
|
||||
else if (missing.has("profit_margin") || missing.has("profit_or_margin")) {
|
||||
checks.push("прибыль/маржу по проверенному финрезультату");
|
||||
}
|
||||
if (missing.has("tax_position")) {
|
||||
|
|
|
|||
|
|
@ -520,7 +520,9 @@ function hasOrganizationLevelEarningsOverviewSignal(text) {
|
|||
}
|
||||
const hasYearRankingCue = /(?:(?:\u043a\u0430\u043a\u043e\u0439|\u043a\u0430\u043a\u0438\u0435|\u043a\u0430\u043a\u0430\u044f|what|which)\s+(?:\u0443\s+\u043d\u0430\u0441\s+)?(?:[\p{L}0-9._-]+\s+){0,4}(?:\u0441\u0430\u043c|\u0441\u0430\u043c\u044b\u0435|best|top|most)[\s\S]{0,80}(?:\u0434\u043e\u0445\u043e\u0434\u043d|\u043f\u0440\u0438\u0431\u044b\u043b|\u0432\u044b\u0440\u0443\u0447\u043a|\u043e\u0431\u043e\u0440\u043e\u0442|revenue|profit|turnover)[\s\S]{0,40}(?:\u0433\u043e\u0434|year)|(?:\u0434\u043e\u0445\u043e\u0434\u043d|\u043f\u0440\u0438\u0431\u044b\u043b|\u0432\u044b\u0440\u0443\u0447\u043a|\u043e\u0431\u043e\u0440\u043e\u0442|revenue|profit|turnover)[\s\S]{0,60}(?:\u0441\u0430\u043c|\u0441\u0430\u043c\u044b\u0435|best|top|most)[\s\S]{0,40}(?:\u0433\u043e\u0434|year))/iu.test(text);
|
||||
const hasCompanyEarningsCue = /(?:(?:\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*|how\s+much)[\s\S]{0,120}(?:\u0437\u0430\u0440\u0430\u0431\u043e\u0442|\u0432\u044b\u0440\u0443\u0447)|(?:\u0437\u0430\u0440\u0430\u0431\u043e\u0442|\u0432\u044b\u0440\u0443\u0447)[\s\S]{0,80}(?:\u0437\u0430\s+(?:\u0432\u0441\u0435\s+\u0432\u0440\u0435\u043c\u044f|\d{2,4}\s*\u0433\u043e\u0434|(?:19|20)\d{2})|all\s+time|\b(?:19|20)\d{2}\b)|(?:\u043e\u0431\u0449\w*\s+(?:\u043e\u0431\u043e\u0440\u043e\u0442|\u0432\u044b\u0440\u0443\u0447\u043a)|(?:\u043e\u0431\u043e\u0440\u043e\u0442|\u0432\u044b\u0440\u0443\u0447\u043a)[\s\S]{0,40}\u0437\u0430\s+\u0432\u0441\u0435\s+\u0432\u0440\u0435\u043c\u044f))/iu.test(text);
|
||||
return hasYearRankingCue || hasCompanyEarningsCue;
|
||||
const hasCompanyProfitMarginCue = /(?:\u043f\u0440\u0438\u0431\u044b\u043b\w*|\u043c\u0430\u0440\u0436\w*|\u0440\u0435\u043d\u0442\u0430\u0431\w*|\u0444\u0438\u043d(?:\u0430\u043d\u0441\w*)?\s*[- ]?\s*\u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442|p\s*&\s*l|profit(?:ability)?|margin|financial\s+result)/iu.test(text) &&
|
||||
/(?:\u043a\u0430\u043a\w*|\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*|\u043f\u043e\u043a\u0430\u0436|\u0434\u0430\u0439|\u0443\s+\u043d\u0430\u0441|\u043d\u0430\u0448\w*|\u043c\u044b\b|\u043a\u043e\u043c\u043f\u0430\u043d|\u0431\u0438\u0437\u043d\u0435\u0441|\u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446|\u0432\s+\u0446\u0435\u043b\u043e\u043c|\u0432\u043e\u043e\u0431\u0449\u0435|(?:19|20)\d{2}|all\s+time|what|which|how\s+much|show|give|company|business|organization|our|we|us)/iu.test(text);
|
||||
return hasYearRankingCue || hasCompanyEarningsCue || hasCompanyProfitMarginCue;
|
||||
}
|
||||
function hasBusinessOverviewSignal(text) {
|
||||
if (hasOrganizationLevelEarningsOverviewSignal(text)) {
|
||||
|
|
|
|||
|
|
@ -122,7 +122,9 @@ function hasOrganizationLevelEarningsOverviewSignal(text) {
|
|||
}
|
||||
const hasYearRankingCue = /(?:(?:\u043a\u0430\u043a\u043e\u0439|\u043a\u0430\u043a\u0438\u0435|\u043a\u0430\u043a\u0430\u044f|what|which)\s+(?:\u0443\s+\u043d\u0430\u0441\s+)?(?:[\p{L}0-9._-]+\s+){0,4}(?:\u0441\u0430\u043c|\u0441\u0430\u043c\u044b\u0435|best|top|most)[\s\S]{0,80}(?:\u0434\u043e\u0445\u043e\u0434\u043d|\u043f\u0440\u0438\u0431\u044b\u043b|\u0432\u044b\u0440\u0443\u0447\u043a|\u043e\u0431\u043e\u0440\u043e\u0442|revenue|profit|turnover)[\s\S]{0,40}(?:\u0433\u043e\u0434|year)|(?:\u0434\u043e\u0445\u043e\u0434\u043d|\u043f\u0440\u0438\u0431\u044b\u043b|\u0432\u044b\u0440\u0443\u0447\u043a|\u043e\u0431\u043e\u0440\u043e\u0442|revenue|profit|turnover)[\s\S]{0,60}(?:\u0441\u0430\u043c|\u0441\u0430\u043c\u044b\u0435|best|top|most)[\s\S]{0,40}(?:\u0433\u043e\u0434|year))/iu.test(normalized);
|
||||
const hasCompanyEarningsCue = /(?:(?:\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*|how\s+much)[\s\S]{0,120}(?:\u0437\u0430\u0440\u0430\u0431\u043e\u0442|\u0432\u044b\u0440\u0443\u0447)|(?:\u0437\u0430\u0440\u0430\u0431\u043e\u0442|\u0432\u044b\u0440\u0443\u0447)[\s\S]{0,80}(?:\u0437\u0430\s+(?:\u0432\u0441\u0435\s+\u0432\u0440\u0435\u043c\u044f|\d{2,4}\s*\u0433\u043e\u0434|(?:19|20)\d{2})|all\s+time|\b(?:19|20)\d{2}\b)|(?:\u043e\u0431\u0449\w*\s+(?:\u043e\u0431\u043e\u0440\u043e\u0442|\u0432\u044b\u0440\u0443\u0447\u043a)|(?:\u043e\u0431\u043e\u0440\u043e\u0442|\u0432\u044b\u0440\u0443\u0447\u043a)[\s\S]{0,40}\u0437\u0430\s+\u0432\u0441\u0435\s+\u0432\u0440\u0435\u043c\u044f))/iu.test(normalized);
|
||||
return hasYearRankingCue || hasCompanyEarningsCue;
|
||||
const hasCompanyProfitMarginCue = /(?:\u043f\u0440\u0438\u0431\u044b\u043b\w*|\u043c\u0430\u0440\u0436\w*|\u0440\u0435\u043d\u0442\u0430\u0431\w*|\u0444\u0438\u043d(?:\u0430\u043d\u0441\w*)?\s*[- ]?\s*\u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442|p\s*&\s*l|profit(?:ability)?|margin|financial\s+result)/iu.test(normalized) &&
|
||||
/(?:\u043a\u0430\u043a\w*|\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*|\u043f\u043e\u043a\u0430\u0436|\u0434\u0430\u0439|\u0443\s+\u043d\u0430\u0441|\u043d\u0430\u0448\w*|\u043c\u044b\b|\u043a\u043e\u043c\u043f\u0430\u043d|\u0431\u0438\u0437\u043d\u0435\u0441|\u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446|\u0432\s+\u0446\u0435\u043b\u043e\u043c|\u0432\u043e\u043e\u0431\u0449\u0435|(?:19|20)\d{2}|all\s+time|what|which|how\s+much|show|give|company|business|organization|our|we|us)/iu.test(normalized);
|
||||
return hasYearRankingCue || hasCompanyEarningsCue || hasCompanyProfitMarginCue;
|
||||
}
|
||||
function detectBroadBusinessEvaluation(text) {
|
||||
const normalized = String(text ?? "");
|
||||
|
|
|
|||
|
|
@ -1863,7 +1863,14 @@ function hasOrganizationLevelEarningsOverviewBridgeSignal(text: string): boolean
|
|||
/(?:(?:\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*|how\s+much)[\s\S]{0,120}(?:\u0437\u0430\u0440\u0430\u0431\u043e\u0442|\u0432\u044b\u0440\u0443\u0447)|(?:\u0437\u0430\u0440\u0430\u0431\u043e\u0442|\u0432\u044b\u0440\u0443\u0447)[\s\S]{0,80}(?:\u0437\u0430\s+(?:\u0432\u0441\u0435\s+\u0432\u0440\u0435\u043c\u044f|\d{2,4}\s*\u0433\u043e\u0434|(?:19|20)\d{2})|all\s+time|\b(?:19|20)\d{2}\b)|(?:\u043e\u0431\u0449\w*\s+(?:\u043e\u0431\u043e\u0440\u043e\u0442|\u0432\u044b\u0440\u0443\u0447\u043a)|(?:\u043e\u0431\u043e\u0440\u043e\u0442|\u0432\u044b\u0440\u0443\u0447\u043a)[\s\S]{0,40}\u0437\u0430\s+\u0432\u0441\u0435\s+\u0432\u0440\u0435\u043c\u044f))/iu.test(
|
||||
normalized
|
||||
);
|
||||
return hasYearRankingCue || hasCompanyEarningsCue;
|
||||
const hasCompanyProfitMarginCue =
|
||||
/(?:\u043f\u0440\u0438\u0431\u044b\u043b\w*|\u043c\u0430\u0440\u0436\w*|\u0440\u0435\u043d\u0442\u0430\u0431\w*|\u0444\u0438\u043d(?:\u0430\u043d\u0441\w*)?\s*[- ]?\s*\u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442|p\s*&\s*l|profit(?:ability)?|margin|financial\s+result)/iu.test(
|
||||
normalized
|
||||
) &&
|
||||
/(?:\u043a\u0430\u043a\w*|\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*|\u043f\u043e\u043a\u0430\u0436|\u0434\u0430\u0439|\u0443\s+\u043d\u0430\u0441|\u043d\u0430\u0448\w*|\u043c\u044b\b|\u043a\u043e\u043c\u043f\u0430\u043d|\u0431\u0438\u0437\u043d\u0435\u0441|\u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446|\u0432\s+\u0446\u0435\u043b\u043e\u043c|\u0432\u043e\u043e\u0431\u0449\u0435|(?:19|20)\d{2}|all\s+time|what|which|how\s+much|show|give|company|business|organization|our|we|us)/iu.test(
|
||||
normalized
|
||||
);
|
||||
return hasYearRankingCue || hasCompanyEarningsCue || hasCompanyProfitMarginCue;
|
||||
}
|
||||
|
||||
function hasSpecificCounterpartyRevenueBridgeSignal(text: string): boolean {
|
||||
|
|
|
|||
|
|
@ -437,9 +437,9 @@ function businessOverviewInventoryUnknownLabel(overview: BusinessOverview): stri
|
|||
function businessOverviewNextStepLine(overview: BusinessOverview): string {
|
||||
const missing = new Set(overview.missing_signal_families);
|
||||
const checks: string[] = [];
|
||||
if (missing.has("profit_or_clean_margin")) {
|
||||
if (missing.has("accounting_profit_margin") || missing.has("profit_or_clean_margin")) {
|
||||
checks.push("чистую прибыль через себестоимость продаж, расходы и закрытие периода");
|
||||
} else if (missing.has("profit_or_margin")) {
|
||||
} else if (missing.has("profit_margin") || missing.has("profit_or_margin")) {
|
||||
checks.push("прибыль/маржу по проверенному финрезультату");
|
||||
}
|
||||
if (missing.has("tax_position")) {
|
||||
|
|
|
|||
|
|
@ -700,7 +700,14 @@ function hasOrganizationLevelEarningsOverviewSignal(text: string): boolean {
|
|||
/(?:(?:\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*|how\s+much)[\s\S]{0,120}(?:\u0437\u0430\u0440\u0430\u0431\u043e\u0442|\u0432\u044b\u0440\u0443\u0447)|(?:\u0437\u0430\u0440\u0430\u0431\u043e\u0442|\u0432\u044b\u0440\u0443\u0447)[\s\S]{0,80}(?:\u0437\u0430\s+(?:\u0432\u0441\u0435\s+\u0432\u0440\u0435\u043c\u044f|\d{2,4}\s*\u0433\u043e\u0434|(?:19|20)\d{2})|all\s+time|\b(?:19|20)\d{2}\b)|(?:\u043e\u0431\u0449\w*\s+(?:\u043e\u0431\u043e\u0440\u043e\u0442|\u0432\u044b\u0440\u0443\u0447\u043a)|(?:\u043e\u0431\u043e\u0440\u043e\u0442|\u0432\u044b\u0440\u0443\u0447\u043a)[\s\S]{0,40}\u0437\u0430\s+\u0432\u0441\u0435\s+\u0432\u0440\u0435\u043c\u044f))/iu.test(
|
||||
text
|
||||
);
|
||||
return hasYearRankingCue || hasCompanyEarningsCue;
|
||||
const hasCompanyProfitMarginCue =
|
||||
/(?:\u043f\u0440\u0438\u0431\u044b\u043b\w*|\u043c\u0430\u0440\u0436\w*|\u0440\u0435\u043d\u0442\u0430\u0431\w*|\u0444\u0438\u043d(?:\u0430\u043d\u0441\w*)?\s*[- ]?\s*\u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442|p\s*&\s*l|profit(?:ability)?|margin|financial\s+result)/iu.test(
|
||||
text
|
||||
) &&
|
||||
/(?:\u043a\u0430\u043a\w*|\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*|\u043f\u043e\u043a\u0430\u0436|\u0434\u0430\u0439|\u0443\s+\u043d\u0430\u0441|\u043d\u0430\u0448\w*|\u043c\u044b\b|\u043a\u043e\u043c\u043f\u0430\u043d|\u0431\u0438\u0437\u043d\u0435\u0441|\u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446|\u0432\s+\u0446\u0435\u043b\u043e\u043c|\u0432\u043e\u043e\u0431\u0449\u0435|(?:19|20)\d{2}|all\s+time|what|which|how\s+much|show|give|company|business|organization|our|we|us)/iu.test(
|
||||
text
|
||||
);
|
||||
return hasYearRankingCue || hasCompanyEarningsCue || hasCompanyProfitMarginCue;
|
||||
}
|
||||
|
||||
function hasBusinessOverviewSignal(text: string): boolean {
|
||||
|
|
|
|||
|
|
@ -136,7 +136,14 @@ function hasOrganizationLevelEarningsOverviewSignal(text) {
|
|||
/(?:(?:\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*|how\s+much)[\s\S]{0,120}(?:\u0437\u0430\u0440\u0430\u0431\u043e\u0442|\u0432\u044b\u0440\u0443\u0447)|(?:\u0437\u0430\u0440\u0430\u0431\u043e\u0442|\u0432\u044b\u0440\u0443\u0447)[\s\S]{0,80}(?:\u0437\u0430\s+(?:\u0432\u0441\u0435\s+\u0432\u0440\u0435\u043c\u044f|\d{2,4}\s*\u0433\u043e\u0434|(?:19|20)\d{2})|all\s+time|\b(?:19|20)\d{2}\b)|(?:\u043e\u0431\u0449\w*\s+(?:\u043e\u0431\u043e\u0440\u043e\u0442|\u0432\u044b\u0440\u0443\u0447\u043a)|(?:\u043e\u0431\u043e\u0440\u043e\u0442|\u0432\u044b\u0440\u0443\u0447\u043a)[\s\S]{0,40}\u0437\u0430\s+\u0432\u0441\u0435\s+\u0432\u0440\u0435\u043c\u044f))/iu.test(
|
||||
normalized
|
||||
);
|
||||
return hasYearRankingCue || hasCompanyEarningsCue;
|
||||
const hasCompanyProfitMarginCue =
|
||||
/(?:\u043f\u0440\u0438\u0431\u044b\u043b\w*|\u043c\u0430\u0440\u0436\w*|\u0440\u0435\u043d\u0442\u0430\u0431\w*|\u0444\u0438\u043d(?:\u0430\u043d\u0441\w*)?\s*[- ]?\s*\u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442|p\s*&\s*l|profit(?:ability)?|margin|financial\s+result)/iu.test(
|
||||
normalized
|
||||
) &&
|
||||
/(?:\u043a\u0430\u043a\w*|\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*|\u043f\u043e\u043a\u0430\u0436|\u0434\u0430\u0439|\u0443\s+\u043d\u0430\u0441|\u043d\u0430\u0448\w*|\u043c\u044b\b|\u043a\u043e\u043c\u043f\u0430\u043d|\u0431\u0438\u0437\u043d\u0435\u0441|\u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446|\u0432\s+\u0446\u0435\u043b\u043e\u043c|\u0432\u043e\u043e\u0431\u0449\u0435|(?:19|20)\d{2}|all\s+time|what|which|how\s+much|show|give|company|business|organization|our|we|us)/iu.test(
|
||||
normalized
|
||||
);
|
||||
return hasYearRankingCue || hasCompanyEarningsCue || hasCompanyProfitMarginCue;
|
||||
}
|
||||
|
||||
function detectBroadBusinessEvaluation(text) {
|
||||
|
|
|
|||
|
|
@ -2613,6 +2613,14 @@ describe("address intent resolver expansion (M2.3a)", () => {
|
|||
expect(result.reasons).toContain("unicode_business_overview_earnings_deferred_to_discovery");
|
||||
});
|
||||
|
||||
it("defers organization-level profit and margin wording to business overview discovery", () => {
|
||||
const result = resolveAddressIntent(
|
||||
"\u043a\u0430\u043a\u0430\u044f \u0443 \u043d\u0430\u0441 \u0447\u0438\u0441\u0442\u0430\u044f \u043f\u0440\u0438\u0431\u044b\u043b\u044c \u0438 \u043c\u0430\u0440\u0436\u0430 \u0437\u0430 2020?"
|
||||
);
|
||||
expect(result.intent).toBe("unknown");
|
||||
expect(result.reasons).toContain("unicode_business_overview_earnings_deferred_to_discovery");
|
||||
});
|
||||
|
||||
it("resolves major-share revenue wording into customer revenue intent", () => {
|
||||
const result = resolveAddressIntent("какие контрагенты принесли основную часть нашей выручки за отчетный период?");
|
||||
expect(result.intent).toBe("customer_revenue_and_payments");
|
||||
|
|
|
|||
|
|
@ -254,6 +254,7 @@ describe("assistant MCP discovery answer adapter", () => {
|
|||
expect(draft.inference_lines.join("\n")).toContain("не прибыль и не маржа");
|
||||
expect(draft.unknown_lines.join("\n")).toContain("Прибыль и маржа");
|
||||
expect(draft.unknown_lines.join("\n")).toContain("Налоговая/VAT-позиция");
|
||||
expect(draft.next_step_line).toContain("прибыль/маржу");
|
||||
expect(draft.must_not_claim).toContain("Do not present business overview cash-flow spread as profit or margin.");
|
||||
expect(draft.must_not_claim).toContain("Do not present business overview yearly operating-flow breakdown as profit, financial result, or a complete annual P&L.");
|
||||
expect(draft.must_not_claim).toContain("Do not present business overview supplier concentration as vendor-risk audit, procurement quality, or full expense structure.");
|
||||
|
|
@ -340,6 +341,7 @@ describe("assistant MCP discovery answer adapter", () => {
|
|||
expect(draft.inference_lines.join("\n")).toContain("не прибыль и не маржа");
|
||||
expect(draft.unknown_lines.join("\n")).toContain("Чистая прибыль");
|
||||
expect(draft.unknown_lines.join("\n")).not.toContain("Налоговая/VAT-позиция");
|
||||
expect(draft.next_step_line).toContain("чистую прибыль");
|
||||
expect(draft.reason_codes).toContain("answer_contains_business_overview_tax_position");
|
||||
expect(draft.reason_codes).toContain("answer_contains_business_overview_trading_margin_proxy");
|
||||
expect(draft.must_not_claim).toContain("Do not present business overview cash-flow spread as profit or margin.");
|
||||
|
|
|
|||
|
|
@ -1961,6 +1961,37 @@ describe("assistant MCP discovery turn input adapter", () => {
|
|||
expect(result.data_need_graph?.reason_codes).toContain("data_need_graph_all_time_scope_hint");
|
||||
});
|
||||
|
||||
it("routes organization-level profit and margin wording to business overview instead of exact value recipes", () => {
|
||||
const orgName = "\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441";
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage:
|
||||
"\u043a\u0430\u043a\u0430\u044f \u0443 \u043d\u0430\u0441 \u0447\u0438\u0441\u0442\u0430\u044f \u043f\u0440\u0438\u0431\u044b\u043b\u044c \u0438 \u043c\u0430\u0440\u0436\u0430 \u0437\u0430 2020?",
|
||||
followupContext: {
|
||||
previous_discovery_pilot_scope: "counterparty_value_flow_query_movements_v1",
|
||||
previous_filters: {
|
||||
organization: orgName,
|
||||
as_of_date: "2026-04-23"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.adapter_status).toBe("ready");
|
||||
expect(result.should_run_discovery).toBe(true);
|
||||
expect(result.semantic_data_need).toBe("business overview evidence with bounded analyst interpretation");
|
||||
expect(result.data_need_graph?.business_fact_family).toBe("business_overview");
|
||||
expect(result.turn_meaning_ref).toMatchObject({
|
||||
asked_domain_family: "business_overview",
|
||||
asked_action_family: "broad_evaluation",
|
||||
explicit_organization_scope: orgName,
|
||||
explicit_date_scope: "2020",
|
||||
unsupported_but_understood_family: "broad_business_evaluation",
|
||||
stale_replay_forbidden: true
|
||||
});
|
||||
expect(result.reason_codes).toContain("mcp_discovery_broad_business_evaluation_route_candidate");
|
||||
expect(result.reason_codes).not.toContain("mcp_discovery_date_scope_from_followup_context");
|
||||
expect(result.data_need_graph?.clarification_gaps).toEqual([]);
|
||||
});
|
||||
|
||||
it("resumes an open-scope total clarification loop from saved state when the user resolves the pending period with all-time wording", () => {
|
||||
const orgName = "ООО Альтернатива Плюс";
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
|
|
|
|||
|
|
@ -167,5 +167,15 @@ describe("assistantTurnMeaningPolicy", () => {
|
|||
expect(bestYear.asked_domain_family).toBe("business_summary");
|
||||
expect(bestYear.asked_action_family).toBe("broad_evaluation");
|
||||
expect(bestYear.reason_codes).toContain("broad_business_evaluation_current_turn_signal");
|
||||
|
||||
const profitMargin = policy.resolveAssistantTurnMeaning({
|
||||
rawUserMessage:
|
||||
"\u043a\u0430\u043a\u0430\u044f \u0443 \u043d\u0430\u0441 \u0447\u0438\u0441\u0442\u0430\u044f \u043f\u0440\u0438\u0431\u044b\u043b\u044c \u0438 \u043c\u0430\u0440\u0436\u0430 \u0437\u0430 2020?"
|
||||
});
|
||||
expect(profitMargin.explicit_intent_candidate).toBeNull();
|
||||
expect(profitMargin.asked_domain_family).toBe("business_summary");
|
||||
expect(profitMargin.asked_action_family).toBe("broad_evaluation");
|
||||
expect(profitMargin.unsupported_but_understood_family).toBe("broad_business_evaluation");
|
||||
expect(profitMargin.reason_codes).toContain("broad_business_evaluation_current_turn_signal");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue