ARCH: усилить metadata ambiguity choice sets в lane arbitration

This commit is contained in:
dctouch 2026-04-22 09:45:49 +03:00
parent d9454fcdef
commit c328c52c9b
6 changed files with 282 additions and 17 deletions

View File

@ -228,9 +228,22 @@ function collectFollowupDiscoverySeed(followupContext) {
dateScope, dateScope,
metadataRouteFamily: toNonEmptyString(followupContext?.previous_discovery_metadata_route_family), metadataRouteFamily: toNonEmptyString(followupContext?.previous_discovery_metadata_route_family),
metadataSelectedEntitySet: toNonEmptyString(followupContext?.previous_discovery_metadata_selected_entity_set), metadataSelectedEntitySet: toNonEmptyString(followupContext?.previous_discovery_metadata_selected_entity_set),
metadataAmbiguityDetected: followupContext?.previous_discovery_metadata_ambiguity_detected === true metadataAmbiguityDetected: followupContext?.previous_discovery_metadata_ambiguity_detected === true,
metadataAmbiguityEntitySets: collectEntityCandidates(followupContext?.previous_discovery_metadata_ambiguity_entity_sets)
}; };
} }
function metadataEntitySetsSuggestDocumentLane(values) {
return values.some((value) => /(?:документ|document|invoice|waybill|накладн|счет[- ]?фактур|акт)/iu.test(value));
}
function metadataEntitySetsSuggestMovementLane(values) {
return values.some((value) => /(?:регистр|register|movement|движени|операц|проводк|bank)/iu.test(value));
}
function metadataAmbiguityCollapsesToDocumentLane(values) {
return values.length > 0 && metadataEntitySetsSuggestDocumentLane(values) && !metadataEntitySetsSuggestMovementLane(values);
}
function metadataAmbiguityCollapsesToMovementLane(values) {
return values.length > 0 && metadataEntitySetsSuggestMovementLane(values) && !metadataEntitySetsSuggestDocumentLane(values);
}
function hasLifecycleSignal(text) { function hasLifecycleSignal(text) {
return /(?:сколько\s+лет|как\s+давно|давно\s+ли|возраст|перв(?:ая|ый)\s+актив|когда\s+начал|когда\s+появ|lifecycle|activity\s+duration|business\s+age|how\s+long)/iu.test(text); return /(?:сколько\s+лет|как\s+давно|давно\s+ли|возраст|перв(?:ая|ый)\s+актив|когда\s+начал|когда\s+появ|lifecycle|activity\s+duration|business\s+age|how\s+long)/iu.test(text);
} }
@ -257,10 +270,10 @@ function hasMetadataObjectHint(text) {
return /(?:\u0440\u0435\u0433\u0438\u0441\u0442\u0440(?:\u044b)?|\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442(?:\u044b)?|\u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a(?:\u0438)?|\u043f\u043e\u043b(?:\u0435|\u044f)|registers?|documents?|catalogs?|fields?)/iu.test(text); return /(?:\u0440\u0435\u0433\u0438\u0441\u0442\u0440(?:\u044b)?|\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442(?:\u044b)?|\u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a(?:\u0438)?|\u043f\u043e\u043b(?:\u0435|\u044f)|registers?|documents?|catalogs?|fields?)/iu.test(text);
} }
function hasDocumentEvidenceFollowupSignal(text) { function hasDocumentEvidenceFollowupSignal(text) {
return /(?:\u043f\u043e\s+\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442(?:\u0430\u043c|\u044b)?|\u0434\u0430\u0432\u0430\u0439\s+\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442(?:\u044b)?|\u0438\u0449\u0438\s+\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442(?:\u044b)?|\u043f\u043e\u043a\u0430\u0436\u0438\s+\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442(?:\u044b)?|document(?:s)?\s+(?:then|next)?|(?:then|next)\s+documents?|go\s+to\s+documents?)/iu.test(text); return /(?:\u043f\u043e\s+\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442(?:\u0430\u043c|\u044b)?|\u0434\u0430\u0432\u0430\u0439\s+\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442(?:\u044b)?|\u0438\u0449\u0438\s+\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442(?:\u044b)?|\u043f\u043e\u043a\u0430\u0436\u0438\s+\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442(?:\u044b)?|(?:\u043f\u043e\u043a\u0430\u0436\u0438|\u043a\u0430\u043a\u0438\u0435|\u0441\u043f\u0438\u0441\u043e\u043a|\u0434\u0430\u0439|\u0438\u0449\u0438)\s+(?:\u0441\u0447(?:[еe]т|\u0435\u0442)[-\u2011 ]?\u0444\u0430\u043a\u0442\u0443\u0440(?:\u044b|\u0430)?|\u043d\u0430\u043a\u043b\u0430\u0434\u043d(?:\u044b\u0435|\u0430\u044f)?|\u0430\u043a\u0442(?:\u044b)?|\u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446(?:\u0438\u0438|\u0438\u044e)|invoice(?:s)?|bill(?:s)?|waybill(?:s)?)|document(?:s)?\s+(?:then|next)?|(?:then|next)\s+documents?|go\s+to\s+documents?)/iu.test(text);
} }
function hasMovementEvidenceFollowupSignal(text) { function hasMovementEvidenceFollowupSignal(text) {
return /(?:\u043f\u043e\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f\u043c|\u0438\u044f)?|\u0434\u0430\u0432\u0430\u0439\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f|\u0438\u0435)|\u0438\u0449\u0438\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f|\u0438\u0435)|\u043f\u043e\u043a\u0430\u0436\u0438\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f|\u0438\u0435)|\u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a(?:\u0438\u0435|\u0438\u0439)\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f|\u0438\u0435)|movement(?:s)?\s+(?:then|next)?|(?:then|next)\s+movements?|go\s+to\s+movements?)/iu.test(text); return /(?:\u043f\u043e\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f\u043c|\u0438\u044f)?|\u0434\u0430\u0432\u0430\u0439\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f|\u0438\u0435)|\u0438\u0449\u0438\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f|\u0438\u0435)|\u043f\u043e\u043a\u0430\u0436\u0438\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f|\u0438\u0435)|\u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a(?:\u0438\u0435|\u0438\u0439)\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f|\u0438\u0435)|(?:\u043f\u043e\u043a\u0430\u0436\u0438|\u043a\u0430\u043a\u0438\u0435|\u0441\u043f\u0438\u0441\u043e\u043a|\u0434\u0430\u0439|\u0438\u0449\u0438)\s+(?:\u043f\u043b\u0430\u0442[еe]\u0436(?:\u0438|\u0438)?|\u043e\u043f\u0435\u0440\u0430\u0446(?:\u0438\u0438|\u0438\u044e)|\u043f\u0440\u043e\u0432\u043e\u0434\u043a(?:\u0438|\u0430)|\u0441\u043f\u0438\u0441\u0430\u043d(?:\u0438\u044f|\u0438\u0435)|\u043f\u043e\u0441\u0442\u0443\u043f\u043b\u0435\u043d(?:\u0438\u044f|\u0438\u0435)|payment(?:s)?|transaction(?:s)?|operation(?:s)?|posting(?:s)?|bank\s+operation(?:s)?)|movement(?:s)?\s+(?:then|next)?|(?:then|next)\s+movements?|go\s+to\s+movements?)/iu.test(text);
} }
function hasMetadataDownstreamContinuationSignal(text) { function hasMetadataDownstreamContinuationSignal(text) {
return /(?:\u0434\u0430\u0432\u0430\u0439\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u0438\u0434(?:\u0435|\u0451)\u043c\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u043f\u043e\u0448\u043b(?:\u0438|\u0451\u043c)\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0439|\u0438\u0449\u0438\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u0438\u0449\u0438\s+\u0434\u0430\u043d\u043d\u044b\u0435|\u043f\u043e\u043a\u0430\u0436\u0438\s+\u0434\u0430\u043d\u043d\u044b\u0435|\u043f\u043e\u043a\u0430\u0436\u0438\s+\u0441\u0442\u0440\u043e\u043a\u0438|\u0433\u043b\u0443\u0431\u0436\u0435|\u0447\u0442\u043e\s+\u0434\u0430\u043b\u044c\u0448\u0435|continue|go\s+ahead|go\s+deeper|look\s+deeper|drill\s+down|show\s+(?:data|rows))/iu.test(text); return /(?:\u0434\u0430\u0432\u0430\u0439\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u0438\u0434(?:\u0435|\u0451)\u043c\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u043f\u043e\u0448\u043b(?:\u0438|\u0451\u043c)\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0439|\u0438\u0449\u0438\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u0438\u0449\u0438\s+\u0434\u0430\u043d\u043d\u044b\u0435|\u043f\u043e\u043a\u0430\u0436\u0438\s+\u0434\u0430\u043d\u043d\u044b\u0435|\u043f\u043e\u043a\u0430\u0436\u0438\s+\u0441\u0442\u0440\u043e\u043a\u0438|\u0433\u043b\u0443\u0431\u0436\u0435|\u0447\u0442\u043e\s+\u0434\u0430\u043b\u044c\u0448\u0435|continue|go\s+ahead|go\s+deeper|look\s+deeper|drill\s+down|show\s+(?:data|rows))/iu.test(text);
@ -351,6 +364,8 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
const monthlyAggregationSignal = hasMonthlyAggregationSignal(rawText); const monthlyAggregationSignal = hasMonthlyAggregationSignal(rawText);
const explicitDateScopeLiteralDetected = hasExplicitDateScopeLiteral(rawText); const explicitDateScopeLiteralDetected = hasExplicitDateScopeLiteral(rawText);
const rawDateScope = collectDateScopeFromRawText(rawText); const rawDateScope = collectDateScopeFromRawText(rawText);
const metadataDocumentHintSignal = hasDocumentEvidenceFollowupSignal(rawText);
const metadataMovementHintSignal = hasMovementEvidenceFollowupSignal(rawText);
const rawDomain = toNonEmptyString(assistantTurnMeaning?.asked_domain_family); const rawDomain = toNonEmptyString(assistantTurnMeaning?.asked_domain_family);
const rawAction = toNonEmptyString(assistantTurnMeaning?.asked_action_family); const rawAction = toNonEmptyString(assistantTurnMeaning?.asked_action_family);
const rawAggregationAxis = toNonEmptyString(assistantTurnMeaning?.asked_aggregation_axis); const rawAggregationAxis = toNonEmptyString(assistantTurnMeaning?.asked_aggregation_axis);
@ -373,26 +388,28 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
followupSeed.counterparty && followupSeed.counterparty &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
hasDocumentEvidenceFollowupSignal(rawText)); metadataDocumentHintSignal);
const metadataAmbiguityResolvedDocumentFollowupApplicable = Boolean(followupSeed.pilotScope === "metadata_inspection_v1" && const metadataAmbiguityResolvedDocumentFollowupApplicable = Boolean(followupSeed.pilotScope === "metadata_inspection_v1" &&
followupSeed.metadataAmbiguityDetected && followupSeed.metadataAmbiguityDetected &&
(followupSeed.metadataAmbiguityEntitySets.length === 0 ||
metadataEntitySetsSuggestDocumentLane(followupSeed.metadataAmbiguityEntitySets)) &&
followupSeed.counterparty && followupSeed.counterparty &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
hasDocumentEvidenceFollowupSignal(rawText)); metadataDocumentHintSignal);
const metadataGroundedMovementFollowupApplicable = Boolean(followupSeed.pilotScope === "metadata_inspection_v1" && const metadataGroundedMovementFollowupApplicable = Boolean(followupSeed.pilotScope === "metadata_inspection_v1" &&
followupSeed.metadataRouteFamily === "movement_evidence" && followupSeed.metadataRouteFamily === "movement_evidence" &&
!followupSeed.metadataAmbiguityDetected && !followupSeed.metadataAmbiguityDetected &&
followupSeed.counterparty && followupSeed.counterparty &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && metadataMovementHintSignal);
hasMovementEvidenceFollowupSignal(rawText));
const metadataAmbiguityResolvedMovementFollowupApplicable = Boolean(followupSeed.pilotScope === "metadata_inspection_v1" && const metadataAmbiguityResolvedMovementFollowupApplicable = Boolean(followupSeed.pilotScope === "metadata_inspection_v1" &&
followupSeed.metadataAmbiguityDetected && followupSeed.metadataAmbiguityDetected &&
(followupSeed.metadataAmbiguityEntitySets.length === 0 ||
metadataEntitySetsSuggestMovementLane(followupSeed.metadataAmbiguityEntitySets)) &&
followupSeed.counterparty && followupSeed.counterparty &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && metadataMovementHintSignal);
hasMovementEvidenceFollowupSignal(rawText));
const metadataGroundedLaneContinuationApplicable = Boolean(followupSeed.pilotScope === "metadata_inspection_v1" && const metadataGroundedLaneContinuationApplicable = Boolean(followupSeed.pilotScope === "metadata_inspection_v1" &&
(followupSeed.metadataRouteFamily === "document_evidence" || (followupSeed.metadataRouteFamily === "document_evidence" ||
followupSeed.metadataRouteFamily === "movement_evidence") && followupSeed.metadataRouteFamily === "movement_evidence") &&
@ -401,15 +418,37 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
!rawMetadataSignal && !rawMetadataSignal &&
!hasDocumentEvidenceFollowupSignal(rawText) && !metadataDocumentHintSignal &&
!hasMovementEvidenceFollowupSignal(rawText) && !metadataMovementHintSignal &&
hasMetadataDownstreamContinuationSignal(rawText));
const metadataAmbiguityCollapsedDocumentLaneContinuationApplicable = Boolean(followupSeed.pilotScope === "metadata_inspection_v1" &&
followupSeed.metadataAmbiguityDetected &&
metadataAmbiguityCollapsesToDocumentLane(followupSeed.metadataAmbiguityEntitySets) &&
followupSeed.counterparty &&
!rawLifecycleSignal &&
!rawValueFlowSignal &&
!rawMetadataSignal &&
!metadataDocumentHintSignal &&
!metadataMovementHintSignal &&
hasMetadataDownstreamContinuationSignal(rawText));
const metadataAmbiguityCollapsedMovementLaneContinuationApplicable = Boolean(followupSeed.pilotScope === "metadata_inspection_v1" &&
followupSeed.metadataAmbiguityDetected &&
metadataAmbiguityCollapsesToMovementLane(followupSeed.metadataAmbiguityEntitySets) &&
followupSeed.counterparty &&
!rawLifecycleSignal &&
!rawValueFlowSignal &&
!rawMetadataSignal &&
!metadataDocumentHintSignal &&
!metadataMovementHintSignal &&
hasMetadataDownstreamContinuationSignal(rawText)); hasMetadataDownstreamContinuationSignal(rawText));
const metadataGroundedDocumentLaneApplicable = metadataGroundedDocumentFollowupApplicable || const metadataGroundedDocumentLaneApplicable = metadataGroundedDocumentFollowupApplicable ||
metadataAmbiguityResolvedDocumentFollowupApplicable || metadataAmbiguityResolvedDocumentFollowupApplicable ||
(metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "document_evidence"); (metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "document_evidence") ||
metadataAmbiguityCollapsedDocumentLaneContinuationApplicable;
const metadataGroundedMovementLaneApplicable = metadataGroundedMovementFollowupApplicable || const metadataGroundedMovementLaneApplicable = metadataGroundedMovementFollowupApplicable ||
metadataAmbiguityResolvedMovementFollowupApplicable || metadataAmbiguityResolvedMovementFollowupApplicable ||
(metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "movement_evidence"); (metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "movement_evidence") ||
metadataAmbiguityCollapsedMovementLaneContinuationApplicable;
const effectiveMetadataFollowupSeedApplicable = metadataFollowupSeedApplicable && const effectiveMetadataFollowupSeedApplicable = metadataFollowupSeedApplicable &&
!metadataGroundedDocumentLaneApplicable && !metadataGroundedDocumentLaneApplicable &&
!metadataGroundedMovementLaneApplicable; !metadataGroundedMovementLaneApplicable;
@ -437,7 +476,9 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
const lifecycleSignal = rawLifecycleSignal || seededDomain === "counterparty_lifecycle"; const lifecycleSignal = rawLifecycleSignal || seededDomain === "counterparty_lifecycle";
const bidirectionalValueFlowSignal = !lifecycleSignal && const bidirectionalValueFlowSignal = !lifecycleSignal &&
(rawBidirectionalValueFlowSignal || seededAction === "net_value_flow"); (rawBidirectionalValueFlowSignal || seededAction === "net_value_flow");
const valueFlowSignal = !lifecycleSignal && (rawValueFlowSignal || seededDomain === "counterparty_value"); const valueFlowSignal = !lifecycleSignal &&
!metadataGroundedMovementLaneApplicable &&
(rawValueFlowSignal || seededDomain === "counterparty_value");
const payoutSignal = valueFlowSignal && const payoutSignal = valueFlowSignal &&
!bidirectionalValueFlowSignal && !bidirectionalValueFlowSignal &&
(rawPayoutSignal || seededAction === "payout"); (rawPayoutSignal || seededAction === "payout");
@ -616,6 +657,12 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
if (metadataGroundedLaneContinuationApplicable) { if (metadataGroundedLaneContinuationApplicable) {
pushReason(reasonCodes, "mcp_discovery_metadata_grounded_lane_continuation"); pushReason(reasonCodes, "mcp_discovery_metadata_grounded_lane_continuation");
} }
if (metadataAmbiguityCollapsedDocumentLaneContinuationApplicable) {
pushReason(reasonCodes, "mcp_discovery_metadata_ambiguity_collapsed_to_document_lane");
}
if (metadataAmbiguityCollapsedMovementLaneContinuationApplicable) {
pushReason(reasonCodes, "mcp_discovery_metadata_ambiguity_collapsed_to_movement_lane");
}
if (unsupported) { if (unsupported) {
pushReason(reasonCodes, "mcp_discovery_unsupported_but_understood_turn"); pushReason(reasonCodes, "mcp_discovery_unsupported_but_understood_turn");
} }

