ARCH: довести surface-aware planner и catalog search

This commit is contained in:
2026-04-23 10:25:03 +03:00
parent 8b6cd4c329
commit 3a89cca6bb
30 changed files with 2521 additions and 157 deletions
@@ -149,7 +149,13 @@ export interface AssistantMcpDiscoveryDerivedMetadataSurface {
matched_objects: string[];
selected_entity_set: string | null;
selected_surface_objects: string[];
surface_family_scores: {
document_evidence: number;
movement_evidence: number;
catalog_drilldown: number;
};
downstream_route_family: "document_evidence" | "movement_evidence" | "catalog_drilldown" | null;
route_family_selection_basis: "selected_entity_set" | "dominant_surface_objects" | null;
recommended_next_primitive: "query_documents" | "query_movements" | "drilldown_related_objects" | null;
ambiguity_detected: boolean;
ambiguity_entity_sets: string[];
@@ -1193,6 +1199,34 @@ function metadataRouteFamilyForEntitySet(
return null;
}
function metadataRouteFamilyForEntitySetRelaxed(
entitySet: string
): "document_evidence" | "movement_evidence" | "catalog_drilldown" | null {
const strict = metadataRouteFamilyForEntitySet(entitySet);
if (strict) {
return strict;
}
const raw = String(entitySet ?? "").trim();
if (!raw) {
return null;
}
if (raw.includes("Документ") || raw.includes("Документ")) {
return "document_evidence";
}
if (
raw.includes("РегистрНакопления") ||
raw.includes("РегистрСведений") ||
raw.includes("РегистрНакопления") ||
raw.includes("РегистрСведений")
) {
return "movement_evidence";
}
if (raw.includes("Справочник") || raw.includes("Справочник")) {
return "catalog_drilldown";
}
return null;
}
function metadataNextPrimitiveForRouteFamily(
routeFamily: "document_evidence" | "movement_evidence" | "catalog_drilldown" | null
): "query_documents" | "query_movements" | "drilldown_related_objects" | null {
@@ -1254,6 +1288,63 @@ function metadataObjectsForEntitySet(entitySet: string | null, matchedObjects: s
return matchedObjects.filter((item) => item.startsWith(`${entitySet}.`) || item.includes(entitySet));
}
function emptyMetadataSurfaceFamilyScores(): AssistantMcpDiscoveryDerivedMetadataSurface["surface_family_scores"] {
return {
document_evidence: 0,
movement_evidence: 0,
catalog_drilldown: 0
};
}
function metadataSurfaceFamilyScores(
matchedObjects: string[]
): AssistantMcpDiscoveryDerivedMetadataSurface["surface_family_scores"] {
const scores = emptyMetadataSurfaceFamilyScores();
for (const objectName of matchedObjects) {
const entitySet = inferMetadataEntitySetFromObjectName(objectName);
const routeFamily = entitySet ? metadataRouteFamilyForEntitySetRelaxed(entitySet) : null;
if (routeFamily) {
scores[routeFamily] += 1;
}
}
return scores;
}
function metadataObjectsForRouteFamily(
routeFamily: "document_evidence" | "movement_evidence" | "catalog_drilldown" | null,
matchedObjects: string[]
): string[] {
if (!routeFamily) {
return [];
}
return matchedObjects.filter((objectName) => {
const entitySet = inferMetadataEntitySetFromObjectName(objectName);
return entitySet ? metadataRouteFamilyForEntitySetRelaxed(entitySet) === routeFamily : false;
});
}
function selectMetadataRouteFamilyFromSurfaceScores(
scores: AssistantMcpDiscoveryDerivedMetadataSurface["surface_family_scores"]
): "document_evidence" | "movement_evidence" | "catalog_drilldown" | null {
const ranked = (Object.entries(scores) as Array<
["document_evidence" | "movement_evidence" | "catalog_drilldown", number]
>)
.filter(([, score]) => score > 0)
.sort((left, right) => right[1] - left[1]);
const top = ranked[0];
const second = ranked[1];
if (!top) {
return null;
}
if (!second) {
return top[0];
}
const absoluteMargin = top[1] - second[1];
const relativeRatio = second[1] > 0 ? top[1] / second[1] : Number.POSITIVE_INFINITY;
const clearlyDominant = absoluteMargin >= 2 || relativeRatio >= 1.5;
return clearlyDominant ? top[0] : null;
}
function deriveMetadataSurface(
result: AddressMcpMetadataRowsResult | null,
metadataScope: string | null,
@@ -1275,15 +1366,34 @@ function deriveMetadataSurface(
}
}
const grounding = selectMetadataEntityGrounding(availableEntitySets, requestedMetaTypes);
const downstreamRouteFamily = grounding.selectedEntitySet
? metadataRouteFamilyForEntitySet(grounding.selectedEntitySet)
const surfaceFamilyScores = metadataSurfaceFamilyScores(matchedObjects);
const selectedEntitySetRouteFamily = grounding.selectedEntitySet
? metadataRouteFamilyForEntitySetRelaxed(grounding.selectedEntitySet)
: null;
const scoredRouteFamily =
selectedEntitySetRouteFamily === null ? selectMetadataRouteFamilyFromSurfaceScores(surfaceFamilyScores) : null;
const downstreamRouteFamily = selectedEntitySetRouteFamily ?? scoredRouteFamily;
const routeFamilySelectionBasis = selectedEntitySetRouteFamily
? "selected_entity_set"
: scoredRouteFamily
? "dominant_surface_objects"
: null;
const selectedSurfaceObjects =
grounding.selectedEntitySet !== null
? metadataObjectsForEntitySet(grounding.selectedEntitySet, matchedObjects)
: metadataObjectsForRouteFamily(downstreamRouteFamily, matchedObjects);
const knownLimitations: string[] = [];
if (grounding.ambiguityDetected && grounding.ambiguityEntitySets.length > 0) {
const ambiguityRemainsUnresolved = grounding.ambiguityDetected && !downstreamRouteFamily;
if (ambiguityRemainsUnresolved && grounding.ambiguityEntitySets.length > 0) {
knownLimitations.push(
`Exact downstream metadata surface remains ambiguous across: ${grounding.ambiguityEntitySets.join(", ")}`
);
}
if (grounding.ambiguityDetected && downstreamRouteFamily && routeFamilySelectionBasis === "dominant_surface_objects") {
knownLimitations.push(
`Metadata surface spans multiple object sets, but dominant confirmed objects point to ${downstreamRouteFamily}`
);
}
return {
metadata_scope: metadataScope,
requested_meta_types: requestedMetaTypes,
@@ -1291,11 +1401,13 @@ function deriveMetadataSurface(
available_entity_sets: availableEntitySets,
matched_objects: matchedObjects,
selected_entity_set: grounding.selectedEntitySet,
selected_surface_objects: metadataObjectsForEntitySet(grounding.selectedEntitySet, matchedObjects),
selected_surface_objects: selectedSurfaceObjects,
surface_family_scores: surfaceFamilyScores,
downstream_route_family: downstreamRouteFamily,
route_family_selection_basis: routeFamilySelectionBasis,
recommended_next_primitive: metadataNextPrimitiveForRouteFamily(downstreamRouteFamily),
ambiguity_detected: grounding.ambiguityDetected,
ambiguity_entity_sets: grounding.ambiguityEntitySets,
ambiguity_detected: ambiguityRemainsUnresolved,
ambiguity_entity_sets: ambiguityRemainsUnresolved ? grounding.ambiguityEntitySets : [],
available_fields: metadataAvailableFields(result.rows),
known_limitations: knownLimitations,
inference_basis: "confirmed_1c_metadata_surface_rows"
@@ -1322,6 +1434,15 @@ function buildMetadataConfirmedFacts(
if (surface.selected_surface_objects.length > 0) {
facts.push(`Selected metadata objects: ${surface.selected_surface_objects.slice(0, 8).join(", ")}`);
}
if (
surface.surface_family_scores.document_evidence > 0 ||
surface.surface_family_scores.movement_evidence > 0 ||
surface.surface_family_scores.catalog_drilldown > 0
) {
facts.push(
`Metadata surface family scores: document=${surface.surface_family_scores.document_evidence}, movement=${surface.surface_family_scores.movement_evidence}, catalog=${surface.surface_family_scores.catalog_drilldown}`
);
}
if (surface.available_fields.length > 0) {
facts.push(`Available metadata fields/sections: ${surface.available_fields.slice(0, 12).join(", ")}`);
}
@@ -1331,7 +1452,7 @@ function buildMetadataConfirmedFacts(
function buildMetadataInferredFacts(
surface: AssistantMcpDiscoveryDerivedMetadataSurface | null
): string[] {
if (!surface || !surface.selected_entity_set || !surface.downstream_route_family || !surface.recommended_next_primitive) {
if (!surface || !surface.downstream_route_family || !surface.recommended_next_primitive) {
return [];
}
return [
@@ -2304,6 +2425,9 @@ export async function executeAssistantMcpDiscoveryPilot(
const derivedMetadataSurface = deriveMetadataSurface(metadataResult, metadataScope, requestedMetaTypes);
if (derivedMetadataSurface) {
pushReason(reasonCodes, "pilot_derived_metadata_surface_from_confirmed_rows");
if (derivedMetadataSurface.route_family_selection_basis === "dominant_surface_objects") {
pushReason(reasonCodes, "pilot_selected_metadata_route_family_from_dominant_surface_objects");
}
}
const evidence = resolveAssistantMcpDiscoveryEvidence({
plan: planner.discovery_plan,