Open-World: направить вопросы о доходности компании в бизнес-обзор

This commit is contained in:
2026-05-05 11:19:28 +03:00
parent 3b97fece2d
commit 0c527e5582
12 changed files with 202 additions and 34 deletions
@@ -1479,6 +1479,18 @@ function hasCustomerRevenueRankingBridgeSignal(text) {
const hasRevenueAggregateCue = /(?:(?:\u043a\u0430\u043a\u043e\u0439|\u043a\u0430\u043a\u0430\u044f)\s+(?:\u0443\s+\u043d\u0430\u0441\s+)?\u0441\u0430\u043c(?:\u044b\u0439|\u0430\u044f|\u043e\u0435)?\s+\u0434\u043e\u0445\u043e\u0434\u043d\w*\s+\u0433\u043e\u0434|(?:\u0441\u043a\u043e\u043b\u044c\u043a\u043e|скок)\s+(?:\u0432\u043e\u043e\u0431\u0449\u0435\s+)?(?:\u0434\u0435\u043d\u0435\u0433\s+)?\u043c\u044b\s+\u0437\u0430\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0438|(?:\u0437\u0430|for)\s+\d{4}\s+\u043c\u044b\s+(?:\u0441\u043a\u043e\u043b\u044c\u043a\u043e|скок)\s+\u0437\u0430\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0438|\u0432\u044b\u0440\u0443\u0447\u043a\w*\s+\u0437\u0430\s+\d{4})/iu.test(normalized);
return hasCustomerRankingCue || hasRevenueAggregateCue;
}
function hasOrganizationLevelEarningsOverviewBridgeSignal(text) {
const normalized = String(text ?? "").trim().toLowerCase();
if (!normalized) {
return false;
}
if (/(?:\u043a\u043b\u0438\u0435\u043d\u0442|\u043f\u043e\u043a\u0443\u043f\u0430\u0442\u0435\u043b|\u0437\u0430\u043a\u0430\u0437\u0447\u0438\u043a|\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442|\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|\u0434\u043e\u0433\u043e\u0432\u043e\u0440|\u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442|\u0442\u043e\u0432\u0430\u0440|\u043d\u043e\u043c\u0435\u043d\u043a\u043b\u0430\u0442\u0443\u0440|\u0441\u0434\u0435\u043b\u043a|customer|client|counterparty|supplier|vendor|contract|item|product|deal)/iu.test(normalized)) {
return false;
}
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;
}
function hasSpecificCounterpartyRevenueBridgeSignal(text) {
const normalized = String(text ?? "").trim().toLowerCase();
if (!normalized) {
@@ -1636,6 +1648,9 @@ function resolveUnicodeAddressIntentBridge(text) {
if (!hasContractCue && hasHighestValueCustomerCue) {
return unicodeBridgeResolution("customer_revenue_and_payments", "high", "unicode_customer_revenue_bridge_signal_detected");
}
if (hasOrganizationLevelEarningsOverviewBridgeSignal(normalized)) {
return unicodeBridgeResolution("unknown", "high", "unicode_business_overview_earnings_deferred_to_discovery");
}
if (hasBidirectionalValueFlowComparisonSignal(normalized)) {
return unicodeBridgeResolution("unknown", "high", "unicode_bidirectional_value_flow_deferred_to_discovery");
}
@@ -511,7 +511,21 @@ function metadataAmbiguityCollapsesToMovementLane(values) {
function hasLifecycleSignal(text) {
return /(?:сколько\s+лет|как\s+давно|давно\s+ли|возраст|перв(?:ая|ый)\s+актив|когда\s+начал|когда\s+появ|lifecycle|activity\s+duration|business\s+age|how\s+long)/iu.test(text);
}
function hasExplicitCounterpartyValueObject(text) {
return /(?:\u043a\u043b\u0438\u0435\u043d\u0442|\u043f\u043e\u043a\u0443\u043f\u0430\u0442\u0435\u043b|\u0437\u0430\u043a\u0430\u0437\u0447\u0438\u043a|\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442|\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|\u0434\u043e\u0433\u043e\u0432\u043e\u0440|\u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442|\u0442\u043e\u0432\u0430\u0440|\u043d\u043e\u043c\u0435\u043d\u043a\u043b\u0430\u0442\u0443\u0440|\u0441\u0434\u0435\u043b\u043a|customer|client|counterparty|supplier|vendor|contract|item|product|deal)/iu.test(text);
}
function hasOrganizationLevelEarningsOverviewSignal(text) {
if (!text || hasExplicitCounterpartyValueObject(text)) {
return false;
}
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;
}
function hasBusinessOverviewSignal(text) {
if (hasOrganizationLevelEarningsOverviewSignal(text)) {
return true;
}
return /(?:\u0431\u0438\u0437\u043d\u0435\u0441[-\s]?\u043e\u0431\u0437\u043e\u0440|\u0431\u0438\u0437\u043d\u0435\u0441[-\s]?\u0430\u0443\u0434\u0438\u0442|\u043f\u043e\u043b\u043d\w*\s+\u0430\u043d\u0430\u043b\u0438\u0437\s+(?:\u043a\u043e\u043c\u043f\u0430\u043d|\u0431\u0438\u0437\u043d\u0435\u0441|\u0434\u0435\u044f\u0442\u0435\u043b)|\u0441\u0432\u043e\u0434\u043d\w*\s+\u0430\u043d\u0430\u043b\u0438\u0437\s+(?:\u043a\u043e\u043c\u043f\u0430\u043d|\u0431\u0438\u0437\u043d\u0435\u0441|\u0434\u0435\u044f\u0442\u0435\u043b)|\u043a\u0430\u043a\s+\u0442\u044b\s+\u043e\u0446\u0435\u043d(?:\u0438\u0448\u044c|\u0438)\s+\u0434\u0435\u044f\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442|\u043a\u043e\u043c\u043f\u0430\u043d(?:\u0438\u0438|\u0438\u044e|\u0438\u044f)\s+\u0432\s+\u0446\u0435\u043b\u043e\u043c|company\s+(?:analysis|overview)|business\s+(?:overview|audit)|llm[-\s]?audit|бизнес[-\s]?обзор|бизнес[-\s]?аудит)/iu.test(text);
}
function hasValueFlowSignal(text) {
@@ -112,6 +112,18 @@ function detectCounterpartyTurnoverFamily(text) {
entity
};
}
function hasExplicitCounterpartyValueObject(text) {
return /(?:\u043a\u043b\u0438\u0435\u043d\u0442|\u043f\u043e\u043a\u0443\u043f\u0430\u0442\u0435\u043b|\u0437\u0430\u043a\u0430\u0437\u0447\u0438\u043a|\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442|\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|\u0434\u043e\u0433\u043e\u0432\u043e\u0440|\u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442|\u0442\u043e\u0432\u0430\u0440|\u043d\u043e\u043c\u0435\u043d\u043a\u043b\u0430\u0442\u0443\u0440|\u0441\u0434\u0435\u043b\u043a|customer|client|counterparty|supplier|vendor|contract|item|product|deal)/iu.test(text);
}
function hasOrganizationLevelEarningsOverviewSignal(text) {
const normalized = String(text ?? "");
if (!normalized || hasExplicitCounterpartyValueObject(normalized)) {
return false;
}
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;
}
function detectBroadBusinessEvaluation(text) {
const normalized = String(text ?? "");
if (!normalized) {
@@ -127,6 +139,11 @@ function detectBroadBusinessEvaluation(text) {
family: "broad_business_evaluation"
};
}
if (hasOrganizationLevelEarningsOverviewSignal(normalized)) {
return {
family: "broad_business_evaluation"
};
}
return null;
}
function buildEntityCandidates(counterpartyTurnover) {
@@ -1843,6 +1843,29 @@ function hasCustomerRevenueRankingBridgeSignal(text: string): boolean {
return hasCustomerRankingCue || hasRevenueAggregateCue;
}
function hasOrganizationLevelEarningsOverviewBridgeSignal(text: string): boolean {
const normalized = String(text ?? "").trim().toLowerCase();
if (!normalized) {
return false;
}
if (
/(?:\u043a\u043b\u0438\u0435\u043d\u0442|\u043f\u043e\u043a\u0443\u043f\u0430\u0442\u0435\u043b|\u0437\u0430\u043a\u0430\u0437\u0447\u0438\u043a|\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442|\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|\u0434\u043e\u0433\u043e\u0432\u043e\u0440|\u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442|\u0442\u043e\u0432\u0430\u0440|\u043d\u043e\u043c\u0435\u043d\u043a\u043b\u0430\u0442\u0443\u0440|\u0441\u0434\u0435\u043b\u043a|customer|client|counterparty|supplier|vendor|contract|item|product|deal)/iu.test(
normalized
)
) {
return false;
}
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;
}
function hasSpecificCounterpartyRevenueBridgeSignal(text: string): boolean {
const normalized = String(text ?? "").trim().toLowerCase();
if (!normalized) {
@@ -2100,6 +2123,10 @@ function resolveUnicodeAddressIntentBridge(text: string): AddressIntentResolutio
);
}
if (hasOrganizationLevelEarningsOverviewBridgeSignal(normalized)) {
return unicodeBridgeResolution("unknown", "high", "unicode_business_overview_earnings_deferred_to_discovery");
}
if (hasBidirectionalValueFlowComparisonSignal(normalized)) {
return unicodeBridgeResolution(
"unknown",
@@ -682,7 +682,31 @@ function hasLifecycleSignal(text: string): boolean {
);
}
function hasExplicitCounterpartyValueObject(text: string): boolean {
return /(?:\u043a\u043b\u0438\u0435\u043d\u0442|\u043f\u043e\u043a\u0443\u043f\u0430\u0442\u0435\u043b|\u0437\u0430\u043a\u0430\u0437\u0447\u0438\u043a|\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442|\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|\u0434\u043e\u0433\u043e\u0432\u043e\u0440|\u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442|\u0442\u043e\u0432\u0430\u0440|\u043d\u043e\u043c\u0435\u043d\u043a\u043b\u0430\u0442\u0443\u0440|\u0441\u0434\u0435\u043b\u043a|customer|client|counterparty|supplier|vendor|contract|item|product|deal)/iu.test(
text
);
}
function hasOrganizationLevelEarningsOverviewSignal(text: string): boolean {
if (!text || hasExplicitCounterpartyValueObject(text)) {
return false;
}
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;
}
function hasBusinessOverviewSignal(text: string): boolean {
if (hasOrganizationLevelEarningsOverviewSignal(text)) {
return true;
}
return /(?:\u0431\u0438\u0437\u043d\u0435\u0441[-\s]?\u043e\u0431\u0437\u043e\u0440|\u0431\u0438\u0437\u043d\u0435\u0441[-\s]?\u0430\u0443\u0434\u0438\u0442|\u043f\u043e\u043b\u043d\w*\s+\u0430\u043d\u0430\u043b\u0438\u0437\s+(?:\u043a\u043e\u043c\u043f\u0430\u043d|\u0431\u0438\u0437\u043d\u0435\u0441|\u0434\u0435\u044f\u0442\u0435\u043b)|\u0441\u0432\u043e\u0434\u043d\w*\s+\u0430\u043d\u0430\u043b\u0438\u0437\s+(?:\u043a\u043e\u043c\u043f\u0430\u043d|\u0431\u0438\u0437\u043d\u0435\u0441|\u0434\u0435\u044f\u0442\u0435\u043b)|\u043a\u0430\u043a\s+\u0442\u044b\s+\u043e\u0446\u0435\u043d(?:\u0438\u0448\u044c|\u0438)\s+\u0434\u0435\u044f\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442|\u043a\u043e\u043c\u043f\u0430\u043d(?:\u0438\u0438|\u0438\u044e|\u0438\u044f)\s+\u0432\s+\u0446\u0435\u043b\u043e\u043c|company\s+(?:analysis|overview)|business\s+(?:overview|audit)|llm[-\s]?audit|бизнес[-\s]?обзор|бизнес[-\s]?аудит)/iu.test(
text
);
@@ -117,6 +117,28 @@ function detectCounterpartyTurnoverFamily(text) {
};
}
function hasExplicitCounterpartyValueObject(text) {
return /(?:\u043a\u043b\u0438\u0435\u043d\u0442|\u043f\u043e\u043a\u0443\u043f\u0430\u0442\u0435\u043b|\u0437\u0430\u043a\u0430\u0437\u0447\u0438\u043a|\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442|\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|\u0434\u043e\u0433\u043e\u0432\u043e\u0440|\u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442|\u0442\u043e\u0432\u0430\u0440|\u043d\u043e\u043c\u0435\u043d\u043a\u043b\u0430\u0442\u0443\u0440|\u0441\u0434\u0435\u043b\u043a|customer|client|counterparty|supplier|vendor|contract|item|product|deal)/iu.test(
text
);
}
function hasOrganizationLevelEarningsOverviewSignal(text) {
const normalized = String(text ?? "");
if (!normalized || hasExplicitCounterpartyValueObject(normalized)) {
return false;
}
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;
}
function detectBroadBusinessEvaluation(text) {
const normalized = String(text ?? "");
if (!normalized) {
@@ -138,6 +160,11 @@ function detectBroadBusinessEvaluation(text) {
family: "broad_business_evaluation"
};
}
if (hasOrganizationLevelEarningsOverviewSignal(normalized)) {
return {
family: "broad_business_evaluation"
};
}
return null;
}
@@ -2605,11 +2605,12 @@ describe("address intent resolver expansion (M2.3a)", () => {
expect(result.intent).toBe("customer_revenue_and_payments");
});
it("resolves yearly profitability wording into customer revenue intent", () => {
it("defers organization-level yearly profitability wording to business overview discovery", () => {
const result = resolveAddressIntent(
"\u043a\u0430\u043a\u0438\u0435 \u0441\u0430\u043c\u044b\u0435 \u0434\u043e\u0445\u043e\u0434\u043d\u044b\u0435 \u0433\u043e\u0434\u0430 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u044b"
);
expect(result.intent).toBe("customer_revenue_and_payments");
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", () => {
@@ -2652,14 +2653,16 @@ describe("address intent resolver expansion (M2.3a)", () => {
expect(result.intent).toBe("contract_usage_and_value");
});
it("resolves revenue-total slang wording into customer revenue intent", () => {
it("defers organization-level revenue-total slang wording to business overview discovery", () => {
const result = resolveAddressIntent("скока денег альтернатива заработала за 22 год");
expect(result.intent).toBe("customer_revenue_and_payments");
expect(result.intent).toBe("unknown");
expect(result.reasons).toContain("unicode_business_overview_earnings_deferred_to_discovery");
});
it("resolves overall-turnover wording into customer revenue intent", () => {
it("defers organization-level overall-turnover wording to business overview discovery", () => {
const result = resolveAddressIntent("какие общие обороты за все время");
expect(result.intent).toBe("customer_revenue_and_payments");
expect(result.intent).toBe("unknown");
expect(result.reasons).toContain("unicode_business_overview_earnings_deferred_to_discovery");
});
it("resolves VAT payment forecast wording into dedicated VAT forecast intent", () => {
@@ -3753,17 +3756,17 @@ describe("address query limited taxonomy and stage diagnostics", { timeout: 1500
expect(["FACTUAL_LIST", "LIMITED_WITH_REASON", "FACTUAL_SUMMARY"]).toContain(result?.response_type);
});
it("routes yearly profitability wording into customer value aggregate recipe", async () => {
it("defers yearly profitability wording out of the exact customer value aggregate recipe", async () => {
const service = new AddressQueryService();
const result = await service.tryHandle(
"\u043a\u0430\u043a\u0438\u0435 \u0441\u0430\u043c\u044b\u0435 \u0434\u043e\u0445\u043e\u0434\u043d\u044b\u0435 \u0433\u043e\u0434\u0430 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u044b"
);
expect(result?.handled).toBe(true);
expect(result?.debug.detected_intent).toBe("customer_revenue_and_payments");
expect(result?.debug.selected_recipe).toBe("address_customer_revenue_and_payments_v1");
expect(result?.debug.limited_reason_category).not.toBe("unsupported");
expect(result?.debug.mcp_call_status).not.toBe("skipped");
expect(["FACTUAL_LIST", "LIMITED_WITH_REASON", "FACTUAL_SUMMARY"]).toContain(result?.response_type);
expect(result?.debug.detected_intent).toBe("unknown");
expect(result?.debug.selected_recipe).toBeNull();
expect(result?.debug.limited_reason_category).toBe("unsupported");
expect(result?.debug.mcp_call_status).toBe("skipped");
expect(result?.response_type).toBe("LIMITED_WITH_REASON");
});
it("routes typo highest-check wording into customer value aggregate recipe", async () => {
@@ -3819,27 +3822,16 @@ describe("address query limited taxonomy and stage diagnostics", { timeout: 1500
expect(["FACTUAL_LIST", "LIMITED_WITH_REASON", "FACTUAL_SUMMARY"]).toContain(result?.response_type);
});
it("routes revenue-total slang wording into customer value aggregate recipe (no account-missing fallback)", async () => {
it("defers revenue-total slang wording out of exact address recipes", async () => {
const service = new AddressQueryService();
const result = await service.tryHandle("скока денег альтернатива заработала за 22 год");
expect(result?.handled).toBe(true);
expect(result?.debug.detected_intent).toBe("customer_revenue_and_payments");
expect(result?.debug.selected_recipe).toBe("address_customer_revenue_and_payments_v1");
expect(result?.debug.limited_reason_category).not.toBe("missing_anchor");
expect(result?.debug.missing_required_filters).not.toContain("account");
expect(result?.debug.mcp_call_status).not.toBe("skipped");
expect(["FACTUAL_LIST", "LIMITED_WITH_REASON", "FACTUAL_SUMMARY"]).toContain(result?.response_type);
expect(result).toBeNull();
});
it("routes overall-turnover wording into customer value aggregate recipe", async () => {
it("defers overall-turnover wording out of exact address recipes", async () => {
const service = new AddressQueryService();
const result = await service.tryHandle("какие общие обороты за все время");
expect(result?.handled).toBe(true);
expect(result?.debug.detected_intent).toBe("customer_revenue_and_payments");
expect(result?.debug.selected_recipe).toBe("address_customer_revenue_and_payments_v1");
expect(result?.debug.limited_reason_category).not.toBe("unsupported");
expect(result?.debug.mcp_call_status).not.toBe("skipped");
expect(["FACTUAL_LIST", "LIMITED_WITH_REASON", "FACTUAL_SUMMARY"]).toContain(result?.response_type);
expect(result).toBeNull();
});
it("routes VAT payment forecast wording into dedicated VAT forecast recipe", async () => {
@@ -1944,12 +1944,13 @@ describe("assistant MCP discovery turn input adapter", () => {
expect(result.adapter_status).toBe("ready");
expect(result.should_run_discovery).toBe(true);
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
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: "counterparty_value",
asked_action_family: "turnover",
asked_domain_family: "business_overview",
asked_action_family: "broad_evaluation",
explicit_organization_scope: orgName,
unsupported_but_understood_family: "counterparty_value_or_turnover",
unsupported_but_understood_family: "broad_business_evaluation",
stale_replay_forbidden: true
});
expect(result.turn_meaning_ref?.explicit_date_scope).toBeUndefined();
@@ -145,4 +145,27 @@ describe("assistantTurnMeaningPolicy", () => {
expect(meaning.stale_replay_forbidden).toBe(true);
expect(meaning.reason_codes).toContain("broad_business_evaluation_current_turn_signal");
});
it("treats organization-level earnings and best-year wording as business overview", () => {
const policy = buildPolicy({
resolveAddressIntent: () => ({ intent: "customer_revenue_and_payments", confidence: "high" })
});
const earnings = policy.resolveAssistantTurnMeaning({
rawUserMessage: "сколько вообще денег мы заработали за все время?"
});
expect(earnings.explicit_intent_candidate).toBeNull();
expect(earnings.asked_domain_family).toBe("business_summary");
expect(earnings.asked_action_family).toBe("broad_evaluation");
expect(earnings.unsupported_but_understood_family).toBe("broad_business_evaluation");
expect(earnings.stale_replay_forbidden).toBe(true);
const bestYear = policy.resolveAssistantTurnMeaning({
rawUserMessage: "какой у нас самый доходный год"
});
expect(bestYear.explicit_intent_candidate).toBeNull();
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");
});
});