ARCH: заземлить metadata surface в следующий MCP lane
This commit is contained in:
@@ -122,6 +122,12 @@ export interface AssistantMcpDiscoveryDerivedMetadataSurface {
|
||||
matched_rows: number;
|
||||
available_entity_sets: string[];
|
||||
matched_objects: string[];
|
||||
selected_entity_set: string | null;
|
||||
selected_surface_objects: string[];
|
||||
downstream_route_family: "document_evidence" | "movement_evidence" | "catalog_drilldown" | null;
|
||||
recommended_next_primitive: "query_documents" | "query_movements" | "drilldown_related_objects" | null;
|
||||
ambiguity_detected: boolean;
|
||||
ambiguity_entity_sets: string[];
|
||||
available_fields: string[];
|
||||
known_limitations: string[];
|
||||
inference_basis: "confirmed_1c_metadata_surface_rows";
|
||||
@@ -741,6 +747,101 @@ function metadataAvailableFields(rows: Array<Record<string, unknown>>): string[]
|
||||
return result;
|
||||
}
|
||||
|
||||
function normalizeMetadataEntitySetToken(value: string): string {
|
||||
return String(value ?? "")
|
||||
.toLowerCase()
|
||||
.replace(/[\s_.-]+/g, "");
|
||||
}
|
||||
|
||||
function metadataMatchesRequestedType(entitySet: string, requestedMetaType: string): boolean {
|
||||
const entityToken = normalizeMetadataEntitySetToken(entitySet);
|
||||
const requestedToken = normalizeMetadataEntitySetToken(requestedMetaType);
|
||||
return entityToken.includes(requestedToken) || requestedToken.includes(entityToken);
|
||||
}
|
||||
|
||||
function metadataRouteFamilyForEntitySet(
|
||||
entitySet: string
|
||||
): "document_evidence" | "movement_evidence" | "catalog_drilldown" | null {
|
||||
const token = normalizeMetadataEntitySetToken(entitySet);
|
||||
if (token.includes("документ") || token.includes("document")) {
|
||||
return "document_evidence";
|
||||
}
|
||||
if (
|
||||
token.includes("регистрнакопления") ||
|
||||
token.includes("регистсведений") ||
|
||||
token.includes("регистрсведений") ||
|
||||
token.includes("accumulationregister") ||
|
||||
token.includes("informationregister")
|
||||
) {
|
||||
return "movement_evidence";
|
||||
}
|
||||
if (token.includes("справочник") || token.includes("catalog") || token.includes("directory")) {
|
||||
return "catalog_drilldown";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function metadataNextPrimitiveForRouteFamily(
|
||||
routeFamily: "document_evidence" | "movement_evidence" | "catalog_drilldown" | null
|
||||
): "query_documents" | "query_movements" | "drilldown_related_objects" | 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 selectMetadataEntityGrounding(
|
||||
availableEntitySets: string[],
|
||||
requestedMetaTypes: string[]
|
||||
): {
|
||||
selectedEntitySet: string | null;
|
||||
ambiguityDetected: boolean;
|
||||
ambiguityEntitySets: string[];
|
||||
} {
|
||||
const requestedMatches = availableEntitySets.filter((entitySet) =>
|
||||
requestedMetaTypes.some((requestedMetaType) => metadataMatchesRequestedType(entitySet, requestedMetaType))
|
||||
);
|
||||
if (requestedMatches.length === 1) {
|
||||
return {
|
||||
selectedEntitySet: requestedMatches[0] ?? null,
|
||||
ambiguityDetected: false,
|
||||
ambiguityEntitySets: []
|
||||
};
|
||||
}
|
||||
if (requestedMatches.length > 1) {
|
||||
return {
|
||||
selectedEntitySet: null,
|
||||
ambiguityDetected: true,
|
||||
ambiguityEntitySets: requestedMatches
|
||||
};
|
||||
}
|
||||
if (availableEntitySets.length === 1) {
|
||||
return {
|
||||
selectedEntitySet: availableEntitySets[0] ?? null,
|
||||
ambiguityDetected: false,
|
||||
ambiguityEntitySets: []
|
||||
};
|
||||
}
|
||||
return {
|
||||
selectedEntitySet: null,
|
||||
ambiguityDetected: availableEntitySets.length > 1,
|
||||
ambiguityEntitySets: availableEntitySets
|
||||
};
|
||||
}
|
||||
|
||||
function metadataObjectsForEntitySet(entitySet: string | null, matchedObjects: string[]): string[] {
|
||||
if (!entitySet) {
|
||||
return [];
|
||||
}
|
||||
return matchedObjects.filter((item) => item.startsWith(`${entitySet}.`) || item.includes(entitySet));
|
||||
}
|
||||
|
||||
function deriveMetadataSurface(
|
||||
result: AddressMcpMetadataRowsResult | null,
|
||||
metadataScope: string | null,
|
||||
@@ -761,14 +862,30 @@ function deriveMetadataSurface(
|
||||
pushUnique(availableEntitySets, entitySet);
|
||||
}
|
||||
}
|
||||
const grounding = selectMetadataEntityGrounding(availableEntitySets, requestedMetaTypes);
|
||||
const downstreamRouteFamily = grounding.selectedEntitySet
|
||||
? metadataRouteFamilyForEntitySet(grounding.selectedEntitySet)
|
||||
: null;
|
||||
const knownLimitations: string[] = [];
|
||||
if (grounding.ambiguityDetected && grounding.ambiguityEntitySets.length > 0) {
|
||||
knownLimitations.push(
|
||||
`Exact downstream metadata surface remains ambiguous across: ${grounding.ambiguityEntitySets.join(", ")}`
|
||||
);
|
||||
}
|
||||
return {
|
||||
metadata_scope: metadataScope,
|
||||
requested_meta_types: requestedMetaTypes,
|
||||
matched_rows: result.rows.length,
|
||||
available_entity_sets: availableEntitySets,
|
||||
matched_objects: matchedObjects,
|
||||
selected_entity_set: grounding.selectedEntitySet,
|
||||
selected_surface_objects: metadataObjectsForEntitySet(grounding.selectedEntitySet, matchedObjects),
|
||||
downstream_route_family: downstreamRouteFamily,
|
||||
recommended_next_primitive: metadataNextPrimitiveForRouteFamily(downstreamRouteFamily),
|
||||
ambiguity_detected: grounding.ambiguityDetected,
|
||||
ambiguity_entity_sets: grounding.ambiguityEntitySets,
|
||||
available_fields: metadataAvailableFields(result.rows),
|
||||
known_limitations: [],
|
||||
known_limitations: knownLimitations,
|
||||
inference_basis: "confirmed_1c_metadata_surface_rows"
|
||||
};
|
||||
}
|
||||
@@ -787,17 +904,37 @@ function buildMetadataConfirmedFacts(
|
||||
if (surface.available_entity_sets.length > 0) {
|
||||
facts.push(`Available metadata object sets: ${surface.available_entity_sets.join(", ")}`);
|
||||
}
|
||||
if (surface.selected_entity_set) {
|
||||
facts.push(`Selected metadata entity set: ${surface.selected_entity_set}`);
|
||||
}
|
||||
if (surface.selected_surface_objects.length > 0) {
|
||||
facts.push(`Selected metadata objects: ${surface.selected_surface_objects.slice(0, 8).join(", ")}`);
|
||||
}
|
||||
if (surface.available_fields.length > 0) {
|
||||
facts.push(`Available metadata fields/sections: ${surface.available_fields.slice(0, 12).join(", ")}`);
|
||||
}
|
||||
return facts;
|
||||
}
|
||||
|
||||
function buildMetadataInferredFacts(
|
||||
surface: AssistantMcpDiscoveryDerivedMetadataSurface | null
|
||||
): string[] {
|
||||
if (!surface || !surface.selected_entity_set || !surface.downstream_route_family || !surface.recommended_next_primitive) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
`A likely next checked lane may be inferred as ${surface.downstream_route_family} from the confirmed metadata surface`
|
||||
];
|
||||
}
|
||||
|
||||
function buildMetadataUnknownFacts(
|
||||
surface: AssistantMcpDiscoveryDerivedMetadataSurface | null,
|
||||
metadataScope: string | null
|
||||
): string[] {
|
||||
if (surface) {
|
||||
if (surface.ambiguity_detected && surface.ambiguity_entity_sets.length > 0) {
|
||||
return [...surface.known_limitations];
|
||||
}
|
||||
if (surface.available_fields.length > 0) {
|
||||
return [];
|
||||
}
|
||||
@@ -1466,6 +1603,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||
plan: planner.discovery_plan,
|
||||
probeResults,
|
||||
confirmedFacts: buildMetadataConfirmedFacts(derivedMetadataSurface),
|
||||
inferredFacts: buildMetadataInferredFacts(derivedMetadataSurface),
|
||||
unknownFacts: buildMetadataUnknownFacts(derivedMetadataSurface, metadataScope),
|
||||
sourceRowsSummary,
|
||||
queryLimitations,
|
||||
|
||||
Reference in New Issue
Block a user