Planner Autonomy: закрепить metadata lane scoring
This commit is contained in:
@@ -120,13 +120,13 @@ function mergeCatalogPrimitivesWithFallback(catalogPrimitives, fallbackPrimitive
|
||||
return result;
|
||||
}
|
||||
function preferredPrimitiveFromMetadataSurface(surface) {
|
||||
if (surface?.ambiguity_detected) {
|
||||
return null;
|
||||
}
|
||||
const recommendedPrimitive = surface?.recommended_next_primitive ?? null;
|
||||
if (recommendedPrimitive) {
|
||||
return recommendedPrimitive;
|
||||
}
|
||||
if (surface?.ambiguity_detected) {
|
||||
return null;
|
||||
}
|
||||
if (surface?.downstream_route_family === "document_evidence") {
|
||||
return "query_documents";
|
||||
}
|
||||
@@ -138,11 +138,30 @@ function preferredPrimitiveFromMetadataSurface(surface) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function preferredPrimitiveFromExplicitDataNeedGraph(graph) {
|
||||
const factFamily = lower(graph?.business_fact_family);
|
||||
const actionFamily = lower(graph?.action_family);
|
||||
if (factFamily === "document_evidence" || actionFamily === "list_documents") {
|
||||
return "query_documents";
|
||||
}
|
||||
if (factFamily === "movement_evidence" || actionFamily === "list_movements") {
|
||||
return "query_movements";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function hasCarriedMetadataSurfaceScoringEvidence(surface) {
|
||||
return Boolean(surface &&
|
||||
(toNonEmptyString(surface.selected_entity_set) ||
|
||||
surface.selected_surface_objects.length > 0 ||
|
||||
surface.ambiguity_entity_sets.length > 0));
|
||||
}
|
||||
function filterCatalogPrimitivesByMetadataSurface(input) {
|
||||
const preferredPrimitive = preferredPrimitiveFromMetadataSurface(input.metadataSurface);
|
||||
const surfacePreferredPrimitive = preferredPrimitiveFromMetadataSurface(input.metadataSurface);
|
||||
const dataNeedPreferredPrimitive = preferredPrimitiveFromExplicitDataNeedGraph(input.dataNeedGraph);
|
||||
const preferredPrimitive = surfacePreferredPrimitive ?? dataNeedPreferredPrimitive;
|
||||
const hasSurfaceScoringEvidence = hasCarriedMetadataSurfaceScoringEvidence(input.metadataSurface);
|
||||
const reasonCodes = [];
|
||||
if (!preferredPrimitive ||
|
||||
input.metadataSurface?.ambiguity_detected ||
|
||||
!input.fallbackPrimitives.includes(preferredPrimitive)) {
|
||||
return {
|
||||
primitives: input.catalogPrimitives,
|
||||
@@ -156,11 +175,15 @@ function filterCatalogPrimitivesByMetadataSurface(input) {
|
||||
]);
|
||||
const filteredPrimitives = input.catalogPrimitives.filter((primitive) => !laneSensitivePrimitives.has(primitive) || primitive === preferredPrimitive);
|
||||
if (filteredPrimitives.length !== input.catalogPrimitives.length) {
|
||||
reasonCodes.push("planner_filtered_catalog_primitives_by_confirmed_metadata_surface");
|
||||
reasonCodes.push(surfacePreferredPrimitive
|
||||
? "planner_filtered_catalog_primitives_by_confirmed_metadata_surface"
|
||||
: "planner_filtered_catalog_primitives_by_explicit_data_need_lane");
|
||||
}
|
||||
if ((filteredPrimitives.includes(preferredPrimitive) || input.fallbackPrimitives.includes(preferredPrimitive)) &&
|
||||
input.metadataSurface?.selected_surface_objects.length) {
|
||||
reasonCodes.push("planner_surface_aware_next_lane_from_confirmed_metadata_objects");
|
||||
hasSurfaceScoringEvidence) {
|
||||
reasonCodes.push(surfacePreferredPrimitive
|
||||
? "planner_surface_aware_next_lane_from_confirmed_metadata_objects"
|
||||
: "planner_metadata_surface_scored_with_explicit_lane_family");
|
||||
}
|
||||
return {
|
||||
primitives: filteredPrimitives,
|
||||
@@ -226,6 +249,7 @@ function selectPrimitivesFromGraphAndCatalog(input) {
|
||||
const filteredCatalogPrimitives = filterCatalogPrimitivesByMetadataSurface({
|
||||
catalogPrimitives: combinedCatalogPrimitives,
|
||||
fallbackPrimitives: input.fallbackPrimitives,
|
||||
dataNeedGraph: input.dataNeedGraph,
|
||||
metadataSurface: input.metadataSurface
|
||||
});
|
||||
reasonCodes.push(...filteredCatalogPrimitives.reasonCodes);
|
||||
|
||||
@@ -243,13 +243,13 @@ function mergeCatalogPrimitivesWithFallback(
|
||||
function preferredPrimitiveFromMetadataSurface(
|
||||
surface: AssistantMcpDiscoveryMetadataSurfaceRef | null | undefined
|
||||
): AssistantMcpDiscoveryPrimitive | null {
|
||||
if (surface?.ambiguity_detected) {
|
||||
return null;
|
||||
}
|
||||
const recommendedPrimitive = surface?.recommended_next_primitive ?? null;
|
||||
if (recommendedPrimitive) {
|
||||
return recommendedPrimitive;
|
||||
}
|
||||
if (surface?.ambiguity_detected) {
|
||||
return null;
|
||||
}
|
||||
if (surface?.downstream_route_family === "document_evidence") {
|
||||
return "query_documents";
|
||||
}
|
||||
@@ -262,16 +262,44 @@ function preferredPrimitiveFromMetadataSurface(
|
||||
return null;
|
||||
}
|
||||
|
||||
function preferredPrimitiveFromExplicitDataNeedGraph(
|
||||
graph: AssistantMcpDiscoveryDataNeedGraphContract | null | undefined
|
||||
): AssistantMcpDiscoveryPrimitive | null {
|
||||
const factFamily = lower(graph?.business_fact_family);
|
||||
const actionFamily = lower(graph?.action_family);
|
||||
if (factFamily === "document_evidence" || actionFamily === "list_documents") {
|
||||
return "query_documents";
|
||||
}
|
||||
if (factFamily === "movement_evidence" || actionFamily === "list_movements") {
|
||||
return "query_movements";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function hasCarriedMetadataSurfaceScoringEvidence(
|
||||
surface: AssistantMcpDiscoveryMetadataSurfaceRef | null | undefined
|
||||
): boolean {
|
||||
return Boolean(
|
||||
surface &&
|
||||
(toNonEmptyString(surface.selected_entity_set) ||
|
||||
surface.selected_surface_objects.length > 0 ||
|
||||
surface.ambiguity_entity_sets.length > 0)
|
||||
);
|
||||
}
|
||||
|
||||
function filterCatalogPrimitivesByMetadataSurface(input: {
|
||||
catalogPrimitives: AssistantMcpDiscoveryPrimitive[];
|
||||
fallbackPrimitives: AssistantMcpDiscoveryPrimitive[];
|
||||
dataNeedGraph?: AssistantMcpDiscoveryDataNeedGraphContract | null;
|
||||
metadataSurface?: AssistantMcpDiscoveryMetadataSurfaceRef | null;
|
||||
}): { primitives: AssistantMcpDiscoveryPrimitive[]; reasonCodes: string[] } {
|
||||
const preferredPrimitive = preferredPrimitiveFromMetadataSurface(input.metadataSurface);
|
||||
const surfacePreferredPrimitive = preferredPrimitiveFromMetadataSurface(input.metadataSurface);
|
||||
const dataNeedPreferredPrimitive = preferredPrimitiveFromExplicitDataNeedGraph(input.dataNeedGraph);
|
||||
const preferredPrimitive = surfacePreferredPrimitive ?? dataNeedPreferredPrimitive;
|
||||
const hasSurfaceScoringEvidence = hasCarriedMetadataSurfaceScoringEvidence(input.metadataSurface);
|
||||
const reasonCodes: string[] = [];
|
||||
if (
|
||||
!preferredPrimitive ||
|
||||
input.metadataSurface?.ambiguity_detected ||
|
||||
!input.fallbackPrimitives.includes(preferredPrimitive)
|
||||
) {
|
||||
return {
|
||||
@@ -289,13 +317,21 @@ function filterCatalogPrimitivesByMetadataSurface(input: {
|
||||
(primitive) => !laneSensitivePrimitives.has(primitive) || primitive === preferredPrimitive
|
||||
);
|
||||
if (filteredPrimitives.length !== input.catalogPrimitives.length) {
|
||||
reasonCodes.push("planner_filtered_catalog_primitives_by_confirmed_metadata_surface");
|
||||
reasonCodes.push(
|
||||
surfacePreferredPrimitive
|
||||
? "planner_filtered_catalog_primitives_by_confirmed_metadata_surface"
|
||||
: "planner_filtered_catalog_primitives_by_explicit_data_need_lane"
|
||||
);
|
||||
}
|
||||
if (
|
||||
(filteredPrimitives.includes(preferredPrimitive) || input.fallbackPrimitives.includes(preferredPrimitive)) &&
|
||||
input.metadataSurface?.selected_surface_objects.length
|
||||
hasSurfaceScoringEvidence
|
||||
) {
|
||||
reasonCodes.push("planner_surface_aware_next_lane_from_confirmed_metadata_objects");
|
||||
reasonCodes.push(
|
||||
surfacePreferredPrimitive
|
||||
? "planner_surface_aware_next_lane_from_confirmed_metadata_objects"
|
||||
: "planner_metadata_surface_scored_with_explicit_lane_family"
|
||||
);
|
||||
}
|
||||
return {
|
||||
primitives: filteredPrimitives,
|
||||
@@ -374,6 +410,7 @@ function selectPrimitivesFromGraphAndCatalog(input: {
|
||||
const filteredCatalogPrimitives = filterCatalogPrimitivesByMetadataSurface({
|
||||
catalogPrimitives: combinedCatalogPrimitives,
|
||||
fallbackPrimitives: input.fallbackPrimitives,
|
||||
dataNeedGraph: input.dataNeedGraph,
|
||||
metadataSurface: input.metadataSurface
|
||||
});
|
||||
reasonCodes.push(...filteredCatalogPrimitives.reasonCodes);
|
||||
|
||||
@@ -351,7 +351,7 @@ describe("assistant MCP discovery planner", () => {
|
||||
expect(result.reason_codes).toContain("planner_selected_catalog_primitives_from_metadata_surface_search");
|
||||
});
|
||||
|
||||
it("does not force a lane from ambiguous metadata surface even when decomposition hints mention both documents and movements", () => {
|
||||
it("scores an explicit document data-need over an ambiguous metadata surface without carrying movement primitives", () => {
|
||||
const result = planAssistantMcpDiscovery({
|
||||
dataNeedGraph: {
|
||||
schema_version: "assistant_data_need_graph_v1",
|
||||
@@ -389,9 +389,55 @@ describe("assistant MCP discovery planner", () => {
|
||||
|
||||
expect(result.planner_status).toBe("ready_for_execution");
|
||||
expect(result.reason_codes).toContain("planner_consumed_metadata_surface_ref_v1");
|
||||
expect(result.reason_codes).toContain("planner_filtered_catalog_primitives_by_explicit_data_need_lane");
|
||||
expect(result.reason_codes).toContain("planner_metadata_surface_scored_with_explicit_lane_family");
|
||||
expect(result.reason_codes).not.toContain("planner_filtered_catalog_primitives_by_confirmed_metadata_surface");
|
||||
expect(result.proposed_primitives).toContain("query_documents");
|
||||
expect(result.proposed_primitives).toContain("query_movements");
|
||||
expect(result.proposed_primitives).toEqual(["resolve_entity_reference", "query_documents", "probe_coverage"]);
|
||||
expect(result.proposed_primitives).not.toContain("query_movements");
|
||||
});
|
||||
|
||||
it("scores an explicit movement data-need over an ambiguous metadata surface without carrying document primitives", () => {
|
||||
const result = planAssistantMcpDiscovery({
|
||||
dataNeedGraph: {
|
||||
schema_version: "assistant_data_need_graph_v1",
|
||||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||||
subject_candidates: ["SVK"],
|
||||
business_fact_family: "movement_evidence",
|
||||
action_family: "list_movements",
|
||||
aggregation_need: null,
|
||||
time_scope_need: "explicit_period",
|
||||
comparison_need: null,
|
||||
ranking_need: null,
|
||||
proof_expectation: "coverage_checked_fact",
|
||||
clarification_gaps: [],
|
||||
decomposition_candidates: ["fetch_scoped_documents", "fetch_scoped_movements", "probe_coverage"],
|
||||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_unchecked_fact_totals"],
|
||||
reason_codes: ["data_need_graph_built"]
|
||||
},
|
||||
metadataSurface: {
|
||||
selected_entity_set: null,
|
||||
selected_surface_objects: ["Document.InvoiceIssued", "Register.BankOperations"],
|
||||
downstream_route_family: null,
|
||||
route_family_selection_basis: null,
|
||||
recommended_next_primitive: null,
|
||||
ambiguity_detected: true,
|
||||
ambiguity_entity_sets: ["Document", "AccumulationRegister"]
|
||||
},
|
||||
turnMeaning: {
|
||||
asked_domain_family: "movements",
|
||||
asked_action_family: "list_movements",
|
||||
explicit_entity_candidates: ["SVK"],
|
||||
explicit_date_scope: "2020",
|
||||
unsupported_but_understood_family: "movement_evidence"
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.planner_status).toBe("ready_for_execution");
|
||||
expect(result.selected_chain_id).toBe("movement_evidence");
|
||||
expect(result.reason_codes).toContain("planner_filtered_catalog_primitives_by_explicit_data_need_lane");
|
||||
expect(result.reason_codes).toContain("planner_metadata_surface_scored_with_explicit_lane_family");
|
||||
expect(result.proposed_primitives).toEqual(["resolve_entity_reference", "query_movements", "probe_coverage"]);
|
||||
expect(result.proposed_primitives).not.toContain("query_documents");
|
||||
});
|
||||
|
||||
it("does not force a thin follow-up into a lane when the carried metadata surface is still ambiguous", () => {
|
||||
|
||||
Reference in New Issue
Block a user