ARCH: сохранять metadata ambiguity через clarification follow-up

This commit is contained in:
2026-04-22 10:05:03 +03:00
parent 40cf71d118
commit 007369a78a
9 changed files with 179 additions and 39 deletions
@@ -73,6 +73,10 @@ function isMovementPilot(pilot) {
function isMetadataPilot(pilot) {
return pilot.pilot_scope === "metadata_inspection_v1";
}
function isMetadataLaneChoiceClarification(pilot) {
return (pilot.reason_codes.includes("planner_selected_metadata_lane_clarification_recipe") ||
pilot.dry_run.reason_codes.includes("planner_selected_metadata_lane_clarification_recipe"));
}
function metadataRouteFamilyLabelRu(routeFamily) {
if (routeFamily === "document_evidence") {
return "контур документов";
@@ -127,6 +131,12 @@ function headlineFor(mode, pilot) {
if (mode === "bounded_inference_only") {
return "Точный факт не подтвержден, но есть ограниченная оценка по найденной активности в 1С.";
}
if (mode === "needs_clarification" && isMetadataLaneChoiceClarification(pilot)) {
return "По подтвержденной metadata-поверхности видно несколько конкурирующих data-lane, и без явного выбора дальше идти нельзя.";
}
if (mode === "needs_clarification" && isMetadataLaneChoiceClarification(pilot)) {
return "Уточните, в какой контур идти дальше: по документам или по движениям/регистрам.";
}
if (mode === "needs_clarification") {
return "Нужно уточнить контекст перед поиском в 1С.";
}
@@ -136,6 +146,9 @@ function headlineFor(mode, pilot) {
return "Я проверил доступный контур, но подтвержденного факта для ответа не получил.";
}
function nextStepFor(mode, pilot) {
if (mode === "needs_clarification" && isMetadataLaneChoiceClarification(pilot)) {
return "Уточните, в какой контур идти дальше: по документам или по движениям/регистрам.";
}
if (mode === "needs_clarification") {
return "Уточните контрагента, период или организацию, и я смогу выполнить проверку по 1С.";
}
@@ -82,6 +82,15 @@ function recipeFor(input) {
const axes = [];
const requestedAggregationAxis = aggregationAxis(meaning);
addScopeAxes(axes, meaning);
if (includesAny(combined, ["metadata_lane_choice_clarification", "resolve_next_lane"])) {
pushUnique(axes, "lane_family_choice");
return {
semanticDataNeed: "metadata lane clarification",
primitives: [],
axes,
reason: "planner_selected_metadata_lane_clarification_recipe"
};
}
if (includesAny(combined, ["turnover", "revenue", "payment", "payout", "value", "net", "netting", "balance", "cashflow"])) {
pushUnique(axes, "aggregate_axis");
pushUnique(axes, "amount");
@@ -441,6 +441,17 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
!metadataDocumentHintSignal &&
!metadataMovementHintSignal &&
hasMetadataDownstreamContinuationSignal(rawText));
const metadataAmbiguityLaneClarificationApplicable = Boolean(followupSeed.pilotScope === "metadata_inspection_v1" &&
followupSeed.metadataAmbiguityDetected &&
!metadataAmbiguityCollapsesToDocumentLane(followupSeed.metadataAmbiguityEntitySets) &&
!metadataAmbiguityCollapsesToMovementLane(followupSeed.metadataAmbiguityEntitySets) &&
followupSeed.counterparty &&
!rawLifecycleSignal &&
!rawValueFlowSignal &&
!rawMetadataSignal &&
!metadataDocumentHintSignal &&
!metadataMovementHintSignal &&
hasMetadataDownstreamContinuationSignal(rawText));
const metadataGroundedDocumentLaneApplicable = metadataGroundedDocumentFollowupApplicable ||
metadataAmbiguityResolvedDocumentFollowupApplicable ||
(metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "document_evidence") ||
@@ -450,29 +461,36 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
(metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "movement_evidence") ||
metadataAmbiguityCollapsedMovementLaneContinuationApplicable;
const effectiveMetadataFollowupSeedApplicable = metadataFollowupSeedApplicable &&
!metadataAmbiguityLaneClarificationApplicable &&
!metadataGroundedDocumentLaneApplicable &&
!metadataGroundedMovementLaneApplicable;
const seededDomain = metadataGroundedDocumentLaneApplicable
? "documents"
: metadataGroundedMovementLaneApplicable
? "movements"
: followupDiscoverySeedApplicable || effectiveMetadataFollowupSeedApplicable
? followupSeed.domain
: null;
const seededAction = metadataGroundedDocumentLaneApplicable
? "list_documents"
: metadataGroundedMovementLaneApplicable
? "list_movements"
: followupDiscoverySeedApplicable || effectiveMetadataFollowupSeedApplicable
? followupSeed.action
: null;
const seededUnsupported = metadataGroundedDocumentLaneApplicable
? "document_evidence"
: metadataGroundedMovementLaneApplicable
? "movement_evidence"
: followupDiscoverySeedApplicable || effectiveMetadataFollowupSeedApplicable
? followupSeed.unsupported
: null;
const seededDomain = metadataAmbiguityLaneClarificationApplicable
? "metadata"
: metadataGroundedDocumentLaneApplicable
? "documents"
: metadataGroundedMovementLaneApplicable
? "movements"
: followupDiscoverySeedApplicable || effectiveMetadataFollowupSeedApplicable
? followupSeed.domain
: null;
const seededAction = metadataAmbiguityLaneClarificationApplicable
? "resolve_next_lane"
: metadataGroundedDocumentLaneApplicable
? "list_documents"
: metadataGroundedMovementLaneApplicable
? "list_movements"
: followupDiscoverySeedApplicable || effectiveMetadataFollowupSeedApplicable
? followupSeed.action
: null;
const seededUnsupported = metadataAmbiguityLaneClarificationApplicable
? "metadata_lane_choice_clarification"
: metadataGroundedDocumentLaneApplicable
? "document_evidence"
: metadataGroundedMovementLaneApplicable
? "movement_evidence"
: followupDiscoverySeedApplicable || effectiveMetadataFollowupSeedApplicable
? followupSeed.unsupported
: null;
const lifecycleSignal = rawLifecycleSignal || seededDomain === "counterparty_lifecycle";
const bidirectionalValueFlowSignal = !lifecycleSignal &&
(rawBidirectionalValueFlowSignal || seededAction === "net_value_flow");
@@ -482,14 +500,16 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
const payoutSignal = valueFlowSignal &&
!bidirectionalValueFlowSignal &&
(rawPayoutSignal || seededAction === "payout");
const semanticDataNeed = semanticNeedFor({
domain: rawDomain ?? seededDomain,
action: rawAction ?? seededAction,
unsupported: unsupported ?? seededUnsupported,
lifecycleSignal,
valueFlowSignal,
metadataSignal: rawMetadataSignal || effectiveMetadataFollowupSeedApplicable
});
const semanticDataNeed = metadataAmbiguityLaneClarificationApplicable
? "metadata lane clarification"
: semanticNeedFor({
domain: rawDomain ?? seededDomain,
action: rawAction ?? seededAction,
unsupported: unsupported ?? seededUnsupported,
lifecycleSignal,
valueFlowSignal,
metadataSignal: rawMetadataSignal || effectiveMetadataFollowupSeedApplicable
});
const entityCandidates = collectEntityCandidates(assistantTurnMeaning?.explicit_entity_candidates);
pushUnique(entityCandidates, predecomposeEntities.counterparty);
pushUnique(entityCandidates, followupSeed.counterparty);
@@ -533,6 +553,9 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
: rawAction ?? seededAction,
asked_aggregation_axis: monthlyAggregationSignal ? "month" : rawAggregationAxis,
explicit_entity_candidates: entityCandidates,
metadata_ambiguity_entity_sets: metadataAmbiguityLaneClarificationApplicable && followupSeed.metadataAmbiguityEntitySets.length > 0
? followupSeed.metadataAmbiguityEntitySets
: undefined,
explicit_organization_scope: explicitOrganizationScope,
explicit_date_scope: explicitDateScope,
unsupported_but_understood_family: unsupported ??
@@ -548,17 +571,20 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
? "movement_evidence"
: metadataGroundedDocumentLaneApplicable
? "document_evidence"
: rawMetadataSignal || effectiveMetadataFollowupSeedApplicable
? "1c_metadata_surface"
: followupDiscoverySeedApplicable
? seededUnsupported
: null),
: metadataAmbiguityLaneClarificationApplicable
? "metadata_lane_choice_clarification"
: rawMetadataSignal || effectiveMetadataFollowupSeedApplicable
? "1c_metadata_surface"
: followupDiscoverySeedApplicable
? seededUnsupported
: null),
stale_replay_forbidden: Boolean(assistantTurnMeaning?.stale_replay_forbidden ||
unsupported ||
lifecycleSignal ||
valueFlowSignal ||
metadataGroundedMovementLaneApplicable ||
metadataGroundedDocumentLaneApplicable ||
metadataAmbiguityLaneClarificationApplicable ||
rawMetadataSignal ||
effectiveMetadataFollowupSeedApplicable ||
followupDiscoverySeedApplicable)
@@ -576,6 +602,9 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
if ((turnMeaning.explicit_entity_candidates?.length ?? 0) > 0) {
cleanTurnMeaning.explicit_entity_candidates = turnMeaning.explicit_entity_candidates;
}
if ((turnMeaning.metadata_ambiguity_entity_sets?.length ?? 0) > 0) {
cleanTurnMeaning.metadata_ambiguity_entity_sets = turnMeaning.metadata_ambiguity_entity_sets;
}
if (toNonEmptyString(turnMeaning.explicit_organization_scope)) {
cleanTurnMeaning.explicit_organization_scope = turnMeaning.explicit_organization_scope;
}
@@ -597,13 +626,14 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
explicitIntentCandidate,
followupDiscoverySeedApplicable: followupDiscoverySeedApplicable ||
effectiveMetadataFollowupSeedApplicable ||
metadataAmbiguityLaneClarificationApplicable ||
metadataGroundedMovementLaneApplicable ||
metadataGroundedDocumentLaneApplicable
});
const hasTurnMeaning = Object.keys(cleanTurnMeaning).length > 0;
const sourceSignal = assistantTurnMeaning
? "assistant_turn_meaning"
: followupDiscoverySeedApplicable || effectiveMetadataFollowupSeedApplicable
: followupDiscoverySeedApplicable || effectiveMetadataFollowupSeedApplicable || metadataAmbiguityLaneClarificationApplicable
? "followup_context"
: metadataGroundedMovementLaneApplicable
? "followup_context"
@@ -663,6 +693,9 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
if (metadataAmbiguityCollapsedMovementLaneContinuationApplicable) {
pushReason(reasonCodes, "mcp_discovery_metadata_ambiguity_collapsed_to_movement_lane");
}
if (metadataAmbiguityLaneClarificationApplicable) {
pushReason(reasonCodes, "mcp_discovery_metadata_ambiguity_requires_lane_choice");
}
if (unsupported) {
pushReason(reasonCodes, "mcp_discovery_unsupported_but_understood_turn");
}