ARCH: продолжить metadata continuity для MCP discovery

This commit is contained in:
2026-04-21 22:14:12 +03:00
parent 561b4ea45c
commit d66e2bfb01
9 changed files with 348 additions and 26 deletions
@@ -112,6 +112,29 @@ function localizeLine(value) {
if (/^Counterparty monthly net value-flow breakdown was grouped by month over confirmed incoming and outgoing 1C rows$/i.test(value)) {
return "Помесячная нетто-раскладка сгруппирована только по подтвержденным входящим и исходящим строкам 1С.";
}
const metadataSurfaceMatch = value.match(/^Confirmed 1C metadata surface(?: for scope "([^"]+)")?: (\d+) rows and (\d+) matching objects$/i);
if (metadataSurfaceMatch) {
const scopePart = metadataSurfaceMatch[1] ? ` по области "${metadataSurfaceMatch[1]}"` : "";
return `В 1С подтверждена metadata-поверхность${scopePart}: ${metadataSurfaceMatch[2]} строк metadata-ответа и ${metadataSurfaceMatch[3]} совпавших объекта(ов).`;
}
const metadataObjectSetsMatch = value.match(/^Available metadata object sets: (.+)$/i);
if (metadataObjectSetsMatch) {
return `Доступные типы metadata-объектов: ${metadataObjectSetsMatch[1]}.`;
}
const metadataFieldsMatch = value.match(/^Available metadata fields\/sections: (.+)$/i);
if (metadataFieldsMatch) {
return `Доступные metadata-поля/секции: ${metadataFieldsMatch[1]}.`;
}
if (/^Detailed metadata fields were not returned by this MCP metadata probe$/i.test(value)) {
return "Эта MCP-проверка metadata не вернула детальный список полей.";
}
const noMatchingMetadataScopeMatch = value.match(/^No matching 1C metadata objects were confirmed for scope "([^"]+)"$/i);
if (noMatchingMetadataScopeMatch) {
return `В 1С не подтверждены metadata-объекты по области "${noMatchingMetadataScopeMatch[1]}".`;
}
if (/^No matching 1C metadata objects were confirmed by this MCP metadata probe$/i.test(value)) {
return "В 1С эта MCP-проверка не подтвердила подходящих metadata-объектов.";
}
if (/^Legal registration date is not proven by this MCP discovery pilot$/i.test(value)) {
return "Юридическая дата регистрации этим поиском не подтверждена.";
}
@@ -141,6 +141,13 @@ function mapPilotScopeToFollowupMeaning(pilotScope) {
unsupported: "counterparty_bidirectional_value_flow_or_netting"
};
}
if (pilotScope === "metadata_inspection_v1") {
return {
domain: "metadata",
action: "inspect_catalog",
unsupported: "1c_metadata_surface"
};
}
return {
domain: null,
action: null,
@@ -183,6 +190,7 @@ function collectFollowupDiscoverySeed(followupContext) {
const mapped = mapPilotScopeToFollowupMeaning(pilotScope).domain !== null
? mapPilotScopeToFollowupMeaning(pilotScope)
: mapAddressIntentToFollowupMeaning(previousIntent);
const discoveryEntities = collectEntityCandidates(followupContext?.previous_discovery_entity_candidates);
const counterparty = toNonEmptyString(previousFilters?.counterparty) ??
toNonEmptyString(rootFilters?.counterparty) ??
(toNonEmptyString(followupContext?.previous_anchor_type) === "counterparty"
@@ -201,6 +209,7 @@ function collectFollowupDiscoverySeed(followupContext) {
action: mapped.action,
unsupported: mapped.unsupported,
counterparty,
discoveryEntity: discoveryEntities[0] ?? null,
organization,
dateScope
};
@@ -227,6 +236,9 @@ function hasMetadataSignal(text) {
return (/(?:\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u044b|\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b|\u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u0438|\u043f\u043e\u043b(?:\u0435|\u044f)|registers?|documents?|catalogs?|fields?)/iu.test(text) &&
/(?:\u0435\u0441\u0442\u044c|\u0434\u043e\u0441\u0442\u0443\u043f\u043d|\u0432\s+1\u0441|available|exist)/iu.test(text));
}
function hasMetadataObjectHint(text) {
return /(?:\u0440\u0435\u0433\u0438\u0441\u0442\u0440(?:\u044b)?|\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442(?:\u044b)?|\u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a(?:\u0438)?|\u043f\u043e\u043b(?:\u0435|\u044f)|registers?|documents?|catalogs?|fields?)/iu.test(text);
}
function metadataActionFromRawText(text) {
if (/(?:\u043f\u043e\u043b(?:\u0435|\u044f)|field)/iu.test(text)) {
return "inspect_fields";
@@ -322,9 +334,13 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
!rawLifecycleSignal &&
!rawValueFlowSignal &&
(monthlyAggregationSignal || explicitDateScopeLiteralDetected || predecomposeDateScope));
const seededDomain = followupDiscoverySeedApplicable ? followupSeed.domain : null;
const seededAction = followupDiscoverySeedApplicable ? followupSeed.action : null;
const seededUnsupported = followupDiscoverySeedApplicable ? followupSeed.unsupported : null;
const metadataFollowupSeedApplicable = Boolean(followupSeed.domain === "metadata" &&
!rawLifecycleSignal &&
!rawValueFlowSignal &&
hasMetadataObjectHint(rawText));
const seededDomain = followupDiscoverySeedApplicable || metadataFollowupSeedApplicable ? followupSeed.domain : null;
const seededAction = followupDiscoverySeedApplicable || metadataFollowupSeedApplicable ? followupSeed.action : null;
const seededUnsupported = followupDiscoverySeedApplicable || metadataFollowupSeedApplicable ? followupSeed.unsupported : null;
const lifecycleSignal = rawLifecycleSignal || seededDomain === "counterparty_lifecycle";
const bidirectionalValueFlowSignal = !lifecycleSignal &&
(rawBidirectionalValueFlowSignal || seededAction === "net_value_flow");
@@ -338,11 +354,14 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
unsupported: unsupported ?? seededUnsupported,
lifecycleSignal,
valueFlowSignal,
metadataSignal: rawMetadataSignal
metadataSignal: rawMetadataSignal || metadataFollowupSeedApplicable
});
const entityCandidates = collectEntityCandidates(assistantTurnMeaning?.explicit_entity_candidates);
pushUnique(entityCandidates, predecomposeEntities.counterparty);
pushUnique(entityCandidates, followupSeed.counterparty);
if ((rawMetadataSignal || metadataFollowupSeedApplicable) && !followupSeed.counterparty) {
pushUnique(entityCandidates, followupSeed.discoveryEntity);
}
if (valueFlowSignal && !predecomposeEntities.counterparty && !followupSeed.counterparty) {
pushUnique(entityCandidates, predecomposeEntities.organization);
pushUnique(entityCandidates, followupSeed.organization);
@@ -356,7 +375,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
? "counterparty_lifecycle"
: valueFlowSignal
? "counterparty_value"
: rawMetadataSignal
: rawMetadataSignal || metadataFollowupSeedApplicable
? "metadata"
: rawDomain ?? seededDomain,
asked_action_family: lifecycleSignal
@@ -367,8 +386,8 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
: payoutSignal
? "payout"
: rawAction ?? seededAction ?? "turnover"
: rawMetadataSignal
? metadataActionFromRawText(rawText)
: rawMetadataSignal || metadataFollowupSeedApplicable
? metadataActionFromRawText(rawText) ?? seededAction
: rawAction ?? seededAction,
asked_aggregation_axis: monthlyAggregationSignal ? "month" : rawAggregationAxis,
explicit_entity_candidates: entityCandidates,
@@ -383,7 +402,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
: payoutSignal
? "counterparty_payouts_or_outflow"
: seededUnsupported ?? "counterparty_value_or_turnover"
: rawMetadataSignal
: rawMetadataSignal || metadataFollowupSeedApplicable
? "1c_metadata_surface"
: followupDiscoverySeedApplicable
? seededUnsupported
@@ -393,6 +412,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
lifecycleSignal ||
valueFlowSignal ||
rawMetadataSignal ||
metadataFollowupSeedApplicable ||
followupDiscoverySeedApplicable)
};
const cleanTurnMeaning = {};
@@ -424,15 +444,15 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
unsupported: unsupported ?? seededUnsupported,
lifecycleSignal,
valueFlowSignal,
metadataSignal: rawMetadataSignal,
metadataSignal: rawMetadataSignal || metadataFollowupSeedApplicable,
semanticDataNeed,
explicitIntentCandidate,
followupDiscoverySeedApplicable
followupDiscoverySeedApplicable: followupDiscoverySeedApplicable || metadataFollowupSeedApplicable
});
const hasTurnMeaning = Object.keys(cleanTurnMeaning).length > 0;
const sourceSignal = assistantTurnMeaning
? "assistant_turn_meaning"
: followupDiscoverySeedApplicable
: followupDiscoverySeedApplicable || metadataFollowupSeedApplicable
? "followup_context"
: predecomposeContract
? "predecompose_contract"
@@ -440,7 +460,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
? "raw_text"
: valueFlowSignal
? "raw_text"
: rawMetadataSignal
: rawMetadataSignal || metadataFollowupSeedApplicable
? "raw_text"
: "none";
if (lifecycleSignal) {
@@ -464,6 +484,9 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
if (followupDiscoverySeedApplicable) {
pushReason(reasonCodes, "mcp_discovery_seeded_from_followup_context");
}
if (metadataFollowupSeedApplicable) {
pushReason(reasonCodes, "mcp_discovery_metadata_seeded_from_followup_context");
}
if (unsupported) {
pushReason(reasonCodes, "mcp_discovery_unsupported_but_understood_turn");
}
@@ -34,8 +34,30 @@ function periodPartForRecap(scopedDate) {
}
return /^\d{2}\.\d{2}\.\d{4}$/.test(scopedDate) ? ` на ${scopedDate}` : ` за период ${scopedDate}`;
}
function readDiscoveryMetadataScope(debug) {
const discoveryEntry = toRecordObject(debug?.assistant_mcp_discovery_entry_point_v1);
const bridge = toRecordObject(discoveryEntry?.bridge);
const pilot = toRecordObject(bridge?.pilot);
const surface = toRecordObject(pilot?.derived_metadata_surface);
const surfaceScope = toNonEmptyString(surface?.metadata_scope);
if (surfaceScope) {
return surfaceScope;
}
const turnInput = toRecordObject(discoveryEntry?.turn_input);
const turnMeaningRef = toRecordObject(turnInput?.turn_meaning_ref);
const entityCandidates = Array.isArray(turnMeaningRef?.explicit_entity_candidates)
? turnMeaningRef.explicit_entity_candidates
: [];
for (const candidate of entityCandidates) {
const text = toNonEmptyString(candidate);
if (text) {
return text;
}
}
return null;
}
function buildDiscoveryRecapFactLine(input) {
if (!input.debug || !input.counterparty) {
if (!input.debug) {
return null;
}
const pilotScope = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryPilotScope)(input.debug, toNonEmptyString);
@@ -43,6 +65,34 @@ function buildDiscoveryRecapFactLine(input) {
const bridge = toRecordObject(discoveryEntry?.bridge);
const pilot = toRecordObject(bridge?.pilot);
const periodPart = periodPartForRecap(input.scopedDate);
if (pilotScope === "metadata_inspection_v1") {
const metadataScope = readDiscoveryMetadataScope(input.debug);
const surface = toRecordObject(pilot?.derived_metadata_surface);
const entitySets = Array.isArray(surface?.available_entity_sets)
? surface.available_entity_sets
.map((item) => toNonEmptyString(item))
.filter((item) => Boolean(item))
: [];
const fields = Array.isArray(surface?.available_fields)
? surface.available_fields
.map((item) => toNonEmptyString(item))
.filter((item) => Boolean(item))
: [];
const objects = Array.isArray(surface?.matched_objects)
? surface.matched_objects
.map((item) => toNonEmptyString(item))
.filter((item) => Boolean(item))
: [];
const rows = Number(surface?.matched_rows ?? 0);
const scopePart = metadataScope ? ` по области «${metadataScope}»` : "";
const objectsPart = objects.length > 0 ? `, нашли объекты ${objects.slice(0, 4).join(", ")}` : "";
const entitySetsPart = entitySets.length > 0 ? `, видны типы ${entitySets.slice(0, 4).join(", ")}` : "";
const fieldsPart = fields.length > 0 ? `, доступны поля/секции ${fields.slice(0, 5).join(", ")}` : "";
return `смотрели metadata-поверхность 1С${scopePart}${periodPart}: ${rows} подтвержденных строк${objectsPart}${entitySetsPart}${fieldsPart}`.trim();
}
if (!input.counterparty) {
return null;
}
if (pilotScope === "counterparty_lifecycle_query_documents_v1") {
const activityPeriod = toRecordObject(pilot?.derived_activity_period);
const duration = toNonEmptyString(activityPeriod?.duration_human_ru);