Semantic Gate: закрепить доминирование intent и reset frame
This commit is contained in:
@@ -1529,6 +1529,16 @@ function hasOrganizationLevelDebtDueDateOverviewBridgeSignal(text) {
|
||||
const hasCompanyScopeCue = /(?:\u0443\s+\u043d\u0430\u0441|\u043d\u0430\u0448\w*|\u043f\u043e\s+\u043a\u043e\u043c\u043f\u0430\u043d|\u043a\u043e\u043c\u043f\u0430\u043d|\u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446|\u0431\u0438\u0437\u043d\u0435\u0441|\u0432\s+\u0446\u0435\u043b\u043e\u043c|\u043e\u0431\u0449\w*|\u043a\u0430\u043a\w*|\u043f\u043e\u043a\u0430\u0436|\u0434\u0430\u0439|\u0441\u0440\u0435\u0437|\u0430\u043d\u0430\u043b\u0438\u0437|(?:19|20)\d{2}|company|business|organization|overall|our|we|us|show|give|analysis)/iu.test(normalized);
|
||||
return hasDueDateDebtCue && hasCompanyScopeCue;
|
||||
}
|
||||
function hasOrganizationLevelDebtPositionOverviewBridgeSignal(text) {
|
||||
const normalized = String(text ?? "").trim().toLowerCase();
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
const hasReceivablesCue = /(?:\u0434\u0435\u0431\u0438\u0442\u043e\u0440\w*|\u043a\u0442\u043e\s+\u043d\u0430\u043c\s+\u0434\u043e\u043b\u0436|\u043d\u0430\u043c\s+\u0434\u043e\u043b\u0436\w*|receivables?|accounts\s+receivable)/iu.test(normalized);
|
||||
const hasPayablesCue = /(?:\u043a\u0440\u0435\u0434\u0438\u0442\u043e\u0440\w*|\u043a\u043e\u043c\u0443\s+\u043c\u044b\s+\u0434\u043e\u043b\u0436|\u043c\u044b\s+\u0434\u043e\u043b\u0436\w*|payables?|accounts\s+payable)/iu.test(normalized);
|
||||
const hasOverviewCue = /(?:\u0441\u0440\u0435\u0437|\u043f\u043e\u0437\u0438\u0446\w*|\u0431\u0430\u043b\u0430\u043d\u0441|\u0441\u0430\u043b\u044c\u0434\u043e|\u043d\u0435\u0442\u0442\u043e|\u043d\u0430\s+\u0441\u0435\u0433\u043e\u0434\u043d|\u043d\u0430\s+\u0434\u0430\u0442\u0443|\u0443\s+\u043d\u0430\u0441|\u043f\u043e\s+\u043a\u043e\u043c\u043f\u0430\u043d|\u043a\u043e\u043c\u043f\u0430\u043d|\u0431\u0438\u0437\u043d\u0435\u0441|\u043e\u0431\u0437\u043e\u0440|\u0430\u043d\u0430\u043b\u0438\u0437|as\s+of|overview|balance|net|company|business)/iu.test(normalized);
|
||||
return hasReceivablesCue && hasPayablesCue && hasOverviewCue;
|
||||
}
|
||||
function hasOrganizationLevelInventoryReserveLiquidationOverviewBridgeSignal(text) {
|
||||
const normalized = String(text ?? "").trim().toLowerCase();
|
||||
if (!normalized) {
|
||||
@@ -1727,6 +1737,9 @@ function resolveUnicodeAddressIntentBridge(text) {
|
||||
if (hasOrganizationLevelEarningsOverviewBridgeSignal(normalized)) {
|
||||
return unicodeBridgeResolution("unknown", "high", "unicode_business_overview_earnings_deferred_to_discovery");
|
||||
}
|
||||
if (hasOrganizationLevelDebtPositionOverviewBridgeSignal(normalized)) {
|
||||
return unicodeBridgeResolution("unknown", "high", "unicode_business_overview_debt_position_deferred_to_discovery");
|
||||
}
|
||||
if (hasOrganizationLevelDebtDueDateOverviewBridgeSignal(normalized)) {
|
||||
return unicodeBridgeResolution("unknown", "high", "unicode_business_overview_debt_due_date_deferred_to_discovery");
|
||||
}
|
||||
|
||||
+61
-8
@@ -579,6 +579,15 @@ function hasOrganizationLevelDebtDueDateOverviewSignal(text) {
|
||||
const hasCompanyScopeCue = /(?:\u0443\s+\u043d\u0430\u0441|\u043d\u0430\u0448\w*|\u043f\u043e\s+\u043a\u043e\u043c\u043f\u0430\u043d|\u043a\u043e\u043c\u043f\u0430\u043d|\u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446|\u0431\u0438\u0437\u043d\u0435\u0441|\u0432\s+\u0446\u0435\u043b\u043e\u043c|\u043e\u0431\u0449\w*|\u043a\u0430\u043a\w*|\u043f\u043e\u043a\u0430\u0436|\u0434\u0430\u0439|\u0441\u0440\u0435\u0437|\u0430\u043d\u0430\u043b\u0438\u0437|(?:19|20)\d{2}|company|business|organization|overall|our|we|us|show|give|analysis)/iu.test(text);
|
||||
return hasDueDateDebtCue && hasCompanyScopeCue;
|
||||
}
|
||||
function hasOrganizationLevelDebtPositionOverviewSignal(text) {
|
||||
if (!text) {
|
||||
return false;
|
||||
}
|
||||
const hasReceivablesCue = /(?:\u0434\u0435\u0431\u0438\u0442\u043e\u0440\w*|\u043a\u0442\u043e\s+\u043d\u0430\u043c\s+\u0434\u043e\u043b\u0436|\u043d\u0430\u043c\s+\u0434\u043e\u043b\u0436\w*|receivables?|accounts\s+receivable)/iu.test(text);
|
||||
const hasPayablesCue = /(?:\u043a\u0440\u0435\u0434\u0438\u0442\u043e\u0440\w*|\u043a\u043e\u043c\u0443\s+\u043c\u044b\s+\u0434\u043e\u043b\u0436|\u043c\u044b\s+\u0434\u043e\u043b\u0436\w*|payables?|accounts\s+payable)/iu.test(text);
|
||||
const hasOverviewCue = /(?:\u0441\u0440\u0435\u0437|\u043f\u043e\u0437\u0438\u0446\w*|\u0431\u0430\u043b\u0430\u043d\u0441|\u0441\u0430\u043b\u044c\u0434\u043e|\u043d\u0435\u0442\u0442\u043e|\u043d\u0430\s+\u0441\u0435\u0433\u043e\u0434\u043d|\u043d\u0430\s+\u0434\u0430\u0442\u0443|\u0443\s+\u043d\u0430\u0441|\u043f\u043e\s+\u043a\u043e\u043c\u043f\u0430\u043d|\u043a\u043e\u043c\u043f\u0430\u043d|\u0431\u0438\u0437\u043d\u0435\u0441|\u043e\u0431\u0437\u043e\u0440|\u0430\u043d\u0430\u043b\u0438\u0437|as\s+of|overview|balance|net|company|business)/iu.test(text);
|
||||
return hasReceivablesCue && hasPayablesCue && hasOverviewCue;
|
||||
}
|
||||
function hasOrganizationLevelInventoryReserveLiquidationOverviewSignal(text) {
|
||||
if (!text) {
|
||||
return false;
|
||||
@@ -605,6 +614,7 @@ function hasOrganizationLevelSupplierQualityOverviewSignal(text) {
|
||||
}
|
||||
function hasBusinessOverviewSignal(text) {
|
||||
if (hasOrganizationLevelEarningsOverviewSignal(text) ||
|
||||
hasOrganizationLevelDebtPositionOverviewSignal(text) ||
|
||||
hasOrganizationLevelDebtDueDateOverviewSignal(text) ||
|
||||
hasOrganizationLevelInventoryReserveLiquidationOverviewSignal(text) ||
|
||||
hasOrganizationLevelSupplierQualityOverviewSignal(text)) {
|
||||
@@ -620,7 +630,11 @@ function hasBusinessOverviewContinuationSignal(text) {
|
||||
const hasEvidenceContinuationCue = /(?:по\s+этим\s+данн|на\s+базе\s+этих\s+данн|из\s+этого|по\s+итогам|что\s+подтвержден|чего\s+не\s+хватает|что\s+пока\s+нельзя|что\s+можно\s+сказать|какой\s+вывод|итогов|резюм|вывод)/iu.test(normalized);
|
||||
const hasAnalystContinuationCue = /(?:можно\s+ли|если\s+нет|proxy|прокси|аудит|оцен|что\s+думаешь|нормальн\p{L}*\s+прибыл|прибыл|марж|рентаб|ликвидн|просроч|качество\s+долг|риск|налогов\p{L}*\s+вывод)/iu.test(normalized);
|
||||
const hasTaxContinuationCue = /(?:ндс|vat)[\s\S]{0,120}(?:позици|период|основан|не\s+хватает|налогов\p{L}*\s+вывод)|(?:позици|налогов\p{L}*\s+вывод)[\s\S]{0,80}(?:ндс|vat)/iu.test(normalized);
|
||||
return hasEvidenceContinuationCue || hasAnalystContinuationCue || hasTaxContinuationCue;
|
||||
const hasFinalSummaryCue = /(?:\u0447\u0442\u043e\s+\u043c\u044b\s+\u0437\u043d\u0430\u0435\u043c|\u0447\u0442\u043e\s+\u043f\u043e\u043d\u044f\u0442\u043d\u043e|\u0447\u0442\u043e\s+\u043f\u0440\u043e\u0432\u0435\u0440\w*\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u0441\u043b\u0435\u0434\u0443\u044e\u0449\w*\s+\u0448\u0430\u0433|\u0438\u0442\u043e\u0433\w*\s+\u0432\u044b\u0432\u043e\u0434|\u043a\u0430\u043a\u043e\u0439\s+\u0432\u044b\u0432\u043e\u0434|\u0447\u0442\u043e\s+\u0441\s+\u044d\u0442\u0438\u043c\s+\u0434\u0435\u043b\u0430\u0442\u044c|what\s+do\s+we\s+know|what\s+is\s+missing|next\s+step|final\s+summary)/iu.test(normalized);
|
||||
return hasEvidenceContinuationCue || hasAnalystContinuationCue || hasTaxContinuationCue || hasFinalSummaryCue;
|
||||
}
|
||||
function hasExplicitTopicSwitchSignal(text) {
|
||||
return /(?:^|\s)(?:\u0442\u0435\u043f\u0435\u0440\u044c|\u0430\s+\u0442\u0435\u043f\u0435\u0440\u044c|\u0434\u0430\u043b\u044c\u0448\u0435|\u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e|\u043f\u0435\u0440\u0435\u0439\u0434[\u0451\u0435]\u043c|\u0441\u043c\u0435\u043d\u0438\u043c\s+\u0442\u0435\u043c\u0443|\u0432\u0435\u0440\u043d[\u0451\u0435]\u043c\u0441\u044f\s+\u043a|now|next|switch\s+to)(?:\s|$)/iu.test(text);
|
||||
}
|
||||
function hasBusinessOverviewFollowupSeed(followupSeed) {
|
||||
return (followupSeed.pilotScope === "business_overview_route_template_v1" ||
|
||||
@@ -997,6 +1011,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
const relativeCurrentDateHintDetected = hasRelativeCurrentDateHint(rawText);
|
||||
const rawDateScope = collectDateScopeFromRawText(dateScopeSignalText);
|
||||
const rawMetadataScopeHint = rawMetadataSignal ? metadataScopeHintFromRawText(rawText) : null;
|
||||
const rawTopicSwitchSignal = hasExplicitTopicSwitchSignal(rawText);
|
||||
const rawEntityCandidate = rawEntityResolutionSignal ? rawEntityResolutionCandidate(rawEntitySourceText) : null;
|
||||
const entityResolutionClarificationCandidate = followupSeed.pilotScope === "entity_resolution_search_v1" &&
|
||||
followupSeed.entityResolutionStatus === "ambiguous"
|
||||
@@ -1044,9 +1059,22 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
(isInvalidEntityCandidate(followupSeed.counterparty) ||
|
||||
sameScopedName(followupSeed.counterparty, followupSeed.organization) ||
|
||||
sameScopedName(followupSeed.counterparty, currentTurnOrganizationScope)));
|
||||
const businessOverviewSuppressesFollowupCounterparty = Boolean(businessOverviewSignal &&
|
||||
(rawBusinessOverviewSignal ||
|
||||
businessOverviewContinuationSignal ||
|
||||
broadBusinessEvaluationUnsupported ||
|
||||
rawDomain === "business_summary" ||
|
||||
rawDomain === "business_overview" ||
|
||||
rawAction === "broad_evaluation"));
|
||||
const effectiveFollowupCounterparty = followupCounterpartyIsMetadataOrganizationScope
|
||||
? null
|
||||
: followupSeed.counterparty;
|
||||
: businessOverviewSuppressesFollowupCounterparty
|
||||
? null
|
||||
: followupSeed.counterparty;
|
||||
const rawMetadataScopeOverridesFollowupEntity = Boolean(rawMetadataSignal &&
|
||||
rawMetadataScopeHint &&
|
||||
(effectiveFollowupCounterparty || followupSeed.discoveryEntity) &&
|
||||
!sameScopedName(rawMetadataScopeHint, effectiveFollowupCounterparty ?? followupSeed.discoveryEntity));
|
||||
const explicitOrganizationScopeSignal = Boolean(rawOrganizationMentionSignal && currentTurnOrganizationScope);
|
||||
const organizationClarificationFollowupApplicable = Boolean(followupSeed.domain === "counterparty_value" &&
|
||||
!effectiveFollowupCounterparty &&
|
||||
@@ -1095,6 +1123,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
hasMetadataObjectHint(rawText));
|
||||
const metadataLaneCarryoverAvailable = Boolean(effectiveFollowupCounterparty ||
|
||||
followupSeed.discoveryEntity ||
|
||||
rawMetadataScopeHint ||
|
||||
followupSeed.metadataScopeHint ||
|
||||
followupSeed.metadataSelectedEntitySet ||
|
||||
followupSeed.metadataSelectedSurfaceObjects.length > 0);
|
||||
@@ -1376,9 +1405,11 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
? null
|
||||
: rawEntitySearchOverridesStaleScope
|
||||
? null
|
||||
: explicitCurrentCounterpartyOverridesFollowupEntity
|
||||
: rawMetadataScopeOverridesFollowupEntity
|
||||
? null
|
||||
: effectiveFollowupCounterparty ?? followupSeed.discoveryEntity;
|
||||
: explicitCurrentCounterpartyOverridesFollowupEntity
|
||||
? null
|
||||
: effectiveFollowupCounterparty ?? followupSeed.discoveryEntity;
|
||||
const entityCandidates = entityResolutionSignal ? [] : [];
|
||||
if (entityResolutionSignal) {
|
||||
pushNormalizedEntityResolutionCandidate(entityCandidates, entityResolutionClarificationCandidate);
|
||||
@@ -1404,8 +1435,10 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
}
|
||||
pushScopedEntityCandidate(entityCandidates, normalizedPredecomposeCounterparty, groundedFollowupEntity);
|
||||
if (!groundedFollowupEntity) {
|
||||
pushScopedEntityCandidate(entityCandidates, effectiveFollowupCounterparty, null);
|
||||
if (!metadataScopedLaneWithoutSubject) {
|
||||
if (!rawMetadataScopeOverridesFollowupEntity) {
|
||||
pushScopedEntityCandidate(entityCandidates, effectiveFollowupCounterparty, null);
|
||||
}
|
||||
if (!metadataScopedLaneWithoutSubject && !rawMetadataScopeOverridesFollowupEntity) {
|
||||
pushScopedEntityCandidate(entityCandidates, followupSeed.discoveryEntity, null);
|
||||
}
|
||||
}
|
||||
@@ -1414,7 +1447,9 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
if ((rawMetadataSignal || metadataFollowupSeedApplicable) &&
|
||||
!groundedFollowupEntity &&
|
||||
!metadataScopedLaneWithoutSubject) {
|
||||
pushUnique(entityCandidates, followupSeed.discoveryEntity);
|
||||
if (!rawMetadataScopeOverridesFollowupEntity) {
|
||||
pushUnique(entityCandidates, followupSeed.discoveryEntity);
|
||||
}
|
||||
pushUnique(entityCandidates, rawMetadataScopeHint);
|
||||
}
|
||||
const openScopeValueFlowWithoutCounterparty = valueFlowSignal && !normalizedPredecomposeCounterparty && !effectiveFollowupCounterparty;
|
||||
@@ -1479,6 +1514,12 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
openScopeValueFlowWithoutResolvedCounterparty ||
|
||||
(valueFlowOrganizationStaysScope && (Boolean(followupSeed.rankingNeed) || bidirectionalValueFlowSignal))));
|
||||
const suppressNegatedTaxOnlyDateScope = Boolean(businessOverviewSignal && negatedTaxDateScopeOnlySignal);
|
||||
const topicSwitchSuppressesFollowupScope = Boolean(rawTopicSwitchSignal &&
|
||||
(rawMetadataSignal ||
|
||||
businessOverviewSignal ||
|
||||
rawEntitySearchOverridesStaleScope ||
|
||||
explicitOrganizationScopeSignal ||
|
||||
rawAllTimeScopeSignal));
|
||||
const normalizedPredecomposeDateScope = (rawEntitySearchOverridesStaleScope && !currentTurnCarriesExplicitPeriod) ||
|
||||
suppressNegatedTaxOnlyDateScope ||
|
||||
businessOverviewRawWithoutDateScope ||
|
||||
@@ -1492,6 +1533,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
: assistantTurnMeaningDateScope;
|
||||
const normalizedFollowupDateScope = rawEntitySearchOverridesStaleScope ||
|
||||
suppressNegatedTaxOnlyDateScope ||
|
||||
topicSwitchSuppressesFollowupScope ||
|
||||
(suppressImplicitCurrentDateScope && isImplicitCurrentDateScope(followupSeed.dateScope))
|
||||
? null
|
||||
: followupSeed.dateScope;
|
||||
@@ -1698,6 +1740,12 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
if (rawMetadataScopeHint) {
|
||||
pushReason(reasonCodes, "mcp_discovery_metadata_scope_hint_from_raw_text");
|
||||
}
|
||||
if (rawMetadataScopeOverridesFollowupEntity) {
|
||||
pushReason(reasonCodes, "mcp_discovery_raw_metadata_scope_overrides_stale_entity");
|
||||
}
|
||||
if (topicSwitchSuppressesFollowupScope) {
|
||||
pushReason(reasonCodes, "mcp_discovery_topic_switch_suppressed_followup_scope");
|
||||
}
|
||||
if (rawEntityCandidate) {
|
||||
pushReason(reasonCodes, "mcp_discovery_entity_scope_from_raw_entity_search");
|
||||
}
|
||||
@@ -1809,11 +1857,16 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
if (businessOverviewContinuationSignal) {
|
||||
pushReason(reasonCodes, "mcp_discovery_business_overview_continuation_from_followup_context");
|
||||
}
|
||||
if (businessOverviewSuppressesFollowupCounterparty) {
|
||||
pushReason(reasonCodes, "mcp_discovery_business_overview_suppressed_stale_counterparty");
|
||||
}
|
||||
if (!(valueFlowOrganizationStaysScope && normalizedPredecomposeCounterparty === explicitOrganizationScope) &&
|
||||
normalizedPredecomposeCounterparty) {
|
||||
pushReason(reasonCodes, "mcp_discovery_counterparty_from_predecompose");
|
||||
}
|
||||
if (effectiveFollowupCounterparty && !rawEntitySearchOverridesStaleScope) {
|
||||
if (effectiveFollowupCounterparty &&
|
||||
!rawEntitySearchOverridesStaleScope &&
|
||||
!rawMetadataScopeOverridesFollowupEntity) {
|
||||
pushReason(reasonCodes, "mcp_discovery_counterparty_from_followup_context");
|
||||
}
|
||||
if (followupDateScopeApplied) {
|
||||
|
||||
@@ -1920,6 +1920,26 @@ function hasOrganizationLevelDebtDueDateOverviewBridgeSignal(text: string): bool
|
||||
return hasDueDateDebtCue && hasCompanyScopeCue;
|
||||
}
|
||||
|
||||
function hasOrganizationLevelDebtPositionOverviewBridgeSignal(text: string): boolean {
|
||||
const normalized = String(text ?? "").trim().toLowerCase();
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
const hasReceivablesCue =
|
||||
/(?:\u0434\u0435\u0431\u0438\u0442\u043e\u0440\w*|\u043a\u0442\u043e\s+\u043d\u0430\u043c\s+\u0434\u043e\u043b\u0436|\u043d\u0430\u043c\s+\u0434\u043e\u043b\u0436\w*|receivables?|accounts\s+receivable)/iu.test(
|
||||
normalized
|
||||
);
|
||||
const hasPayablesCue =
|
||||
/(?:\u043a\u0440\u0435\u0434\u0438\u0442\u043e\u0440\w*|\u043a\u043e\u043c\u0443\s+\u043c\u044b\s+\u0434\u043e\u043b\u0436|\u043c\u044b\s+\u0434\u043e\u043b\u0436\w*|payables?|accounts\s+payable)/iu.test(
|
||||
normalized
|
||||
);
|
||||
const hasOverviewCue =
|
||||
/(?:\u0441\u0440\u0435\u0437|\u043f\u043e\u0437\u0438\u0446\w*|\u0431\u0430\u043b\u0430\u043d\u0441|\u0441\u0430\u043b\u044c\u0434\u043e|\u043d\u0435\u0442\u0442\u043e|\u043d\u0430\s+\u0441\u0435\u0433\u043e\u0434\u043d|\u043d\u0430\s+\u0434\u0430\u0442\u0443|\u0443\s+\u043d\u0430\u0441|\u043f\u043e\s+\u043a\u043e\u043c\u043f\u0430\u043d|\u043a\u043e\u043c\u043f\u0430\u043d|\u0431\u0438\u0437\u043d\u0435\u0441|\u043e\u0431\u0437\u043e\u0440|\u0430\u043d\u0430\u043b\u0438\u0437|as\s+of|overview|balance|net|company|business)/iu.test(
|
||||
normalized
|
||||
);
|
||||
return hasReceivablesCue && hasPayablesCue && hasOverviewCue;
|
||||
}
|
||||
|
||||
function hasOrganizationLevelInventoryReserveLiquidationOverviewBridgeSignal(text: string): boolean {
|
||||
const normalized = String(text ?? "").trim().toLowerCase();
|
||||
if (!normalized) {
|
||||
@@ -2247,6 +2267,10 @@ function resolveUnicodeAddressIntentBridge(text: string): AddressIntentResolutio
|
||||
return unicodeBridgeResolution("unknown", "high", "unicode_business_overview_earnings_deferred_to_discovery");
|
||||
}
|
||||
|
||||
if (hasOrganizationLevelDebtPositionOverviewBridgeSignal(normalized)) {
|
||||
return unicodeBridgeResolution("unknown", "high", "unicode_business_overview_debt_position_deferred_to_discovery");
|
||||
}
|
||||
|
||||
if (hasOrganizationLevelDebtDueDateOverviewBridgeSignal(normalized)) {
|
||||
return unicodeBridgeResolution("unknown", "high", "unicode_business_overview_debt_due_date_deferred_to_discovery");
|
||||
}
|
||||
|
||||
@@ -783,6 +783,25 @@ function hasOrganizationLevelDebtDueDateOverviewSignal(text: string): boolean {
|
||||
return hasDueDateDebtCue && hasCompanyScopeCue;
|
||||
}
|
||||
|
||||
function hasOrganizationLevelDebtPositionOverviewSignal(text: string): boolean {
|
||||
if (!text) {
|
||||
return false;
|
||||
}
|
||||
const hasReceivablesCue =
|
||||
/(?:\u0434\u0435\u0431\u0438\u0442\u043e\u0440\w*|\u043a\u0442\u043e\s+\u043d\u0430\u043c\s+\u0434\u043e\u043b\u0436|\u043d\u0430\u043c\s+\u0434\u043e\u043b\u0436\w*|receivables?|accounts\s+receivable)/iu.test(
|
||||
text
|
||||
);
|
||||
const hasPayablesCue =
|
||||
/(?:\u043a\u0440\u0435\u0434\u0438\u0442\u043e\u0440\w*|\u043a\u043e\u043c\u0443\s+\u043c\u044b\s+\u0434\u043e\u043b\u0436|\u043c\u044b\s+\u0434\u043e\u043b\u0436\w*|payables?|accounts\s+payable)/iu.test(
|
||||
text
|
||||
);
|
||||
const hasOverviewCue =
|
||||
/(?:\u0441\u0440\u0435\u0437|\u043f\u043e\u0437\u0438\u0446\w*|\u0431\u0430\u043b\u0430\u043d\u0441|\u0441\u0430\u043b\u044c\u0434\u043e|\u043d\u0435\u0442\u0442\u043e|\u043d\u0430\s+\u0441\u0435\u0433\u043e\u0434\u043d|\u043d\u0430\s+\u0434\u0430\u0442\u0443|\u0443\s+\u043d\u0430\u0441|\u043f\u043e\s+\u043a\u043e\u043c\u043f\u0430\u043d|\u043a\u043e\u043c\u043f\u0430\u043d|\u0431\u0438\u0437\u043d\u0435\u0441|\u043e\u0431\u0437\u043e\u0440|\u0430\u043d\u0430\u043b\u0438\u0437|as\s+of|overview|balance|net|company|business)/iu.test(
|
||||
text
|
||||
);
|
||||
return hasReceivablesCue && hasPayablesCue && hasOverviewCue;
|
||||
}
|
||||
|
||||
function hasOrganizationLevelInventoryReserveLiquidationOverviewSignal(text: string): boolean {
|
||||
if (!text) {
|
||||
return false;
|
||||
@@ -838,6 +857,7 @@ function hasOrganizationLevelSupplierQualityOverviewSignal(text: string): boolea
|
||||
function hasBusinessOverviewSignal(text: string): boolean {
|
||||
if (
|
||||
hasOrganizationLevelEarningsOverviewSignal(text) ||
|
||||
hasOrganizationLevelDebtPositionOverviewSignal(text) ||
|
||||
hasOrganizationLevelDebtDueDateOverviewSignal(text) ||
|
||||
hasOrganizationLevelInventoryReserveLiquidationOverviewSignal(text) ||
|
||||
hasOrganizationLevelSupplierQualityOverviewSignal(text)
|
||||
@@ -866,7 +886,17 @@ function hasBusinessOverviewContinuationSignal(text: string): boolean {
|
||||
/(?:ндс|vat)[\s\S]{0,120}(?:позици|период|основан|не\s+хватает|налогов\p{L}*\s+вывод)|(?:позици|налогов\p{L}*\s+вывод)[\s\S]{0,80}(?:ндс|vat)/iu.test(
|
||||
normalized
|
||||
);
|
||||
return hasEvidenceContinuationCue || hasAnalystContinuationCue || hasTaxContinuationCue;
|
||||
const hasFinalSummaryCue =
|
||||
/(?:\u0447\u0442\u043e\s+\u043c\u044b\s+\u0437\u043d\u0430\u0435\u043c|\u0447\u0442\u043e\s+\u043f\u043e\u043d\u044f\u0442\u043d\u043e|\u0447\u0442\u043e\s+\u043f\u0440\u043e\u0432\u0435\u0440\w*\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u0441\u043b\u0435\u0434\u0443\u044e\u0449\w*\s+\u0448\u0430\u0433|\u0438\u0442\u043e\u0433\w*\s+\u0432\u044b\u0432\u043e\u0434|\u043a\u0430\u043a\u043e\u0439\s+\u0432\u044b\u0432\u043e\u0434|\u0447\u0442\u043e\s+\u0441\s+\u044d\u0442\u0438\u043c\s+\u0434\u0435\u043b\u0430\u0442\u044c|what\s+do\s+we\s+know|what\s+is\s+missing|next\s+step|final\s+summary)/iu.test(
|
||||
normalized
|
||||
);
|
||||
return hasEvidenceContinuationCue || hasAnalystContinuationCue || hasTaxContinuationCue || hasFinalSummaryCue;
|
||||
}
|
||||
|
||||
function hasExplicitTopicSwitchSignal(text: string): boolean {
|
||||
return /(?:^|\s)(?:\u0442\u0435\u043f\u0435\u0440\u044c|\u0430\s+\u0442\u0435\u043f\u0435\u0440\u044c|\u0434\u0430\u043b\u044c\u0448\u0435|\u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e|\u043f\u0435\u0440\u0435\u0439\u0434[\u0451\u0435]\u043c|\u0441\u043c\u0435\u043d\u0438\u043c\s+\u0442\u0435\u043c\u0443|\u0432\u0435\u0440\u043d[\u0451\u0435]\u043c\u0441\u044f\s+\u043a|now|next|switch\s+to)(?:\s|$)/iu.test(
|
||||
text
|
||||
);
|
||||
}
|
||||
|
||||
function hasBusinessOverviewFollowupSeed(followupSeed: ReturnType<typeof collectFollowupDiscoverySeed>): boolean {
|
||||
@@ -1387,6 +1417,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||
const relativeCurrentDateHintDetected = hasRelativeCurrentDateHint(rawText);
|
||||
const rawDateScope = collectDateScopeFromRawText(dateScopeSignalText);
|
||||
const rawMetadataScopeHint = rawMetadataSignal ? metadataScopeHintFromRawText(rawText) : null;
|
||||
const rawTopicSwitchSignal = hasExplicitTopicSwitchSignal(rawText);
|
||||
const rawEntityCandidate = rawEntityResolutionSignal ? rawEntityResolutionCandidate(rawEntitySourceText) : null;
|
||||
const entityResolutionClarificationCandidate =
|
||||
followupSeed.pilotScope === "entity_resolution_search_v1" &&
|
||||
@@ -1447,9 +1478,26 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||
sameScopedName(followupSeed.counterparty, followupSeed.organization) ||
|
||||
sameScopedName(followupSeed.counterparty, currentTurnOrganizationScope))
|
||||
);
|
||||
const businessOverviewSuppressesFollowupCounterparty = Boolean(
|
||||
businessOverviewSignal &&
|
||||
(rawBusinessOverviewSignal ||
|
||||
businessOverviewContinuationSignal ||
|
||||
broadBusinessEvaluationUnsupported ||
|
||||
rawDomain === "business_summary" ||
|
||||
rawDomain === "business_overview" ||
|
||||
rawAction === "broad_evaluation")
|
||||
);
|
||||
const effectiveFollowupCounterparty = followupCounterpartyIsMetadataOrganizationScope
|
||||
? null
|
||||
: businessOverviewSuppressesFollowupCounterparty
|
||||
? null
|
||||
: followupSeed.counterparty;
|
||||
const rawMetadataScopeOverridesFollowupEntity = Boolean(
|
||||
rawMetadataSignal &&
|
||||
rawMetadataScopeHint &&
|
||||
(effectiveFollowupCounterparty || followupSeed.discoveryEntity) &&
|
||||
!sameScopedName(rawMetadataScopeHint, effectiveFollowupCounterparty ?? followupSeed.discoveryEntity)
|
||||
);
|
||||
const explicitOrganizationScopeSignal = Boolean(rawOrganizationMentionSignal && currentTurnOrganizationScope);
|
||||
const organizationClarificationFollowupApplicable = Boolean(
|
||||
followupSeed.domain === "counterparty_value" &&
|
||||
@@ -1514,6 +1562,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||
const metadataLaneCarryoverAvailable = Boolean(
|
||||
effectiveFollowupCounterparty ||
|
||||
followupSeed.discoveryEntity ||
|
||||
rawMetadataScopeHint ||
|
||||
followupSeed.metadataScopeHint ||
|
||||
followupSeed.metadataSelectedEntitySet ||
|
||||
followupSeed.metadataSelectedSurfaceObjects.length > 0
|
||||
@@ -1852,6 +1901,8 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||
? null
|
||||
: rawEntitySearchOverridesStaleScope
|
||||
? null
|
||||
: rawMetadataScopeOverridesFollowupEntity
|
||||
? null
|
||||
: explicitCurrentCounterpartyOverridesFollowupEntity
|
||||
? null
|
||||
: effectiveFollowupCounterparty ?? followupSeed.discoveryEntity;
|
||||
@@ -1879,8 +1930,10 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||
}
|
||||
pushScopedEntityCandidate(entityCandidates, normalizedPredecomposeCounterparty, groundedFollowupEntity);
|
||||
if (!groundedFollowupEntity) {
|
||||
pushScopedEntityCandidate(entityCandidates, effectiveFollowupCounterparty, null);
|
||||
if (!metadataScopedLaneWithoutSubject) {
|
||||
if (!rawMetadataScopeOverridesFollowupEntity) {
|
||||
pushScopedEntityCandidate(entityCandidates, effectiveFollowupCounterparty, null);
|
||||
}
|
||||
if (!metadataScopedLaneWithoutSubject && !rawMetadataScopeOverridesFollowupEntity) {
|
||||
pushScopedEntityCandidate(entityCandidates, followupSeed.discoveryEntity, null);
|
||||
}
|
||||
}
|
||||
@@ -1891,7 +1944,9 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||
!groundedFollowupEntity &&
|
||||
!metadataScopedLaneWithoutSubject
|
||||
) {
|
||||
pushUnique(entityCandidates, followupSeed.discoveryEntity);
|
||||
if (!rawMetadataScopeOverridesFollowupEntity) {
|
||||
pushUnique(entityCandidates, followupSeed.discoveryEntity);
|
||||
}
|
||||
pushUnique(entityCandidates, rawMetadataScopeHint);
|
||||
}
|
||||
const openScopeValueFlowWithoutCounterparty =
|
||||
@@ -1977,6 +2032,14 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||
(valueFlowOrganizationStaysScope && (Boolean(followupSeed.rankingNeed) || bidirectionalValueFlowSignal)))
|
||||
);
|
||||
const suppressNegatedTaxOnlyDateScope = Boolean(businessOverviewSignal && negatedTaxDateScopeOnlySignal);
|
||||
const topicSwitchSuppressesFollowupScope = Boolean(
|
||||
rawTopicSwitchSignal &&
|
||||
(rawMetadataSignal ||
|
||||
businessOverviewSignal ||
|
||||
rawEntitySearchOverridesStaleScope ||
|
||||
explicitOrganizationScopeSignal ||
|
||||
rawAllTimeScopeSignal)
|
||||
);
|
||||
const normalizedPredecomposeDateScope =
|
||||
(rawEntitySearchOverridesStaleScope && !currentTurnCarriesExplicitPeriod) ||
|
||||
suppressNegatedTaxOnlyDateScope ||
|
||||
@@ -1993,6 +2056,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||
const normalizedFollowupDateScope =
|
||||
rawEntitySearchOverridesStaleScope ||
|
||||
suppressNegatedTaxOnlyDateScope ||
|
||||
topicSwitchSuppressesFollowupScope ||
|
||||
(suppressImplicitCurrentDateScope && isImplicitCurrentDateScope(followupSeed.dateScope))
|
||||
? null
|
||||
: followupSeed.dateScope;
|
||||
@@ -2219,6 +2283,12 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||
if (rawMetadataScopeHint) {
|
||||
pushReason(reasonCodes, "mcp_discovery_metadata_scope_hint_from_raw_text");
|
||||
}
|
||||
if (rawMetadataScopeOverridesFollowupEntity) {
|
||||
pushReason(reasonCodes, "mcp_discovery_raw_metadata_scope_overrides_stale_entity");
|
||||
}
|
||||
if (topicSwitchSuppressesFollowupScope) {
|
||||
pushReason(reasonCodes, "mcp_discovery_topic_switch_suppressed_followup_scope");
|
||||
}
|
||||
if (rawEntityCandidate) {
|
||||
pushReason(reasonCodes, "mcp_discovery_entity_scope_from_raw_entity_search");
|
||||
}
|
||||
@@ -2330,13 +2400,20 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||
if (businessOverviewContinuationSignal) {
|
||||
pushReason(reasonCodes, "mcp_discovery_business_overview_continuation_from_followup_context");
|
||||
}
|
||||
if (businessOverviewSuppressesFollowupCounterparty) {
|
||||
pushReason(reasonCodes, "mcp_discovery_business_overview_suppressed_stale_counterparty");
|
||||
}
|
||||
if (
|
||||
!(valueFlowOrganizationStaysScope && normalizedPredecomposeCounterparty === explicitOrganizationScope) &&
|
||||
normalizedPredecomposeCounterparty
|
||||
) {
|
||||
pushReason(reasonCodes, "mcp_discovery_counterparty_from_predecompose");
|
||||
}
|
||||
if (effectiveFollowupCounterparty && !rawEntitySearchOverridesStaleScope) {
|
||||
if (
|
||||
effectiveFollowupCounterparty &&
|
||||
!rawEntitySearchOverridesStaleScope &&
|
||||
!rawMetadataScopeOverridesFollowupEntity
|
||||
) {
|
||||
pushReason(reasonCodes, "mcp_discovery_counterparty_from_followup_context");
|
||||
}
|
||||
if (followupDateScopeApplied) {
|
||||
|
||||
@@ -36,6 +36,15 @@ describe("addressIntentResolver regression bridges", () => {
|
||||
expect(result.reasons).toContain("unicode_business_overview_earnings_deferred_to_discovery");
|
||||
});
|
||||
|
||||
it("defers paired receivables and payables wording to business overview instead of one debt side", () => {
|
||||
const result = resolveAddressIntent(
|
||||
"\u043f\u043e\u043a\u0430\u0436\u0438 \u0441\u0440\u0435\u0437 \u0434\u0435\u0431\u0438\u0442\u043e\u0440\u043a\u0438 \u0438 \u043a\u0440\u0435\u0434\u0438\u0442\u043e\u0440\u043a\u0438 \u043d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u043f\u043e \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438"
|
||||
);
|
||||
|
||||
expect(result.intent).toBe("unknown");
|
||||
expect(result.reasons).toContain("unicode_business_overview_debt_position_deferred_to_discovery");
|
||||
});
|
||||
|
||||
it("detects specific counterparty turnover wording as revenue profile", () => {
|
||||
const result = resolveAddressIntent(
|
||||
"\u043a\u0430\u043a\u043e\u0439 \u043e\u0431\u043e\u0440\u043e\u0442 \u0431\u044b\u043b \u0441\u0432\u043a"
|
||||
|
||||
@@ -2648,6 +2648,115 @@ describe("assistant MCP discovery turn input adapter", () => {
|
||||
expect(result.reason_codes).not.toContain("mcp_discovery_counterparty_from_followup_context");
|
||||
});
|
||||
|
||||
it("routes paired receivables and payables wording to business overview instead of one debt side", () => {
|
||||
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:
|
||||
"\u041f\u043e\u043a\u0430\u0436\u0438 \u0441\u0440\u0435\u0437 \u0434\u0435\u0431\u0438\u0442\u043e\u0440\u043a\u0438 \u0438 \u043a\u0440\u0435\u0434\u0438\u0442\u043e\u0440\u043a\u0438 \u043d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u043f\u043e \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438",
|
||||
assistantTurnMeaning: {
|
||||
asked_domain_family: "counterparty",
|
||||
asked_action_family: "debt_snapshot",
|
||||
explicit_intent_candidate: "payables_confirmed_as_of_date"
|
||||
},
|
||||
predecomposeContract: {
|
||||
entities: { organization: orgName },
|
||||
period: { as_of_date: "2026-05-05" }
|
||||
}
|
||||
});
|
||||
|
||||
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,
|
||||
unsupported_but_understood_family: "broad_business_evaluation",
|
||||
stale_replay_forbidden: true
|
||||
});
|
||||
});
|
||||
|
||||
it("lets raw metadata scope override stale document evidence subject on topic switch", () => {
|
||||
const orgName =
|
||||
"\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441";
|
||||
const staleCounterparty = "\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a";
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage:
|
||||
"\u0422\u0435\u043f\u0435\u0440\u044c \u043a\u0430\u043a\u0438\u0435 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u044b \u0438 \u043f\u043e\u043b\u044f \u0435\u0441\u0442\u044c \u043f\u043e \u041d\u0414\u0421?",
|
||||
assistantTurnMeaning: {
|
||||
asked_domain_family: "documents",
|
||||
asked_action_family: "list_documents",
|
||||
explicit_intent_candidate: "list_documents_by_counterparty"
|
||||
},
|
||||
followupContext: {
|
||||
previous_discovery_pilot_scope: "counterparty_document_evidence_query_documents_v1",
|
||||
previous_discovery_entity_candidates: [staleCounterparty],
|
||||
previous_filters: {
|
||||
counterparty: staleCounterparty,
|
||||
organization: orgName,
|
||||
period_from: "2020-01-01",
|
||||
period_to: "2020-12-31"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.adapter_status).toBe("ready");
|
||||
expect(result.should_run_discovery).toBe(true);
|
||||
expect(result.semantic_data_need).toBe("1C metadata evidence");
|
||||
expect(result.turn_meaning_ref).toMatchObject({
|
||||
asked_domain_family: "metadata",
|
||||
metadata_scope_hint: "\u041d\u0414\u0421",
|
||||
stale_replay_forbidden: true
|
||||
});
|
||||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toEqual(["\u041d\u0414\u0421"]);
|
||||
expect(result.turn_meaning_ref?.explicit_date_scope).toBeUndefined();
|
||||
expect(result.data_need_graph?.subject_candidates).toEqual(["\u041d\u0414\u0421"]);
|
||||
expect(result.reason_codes).toContain("mcp_discovery_raw_metadata_scope_overrides_stale_entity");
|
||||
expect(result.reason_codes).toContain("mcp_discovery_topic_switch_suppressed_followup_scope");
|
||||
expect(result.reason_codes).not.toContain("mcp_discovery_counterparty_from_followup_context");
|
||||
});
|
||||
|
||||
it("keeps final next-step summary wording in business overview after stale document meaning", () => {
|
||||
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:
|
||||
"\u0427\u0442\u043e \u043c\u044b \u0437\u043d\u0430\u0435\u043c \u043f\u043e \u0438\u0442\u043e\u0433\u0443 \u0438 \u0447\u0442\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0434\u0430\u043b\u044c\u0448\u0435?",
|
||||
assistantTurnMeaning: {
|
||||
asked_domain_family: "documents",
|
||||
asked_action_family: "list_documents",
|
||||
explicit_intent_candidate: "list_documents_by_counterparty",
|
||||
explicit_entity_candidates: ["\u0438\u0442\u043e\u0433\u0443"]
|
||||
},
|
||||
followupContext: {
|
||||
previous_discovery_pilot_scope: "business_overview_route_template_v1",
|
||||
previous_filters: {
|
||||
organization: orgName,
|
||||
period_from: "2020-01-01",
|
||||
period_to: "2020-12-31"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
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.data_need_graph?.subject_candidates).toEqual([]);
|
||||
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.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||||
expect(result.reason_codes).toContain("mcp_discovery_business_overview_continuation_from_followup_context");
|
||||
});
|
||||
|
||||
it("continues business overview on by-these-data profit wording without grounding pseudo anchors", () => {
|
||||
const orgName =
|
||||
"\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441";
|
||||
|
||||
Reference in New Issue
Block a user