Planner Autonomy: выводить lane из metadata surface

This commit is contained in:
2026-05-01 13:57:40 +03:00
parent a4db26a76e
commit ccfa9283e9
6 changed files with 249 additions and 53 deletions
@@ -123,6 +123,59 @@ function mergeCatalogPrimitivesWithFallback(catalogPrimitives, fallbackPrimitive
}
return result;
}
function normalizeMetadataSurfaceToken(value) {
return String(value ?? "").trim().toLowerCase().replace(/[\s_.-]+/g, "");
}
function metadataSurfaceValueSuggestsDocument(value) {
const token = normalizeMetadataSurfaceToken(value);
return token.includes("document") || token.includes("invoice") || token.includes("waybill") || token.includes("act");
}
function metadataSurfaceValueSuggestsMovement(value) {
const token = normalizeMetadataSurfaceToken(value);
return token.includes("register") || token.includes("movement") || token.includes("operation") || token.includes("bank");
}
function metadataSurfaceValueSuggestsCatalog(value) {
const token = normalizeMetadataSurfaceToken(value);
return token.includes("catalog") || token.includes("directory");
}
function routeFamilyFromMetadataSurfaceRef(surface) {
if (!surface || surface.ambiguity_detected) {
return null;
}
if (surface.downstream_route_family) {
return surface.downstream_route_family;
}
const values = [surface.selected_entity_set ?? "", ...surface.selected_surface_objects];
const documentHit = values.some(metadataSurfaceValueSuggestsDocument);
const movementHit = values.some(metadataSurfaceValueSuggestsMovement);
const catalogHit = values.some(metadataSurfaceValueSuggestsCatalog);
const hitCount = [documentHit, movementHit, catalogHit].filter(Boolean).length;
if (hitCount !== 1) {
return null;
}
if (documentHit) {
return "document_evidence";
}
if (movementHit) {
return "movement_evidence";
}
return "catalog_drilldown";
}
function inferredRouteFamilyFromMetadataSurfaceRef(surface) {
return Boolean(surface && !surface.downstream_route_family && routeFamilyFromMetadataSurfaceRef(surface));
}
function preferredPrimitiveForRouteFamily(routeFamily) {
if (routeFamily === "document_evidence") {
return "query_documents";
}
if (routeFamily === "movement_evidence") {
return "query_movements";
}
if (routeFamily === "catalog_drilldown") {
return "drilldown_related_objects";
}
return null;
}
function preferredPrimitiveFromMetadataSurface(surface) {
if (surface?.ambiguity_detected) {
return null;
@@ -131,16 +184,7 @@ function preferredPrimitiveFromMetadataSurface(surface) {
if (recommendedPrimitive) {
return recommendedPrimitive;
}
if (surface?.downstream_route_family === "document_evidence") {
return "query_documents";
}
if (surface?.downstream_route_family === "movement_evidence") {
return "query_movements";
}
if (surface?.downstream_route_family === "catalog_drilldown") {
return "drilldown_related_objects";
}
return null;
return preferredPrimitiveForRouteFamily(routeFamilyFromMetadataSurfaceRef(surface));
}
function preferredPrimitiveFromExplicitDataNeedGraph(graph) {
const factFamily = lower(graph?.business_fact_family);
@@ -289,7 +333,8 @@ function budgetOverrideFor(input, recipe) {
}
function routeFamilyFromThinMetadataSurfaceInput(input) {
const surface = input.metadataSurface ?? null;
if (!surface || surface.ambiguity_detected || !surface.downstream_route_family || !surface.recommended_next_primitive) {
const surfaceRouteFamily = routeFamilyFromMetadataSurfaceRef(surface);
if (!surface || surface.ambiguity_detected || !surfaceRouteFamily) {
return null;
}
const meaning = input.turnMeaning ?? null;
@@ -305,20 +350,16 @@ function routeFamilyFromThinMetadataSurfaceInput(input) {
return null;
}
if (graphFactFamily === "document_evidence" || includesAny(combined, ["document", "documents", "list_documents"])) {
return surface.downstream_route_family === "document_evidence" ? "document_evidence" : null;
return surfaceRouteFamily === "document_evidence" ? "document_evidence" : null;
}
if (graphFactFamily === "movement_evidence" || includesAny(combined, ["movement", "movements", "list_movements", "bank_operations"])) {
return surface.downstream_route_family === "movement_evidence" ? "movement_evidence" : null;
return surfaceRouteFamily === "movement_evidence" ? "movement_evidence" : null;
}
if (graphFactFamily === "schema_surface" || includesAny(combined, ["catalog", "directory", "inspect_catalog"])) {
return surface.downstream_route_family === "catalog_drilldown" ? "catalog_drilldown" : null;
return surfaceRouteFamily === "catalog_drilldown" ? "catalog_drilldown" : null;
}
if (!graphFactFamily && !domain && !action) {
if (surface.downstream_route_family === "document_evidence" ||
surface.downstream_route_family === "movement_evidence" ||
surface.downstream_route_family === "catalog_drilldown") {
return surface.downstream_route_family;
}
return surfaceRouteFamily;
}
return null;
}
@@ -370,7 +411,12 @@ function recipeFor(input) {
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.",
extraReasons: primitiveSelection.reasonCodes
extraReasons: [
...primitiveSelection.reasonCodes,
...(inferredRouteFamilyFromMetadataSurfaceRef(input.metadataSurface)
? ["planner_inferred_next_lane_from_unambiguous_metadata_surface"]
: [])
]
});
}
if (thinSurfaceRouteFamily === "movement_evidence") {
@@ -389,7 +435,12 @@ function recipeFor(input) {
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.",
extraReasons: primitiveSelection.reasonCodes
extraReasons: [
...primitiveSelection.reasonCodes,
...(inferredRouteFamilyFromMetadataSurfaceRef(input.metadataSurface)
? ["planner_inferred_next_lane_from_unambiguous_metadata_surface"]
: [])
]
});
}
if (thinSurfaceRouteFamily === "catalog_drilldown") {
@@ -408,7 +459,12 @@ function recipeFor(input) {
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.",
extraReasons: primitiveSelection.reasonCodes
extraReasons: [
...primitiveSelection.reasonCodes,
...(inferredRouteFamilyFromMetadataSurfaceRef(input.metadataSurface)
? ["planner_inferred_next_lane_from_unambiguous_metadata_surface"]
: [])
]
});
}
if (graphFactFamily === "value_flow") {
@@ -249,6 +249,73 @@ function mergeCatalogPrimitivesWithFallback(
return result;
}
function normalizeMetadataSurfaceToken(value: unknown): string {
return String(value ?? "").trim().toLowerCase().replace(/[\s_.-]+/g, "");
}
function metadataSurfaceValueSuggestsDocument(value: unknown): boolean {
const token = normalizeMetadataSurfaceToken(value);
return token.includes("document") || token.includes("invoice") || token.includes("waybill") || token.includes("act");
}
function metadataSurfaceValueSuggestsMovement(value: unknown): boolean {
const token = normalizeMetadataSurfaceToken(value);
return token.includes("register") || token.includes("movement") || token.includes("operation") || token.includes("bank");
}
function metadataSurfaceValueSuggestsCatalog(value: unknown): boolean {
const token = normalizeMetadataSurfaceToken(value);
return token.includes("catalog") || token.includes("directory");
}
function routeFamilyFromMetadataSurfaceRef(
surface: AssistantMcpDiscoveryMetadataSurfaceRef | null | undefined
): AssistantMcpDiscoveryMetadataRouteFamily | null {
if (!surface || surface.ambiguity_detected) {
return null;
}
if (surface.downstream_route_family) {
return surface.downstream_route_family;
}
const values = [surface.selected_entity_set ?? "", ...surface.selected_surface_objects];
const documentHit = values.some(metadataSurfaceValueSuggestsDocument);
const movementHit = values.some(metadataSurfaceValueSuggestsMovement);
const catalogHit = values.some(metadataSurfaceValueSuggestsCatalog);
const hitCount = [documentHit, movementHit, catalogHit].filter(Boolean).length;
if (hitCount !== 1) {
return null;
}
if (documentHit) {
return "document_evidence";
}
if (movementHit) {
return "movement_evidence";
}
return "catalog_drilldown";
}
function inferredRouteFamilyFromMetadataSurfaceRef(
surface: AssistantMcpDiscoveryMetadataSurfaceRef | null | undefined
): boolean {
return Boolean(surface && !surface.downstream_route_family && routeFamilyFromMetadataSurfaceRef(surface));
}
function preferredPrimitiveForRouteFamily(
routeFamily: AssistantMcpDiscoveryMetadataRouteFamily | null
): AssistantMcpDiscoveryPrimitive | null {
if (routeFamily === "document_evidence") {
return "query_documents";
}
if (routeFamily === "movement_evidence") {
return "query_movements";
}
if (routeFamily === "catalog_drilldown") {
return "drilldown_related_objects";
}
return null;
}
function preferredPrimitiveFromMetadataSurface(
surface: AssistantMcpDiscoveryMetadataSurfaceRef | null | undefined
): AssistantMcpDiscoveryPrimitive | null {
@@ -259,16 +326,7 @@ function preferredPrimitiveFromMetadataSurface(
if (recommendedPrimitive) {
return recommendedPrimitive;
}
if (surface?.downstream_route_family === "document_evidence") {
return "query_documents";
}
if (surface?.downstream_route_family === "movement_evidence") {
return "query_movements";
}
if (surface?.downstream_route_family === "catalog_drilldown") {
return "drilldown_related_objects";
}
return null;
return preferredPrimitiveForRouteFamily(routeFamilyFromMetadataSurfaceRef(surface));
}
function preferredPrimitiveFromExplicitDataNeedGraph(
@@ -466,7 +524,8 @@ function routeFamilyFromThinMetadataSurfaceInput(
input: AssistantMcpDiscoveryPlannerInput
): AssistantMcpDiscoveryMetadataRouteFamily | null {
const surface = input.metadataSurface ?? null;
if (!surface || surface.ambiguity_detected || !surface.downstream_route_family || !surface.recommended_next_primitive) {
const surfaceRouteFamily = routeFamilyFromMetadataSurfaceRef(surface);
if (!surface || surface.ambiguity_detected || !surfaceRouteFamily) {
return null;
}
const meaning = input.turnMeaning ?? null;
@@ -483,22 +542,16 @@ function routeFamilyFromThinMetadataSurfaceInput(
return null;
}
if (graphFactFamily === "document_evidence" || includesAny(combined, ["document", "documents", "list_documents"])) {
return surface.downstream_route_family === "document_evidence" ? "document_evidence" : null;
return surfaceRouteFamily === "document_evidence" ? "document_evidence" : null;
}
if (graphFactFamily === "movement_evidence" || includesAny(combined, ["movement", "movements", "list_movements", "bank_operations"])) {
return surface.downstream_route_family === "movement_evidence" ? "movement_evidence" : null;
return surfaceRouteFamily === "movement_evidence" ? "movement_evidence" : null;
}
if (graphFactFamily === "schema_surface" || includesAny(combined, ["catalog", "directory", "inspect_catalog"])) {
return surface.downstream_route_family === "catalog_drilldown" ? "catalog_drilldown" : null;
return surfaceRouteFamily === "catalog_drilldown" ? "catalog_drilldown" : null;
}
if (!graphFactFamily && !domain && !action) {
if (
surface.downstream_route_family === "document_evidence" ||
surface.downstream_route_family === "movement_evidence" ||
surface.downstream_route_family === "catalog_drilldown"
) {
return surface.downstream_route_family;
}
return surfaceRouteFamily;
}
return null;
}
@@ -555,7 +608,12 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
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.",
extraReasons: primitiveSelection.reasonCodes
extraReasons: [
...primitiveSelection.reasonCodes,
...(inferredRouteFamilyFromMetadataSurfaceRef(input.metadataSurface)
? ["planner_inferred_next_lane_from_unambiguous_metadata_surface"]
: [])
]
});
}
if (thinSurfaceRouteFamily === "movement_evidence") {
@@ -575,7 +633,12 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
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.",
extraReasons: primitiveSelection.reasonCodes
extraReasons: [
...primitiveSelection.reasonCodes,
...(inferredRouteFamilyFromMetadataSurfaceRef(input.metadataSurface)
? ["planner_inferred_next_lane_from_unambiguous_metadata_surface"]
: [])
]
});
}
if (thinSurfaceRouteFamily === "catalog_drilldown") {
@@ -595,7 +658,12 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
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.",
extraReasons: primitiveSelection.reasonCodes
extraReasons: [
...primitiveSelection.reasonCodes,
...(inferredRouteFamilyFromMetadataSurfaceRef(input.metadataSurface)
? ["planner_inferred_next_lane_from_unambiguous_metadata_surface"]
: [])
]
});
}
@@ -218,6 +218,31 @@ describe("assistant MCP discovery planner", () => {
expect(result.reason_codes).toContain("planner_selected_catalog_primitives_from_metadata_surface_search");
});
it("infers document evidence from an unambiguous document metadata surface even before a downstream lane is labeled", () => {
const result = planAssistantMcpDiscovery({
metadataSurface: {
selected_entity_set: "Document",
selected_surface_objects: ["Document.InvoiceIssued"],
downstream_route_family: null,
route_family_selection_basis: null,
recommended_next_primitive: null,
ambiguity_detected: false,
ambiguity_entity_sets: []
},
turnMeaning: {
explicit_entity_candidates: ["SVK"],
explicit_date_scope: "2020"
}
});
expect(result.planner_status).toBe("ready_for_execution");
expect(result.selected_chain_id).toBe("document_evidence");
expect(result.proposed_primitives).toEqual(["resolve_entity_reference", "query_documents", "probe_coverage"]);
expect(result.proposed_primitives).not.toContain("query_movements");
expect(result.reason_codes).toContain("planner_inferred_next_lane_from_unambiguous_metadata_surface");
expect(result.reason_codes).toContain("planner_surface_aware_next_lane_from_confirmed_metadata_objects");
});
it("builds a movement discovery plan without aggregating value-flow totals", () => {
const result = planAssistantMcpDiscovery({
dataNeedGraph: {
@@ -325,6 +350,31 @@ describe("assistant MCP discovery planner", () => {
expect(result.reason_codes).toContain("planner_selected_catalog_primitives_from_metadata_surface_search");
});
it("infers movement evidence from an unambiguous register metadata surface even before a downstream lane is labeled", () => {
const result = planAssistantMcpDiscovery({
metadataSurface: {
selected_entity_set: "AccumulationRegister",
selected_surface_objects: ["AccumulationRegister.BankOperations"],
downstream_route_family: null,
route_family_selection_basis: null,
recommended_next_primitive: null,
ambiguity_detected: false,
ambiguity_entity_sets: []
},
turnMeaning: {
explicit_entity_candidates: ["SVK"],
explicit_date_scope: "2020"
}
});
expect(result.planner_status).toBe("ready_for_execution");
expect(result.selected_chain_id).toBe("movement_evidence");
expect(result.proposed_primitives).toEqual(["resolve_entity_reference", "query_movements", "probe_coverage"]);
expect(result.proposed_primitives).not.toContain("query_documents");
expect(result.reason_codes).toContain("planner_inferred_next_lane_from_unambiguous_metadata_surface");
expect(result.reason_codes).toContain("planner_surface_aware_next_lane_from_confirmed_metadata_objects");
});
it("can select catalog drilldown directly from a confirmed catalog metadata surface when the follow-up itself is thin", () => {
const result = planAssistantMcpDiscovery({
metadataSurface: {