View File

@ -192,6 +192,17 @@ export function readAssistantMcpDiscoveryMetadataAmbiguityDetected(
return readAssistantMcpDiscoveryDerivedMetadataSurface(debug)?.ambiguity_detected === true; return readAssistantMcpDiscoveryDerivedMetadataSurface(debug)?.ambiguity_detected === true;
} }
export function readAssistantMcpDiscoveryMetadataAmbiguityEntitySets(
debug: Record<string, unknown> | null,
toNonEmptyString: (value: unknown) => string | null = fallbackToNonEmptyString
): string[] {
const values = readAssistantMcpDiscoveryDerivedMetadataSurface(debug)?.ambiguity_entity_sets;
if (!Array.isArray(values)) {
return [];
}
return values.map((item) => toNonEmptyString(item)).filter((item): item is string => Boolean(item));
}
function mapAssistantMcpDiscoveryPilotScopeToAddressIntent( function mapAssistantMcpDiscoveryPilotScopeToAddressIntent(
pilotScope: string | null, pilotScope: string | null,
actionFamily: string | null actionFamily: string | null

View File

@ -265,6 +265,7 @@ function collectFollowupDiscoverySeed(followupContext: Record<string, unknown> |
metadataRouteFamily: string | null; metadataRouteFamily: string | null;
metadataSelectedEntitySet: string | null; metadataSelectedEntitySet: string | null;
metadataAmbiguityDetected: boolean; metadataAmbiguityDetected: boolean;
metadataAmbiguityEntitySets: string[];
} { } {
const previousFilters = toRecordObject(followupContext?.previous_filters); const previousFilters = toRecordObject(followupContext?.previous_filters);
const rootFilters = toRecordObject(followupContext?.root_filters); const rootFilters = toRecordObject(followupContext?.root_filters);
@ -302,10 +303,29 @@ function collectFollowupDiscoverySeed(followupContext: Record<string, unknown> |
dateScope, dateScope,
metadataRouteFamily: toNonEmptyString(followupContext?.previous_discovery_metadata_route_family), metadataRouteFamily: toNonEmptyString(followupContext?.previous_discovery_metadata_route_family),
metadataSelectedEntitySet: toNonEmptyString(followupContext?.previous_discovery_metadata_selected_entity_set), metadataSelectedEntitySet: toNonEmptyString(followupContext?.previous_discovery_metadata_selected_entity_set),
metadataAmbiguityDetected: followupContext?.previous_discovery_metadata_ambiguity_detected === true metadataAmbiguityDetected: followupContext?.previous_discovery_metadata_ambiguity_detected === true,
metadataAmbiguityEntitySets: collectEntityCandidates(followupContext?.previous_discovery_metadata_ambiguity_entity_sets)
}; };
} }
function metadataEntitySetsSuggestDocumentLane(values: string[]): boolean {
return values.some((value) => /(?:документ|document|invoice|waybill|накладн|счет[- ]?фактур|акт)/iu.test(value));
}
function metadataEntitySetsSuggestMovementLane(values: string[]): boolean {
return values.some((value) =>
/(?:регистр|register|movement|движени|операц|проводк|bank)/iu.test(value)
);
}
function metadataAmbiguityCollapsesToDocumentLane(values: string[]): boolean {
return values.length > 0 && metadataEntitySetsSuggestDocumentLane(values) && !metadataEntitySetsSuggestMovementLane(values);
}
function metadataAmbiguityCollapsesToMovementLane(values: string[]): boolean {
return values.length > 0 && metadataEntitySetsSuggestMovementLane(values) && !metadataEntitySetsSuggestDocumentLane(values);
}
function hasLifecycleSignal(text: string): boolean { function hasLifecycleSignal(text: string): boolean {
return /(?:сколько\s+лет|как\s+давно|давно\s+ли|возраст|перв(?:ая|ый)\s+актив|когда\s+начал|когда\s+появ|lifecycle|activity\s+duration|business\s+age|how\s+long)/iu.test( return /(?:сколько\s+лет|как\s+давно|давно\s+ли|возраст|перв(?:ая|ый)\s+актив|когда\s+начал|когда\s+появ|lifecycle|activity\s+duration|business\s+age|how\s+long)/iu.test(
text text
@ -520,6 +540,8 @@ export function buildAssistantMcpDiscoveryTurnInput(
const metadataAmbiguityResolvedDocumentFollowupApplicable = Boolean( const metadataAmbiguityResolvedDocumentFollowupApplicable = Boolean(
followupSeed.pilotScope === "metadata_inspection_v1" && followupSeed.pilotScope === "metadata_inspection_v1" &&
followupSeed.metadataAmbiguityDetected && followupSeed.metadataAmbiguityDetected &&
(followupSeed.metadataAmbiguityEntitySets.length === 0 ||
metadataEntitySetsSuggestDocumentLane(followupSeed.metadataAmbiguityEntitySets)) &&
followupSeed.counterparty && followupSeed.counterparty &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
@ -536,6 +558,8 @@ export function buildAssistantMcpDiscoveryTurnInput(
const metadataAmbiguityResolvedMovementFollowupApplicable = Boolean( const metadataAmbiguityResolvedMovementFollowupApplicable = Boolean(
followupSeed.pilotScope === "metadata_inspection_v1" && followupSeed.pilotScope === "metadata_inspection_v1" &&
followupSeed.metadataAmbiguityDetected && followupSeed.metadataAmbiguityDetected &&
(followupSeed.metadataAmbiguityEntitySets.length === 0 ||
metadataEntitySetsSuggestMovementLane(followupSeed.metadataAmbiguityEntitySets)) &&
followupSeed.counterparty && followupSeed.counterparty &&
!rawLifecycleSignal && !rawLifecycleSignal &&
metadataMovementHintSignal metadataMovementHintSignal
@ -553,14 +577,40 @@ export function buildAssistantMcpDiscoveryTurnInput(
!metadataMovementHintSignal && !metadataMovementHintSignal &&
hasMetadataDownstreamContinuationSignal(rawText) hasMetadataDownstreamContinuationSignal(rawText)
); );
const metadataAmbiguityCollapsedDocumentLaneContinuationApplicable = Boolean(
followupSeed.pilotScope === "metadata_inspection_v1" &&
followupSeed.metadataAmbiguityDetected &&
metadataAmbiguityCollapsesToDocumentLane(followupSeed.metadataAmbiguityEntitySets) &&
followupSeed.counterparty &&
!rawLifecycleSignal &&
!rawValueFlowSignal &&
!rawMetadataSignal &&
!metadataDocumentHintSignal &&
!metadataMovementHintSignal &&
hasMetadataDownstreamContinuationSignal(rawText)
);
const metadataAmbiguityCollapsedMovementLaneContinuationApplicable = Boolean(
followupSeed.pilotScope === "metadata_inspection_v1" &&
followupSeed.metadataAmbiguityDetected &&
metadataAmbiguityCollapsesToMovementLane(followupSeed.metadataAmbiguityEntitySets) &&
followupSeed.counterparty &&
!rawLifecycleSignal &&
!rawValueFlowSignal &&
!rawMetadataSignal &&
!metadataDocumentHintSignal &&
!metadataMovementHintSignal &&
hasMetadataDownstreamContinuationSignal(rawText)
);
const metadataGroundedDocumentLaneApplicable = const metadataGroundedDocumentLaneApplicable =
metadataGroundedDocumentFollowupApplicable || metadataGroundedDocumentFollowupApplicable ||
metadataAmbiguityResolvedDocumentFollowupApplicable || metadataAmbiguityResolvedDocumentFollowupApplicable ||
(metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "document_evidence"); (metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "document_evidence") ||
metadataAmbiguityCollapsedDocumentLaneContinuationApplicable;
const metadataGroundedMovementLaneApplicable = const metadataGroundedMovementLaneApplicable =
metadataGroundedMovementFollowupApplicable || metadataGroundedMovementFollowupApplicable ||
metadataAmbiguityResolvedMovementFollowupApplicable || metadataAmbiguityResolvedMovementFollowupApplicable ||
(metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "movement_evidence"); (metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "movement_evidence") ||
metadataAmbiguityCollapsedMovementLaneContinuationApplicable;
const effectiveMetadataFollowupSeedApplicable = const effectiveMetadataFollowupSeedApplicable =
metadataFollowupSeedApplicable && metadataFollowupSeedApplicable &&
!metadataGroundedDocumentLaneApplicable && !metadataGroundedDocumentLaneApplicable &&
@ -784,6 +834,12 @@ export function buildAssistantMcpDiscoveryTurnInput(
if (metadataGroundedLaneContinuationApplicable) { if (metadataGroundedLaneContinuationApplicable) {
pushReason(reasonCodes, "mcp_discovery_metadata_grounded_lane_continuation"); pushReason(reasonCodes, "mcp_discovery_metadata_grounded_lane_continuation");
} }
if (metadataAmbiguityCollapsedDocumentLaneContinuationApplicable) {
pushReason(reasonCodes, "mcp_discovery_metadata_ambiguity_collapsed_to_document_lane");
}
if (metadataAmbiguityCollapsedMovementLaneContinuationApplicable) {
pushReason(reasonCodes, "mcp_discovery_metadata_ambiguity_collapsed_to_movement_lane");
}
if (unsupported) { if (unsupported) {
pushReason(reasonCodes, "mcp_discovery_unsupported_but_understood_turn"); pushReason(reasonCodes, "mcp_discovery_unsupported_but_understood_turn");
} }

View File

@ -11,6 +11,7 @@ import {
readAddressDebugFilters, readAddressDebugFilters,
readAddressDebugItem, readAddressDebugItem,
readAssistantMcpDiscoveryMetadataAmbiguityDetected, readAssistantMcpDiscoveryMetadataAmbiguityDetected,
readAssistantMcpDiscoveryMetadataAmbiguityEntitySets,
readAssistantMcpDiscoveryMetadataRouteFamily, readAssistantMcpDiscoveryMetadataRouteFamily,
readAssistantMcpDiscoveryMetadataSelectedEntitySet, readAssistantMcpDiscoveryMetadataSelectedEntitySet,
readAddressDebugTemporalScope, readAddressDebugTemporalScope,
@ -621,6 +622,10 @@ export function createAssistantTransitionPolicy(deps) {
const sourceDiscoveryMetadataAmbiguityDetected = readAssistantMcpDiscoveryMetadataAmbiguityDetected( const sourceDiscoveryMetadataAmbiguityDetected = readAssistantMcpDiscoveryMetadataAmbiguityDetected(
carryoverSourceDebug carryoverSourceDebug
); );
const sourceDiscoveryMetadataAmbiguityEntitySets = readAssistantMcpDiscoveryMetadataAmbiguityEntitySets(
carryoverSourceDebug,
deps.toNonEmptyString
);
const llmExplicitIntent = deps.toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent); const llmExplicitIntent = deps.toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
const llmSelectedObjectScopeDetected = const llmSelectedObjectScopeDetected =
llmPreDecomposeMeta?.predecomposeContract?.semantics?.selected_object_scope_detected === true; llmPreDecomposeMeta?.predecomposeContract?.semantics?.selected_object_scope_detected === true;
@ -956,6 +961,8 @@ export function createAssistantTransitionPolicy(deps) {
previous_discovery_metadata_route_family: sourceDiscoveryMetadataRouteFamily ?? undefined, previous_discovery_metadata_route_family: sourceDiscoveryMetadataRouteFamily ?? undefined,
previous_discovery_metadata_selected_entity_set: sourceDiscoveryMetadataSelectedEntitySet ?? undefined, previous_discovery_metadata_selected_entity_set: sourceDiscoveryMetadataSelectedEntitySet ?? undefined,
previous_discovery_metadata_ambiguity_detected: sourceDiscoveryMetadataAmbiguityDetected || undefined, previous_discovery_metadata_ambiguity_detected: sourceDiscoveryMetadataAmbiguityDetected || undefined,
previous_discovery_metadata_ambiguity_entity_sets:
sourceDiscoveryMetadataAmbiguityEntitySets.length > 0 ? sourceDiscoveryMetadataAmbiguityEntitySets : undefined,
resolved_counterparty_from_display: resolvedCounterpartyFromDisplay || undefined, resolved_counterparty_from_display: resolvedCounterpartyFromDisplay || undefined,
root_context_only: rootScopedPivot || undefined, root_context_only: rootScopedPivot || undefined,
root_intent: shouldAttachInventoryRootFrame ? inventoryRootFrame?.intent ?? undefined : undefined, root_intent: shouldAttachInventoryRootFrame ? inventoryRootFrame?.intent ?? undefined : undefined,

View File

@ -367,6 +367,7 @@ describe("assistant MCP discovery turn input adapter", () => {
followupContext: { followupContext: {
previous_discovery_pilot_scope: "metadata_inspection_v1", previous_discovery_pilot_scope: "metadata_inspection_v1",
previous_discovery_metadata_ambiguity_detected: true, previous_discovery_metadata_ambiguity_detected: true,
previous_discovery_metadata_ambiguity_entity_sets: ["Документ", "РегистрНакопления"],
previous_filters: { previous_filters: {
counterparty: "SVK", counterparty: "SVK",
period_from: "2020-01-01", period_from: "2020-01-01",
@ -398,6 +399,7 @@ describe("assistant MCP discovery turn input adapter", () => {
followupContext: { followupContext: {
previous_discovery_pilot_scope: "metadata_inspection_v1", previous_discovery_pilot_scope: "metadata_inspection_v1",
previous_discovery_metadata_ambiguity_detected: true, previous_discovery_metadata_ambiguity_detected: true,
previous_discovery_metadata_ambiguity_entity_sets: ["Документ", "РегистрНакопления"],
previous_filters: { previous_filters: {
counterparty: "SVK", counterparty: "SVK",
period_from: "2020-01-01", period_from: "2020-01-01",
@ -429,6 +431,7 @@ describe("assistant MCP discovery turn input adapter", () => {
followupContext: { followupContext: {
previous_discovery_pilot_scope: "metadata_inspection_v1", previous_discovery_pilot_scope: "metadata_inspection_v1",
previous_discovery_metadata_ambiguity_detected: true, previous_discovery_metadata_ambiguity_detected: true,
previous_discovery_metadata_ambiguity_entity_sets: ["Документ", "РегистрНакопления"],
previous_filters: { previous_filters: {
counterparty: "SVK", counterparty: "SVK",
period_from: "2020-01-01", period_from: "2020-01-01",
@ -459,6 +462,7 @@ describe("assistant MCP discovery turn input adapter", () => {
followupContext: { followupContext: {
previous_discovery_pilot_scope: "metadata_inspection_v1", previous_discovery_pilot_scope: "metadata_inspection_v1",
previous_discovery_metadata_ambiguity_detected: true, previous_discovery_metadata_ambiguity_detected: true,
previous_discovery_metadata_ambiguity_entity_sets: ["Документ", "РегистрНакопления"],
previous_filters: { previous_filters: {
counterparty: "SVK", counterparty: "SVK",
period_from: "2020-01-01", period_from: "2020-01-01",
@ -484,6 +488,88 @@ describe("assistant MCP discovery turn input adapter", () => {
expect(result.reason_codes).not.toContain("mcp_discovery_value_flow_signal_detected"); expect(result.reason_codes).not.toContain("mcp_discovery_value_flow_signal_detected");
}); });
it("does not resolve metadata ambiguity into movement lane when confirmed ambiguity sets contain documents only", () => {
const result = buildAssistantMcpDiscoveryTurnInput({
userMessage: "по движениям",
followupContext: {
previous_discovery_pilot_scope: "metadata_inspection_v1",
previous_discovery_metadata_ambiguity_detected: true,
previous_discovery_metadata_ambiguity_entity_sets: ["Документ"],
previous_filters: {
counterparty: "SVK",
period_from: "2020-01-01",
period_to: "2020-12-31"
},
previous_anchor_type: "counterparty",
previous_anchor_value: "SVK"
}
});
expect(result.reason_codes).not.toContain("mcp_discovery_metadata_ambiguity_resolved_to_movement_lane");
});
it("continues from ambiguous metadata into document lane when ambiguity choice set collapses to documents on a generic downstream follow-up", () => {
const result = buildAssistantMcpDiscoveryTurnInput({
userMessage: "continue with data",
followupContext: {
previous_discovery_pilot_scope: "metadata_inspection_v1",
previous_discovery_metadata_ambiguity_detected: true,
previous_discovery_metadata_ambiguity_entity_sets: ["Документ", "invoice"],
previous_filters: {
counterparty: "SVK",
period_from: "2020-01-01",
period_to: "2020-12-31"
},
previous_anchor_type: "counterparty",
previous_anchor_value: "SVK"
}
});
expect(result.adapter_status).toBe("ready");
expect(result.should_run_discovery).toBe(true);
expect(result.semantic_data_need).toBe("document evidence");
expect(result.turn_meaning_ref).toMatchObject({
asked_domain_family: "documents",
asked_action_family: "list_documents",
explicit_entity_candidates: ["SVK"],
explicit_date_scope: "2020",
unsupported_but_understood_family: "document_evidence",
stale_replay_forbidden: true
});
expect(result.reason_codes).toContain("mcp_discovery_metadata_ambiguity_collapsed_to_document_lane");
});
it("continues from ambiguous metadata into movement lane when ambiguity choice set collapses to movements on a generic downstream follow-up", () => {
const result = buildAssistantMcpDiscoveryTurnInput({
userMessage: "continue with data",
followupContext: {
previous_discovery_pilot_scope: "metadata_inspection_v1",
previous_discovery_metadata_ambiguity_detected: true,
previous_discovery_metadata_ambiguity_entity_sets: ["РегистрНакопления", "movement"],
previous_filters: {
counterparty: "SVK",
period_from: "2020-01-01",
period_to: "2020-12-31"
},
previous_anchor_type: "counterparty",
previous_anchor_value: "SVK"
}
});
expect(result.adapter_status).toBe("ready");
expect(result.should_run_discovery).toBe(true);
expect(result.semantic_data_need).toBe("movement evidence");
expect(result.turn_meaning_ref).toMatchObject({
asked_domain_family: "movements",
asked_action_family: "list_movements",
explicit_entity_candidates: ["SVK"],
explicit_date_scope: "2020",
unsupported_but_understood_family: "movement_evidence",
stale_replay_forbidden: true
});
expect(result.reason_codes).toContain("mcp_discovery_metadata_ambiguity_collapsed_to_movement_lane");
});
it("switches the checked year on a short payout follow-up while keeping prior discovery counterparty", () => { it("switches the checked year on a short payout follow-up while keeping prior discovery counterparty", () => {
const result = buildAssistantMcpDiscoveryTurnInput({ const result = buildAssistantMcpDiscoveryTurnInput({
userMessage: "а теперь за 2021?", userMessage: "а теперь за 2021?",

View File

@ -1169,6 +1169,64 @@ describe("assistantTransitionPolicy", () => {
expect(carryover?.followupContext?.previous_discovery_metadata_selected_entity_set).toBe("Документ"); expect(carryover?.followupContext?.previous_discovery_metadata_selected_entity_set).toBe("Документ");
expect(carryover?.followupContext?.previous_discovery_metadata_ambiguity_detected).toBeUndefined(); expect(carryover?.followupContext?.previous_discovery_metadata_ambiguity_detected).toBeUndefined();
}); });
it("carries metadata ambiguity entity sets into follow-up context for downstream lane arbitration", () => {
const policy = buildPolicy({
findLastAddressAssistantItem: () => ({
text: "metadata ambiguity",
debug: {
execution_lane: "living_chat",
mcp_discovery_response_applied: true,
assistant_mcp_discovery_entry_point_v1: {
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
entry_status: "bridge_executed",
turn_input: {
turn_meaning_ref: {
asked_domain_family: "metadata",
asked_action_family: "inspect_documents",
explicit_entity_candidates: ["SVK"],
explicit_date_scope: "2020"
}
},
bridge: {
bridge_status: "answer_draft_ready",
business_fact_answer_allowed: true,
pilot: {
pilot_scope: "metadata_inspection_v1",
derived_metadata_surface: {
selected_entity_set: null,
downstream_route_family: null,
ambiguity_detected: true,
ambiguity_entity_sets: ["Документ", "РегистрНакопления"]
}
},
answer_draft: {
answer_mode: "confirmed_with_bounded_inference"
}
}
}
}
}),
hasAddressFollowupContextSignal: () => true,
hasReferentialPointer: () => false,
resolveAddressIntent: () => ({ intent: "unknown" }),
resolveAddressIntentFamily: () => null,
resolveAssistantTurnMeaning: () => null
});
const carryover = policy.resolveAddressFollowupCarryoverContext(
"по документам",
[{ kind: "assistant", text: "metadata ambiguity" }],
"по документам",
{ predecomposeContract: { intent: "unknown" } },
null
);
expect(carryover?.followupContext?.previous_discovery_metadata_ambiguity_detected).toBe(true);
expect(carryover?.followupContext?.previous_discovery_metadata_ambiguity_entity_sets).toEqual([
"Документ",
"РегистрНакопления"
]);
});
it("switches to VAT tax-period intent while preserving carried period filters", () => { it("switches to VAT tax-period intent while preserving carried period filters", () => {
const policy = buildPolicy({ const policy = buildPolicy({
findLastAddressAssistantItem: () => ({ findLastAddressAssistantItem: () => ({