Согласовать MCP planner с value-flow цепочками

This commit is contained in:
2026-05-01 22:38:11 +03:00
parent 924f6fb0ea
commit 4c00d8c854
11 changed files with 316 additions and 22 deletions
@@ -622,9 +622,7 @@ function searchAssistantMcpCatalogPrimitivesByDecompositionCandidates(input) {
continue;
}
for (const contract of PRIMITIVE_CONTRACTS) {
if (contract.primitive_id === "aggregate_by_axis" &&
normalizedCandidate === "aggregate_by_month" &&
!allowAggregateByAxis) {
if (contract.primitive_id === "aggregate_by_axis" && !allowAggregateByAxis) {
continue;
}
if (!contract.decomposition_hints.includes(normalizedCandidate)) {
@@ -97,6 +97,13 @@ function dateScopeToFilters(dateScope) {
period_to: `${yearMatch[1]}-12-31`
};
}
const rangeMatch = dateScope.match(/^(\d{4}-\d{2}-\d{2})\.\.(\d{4}-\d{2}-\d{2})$/);
if (rangeMatch) {
return {
period_from: rangeMatch[1],
period_to: rangeMatch[2]
};
}
const dateMatch = dateScope.match(/^(\d{4})-(\d{2})-(\d{2})/);
if (dateMatch) {
const date = `${dateMatch[1]}-${dateMatch[2]}-${dateMatch[3]}`;
@@ -217,6 +217,26 @@ function preferredPrimitiveFromExplicitDataNeedGraph(graph) {
}
return null;
}
function catalogTemplateIdFromChainId(chainId) {
if (chainId === "metadata_lane_clarification") {
return null;
}
return chainId;
}
function promoteConfirmedMetadataSurfaceChainTemplate(input) {
const surfaceRouteFamily = routeFamilyFromMetadataSurfaceRef(input.metadataSurface);
const selectedChainId = input.selectedChainId ?? null;
if (!surfaceRouteFamily || !selectedChainId || selectedChainId !== surfaceRouteFamily) {
return input.matches;
}
if (input.matches[0] === selectedChainId || !input.matches.includes(selectedChainId)) {
return input.matches;
}
return [
selectedChainId,
...input.matches.filter((chainId) => chainId !== selectedChainId)
];
}
function hasCarriedMetadataSurfaceScoringEvidence(surface) {
return Boolean(surface &&
(toNonEmptyString(surface.selected_entity_set) ||
@@ -298,7 +318,7 @@ function selectPrimitivesFromGraphAndCatalog(input) {
if (factAxisPrimitives.length > 0) {
reasonCodes.push("planner_selected_catalog_primitives_from_fact_axis_search");
}
const chainTemplateMatches = input.dataNeedGraph
const rawChainTemplateMatches = input.dataNeedGraph
? (0, assistantMcpCatalogIndex_1.searchAssistantMcpCatalogChainTemplatesByFactAxis)({
business_fact_family: input.dataNeedGraph.business_fact_family,
action_family: input.actionFamily ?? input.dataNeedGraph.action_family,
@@ -308,8 +328,16 @@ function selectPrimitivesFromGraphAndCatalog(input) {
aggregation_need: input.dataNeedGraph.aggregation_need
})
: [];
const chainTemplateMatches = promoteConfirmedMetadataSurfaceChainTemplate({
matches: rawChainTemplateMatches,
metadataSurface: input.metadataSurface,
selectedChainId: input.selectedChainId
});
if (chainTemplateMatches.length > 0) {
reasonCodes.push("planner_scored_catalog_chain_templates_from_fact_axis");
if (rawChainTemplateMatches[0] !== chainTemplateMatches[0]) {
reasonCodes.push("planner_catalog_chain_template_promoted_by_confirmed_metadata_surface");
}
reasonCodes.push(`planner_catalog_chain_template_search_top_${chainTemplateMatches[0]}`);
}
const combinedCatalogPrimitives = [];
@@ -353,8 +381,10 @@ function selectPrimitivesFromGraphAndCatalog(input) {
function budgetOverrideFor(input, recipe) {
const meaning = input.turnMeaning ?? null;
const requestedAggregationAxis = aggregationAxis(meaning);
const isValueFlowRecipe = recipe.semanticDataNeed === "counterparty value-flow evidence" &&
recipe.primitives.includes("query_movements");
const isValueFlowRecipe = recipe.primitives.includes("query_movements") &&
(recipe.semanticDataNeed === "counterparty value-flow evidence" ||
recipe.semanticDataNeed === "bidirectional value-flow comparison evidence" ||
recipe.semanticDataNeed === "ranked value-flow evidence");
if (!isValueFlowRecipe) {
return {};
}
@@ -370,7 +400,7 @@ function catalogChainTemplateMatchesForContract(input, recipe) {
if (!dataNeedGraph) {
return [];
}
return (0, assistantMcpCatalogIndex_1.searchAssistantMcpCatalogChainTemplatesByFactAxis)({
const matches = (0, assistantMcpCatalogIndex_1.searchAssistantMcpCatalogChainTemplatesByFactAxis)({
business_fact_family: dataNeedGraph.business_fact_family,
action_family: toNonEmptyString(input.turnMeaning?.asked_action_family) ?? dataNeedGraph.action_family,
required_axes: recipe.axes,
@@ -378,6 +408,11 @@ function catalogChainTemplateMatchesForContract(input, recipe) {
ranking_need: dataNeedGraph.ranking_need,
aggregation_need: dataNeedGraph.aggregation_need
});
return promoteConfirmedMetadataSurfaceChainTemplate({
matches,
metadataSurface: input.metadataSurface,
selectedChainId: catalogTemplateIdFromChainId(recipe.chainId)
});
}
function catalogChainTemplateAlignmentForContract(recipe, matches) {
const selectedChainIsCatalogTemplate = recipe.chainId !== "metadata_lane_clarification";
@@ -472,7 +507,8 @@ function recipeFor(input) {
fallbackPrimitives: template.fallback_primitives,
requiredAxes: axes,
metadataSurface: input.metadataSurface,
actionFamily: action
actionFamily: action,
selectedChainId: "document_evidence"
});
return recipeFromCatalogChainTemplate({
chainId: "document_evidence",
@@ -496,7 +532,8 @@ function recipeFor(input) {
fallbackPrimitives: template.fallback_primitives,
requiredAxes: axes,
metadataSurface: input.metadataSurface,
actionFamily: action
actionFamily: action,
selectedChainId: "movement_evidence"
});
return recipeFromCatalogChainTemplate({
chainId: "movement_evidence",
@@ -520,7 +557,8 @@ function recipeFor(input) {
fallbackPrimitives: template.fallback_primitives,
requiredAxes: axes,
metadataSurface: input.metadataSurface,
actionFamily: action
actionFamily: action,
selectedChainId: "catalog_drilldown"
});
return recipeFromCatalogChainTemplate({
chainId: "catalog_drilldown",
@@ -202,6 +202,28 @@ function readStringArray(value) {
? value.map((item) => toNonEmptyString(item)).filter((item) => Boolean(item))
: [];
}
function hasValueFlowActionConflictWithDiscoveryTurnMeaning(input, entryPoint) {
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
return false;
}
if (!hasEffectivelyFactualAddressReply(input)) {
return false;
}
const turnMeaning = readDiscoveryTurnMeaning(entryPoint);
const askedDomain = toNonEmptyString(turnMeaning?.asked_domain_family);
const askedAction = toNonEmptyString(turnMeaning?.asked_action_family);
if (askedDomain !== "counterparty_value") {
return false;
}
const detectedIntent = toNonEmptyString(input.addressRuntimeMeta?.detected_intent);
if (askedAction === "payout") {
return detectedIntent !== "supplier_payouts_profile";
}
if (askedAction === "net_value_flow") {
return true;
}
return false;
}
function hasExactMatchedFactualAddressReply(input, entryPoint) {
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
return false;
@@ -212,6 +234,9 @@ function hasExactMatchedFactualAddressReply(input, entryPoint) {
if (hasOpenScopeValueFlowDiscoveryPriority(input, entryPoint)) {
return false;
}
if (hasValueFlowActionConflictWithDiscoveryTurnMeaning(input, entryPoint)) {
return false;
}
const mcpCallStatus = toNonEmptyString(input.addressRuntimeMeta?.mcp_call_status);
const truthMode = toNonEmptyString(input.addressRuntimeMeta?.truth_mode);
const selectedRecipe = toNonEmptyString(input.addressRuntimeMeta?.selected_recipe);
@@ -378,6 +403,7 @@ function applyAssistantMcpDiscoveryResponsePolicy(input) {
const exactMatchedFactualAddressReply = hasExactMatchedFactualAddressReply(input, entryPoint);
const runtimeAdjustedExactReply = hasRuntimeAdjustedExactReply(input, entryPoint);
const openScopeValueFlowDiscoveryPriority = hasOpenScopeValueFlowDiscoveryPriority(input, entryPoint);
const valueFlowActionConflictWithDiscoveryTurnMeaning = hasValueFlowActionConflictWithDiscoveryTurnMeaning(input, entryPoint);
if (!entryPoint) {
pushReason(reasonCodes, "mcp_discovery_response_policy_no_entry_point");
}
@@ -399,6 +425,9 @@ function applyAssistantMcpDiscoveryResponsePolicy(input) {
if (semanticConflictWithDiscoveryTurnMeaning) {
pushReason(reasonCodes, "mcp_discovery_response_policy_semantic_conflict_allows_candidate_override");
}
if (valueFlowActionConflictWithDiscoveryTurnMeaning) {
pushReason(reasonCodes, "mcp_discovery_response_policy_value_flow_action_conflict_allows_candidate_override");
}
if (openScopeValueFlowDiscoveryPriority) {
pushReason(reasonCodes, "mcp_discovery_response_policy_open_scope_value_flow_candidate_priority");
}