Planner Autonomy: вынести MCP chain templates в route fabric
This commit is contained in:
@@ -9,6 +9,16 @@ export const ASSISTANT_MCP_CATALOG_PLAN_REVIEW_SCHEMA_VERSION = "assistant_mcp_c
|
||||
|
||||
export type AssistantMcpCatalogEvidenceFloor = "none" | "rows_received" | "rows_matched" | "source_summary";
|
||||
export type AssistantMcpCatalogPlanReviewStatus = "catalog_compatible" | "needs_more_axes" | "catalog_blocked";
|
||||
export type AssistantMcpCatalogChainTemplateId =
|
||||
| "metadata_inspection"
|
||||
| "catalog_drilldown"
|
||||
| "value_flow"
|
||||
| "value_flow_comparison"
|
||||
| "value_flow_ranking"
|
||||
| "lifecycle"
|
||||
| "movement_evidence"
|
||||
| "document_evidence"
|
||||
| "entity_resolution";
|
||||
|
||||
export interface AssistantMcpCatalogPrimitiveContract {
|
||||
primitive_id: AssistantMcpDiscoveryPrimitive;
|
||||
@@ -29,9 +39,23 @@ export interface AssistantMcpCatalogIndexContract {
|
||||
schema_version: typeof ASSISTANT_MCP_CATALOG_INDEX_SCHEMA_VERSION;
|
||||
policy_owner: "assistantMcpCatalogIndex";
|
||||
primitives: AssistantMcpCatalogPrimitiveContract[];
|
||||
chain_templates: AssistantMcpCatalogChainTemplateContract[];
|
||||
reason_codes: string[];
|
||||
}
|
||||
|
||||
export interface AssistantMcpCatalogChainTemplateContract {
|
||||
chain_id: AssistantMcpCatalogChainTemplateId;
|
||||
semantic_data_need: string;
|
||||
chain_summary: string;
|
||||
fallback_primitives: AssistantMcpDiscoveryPrimitive[];
|
||||
base_required_axes: string[];
|
||||
supported_fact_families: string[];
|
||||
supported_action_families: string[];
|
||||
planning_tags: string[];
|
||||
safe_for_model_planning: true;
|
||||
requires_evidence_gate: true;
|
||||
}
|
||||
|
||||
export interface AssistantMcpCatalogPlanReview {
|
||||
schema_version: typeof ASSISTANT_MCP_CATALOG_PLAN_REVIEW_SCHEMA_VERSION;
|
||||
policy_owner: "assistantMcpCatalogIndex";
|
||||
@@ -220,6 +244,127 @@ const PRIMITIVE_CONTRACT_MAP = new Map<AssistantMcpDiscoveryPrimitive, Assistant
|
||||
PRIMITIVE_CONTRACTS.map((contract) => [contract.primitive_id, contract])
|
||||
);
|
||||
|
||||
const CHAIN_TEMPLATES: AssistantMcpCatalogChainTemplateContract[] = [
|
||||
{
|
||||
chain_id: "metadata_inspection",
|
||||
semantic_data_need: "1C metadata evidence",
|
||||
chain_summary: "Inspect the 1C metadata surface first, then ground the next safe lane from confirmed schema evidence.",
|
||||
fallback_primitives: ["inspect_1c_metadata"],
|
||||
base_required_axes: ["metadata_scope"],
|
||||
supported_fact_families: ["schema_surface"],
|
||||
supported_action_families: ["inspect_catalog", "inspect_documents", "inspect_registers", "inspect_fields", "inspect_surface"],
|
||||
planning_tags: ["metadata", "surface_inspection"],
|
||||
safe_for_model_planning: true,
|
||||
requires_evidence_gate: true
|
||||
},
|
||||
{
|
||||
chain_id: "catalog_drilldown",
|
||||
semantic_data_need: "catalog drilldown metadata evidence",
|
||||
chain_summary:
|
||||
"Drill deeper into the confirmed catalog-oriented metadata surface, inspect related metadata objects, and keep the next safe lane grounded in checked schema evidence.",
|
||||
fallback_primitives: ["inspect_1c_metadata"],
|
||||
base_required_axes: ["metadata_scope"],
|
||||
supported_fact_families: ["schema_surface"],
|
||||
supported_action_families: ["inspect_catalog", "inspect_surface"],
|
||||
planning_tags: ["metadata", "surface_inspection", "drilldown"],
|
||||
safe_for_model_planning: true,
|
||||
requires_evidence_gate: true
|
||||
},
|
||||
{
|
||||
chain_id: "entity_resolution",
|
||||
semantic_data_need: "entity discovery evidence",
|
||||
chain_summary:
|
||||
"Search candidate business entities, resolve the most relevant 1C reference, and prove whether the entity grounding is stable enough for the next probe.",
|
||||
fallback_primitives: ["search_business_entity", "resolve_entity_reference", "probe_coverage"],
|
||||
base_required_axes: ["business_entity", "coverage_target"],
|
||||
supported_fact_families: ["entity_grounding"],
|
||||
supported_action_families: ["search_business_entity"],
|
||||
planning_tags: ["subject_resolution", "coverage"],
|
||||
safe_for_model_planning: true,
|
||||
requires_evidence_gate: true
|
||||
},
|
||||
{
|
||||
chain_id: "document_evidence",
|
||||
semantic_data_need: "document evidence",
|
||||
chain_summary: "Resolve the business entity, fetch scoped document rows, and probe coverage before stating the checked document evidence.",
|
||||
fallback_primitives: ["resolve_entity_reference", "query_documents", "probe_coverage"],
|
||||
base_required_axes: ["coverage_target"],
|
||||
supported_fact_families: ["document_evidence", "activity_lifecycle"],
|
||||
supported_action_families: ["list_documents", "activity_duration"],
|
||||
planning_tags: ["document", "subject_resolution", "coverage"],
|
||||
safe_for_model_planning: true,
|
||||
requires_evidence_gate: true
|
||||
},
|
||||
{
|
||||
chain_id: "movement_evidence",
|
||||
semantic_data_need: "movement evidence",
|
||||
chain_summary:
|
||||
"Resolve the business entity, fetch scoped movement rows, and probe coverage without pretending to have a full movement universe.",
|
||||
fallback_primitives: ["resolve_entity_reference", "query_movements", "probe_coverage"],
|
||||
base_required_axes: ["coverage_target"],
|
||||
supported_fact_families: ["movement_evidence", "value_flow"],
|
||||
supported_action_families: ["list_movements", "turnover", "payout", "net_value_flow"],
|
||||
planning_tags: ["movement", "subject_resolution", "coverage"],
|
||||
safe_for_model_planning: true,
|
||||
requires_evidence_gate: true
|
||||
},
|
||||
{
|
||||
chain_id: "value_flow",
|
||||
semantic_data_need: "counterparty value-flow evidence",
|
||||
chain_summary: "Resolve the business entity, query scoped movements, aggregate checked amounts, then probe coverage before answering.",
|
||||
fallback_primitives: ["resolve_entity_reference", "query_movements", "aggregate_by_axis", "probe_coverage"],
|
||||
base_required_axes: ["aggregate_axis", "amount", "coverage_target"],
|
||||
supported_fact_families: ["value_flow"],
|
||||
supported_action_families: ["turnover", "payout", "net_value_flow"],
|
||||
planning_tags: ["movement", "aggregation", "coverage"],
|
||||
safe_for_model_planning: true,
|
||||
requires_evidence_gate: true
|
||||
},
|
||||
{
|
||||
chain_id: "value_flow_comparison",
|
||||
semantic_data_need: "bidirectional value-flow comparison evidence",
|
||||
chain_summary:
|
||||
"Query incoming and outgoing movements for the checked period and organization, compare the checked sides, and probe coverage before answering a bounded comparison.",
|
||||
fallback_primitives: ["query_movements", "probe_coverage"],
|
||||
base_required_axes: ["amount", "coverage_target"],
|
||||
supported_fact_families: ["value_flow"],
|
||||
supported_action_families: ["net_value_flow"],
|
||||
planning_tags: ["movement", "comparison", "coverage"],
|
||||
safe_for_model_planning: true,
|
||||
requires_evidence_gate: true
|
||||
},
|
||||
{
|
||||
chain_id: "value_flow_ranking",
|
||||
semantic_data_need: "ranked value-flow evidence",
|
||||
chain_summary:
|
||||
"Query scoped movements for the checked period and organization, aggregate checked amounts by counterparty, then probe coverage before answering a bounded ranking.",
|
||||
fallback_primitives: ["query_movements", "aggregate_by_axis", "probe_coverage"],
|
||||
base_required_axes: ["aggregate_axis", "amount", "coverage_target"],
|
||||
supported_fact_families: ["value_flow"],
|
||||
supported_action_families: ["turnover", "payout"],
|
||||
planning_tags: ["movement", "ranking", "aggregation", "coverage"],
|
||||
safe_for_model_planning: true,
|
||||
requires_evidence_gate: true
|
||||
},
|
||||
{
|
||||
chain_id: "lifecycle",
|
||||
semantic_data_need: "counterparty lifecycle evidence",
|
||||
chain_summary:
|
||||
"Resolve the business entity, query supporting documents, probe coverage, then explain the evidence basis for the inferred activity window.",
|
||||
fallback_primitives: ["resolve_entity_reference", "query_documents", "probe_coverage", "explain_evidence_basis"],
|
||||
base_required_axes: ["document_date", "coverage_target", "evidence_basis"],
|
||||
supported_fact_families: ["activity_lifecycle"],
|
||||
supported_action_families: ["activity_duration"],
|
||||
planning_tags: ["document", "explanation", "coverage"],
|
||||
safe_for_model_planning: true,
|
||||
requires_evidence_gate: true
|
||||
}
|
||||
];
|
||||
|
||||
const CHAIN_TEMPLATE_MAP = new Map<AssistantMcpCatalogChainTemplateId, AssistantMcpCatalogChainTemplateContract>(
|
||||
CHAIN_TEMPLATES.map((template) => [template.chain_id, template])
|
||||
);
|
||||
|
||||
function toStringSet(values: string[]): Set<string> {
|
||||
return new Set(values.map((item) => item.trim()).filter((item) => item.length > 0));
|
||||
}
|
||||
@@ -626,10 +771,19 @@ export function buildAssistantMcpCatalogIndex(): AssistantMcpCatalogIndexContrac
|
||||
} else {
|
||||
pushReason(reasonCodes, "catalog_covers_all_discovery_primitives");
|
||||
}
|
||||
const unknownChainPrimitives = CHAIN_TEMPLATES.flatMap((template) =>
|
||||
template.fallback_primitives.filter((primitive) => !PRIMITIVE_CONTRACT_MAP.has(primitive))
|
||||
);
|
||||
if (unknownChainPrimitives.length > 0) {
|
||||
pushReason(reasonCodes, "catalog_chain_template_references_unknown_primitive");
|
||||
} else {
|
||||
pushReason(reasonCodes, "catalog_chain_templates_reference_reviewed_primitives");
|
||||
}
|
||||
return {
|
||||
schema_version: ASSISTANT_MCP_CATALOG_INDEX_SCHEMA_VERSION,
|
||||
policy_owner: "assistantMcpCatalogIndex",
|
||||
primitives: PRIMITIVE_CONTRACTS,
|
||||
chain_templates: CHAIN_TEMPLATES,
|
||||
reason_codes: reasonCodes
|
||||
};
|
||||
}
|
||||
@@ -644,6 +798,16 @@ export function getAssistantMcpCatalogPrimitive(
|
||||
return contract;
|
||||
}
|
||||
|
||||
export function getAssistantMcpCatalogChainTemplate(
|
||||
chainId: AssistantMcpCatalogChainTemplateId
|
||||
): AssistantMcpCatalogChainTemplateContract {
|
||||
const template = CHAIN_TEMPLATE_MAP.get(chainId);
|
||||
if (!template) {
|
||||
throw new Error(`Missing MCP catalog chain template: ${chainId}`);
|
||||
}
|
||||
return template;
|
||||
}
|
||||
|
||||
export function reviewAssistantMcpDiscoveryPlanAgainstCatalog(
|
||||
plan: AssistantMcpDiscoveryPlanContract
|
||||
): AssistantMcpCatalogPlanReview {
|
||||
|
||||
@@ -5,10 +5,12 @@ import {
|
||||
type AssistantMcpDiscoveryTurnMeaningRef
|
||||
} from "./assistantMcpDiscoveryPolicy";
|
||||
import {
|
||||
getAssistantMcpCatalogChainTemplate,
|
||||
searchAssistantMcpCatalogPrimitivesByDecompositionCandidates,
|
||||
searchAssistantMcpCatalogPrimitivesByFactAxis,
|
||||
searchAssistantMcpCatalogPrimitivesByMetadataSurface,
|
||||
reviewAssistantMcpDiscoveryPlanAgainstCatalog,
|
||||
type AssistantMcpCatalogChainTemplateId,
|
||||
type AssistantMcpCatalogPlanReview
|
||||
} from "./assistantMcpCatalogIndex";
|
||||
import type { AssistantMcpDiscoveryDataNeedGraphContract } from "./assistantMcpDiscoveryDataNeedGraph";
|
||||
@@ -119,6 +121,38 @@ function pushUnique(target: string[], value: string): void {
|
||||
}
|
||||
}
|
||||
|
||||
function pushAllUnique(target: string[], values: string[]): void {
|
||||
for (const value of values) {
|
||||
pushUnique(target, value);
|
||||
}
|
||||
}
|
||||
|
||||
function recipeFromCatalogChainTemplate(input: {
|
||||
chainId: AssistantMcpCatalogChainTemplateId;
|
||||
axes: string[];
|
||||
primitives?: AssistantMcpDiscoveryPrimitive[];
|
||||
reason: string;
|
||||
extraReasons?: string[];
|
||||
chainSummary?: string;
|
||||
semanticDataNeed?: string;
|
||||
}): PlannerRecipe {
|
||||
const template = getAssistantMcpCatalogChainTemplate(input.chainId);
|
||||
const axes = [...input.axes];
|
||||
pushAllUnique(axes, template.base_required_axes);
|
||||
return {
|
||||
semanticDataNeed: input.semanticDataNeed ?? template.semantic_data_need,
|
||||
chainId: template.chain_id,
|
||||
chainSummary: input.chainSummary ?? template.chain_summary,
|
||||
primitives: input.primitives ?? template.fallback_primitives,
|
||||
axes,
|
||||
reason: input.reason,
|
||||
extraReasons: [
|
||||
`planner_instantiated_catalog_chain_template_${template.chain_id}`,
|
||||
...(input.extraReasons ?? [])
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
function hasEntity(meaning: AssistantMcpDiscoveryTurnMeaningRef | null | undefined): boolean {
|
||||
return (meaning?.explicit_entity_candidates?.length ?? 0) > 0;
|
||||
}
|
||||
@@ -460,63 +494,63 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
|
||||
const thinSurfaceRouteFamily = routeFamilyFromThinMetadataSurfaceInput(input);
|
||||
if (thinSurfaceRouteFamily === "document_evidence") {
|
||||
pushUnique(axes, "coverage_target");
|
||||
const template = getAssistantMcpCatalogChainTemplate("document_evidence");
|
||||
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
||||
dataNeedGraph,
|
||||
fallbackPrimitives: ["resolve_entity_reference", "query_documents", "probe_coverage"],
|
||||
fallbackPrimitives: template.fallback_primitives,
|
||||
requiredAxes: axes,
|
||||
metadataSurface: input.metadataSurface,
|
||||
actionFamily: action
|
||||
});
|
||||
return {
|
||||
semanticDataNeed: "document evidence",
|
||||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "document_evidence",
|
||||
axes,
|
||||
primitives: primitiveSelection.primitives,
|
||||
reason: "planner_selected_document_from_confirmed_metadata_surface_ref",
|
||||
chainSummary:
|
||||
"Ground the next checked document lane from the confirmed metadata surface, then fetch scoped document rows and probe coverage before answering.",
|
||||
primitives: primitiveSelection.primitives,
|
||||
axes,
|
||||
reason: "planner_selected_document_from_confirmed_metadata_surface_ref",
|
||||
extraReasons: primitiveSelection.reasonCodes
|
||||
};
|
||||
});
|
||||
}
|
||||
if (thinSurfaceRouteFamily === "movement_evidence") {
|
||||
pushUnique(axes, "coverage_target");
|
||||
const template = getAssistantMcpCatalogChainTemplate("movement_evidence");
|
||||
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
||||
dataNeedGraph,
|
||||
fallbackPrimitives: ["resolve_entity_reference", "query_movements", "probe_coverage"],
|
||||
fallbackPrimitives: template.fallback_primitives,
|
||||
requiredAxes: axes,
|
||||
metadataSurface: input.metadataSurface,
|
||||
actionFamily: action
|
||||
});
|
||||
return {
|
||||
semanticDataNeed: "movement evidence",
|
||||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "movement_evidence",
|
||||
axes,
|
||||
primitives: primitiveSelection.primitives,
|
||||
reason: "planner_selected_movement_from_confirmed_metadata_surface_ref",
|
||||
chainSummary:
|
||||
"Ground the next checked movement lane from the confirmed metadata surface, then fetch scoped movement rows and probe coverage before answering.",
|
||||
primitives: primitiveSelection.primitives,
|
||||
axes,
|
||||
reason: "planner_selected_movement_from_confirmed_metadata_surface_ref",
|
||||
extraReasons: primitiveSelection.reasonCodes
|
||||
};
|
||||
});
|
||||
}
|
||||
if (thinSurfaceRouteFamily === "catalog_drilldown") {
|
||||
pushUnique(axes, "metadata_scope");
|
||||
const template = getAssistantMcpCatalogChainTemplate("catalog_drilldown");
|
||||
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
||||
dataNeedGraph,
|
||||
fallbackPrimitives: ["inspect_1c_metadata"],
|
||||
fallbackPrimitives: template.fallback_primitives,
|
||||
requiredAxes: axes,
|
||||
metadataSurface: input.metadataSurface,
|
||||
actionFamily: action
|
||||
});
|
||||
return {
|
||||
semanticDataNeed: "catalog drilldown metadata evidence",
|
||||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "catalog_drilldown",
|
||||
axes,
|
||||
primitives: primitiveSelection.primitives,
|
||||
reason: "planner_selected_catalog_drilldown_from_confirmed_metadata_surface_ref",
|
||||
chainSummary:
|
||||
"Drill deeper into the confirmed catalog-oriented metadata surface, inspect related metadata objects, and keep the next safe lane grounded in checked schema evidence.",
|
||||
primitives: primitiveSelection.primitives,
|
||||
axes,
|
||||
reason: "planner_selected_catalog_drilldown_from_confirmed_metadata_surface_ref",
|
||||
extraReasons: primitiveSelection.reasonCodes
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (graphFactFamily === "value_flow") {
|
||||
@@ -526,50 +560,46 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
|
||||
if (requestedAggregationAxis === "month" || graphAggregation === "by_month") {
|
||||
pushUnique(axes, "calendar_month");
|
||||
}
|
||||
const template = getAssistantMcpCatalogChainTemplate("value_flow_comparison");
|
||||
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
||||
dataNeedGraph,
|
||||
fallbackPrimitives: ["query_movements", "probe_coverage"],
|
||||
fallbackPrimitives: template.fallback_primitives,
|
||||
requiredAxes: axes,
|
||||
metadataSurface: input.metadataSurface,
|
||||
actionFamily: action,
|
||||
allowAggregateByAxis: false
|
||||
});
|
||||
return {
|
||||
semanticDataNeed: "bidirectional value-flow comparison evidence",
|
||||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "value_flow_comparison",
|
||||
chainSummary:
|
||||
"Query incoming and outgoing movements for the checked period and organization, compare the checked sides, and probe coverage before answering a bounded comparison.",
|
||||
primitives: primitiveSelection.primitives,
|
||||
axes,
|
||||
primitives: primitiveSelection.primitives,
|
||||
reason: "planner_selected_bidirectional_value_flow_comparison_from_data_need_graph",
|
||||
extraReasons: primitiveSelection.reasonCodes
|
||||
};
|
||||
});
|
||||
}
|
||||
if (dataNeedGraph?.ranking_need && !hasSubjectCandidates(dataNeedGraph)) {
|
||||
pushUnique(axes, "aggregate_axis");
|
||||
pushUnique(axes, "amount");
|
||||
pushUnique(axes, "coverage_target");
|
||||
const template = getAssistantMcpCatalogChainTemplate("value_flow_ranking");
|
||||
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
||||
dataNeedGraph,
|
||||
fallbackPrimitives: ["query_movements", "aggregate_by_axis", "probe_coverage"],
|
||||
fallbackPrimitives: template.fallback_primitives,
|
||||
requiredAxes: axes,
|
||||
metadataSurface: input.metadataSurface,
|
||||
actionFamily: action,
|
||||
allowAggregateByAxis: true
|
||||
});
|
||||
return {
|
||||
semanticDataNeed: "ranked value-flow evidence",
|
||||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "value_flow_ranking",
|
||||
chainSummary:
|
||||
"Query scoped movements for the checked period and organization, aggregate checked amounts by counterparty, then probe coverage before answering a bounded ranking.",
|
||||
primitives: primitiveSelection.primitives,
|
||||
axes,
|
||||
primitives: primitiveSelection.primitives,
|
||||
reason:
|
||||
dataNeedGraph.ranking_need === "bottom_asc"
|
||||
? "planner_selected_bottom_ranked_value_flow_from_data_need_graph"
|
||||
: "planner_selected_top_ranked_value_flow_from_data_need_graph",
|
||||
: "planner_selected_top_ranked_value_flow_from_data_need_graph",
|
||||
extraReasons: primitiveSelection.reasonCodes
|
||||
};
|
||||
});
|
||||
}
|
||||
if (openScopeTotalWithoutSubject) {
|
||||
pushUnique(axes, "organization");
|
||||
@@ -586,19 +616,19 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
|
||||
actionFamily: action,
|
||||
allowAggregateByAxis: true
|
||||
});
|
||||
return {
|
||||
semanticDataNeed: "organization-scoped value-flow evidence",
|
||||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "value_flow",
|
||||
axes,
|
||||
primitives: primitiveSelection.primitives,
|
||||
semanticDataNeed: "organization-scoped value-flow evidence",
|
||||
chainSummary:
|
||||
"Query scoped movements for the checked period and organization without a preselected counterparty, aggregate checked amounts, then probe coverage before answering a bounded total.",
|
||||
primitives: primitiveSelection.primitives,
|
||||
axes,
|
||||
reason:
|
||||
requestedAggregationAxis === "month" || graphAggregation === "by_month"
|
||||
? "planner_selected_monthly_open_scope_value_flow_total_from_data_need_graph"
|
||||
: "planner_selected_open_scope_value_flow_total_from_data_need_graph",
|
||||
extraReasons: primitiveSelection.reasonCodes
|
||||
};
|
||||
});
|
||||
}
|
||||
pushUnique(axes, "aggregate_axis");
|
||||
pushUnique(axes, "amount");
|
||||
@@ -606,74 +636,72 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
|
||||
if (requestedAggregationAxis === "month" || graphAggregation === "by_month") {
|
||||
pushUnique(axes, "calendar_month");
|
||||
}
|
||||
const template = getAssistantMcpCatalogChainTemplate("value_flow");
|
||||
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
||||
dataNeedGraph,
|
||||
fallbackPrimitives: ["resolve_entity_reference", "query_movements", "aggregate_by_axis", "probe_coverage"],
|
||||
fallbackPrimitives: template.fallback_primitives,
|
||||
requiredAxes: axes,
|
||||
metadataSurface: input.metadataSurface,
|
||||
actionFamily: action,
|
||||
allowAggregateByAxis: true
|
||||
});
|
||||
return {
|
||||
semanticDataNeed: "counterparty value-flow evidence",
|
||||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "value_flow",
|
||||
chainSummary: "Resolve the business entity, query scoped movements, aggregate checked amounts, then probe coverage before answering.",
|
||||
primitives: primitiveSelection.primitives,
|
||||
axes,
|
||||
primitives: primitiveSelection.primitives,
|
||||
reason:
|
||||
requestedAggregationAxis === "month" || graphAggregation === "by_month"
|
||||
? "planner_selected_monthly_value_flow_from_data_need_graph"
|
||||
: "planner_selected_value_flow_from_data_need_graph",
|
||||
extraReasons: primitiveSelection.reasonCodes
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (graphFactFamily === "activity_lifecycle") {
|
||||
pushUnique(axes, "document_date");
|
||||
pushUnique(axes, "coverage_target");
|
||||
pushUnique(axes, "evidence_basis");
|
||||
const template = getAssistantMcpCatalogChainTemplate("lifecycle");
|
||||
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
||||
dataNeedGraph,
|
||||
fallbackPrimitives: ["resolve_entity_reference", "query_documents", "probe_coverage", "explain_evidence_basis"],
|
||||
fallbackPrimitives: template.fallback_primitives,
|
||||
requiredAxes: axes,
|
||||
metadataSurface: input.metadataSurface,
|
||||
actionFamily: action
|
||||
});
|
||||
return {
|
||||
semanticDataNeed: "counterparty lifecycle evidence",
|
||||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "lifecycle",
|
||||
chainSummary: "Resolve the business entity, query supporting documents, probe coverage, then explain the evidence basis for the inferred activity window.",
|
||||
primitives: primitiveSelection.primitives,
|
||||
axes,
|
||||
primitives: primitiveSelection.primitives,
|
||||
reason: "planner_selected_lifecycle_from_data_need_graph",
|
||||
extraReasons: primitiveSelection.reasonCodes
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (graphFactFamily === "schema_surface") {
|
||||
pushUnique(axes, "metadata_scope");
|
||||
const template = getAssistantMcpCatalogChainTemplate("metadata_inspection");
|
||||
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
||||
dataNeedGraph,
|
||||
fallbackPrimitives: ["inspect_1c_metadata"],
|
||||
fallbackPrimitives: template.fallback_primitives,
|
||||
requiredAxes: axes,
|
||||
metadataSurface: input.metadataSurface,
|
||||
actionFamily: action
|
||||
});
|
||||
return {
|
||||
semanticDataNeed: "1C metadata evidence",
|
||||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "metadata_inspection",
|
||||
chainSummary: "Inspect the 1C metadata surface first, then ground the next safe lane from confirmed schema evidence.",
|
||||
primitives: primitiveSelection.primitives,
|
||||
axes,
|
||||
primitives: primitiveSelection.primitives,
|
||||
reason: "planner_selected_metadata_from_data_need_graph",
|
||||
extraReasons: primitiveSelection.reasonCodes
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (graphFactFamily === "movement_evidence") {
|
||||
if (metadataScopedOpenLane) {
|
||||
pushUnique(axes, "organization");
|
||||
pushUnique(axes, "coverage_target");
|
||||
const template = getAssistantMcpCatalogChainTemplate("movement_evidence");
|
||||
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
||||
dataNeedGraph,
|
||||
fallbackPrimitives: ["query_movements", "probe_coverage"],
|
||||
@@ -681,40 +709,40 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
|
||||
metadataSurface: input.metadataSurface,
|
||||
actionFamily: action
|
||||
});
|
||||
return {
|
||||
semanticDataNeed: "movement evidence",
|
||||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "movement_evidence",
|
||||
axes,
|
||||
primitives: primitiveSelection.primitives,
|
||||
reason: "planner_selected_metadata_scoped_movement_from_data_need_graph",
|
||||
chainSummary:
|
||||
"Keep the metadata-scoped movement lane, ask only for the remaining business scope, then fetch scoped movement rows and probe coverage without pretending there is a grounded counterparty.",
|
||||
primitives: primitiveSelection.primitives,
|
||||
axes,
|
||||
reason: "planner_selected_metadata_scoped_movement_from_data_need_graph",
|
||||
semanticDataNeed: template.semantic_data_need,
|
||||
extraReasons: primitiveSelection.reasonCodes
|
||||
};
|
||||
});
|
||||
}
|
||||
pushUnique(axes, "coverage_target");
|
||||
const template = getAssistantMcpCatalogChainTemplate("movement_evidence");
|
||||
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
||||
dataNeedGraph,
|
||||
fallbackPrimitives: ["resolve_entity_reference", "query_movements", "probe_coverage"],
|
||||
fallbackPrimitives: template.fallback_primitives,
|
||||
requiredAxes: axes,
|
||||
metadataSurface: input.metadataSurface,
|
||||
actionFamily: action
|
||||
});
|
||||
return {
|
||||
semanticDataNeed: "movement evidence",
|
||||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "movement_evidence",
|
||||
chainSummary: "Resolve the business entity, fetch scoped movement rows, and probe coverage without pretending to have a full movement universe.",
|
||||
primitives: primitiveSelection.primitives,
|
||||
axes,
|
||||
primitives: primitiveSelection.primitives,
|
||||
reason: "planner_selected_movement_from_data_need_graph",
|
||||
extraReasons: primitiveSelection.reasonCodes
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (graphFactFamily === "document_evidence") {
|
||||
if (metadataScopedOpenLane) {
|
||||
pushUnique(axes, "organization");
|
||||
pushUnique(axes, "coverage_target");
|
||||
const template = getAssistantMcpCatalogChainTemplate("document_evidence");
|
||||
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
||||
dataNeedGraph,
|
||||
fallbackPrimitives: ["query_documents", "probe_coverage"],
|
||||
@@ -722,58 +750,56 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
|
||||
metadataSurface: input.metadataSurface,
|
||||
actionFamily: action
|
||||
});
|
||||
return {
|
||||
semanticDataNeed: "document evidence",
|
||||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "document_evidence",
|
||||
axes,
|
||||
primitives: primitiveSelection.primitives,
|
||||
reason: "planner_selected_metadata_scoped_document_from_data_need_graph",
|
||||
chainSummary:
|
||||
"Keep the metadata-scoped document lane, ask only for the remaining business scope, then fetch scoped document rows and probe coverage without pretending there is a grounded counterparty.",
|
||||
primitives: primitiveSelection.primitives,
|
||||
axes,
|
||||
reason: "planner_selected_metadata_scoped_document_from_data_need_graph",
|
||||
semanticDataNeed: template.semantic_data_need,
|
||||
extraReasons: primitiveSelection.reasonCodes
|
||||
};
|
||||
});
|
||||
}
|
||||
pushUnique(axes, "coverage_target");
|
||||
const template = getAssistantMcpCatalogChainTemplate("document_evidence");
|
||||
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
||||
dataNeedGraph,
|
||||
fallbackPrimitives: ["resolve_entity_reference", "query_documents", "probe_coverage"],
|
||||
fallbackPrimitives: template.fallback_primitives,
|
||||
requiredAxes: axes,
|
||||
metadataSurface: input.metadataSurface,
|
||||
actionFamily: action
|
||||
});
|
||||
return {
|
||||
semanticDataNeed: "document evidence",
|
||||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "document_evidence",
|
||||
chainSummary: "Resolve the business entity, fetch scoped document rows, and probe coverage before stating the checked document evidence.",
|
||||
primitives: primitiveSelection.primitives,
|
||||
axes,
|
||||
primitives: primitiveSelection.primitives,
|
||||
reason: "planner_selected_document_from_data_need_graph",
|
||||
extraReasons: primitiveSelection.reasonCodes
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (graphFactFamily === "entity_grounding" || (!graphFactFamily && (dataNeedGraph?.subject_candidates.length ?? 0) > 0)) {
|
||||
pushUnique(axes, "business_entity");
|
||||
pushUnique(axes, "coverage_target");
|
||||
const template = getAssistantMcpCatalogChainTemplate("entity_resolution");
|
||||
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
||||
dataNeedGraph,
|
||||
fallbackPrimitives: ["search_business_entity", "resolve_entity_reference", "probe_coverage"],
|
||||
fallbackPrimitives: template.fallback_primitives,
|
||||
requiredAxes: axes,
|
||||
metadataSurface: input.metadataSurface,
|
||||
actionFamily: action
|
||||
});
|
||||
return {
|
||||
semanticDataNeed: "entity discovery evidence",
|
||||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "entity_resolution",
|
||||
chainSummary: "Search candidate business entities, resolve the most relevant 1C reference, and prove whether the entity grounding is stable enough for the next probe.",
|
||||
primitives: primitiveSelection.primitives,
|
||||
axes,
|
||||
primitives: primitiveSelection.primitives,
|
||||
reason:
|
||||
graphAction === "search_business_entity"
|
||||
? "planner_selected_entity_resolution_from_data_need_graph"
|
||||
: "planner_selected_entity_resolution_recipe",
|
||||
extraReasons: primitiveSelection.reasonCodes
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (includesAny(combined, ["metadata_lane_choice_clarification", "resolve_next_lane"])) {
|
||||
@@ -795,89 +821,70 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
|
||||
if (requestedAggregationAxis === "month") {
|
||||
pushUnique(axes, "calendar_month");
|
||||
}
|
||||
return {
|
||||
semanticDataNeed: "counterparty value-flow evidence",
|
||||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "value_flow",
|
||||
chainSummary: "Resolve the business entity, query scoped movements, aggregate checked amounts, then probe coverage before answering.",
|
||||
primitives: ["resolve_entity_reference", "query_movements", "aggregate_by_axis", "probe_coverage"],
|
||||
axes,
|
||||
reason: requestedAggregationAxis === "month"
|
||||
? "planner_selected_monthly_value_flow_recipe"
|
||||
: "planner_selected_value_flow_recipe"
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (includesAny(combined, ["lifecycle", "activity", "duration", "age"])) {
|
||||
pushUnique(axes, "document_date");
|
||||
pushUnique(axes, "coverage_target");
|
||||
pushUnique(axes, "evidence_basis");
|
||||
return {
|
||||
semanticDataNeed: "counterparty lifecycle evidence",
|
||||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "lifecycle",
|
||||
chainSummary: "Resolve the business entity, query supporting documents, probe coverage, then explain the evidence basis for the inferred activity window.",
|
||||
primitives: ["resolve_entity_reference", "query_documents", "probe_coverage", "explain_evidence_basis"],
|
||||
axes,
|
||||
reason: "planner_selected_lifecycle_recipe"
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (includesAny(combined, ["metadata", "schema", "catalog"])) {
|
||||
pushUnique(axes, "metadata_scope");
|
||||
return {
|
||||
semanticDataNeed: "1C metadata evidence",
|
||||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "metadata_inspection",
|
||||
chainSummary: "Inspect the 1C metadata surface first, then ground the next safe lane from confirmed schema evidence.",
|
||||
primitives: ["inspect_1c_metadata"],
|
||||
axes,
|
||||
reason: "planner_selected_metadata_recipe"
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (includesAny(combined, ["movement", "movements", "bank_operations", "movement_evidence", "list_movements"])) {
|
||||
pushUnique(axes, "coverage_target");
|
||||
return {
|
||||
semanticDataNeed: "movement evidence",
|
||||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "movement_evidence",
|
||||
chainSummary: "Resolve the business entity, fetch scoped movement rows, and probe coverage without pretending to have a full movement universe.",
|
||||
primitives: ["resolve_entity_reference", "query_movements", "probe_coverage"],
|
||||
axes,
|
||||
reason: "planner_selected_movement_recipe"
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (includesAny(combined, ["document", "documents"])) {
|
||||
pushUnique(axes, "coverage_target");
|
||||
return {
|
||||
semanticDataNeed: "document evidence",
|
||||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "document_evidence",
|
||||
chainSummary: "Resolve the business entity, fetch scoped document rows, and probe coverage before stating the checked document evidence.",
|
||||
primitives: ["resolve_entity_reference", "query_documents", "probe_coverage"],
|
||||
axes,
|
||||
reason: "planner_selected_document_recipe"
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (hasEntity(meaning)) {
|
||||
pushUnique(axes, "business_entity");
|
||||
pushUnique(axes, "coverage_target");
|
||||
return {
|
||||
semanticDataNeed: "entity discovery evidence",
|
||||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "entity_resolution",
|
||||
chainSummary: "Search candidate business entities, resolve the most relevant 1C reference, and prove whether the entity grounding is stable enough for the next probe.",
|
||||
primitives: ["search_business_entity", "resolve_entity_reference", "probe_coverage"],
|
||||
axes,
|
||||
reason: "planner_selected_entity_resolution_recipe"
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
semanticDataNeed: "unclassified 1C discovery need",
|
||||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "metadata_inspection",
|
||||
semanticDataNeed: "unclassified 1C discovery need",
|
||||
chainSummary: "Start with metadata inspection instead of guessing a deeper fact route when the business need is still under-specified.",
|
||||
primitives: ["inspect_1c_metadata"],
|
||||
axes,
|
||||
reason: "planner_selected_clarification_recipe"
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function statusFrom(
|
||||
|
||||
Reference in New Issue
Block a user