Semantic Gate: закрепить доминирование intent и reset frame

This commit is contained in:
2026-05-05 16:02:11 +03:00
parent d797b3fa99
commit 2ec2f81dd8
11 changed files with 339 additions and 30 deletions
@@ -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) {