ARCH: связать metadata grounding с movement lane и reusable follow-up
This commit is contained in:
parent
7ef788fa50
commit
eac3709f2b
|
|
@ -104,6 +104,9 @@ function mapAssistantMcpDiscoveryPilotScopeToAddressIntent(pilotScope, actionFam
|
|||
if (pilotScope === "counterparty_document_evidence_query_documents_v1") {
|
||||
return "list_documents_by_counterparty";
|
||||
}
|
||||
if (pilotScope === "counterparty_movement_evidence_query_movements_v1") {
|
||||
return "bank_operations_by_counterparty";
|
||||
}
|
||||
if (pilotScope === "counterparty_supplier_payout_query_movements_v1") {
|
||||
return "supplier_payouts_profile";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,9 @@ function isValueFlowPilot(pilot) {
|
|||
function isDocumentPilot(pilot) {
|
||||
return pilot.pilot_scope === "counterparty_document_evidence_query_documents_v1";
|
||||
}
|
||||
function isMovementPilot(pilot) {
|
||||
return pilot.pilot_scope === "counterparty_movement_evidence_query_movements_v1";
|
||||
}
|
||||
function isMetadataPilot(pilot) {
|
||||
return pilot.pilot_scope === "metadata_inspection_v1";
|
||||
}
|
||||
|
|
@ -85,6 +88,9 @@ function metadataRouteFamilyLabelRu(routeFamily) {
|
|||
function headlineFor(mode, pilot) {
|
||||
const askedMonthlyBreakdown = pilot.derived_bidirectional_value_flow?.aggregation_axis === "month" ||
|
||||
pilot.derived_value_flow?.aggregation_axis === "month";
|
||||
if (isMovementPilot(pilot) && mode === "confirmed_with_bounded_inference") {
|
||||
return "По данным 1С найдены строки движений; ответ ограничен проверенным периодом и найденными строками.";
|
||||
}
|
||||
if (pilot.derived_metadata_surface && mode === "confirmed_with_bounded_inference") {
|
||||
if (pilot.derived_metadata_surface.ambiguity_detected) {
|
||||
return "По метаданным 1С найдены конкурирующие schema-поверхности; перед следующим шагом нужно удержать неоднозначность явно.";
|
||||
|
|
@ -168,6 +174,10 @@ function buildMustNotClaim(pilot) {
|
|||
claims.push("Do not claim full document history outside the checked period.");
|
||||
claims.push("Do not present the confirmed document rows as a complete document universe.");
|
||||
}
|
||||
if (isMovementPilot(pilot)) {
|
||||
claims.push("Do not claim full movement history outside the checked period.");
|
||||
claims.push("Do not present the confirmed movement rows as a complete movement universe.");
|
||||
}
|
||||
if (isMetadataPilot(pilot)) {
|
||||
claims.push("Do not present metadata surface as confirmed business data rows.");
|
||||
claims.push("Do not claim a document/register exists outside the checked metadata probe results.");
|
||||
|
|
|
|||
|
|
@ -116,6 +116,20 @@ function isDocumentEvidencePilotEligible(planner) {
|
|||
return (planner.proposed_primitives.includes("query_documents") &&
|
||||
(combined.includes("document") || combined.includes("list_documents")));
|
||||
}
|
||||
function isMovementEvidencePilotEligible(planner) {
|
||||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||||
const domain = String(meaning?.asked_domain_family ?? "").toLowerCase();
|
||||
const action = String(meaning?.asked_action_family ?? "").toLowerCase();
|
||||
const unsupported = String(meaning?.unsupported_but_understood_family ?? "").toLowerCase();
|
||||
const semanticNeed = String(planner.semantic_data_need ?? "").toLowerCase();
|
||||
const combined = `${domain} ${action} ${unsupported} ${semanticNeed}`;
|
||||
return (planner.proposed_primitives.includes("query_movements") &&
|
||||
(combined.includes("movement") ||
|
||||
combined.includes("movements") ||
|
||||
combined.includes("bank_operations") ||
|
||||
combined.includes("movement_evidence") ||
|
||||
combined.includes("list_movements")));
|
||||
}
|
||||
function isValueFlowPilotEligible(planner) {
|
||||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||||
const domain = String(meaning?.asked_domain_family ?? "").toLowerCase();
|
||||
|
|
@ -397,6 +411,15 @@ function summarizeDocumentRows(result) {
|
|||
}
|
||||
return `${result.fetched_rows} MCP document rows fetched, ${result.matched_rows} matched document scope`;
|
||||
}
|
||||
function summarizeMovementRows(result) {
|
||||
if (result.error) {
|
||||
return null;
|
||||
}
|
||||
if (result.fetched_rows <= 0) {
|
||||
return "0 MCP movement rows fetched";
|
||||
}
|
||||
return `${result.fetched_rows} MCP movement rows fetched, ${result.matched_rows} matched movement scope`;
|
||||
}
|
||||
function summarizeValueFlowRows(result) {
|
||||
if (result.error) {
|
||||
return null;
|
||||
|
|
@ -981,6 +1004,16 @@ function buildDocumentConfirmedFacts(result, counterparty) {
|
|||
: "1C document rows were found for the requested scope"
|
||||
];
|
||||
}
|
||||
function buildMovementConfirmedFacts(result, counterparty) {
|
||||
if (result.error || result.matched_rows <= 0) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
counterparty
|
||||
? `1C movement rows were found for counterparty ${counterparty}`
|
||||
: "1C movement rows were found for the requested scope"
|
||||
];
|
||||
}
|
||||
function buildValueFlowConfirmedFacts(result, counterparty, direction) {
|
||||
if (result.error || result.matched_rows <= 0) {
|
||||
return [];
|
||||
|
|
@ -1025,6 +1058,12 @@ function buildDocumentInferredFacts(result) {
|
|||
}
|
||||
return ["Counterparty document evidence is limited to confirmed 1C document rows in the checked scope"];
|
||||
}
|
||||
function buildMovementInferredFacts(result) {
|
||||
if (result.error || result.fetched_rows <= 0) {
|
||||
return [];
|
||||
}
|
||||
return ["Counterparty movement evidence is limited to confirmed 1C movement rows in the checked scope"];
|
||||
}
|
||||
function buildValueFlowInferredFacts(derived) {
|
||||
if (!derived) {
|
||||
return [];
|
||||
|
|
@ -1067,6 +1106,13 @@ function buildDocumentUnknownFacts(periodScope) {
|
|||
: "Full document history is not proven without an explicit checked period"
|
||||
];
|
||||
}
|
||||
function buildMovementUnknownFacts(periodScope) {
|
||||
return [
|
||||
periodScope
|
||||
? "Full movement history outside the checked period is not proven by this MCP discovery pilot"
|
||||
: "Full movement history is not proven without an explicit checked period"
|
||||
];
|
||||
}
|
||||
function buildValueFlowUnknownFacts(periodScope, direction, derived) {
|
||||
const unknownFacts = [];
|
||||
if (derived?.coverage_limited_by_probe_limit) {
|
||||
|
|
@ -1106,6 +1152,9 @@ function pilotScopeForPlanner(planner) {
|
|||
if (isMetadataPilotEligible(planner)) {
|
||||
return "metadata_inspection_v1";
|
||||
}
|
||||
if (isMovementEvidencePilotEligible(planner)) {
|
||||
return "counterparty_movement_evidence_query_movements_v1";
|
||||
}
|
||||
if (isValueFlowPilotEligible(planner)) {
|
||||
return valueFlowPilotProfile(planner).scope;
|
||||
}
|
||||
|
|
@ -1174,9 +1223,10 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
|||
}
|
||||
const metadataPilotEligible = isMetadataPilotEligible(planner);
|
||||
const documentPilotEligible = isDocumentEvidencePilotEligible(planner);
|
||||
const movementPilotEligible = isMovementEvidencePilotEligible(planner);
|
||||
const lifecyclePilotEligible = isLifecyclePilotEligible(planner);
|
||||
const valueFlowPilotEligible = isValueFlowPilotEligible(planner);
|
||||
if (!metadataPilotEligible && !documentPilotEligible && !lifecyclePilotEligible && !valueFlowPilotEligible) {
|
||||
if (!metadataPilotEligible && !documentPilotEligible && !movementPilotEligible && !lifecyclePilotEligible && !valueFlowPilotEligible) {
|
||||
pushReason(reasonCodes, "pilot_scope_unsupported_for_live_execution");
|
||||
for (const step of dryRun.execution_steps) {
|
||||
skippedPrimitives.push(step.primitive_id);
|
||||
|
|
@ -1346,6 +1396,86 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
|||
reason_codes: reasonCodes
|
||||
};
|
||||
}
|
||||
if (movementPilotEligible) {
|
||||
let queryResult = null;
|
||||
const filters = buildValueFlowFilters(planner);
|
||||
const selection = (0, addressRecipeCatalog_1.selectAddressRecipe)("bank_operations_by_counterparty", filters);
|
||||
if (!selection.selected_recipe) {
|
||||
pushReason(reasonCodes, "pilot_movement_recipe_not_available");
|
||||
const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "Movement-evidence recipe is not available");
|
||||
return {
|
||||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||||
pilot_status: "unsupported",
|
||||
pilot_scope: "counterparty_movement_evidence_query_movements_v1",
|
||||
dry_run: dryRun,
|
||||
mcp_execution_performed: false,
|
||||
executed_primitives: executedPrimitives,
|
||||
skipped_primitives: skippedPrimitives,
|
||||
probe_results: probeResults,
|
||||
evidence,
|
||||
source_rows_summary: null,
|
||||
derived_metadata_surface: null,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
query_limitations: ["Movement-evidence recipe is not available"],
|
||||
reason_codes: reasonCodes
|
||||
};
|
||||
}
|
||||
const recipePlan = (0, addressRecipeCatalog_1.buildAddressRecipePlan)(selection.selected_recipe, filters);
|
||||
for (const step of dryRun.execution_steps) {
|
||||
if (step.primitive_id !== "query_movements") {
|
||||
skippedPrimitives.push(step.primitive_id);
|
||||
probeResults.push(skippedProbeResult(step, "pilot_only_executes_query_movements"));
|
||||
continue;
|
||||
}
|
||||
queryResult = await runtimeDeps.executeAddressMcpQuery({
|
||||
query: recipePlan.query,
|
||||
limit: recipePlan.limit,
|
||||
account_scope: recipePlan.account_scope
|
||||
});
|
||||
executedPrimitives.push(step.primitive_id);
|
||||
probeResults.push(queryResultToProbeResult(step.primitive_id, queryResult));
|
||||
if (queryResult.error) {
|
||||
pushUnique(queryLimitations, queryResult.error);
|
||||
pushReason(reasonCodes, "pilot_query_movements_mcp_error");
|
||||
}
|
||||
else {
|
||||
pushReason(reasonCodes, "pilot_query_movements_mcp_executed");
|
||||
}
|
||||
}
|
||||
const sourceRowsSummary = queryResult ? summarizeMovementRows(queryResult) : null;
|
||||
const evidence = (0, assistantMcpDiscoveryPolicy_1.resolveAssistantMcpDiscoveryEvidence)({
|
||||
plan: planner.discovery_plan,
|
||||
probeResults,
|
||||
confirmedFacts: queryResult ? buildMovementConfirmedFacts(queryResult, counterparty) : [],
|
||||
inferredFacts: queryResult ? buildMovementInferredFacts(queryResult) : [],
|
||||
unknownFacts: buildMovementUnknownFacts(dateScope),
|
||||
sourceRowsSummary,
|
||||
queryLimitations,
|
||||
recommendedNextProbe: "explain_evidence_basis"
|
||||
});
|
||||
return {
|
||||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||||
pilot_status: "executed",
|
||||
pilot_scope: "counterparty_movement_evidence_query_movements_v1",
|
||||
dry_run: dryRun,
|
||||
mcp_execution_performed: executedPrimitives.length > 0,
|
||||
executed_primitives: executedPrimitives,
|
||||
skipped_primitives: skippedPrimitives,
|
||||
probe_results: probeResults,
|
||||
evidence,
|
||||
source_rows_summary: sourceRowsSummary,
|
||||
derived_metadata_surface: null,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
query_limitations: queryLimitations,
|
||||
reason_codes: reasonCodes
|
||||
};
|
||||
}
|
||||
if (valueFlowPilotEligible) {
|
||||
let queryResult = null;
|
||||
const filters = buildValueFlowFilters(planner);
|
||||
|
|
|
|||
|
|
@ -118,6 +118,15 @@ function recipeFor(input) {
|
|||
reason: "planner_selected_metadata_recipe"
|
||||
};
|
||||
}
|
||||
if (includesAny(combined, ["movement", "movements", "bank_operations", "movement_evidence", "list_movements"])) {
|
||||
pushUnique(axes, "coverage_target");
|
||||
return {
|
||||
semanticDataNeed: "movement evidence",
|
||||
primitives: ["resolve_entity_reference", "query_movements", "probe_coverage"],
|
||||
axes,
|
||||
reason: "planner_selected_movement_recipe"
|
||||
};
|
||||
}
|
||||
if (includesAny(combined, ["document", "documents"])) {
|
||||
pushUnique(axes, "coverage_target");
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -82,6 +82,13 @@ function localizeLine(value) {
|
|||
if (/^1C document rows were found for the requested scope$/i.test(value)) {
|
||||
return "В 1С найдены строки документов по запрошенному контуру.";
|
||||
}
|
||||
const movementRowsMatch = value.match(/^1C movement rows were found for counterparty\s+(.+)$/i);
|
||||
if (movementRowsMatch) {
|
||||
return `В 1С найдены строки движений по контрагенту ${movementRowsMatch[1]}.`;
|
||||
}
|
||||
if (/^1C movement rows were found for the requested scope$/i.test(value)) {
|
||||
return "В 1С найдены строки движений по запрошенному контуру.";
|
||||
}
|
||||
const supplierPayoutMatch = value.match(/^1C supplier-payout rows were found for counterparty\s+(.+)$/i);
|
||||
if (supplierPayoutMatch) {
|
||||
return `В 1С найдены строки исходящих платежей/списаний по контрагенту ${supplierPayoutMatch[1]}.`;
|
||||
|
|
@ -107,6 +114,9 @@ function localizeLine(value) {
|
|||
if (/^Counterparty document evidence is limited to confirmed 1C document rows in the checked scope$/i.test(value)) {
|
||||
return "Срез документов ограничен только подтвержденными строками документов в проверенном окне.";
|
||||
}
|
||||
if (/^Counterparty movement evidence is limited to confirmed 1C movement rows in the checked scope$/i.test(value)) {
|
||||
return "Срез движений ограничен только подтвержденными строками движений в проверенном окне.";
|
||||
}
|
||||
if (/^Counterparty value-flow total was calculated from confirmed 1C movement rows$/i.test(value)) {
|
||||
return "Сумма рассчитана только по подтвержденным строкам денежных движений в 1С.";
|
||||
}
|
||||
|
|
@ -187,6 +197,12 @@ function localizeLine(value) {
|
|||
if (/^Full document history is not proven without an explicit checked period$/i.test(value)) {
|
||||
return "Полный срез документов без явно проверенного периода не подтвержден.";
|
||||
}
|
||||
if (/^Full movement history outside the checked period is not proven by this MCP discovery pilot$/i.test(value)) {
|
||||
return "Полный исторический срез движений вне проверенного периода этим поиском не подтвержден.";
|
||||
}
|
||||
if (/^Full movement history is not proven without an explicit checked period$/i.test(value)) {
|
||||
return "Полный срез движений без явно проверенного периода не подтвержден.";
|
||||
}
|
||||
if (/^Full supplier-payout amount outside the checked period is not proven by this MCP discovery pilot$/i.test(value)) {
|
||||
return "Полный объем исходящих платежей вне проверенного периода этим поиском не подтвержден.";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,6 +120,13 @@ function mapPilotScopeToFollowupMeaning(pilotScope) {
|
|||
unsupported: "counterparty_lifecycle"
|
||||
};
|
||||
}
|
||||
if (pilotScope === "counterparty_movement_evidence_query_movements_v1") {
|
||||
return {
|
||||
domain: "movements",
|
||||
action: "list_movements",
|
||||
unsupported: "movement_evidence"
|
||||
};
|
||||
}
|
||||
if (pilotScope === "counterparty_supplier_payout_query_movements_v1") {
|
||||
return {
|
||||
domain: "counterparty_value",
|
||||
|
|
@ -176,6 +183,13 @@ function mapAddressIntentToFollowupMeaning(intent) {
|
|||
unsupported: "counterparty_value_or_turnover"
|
||||
};
|
||||
}
|
||||
if (intent === "bank_operations_by_counterparty") {
|
||||
return {
|
||||
domain: "movements",
|
||||
action: "list_movements",
|
||||
unsupported: "movement_evidence"
|
||||
};
|
||||
}
|
||||
return {
|
||||
domain: null,
|
||||
action: null,
|
||||
|
|
@ -245,6 +259,12 @@ function hasMetadataObjectHint(text) {
|
|||
function hasDocumentEvidenceFollowupSignal(text) {
|
||||
return /(?:\u043f\u043e\s+\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442(?:\u0430\u043c|\u044b)?|\u0434\u0430\u0432\u0430\u0439\s+\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442(?:\u044b)?|\u0438\u0449\u0438\s+\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442(?:\u044b)?|\u043f\u043e\u043a\u0430\u0436\u0438\s+\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442(?:\u044b)?|document(?:s)?\s+(?:then|next)?|(?:then|next)\s+documents?|go\s+to\s+documents?)/iu.test(text);
|
||||
}
|
||||
function hasMovementEvidenceFollowupSignal(text) {
|
||||
return /(?:\u043f\u043e\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f\u043c|\u0438\u044f)?|\u0434\u0430\u0432\u0430\u0439\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f|\u0438\u0435)|\u0438\u0449\u0438\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f|\u0438\u0435)|\u043f\u043e\u043a\u0430\u0436\u0438\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f|\u0438\u0435)|\u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a(?:\u0438\u0435|\u0438\u0439)\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f|\u0438\u0435)|movement(?:s)?\s+(?:then|next)?|(?:then|next)\s+movements?|go\s+to\s+movements?)/iu.test(text);
|
||||
}
|
||||
function hasMetadataDownstreamContinuationSignal(text) {
|
||||
return /(?:\u0434\u0430\u0432\u0430\u0439\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u0438\u0434(?:\u0435|\u0451)\u043c\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u043f\u043e\u0448\u043b(?:\u0438|\u0451\u043c)\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0439|\u0438\u0449\u0438\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u0438\u0449\u0438\s+\u0434\u0430\u043d\u043d\u044b\u0435|\u043f\u043e\u043a\u0430\u0436\u0438\s+\u0434\u0430\u043d\u043d\u044b\u0435|\u043f\u043e\u043a\u0430\u0436\u0438\s+\u0441\u0442\u0440\u043e\u043a\u0438|\u0433\u043b\u0443\u0431\u0436\u0435|\u0447\u0442\u043e\s+\u0434\u0430\u043b\u044c\u0448\u0435|continue|go\s+ahead|go\s+deeper|look\s+deeper|drill\s+down|show\s+(?:data|rows))/iu.test(text);
|
||||
}
|
||||
function metadataActionFromRawText(text) {
|
||||
if (/(?:\u043f\u043e\u043b(?:\u0435|\u044f)|field)/iu.test(text)) {
|
||||
return "inspect_fields";
|
||||
|
|
@ -289,6 +309,9 @@ function semanticNeedFor(input) {
|
|||
if (input.valueFlowSignal || /(?:turnover|revenue|payment|payout|value|net|netting|balance|cashflow)/iu.test(combined)) {
|
||||
return "counterparty value-flow evidence";
|
||||
}
|
||||
if (/(?:movement|movements|bank_operations|movement_evidence|list_movements)/iu.test(combined)) {
|
||||
return "movement evidence";
|
||||
}
|
||||
if (/(?:document|documents|list_documents)/iu.test(combined)) {
|
||||
return "document evidence";
|
||||
}
|
||||
|
|
@ -351,22 +374,52 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
|||
!rawLifecycleSignal &&
|
||||
!rawValueFlowSignal &&
|
||||
hasDocumentEvidenceFollowupSignal(rawText));
|
||||
const effectiveMetadataFollowupSeedApplicable = metadataFollowupSeedApplicable && !metadataGroundedDocumentFollowupApplicable;
|
||||
const seededDomain = metadataGroundedDocumentFollowupApplicable
|
||||
const metadataGroundedMovementFollowupApplicable = Boolean(followupSeed.pilotScope === "metadata_inspection_v1" &&
|
||||
followupSeed.metadataRouteFamily === "movement_evidence" &&
|
||||
!followupSeed.metadataAmbiguityDetected &&
|
||||
followupSeed.counterparty &&
|
||||
!rawLifecycleSignal &&
|
||||
!rawValueFlowSignal &&
|
||||
hasMovementEvidenceFollowupSignal(rawText));
|
||||
const metadataGroundedLaneContinuationApplicable = Boolean(followupSeed.pilotScope === "metadata_inspection_v1" &&
|
||||
(followupSeed.metadataRouteFamily === "document_evidence" ||
|
||||
followupSeed.metadataRouteFamily === "movement_evidence") &&
|
||||
!followupSeed.metadataAmbiguityDetected &&
|
||||
followupSeed.counterparty &&
|
||||
!rawLifecycleSignal &&
|
||||
!rawValueFlowSignal &&
|
||||
!rawMetadataSignal &&
|
||||
!hasDocumentEvidenceFollowupSignal(rawText) &&
|
||||
!hasMovementEvidenceFollowupSignal(rawText) &&
|
||||
hasMetadataDownstreamContinuationSignal(rawText));
|
||||
const metadataGroundedDocumentLaneApplicable = metadataGroundedDocumentFollowupApplicable ||
|
||||
(metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "document_evidence");
|
||||
const metadataGroundedMovementLaneApplicable = metadataGroundedMovementFollowupApplicable ||
|
||||
(metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "movement_evidence");
|
||||
const effectiveMetadataFollowupSeedApplicable = metadataFollowupSeedApplicable &&
|
||||
!metadataGroundedDocumentLaneApplicable &&
|
||||
!metadataGroundedMovementLaneApplicable;
|
||||
const seededDomain = metadataGroundedDocumentLaneApplicable
|
||||
? "documents"
|
||||
: followupDiscoverySeedApplicable || effectiveMetadataFollowupSeedApplicable
|
||||
? followupSeed.domain
|
||||
: null;
|
||||
const seededAction = metadataGroundedDocumentFollowupApplicable
|
||||
: metadataGroundedMovementLaneApplicable
|
||||
? "movements"
|
||||
: followupDiscoverySeedApplicable || effectiveMetadataFollowupSeedApplicable
|
||||
? followupSeed.domain
|
||||
: null;
|
||||
const seededAction = metadataGroundedDocumentLaneApplicable
|
||||
? "list_documents"
|
||||
: followupDiscoverySeedApplicable || effectiveMetadataFollowupSeedApplicable
|
||||
? followupSeed.action
|
||||
: null;
|
||||
const seededUnsupported = metadataGroundedDocumentFollowupApplicable
|
||||
: metadataGroundedMovementLaneApplicable
|
||||
? "list_movements"
|
||||
: followupDiscoverySeedApplicable || effectiveMetadataFollowupSeedApplicable
|
||||
? followupSeed.action
|
||||
: null;
|
||||
const seededUnsupported = metadataGroundedDocumentLaneApplicable
|
||||
? "document_evidence"
|
||||
: followupDiscoverySeedApplicable || effectiveMetadataFollowupSeedApplicable
|
||||
? followupSeed.unsupported
|
||||
: null;
|
||||
: metadataGroundedMovementLaneApplicable
|
||||
? "movement_evidence"
|
||||
: followupDiscoverySeedApplicable || effectiveMetadataFollowupSeedApplicable
|
||||
? followupSeed.unsupported
|
||||
: null;
|
||||
const lifecycleSignal = rawLifecycleSignal || seededDomain === "counterparty_lifecycle";
|
||||
const bidirectionalValueFlowSignal = !lifecycleSignal &&
|
||||
(rawBidirectionalValueFlowSignal || seededAction === "net_value_flow");
|
||||
|
|
@ -401,11 +454,13 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
|||
? "counterparty_lifecycle"
|
||||
: valueFlowSignal
|
||||
? "counterparty_value"
|
||||
: metadataGroundedDocumentFollowupApplicable
|
||||
? "documents"
|
||||
: rawMetadataSignal || effectiveMetadataFollowupSeedApplicable
|
||||
? "metadata"
|
||||
: rawDomain ?? seededDomain,
|
||||
: metadataGroundedMovementLaneApplicable
|
||||
? "movements"
|
||||
: metadataGroundedDocumentLaneApplicable
|
||||
? "documents"
|
||||
: rawMetadataSignal || effectiveMetadataFollowupSeedApplicable
|
||||
? "metadata"
|
||||
: rawDomain ?? seededDomain,
|
||||
asked_action_family: lifecycleSignal
|
||||
? "activity_duration"
|
||||
: valueFlowSignal
|
||||
|
|
@ -414,11 +469,13 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
|||
: payoutSignal
|
||||
? "payout"
|
||||
: rawAction ?? seededAction ?? "turnover"
|
||||
: metadataGroundedDocumentFollowupApplicable
|
||||
? "list_documents"
|
||||
: rawMetadataSignal || effectiveMetadataFollowupSeedApplicable
|
||||
? metadataActionFromRawText(rawText) ?? seededAction
|
||||
: rawAction ?? seededAction,
|
||||
: metadataGroundedMovementLaneApplicable
|
||||
? "list_movements"
|
||||
: metadataGroundedDocumentLaneApplicable
|
||||
? "list_documents"
|
||||
: rawMetadataSignal || effectiveMetadataFollowupSeedApplicable
|
||||
? metadataActionFromRawText(rawText) ?? seededAction
|
||||
: rawAction ?? seededAction,
|
||||
asked_aggregation_axis: monthlyAggregationSignal ? "month" : rawAggregationAxis,
|
||||
explicit_entity_candidates: entityCandidates,
|
||||
explicit_organization_scope: explicitOrganizationScope,
|
||||
|
|
@ -432,18 +489,21 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
|||
: payoutSignal
|
||||
? "counterparty_payouts_or_outflow"
|
||||
: seededUnsupported ?? "counterparty_value_or_turnover"
|
||||
: metadataGroundedDocumentFollowupApplicable
|
||||
? "document_evidence"
|
||||
: rawMetadataSignal || effectiveMetadataFollowupSeedApplicable
|
||||
? "1c_metadata_surface"
|
||||
: followupDiscoverySeedApplicable
|
||||
? seededUnsupported
|
||||
: null),
|
||||
: metadataGroundedMovementLaneApplicable
|
||||
? "movement_evidence"
|
||||
: metadataGroundedDocumentLaneApplicable
|
||||
? "document_evidence"
|
||||
: rawMetadataSignal || effectiveMetadataFollowupSeedApplicable
|
||||
? "1c_metadata_surface"
|
||||
: followupDiscoverySeedApplicable
|
||||
? seededUnsupported
|
||||
: null),
|
||||
stale_replay_forbidden: Boolean(assistantTurnMeaning?.stale_replay_forbidden ||
|
||||
unsupported ||
|
||||
lifecycleSignal ||
|
||||
valueFlowSignal ||
|
||||
metadataGroundedDocumentFollowupApplicable ||
|
||||
metadataGroundedMovementLaneApplicable ||
|
||||
metadataGroundedDocumentLaneApplicable ||
|
||||
rawMetadataSignal ||
|
||||
effectiveMetadataFollowupSeedApplicable ||
|
||||
followupDiscoverySeedApplicable)
|
||||
|
|
@ -482,24 +542,27 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
|||
explicitIntentCandidate,
|
||||
followupDiscoverySeedApplicable: followupDiscoverySeedApplicable ||
|
||||
effectiveMetadataFollowupSeedApplicable ||
|
||||
metadataGroundedDocumentFollowupApplicable
|
||||
metadataGroundedMovementLaneApplicable ||
|
||||
metadataGroundedDocumentLaneApplicable
|
||||
});
|
||||
const hasTurnMeaning = Object.keys(cleanTurnMeaning).length > 0;
|
||||
const sourceSignal = assistantTurnMeaning
|
||||
? "assistant_turn_meaning"
|
||||
: followupDiscoverySeedApplicable || effectiveMetadataFollowupSeedApplicable
|
||||
? "followup_context"
|
||||
: metadataGroundedDocumentFollowupApplicable
|
||||
: metadataGroundedMovementLaneApplicable
|
||||
? "followup_context"
|
||||
: predecomposeContract
|
||||
? "predecompose_contract"
|
||||
: lifecycleSignal
|
||||
? "raw_text"
|
||||
: valueFlowSignal
|
||||
: metadataGroundedDocumentLaneApplicable
|
||||
? "followup_context"
|
||||
: predecomposeContract
|
||||
? "predecompose_contract"
|
||||
: lifecycleSignal
|
||||
? "raw_text"
|
||||
: rawMetadataSignal || effectiveMetadataFollowupSeedApplicable
|
||||
: valueFlowSignal
|
||||
? "raw_text"
|
||||
: "none";
|
||||
: rawMetadataSignal || effectiveMetadataFollowupSeedApplicable
|
||||
? "raw_text"
|
||||
: "none";
|
||||
if (lifecycleSignal) {
|
||||
pushReason(reasonCodes, "mcp_discovery_lifecycle_signal_detected");
|
||||
}
|
||||
|
|
@ -527,6 +590,12 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
|||
if (metadataGroundedDocumentFollowupApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_metadata_grounded_document_followup");
|
||||
}
|
||||
if (metadataGroundedMovementFollowupApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_metadata_grounded_movement_followup");
|
||||
}
|
||||
if (metadataGroundedLaneContinuationApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_metadata_grounded_lane_continuation");
|
||||
}
|
||||
if (unsupported) {
|
||||
pushReason(reasonCodes, "mcp_discovery_unsupported_but_understood_turn");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -202,6 +202,9 @@ function mapAssistantMcpDiscoveryPilotScopeToAddressIntent(
|
|||
if (pilotScope === "counterparty_document_evidence_query_documents_v1") {
|
||||
return "list_documents_by_counterparty";
|
||||
}
|
||||
if (pilotScope === "counterparty_movement_evidence_query_movements_v1") {
|
||||
return "bank_operations_by_counterparty";
|
||||
}
|
||||
if (pilotScope === "counterparty_supplier_payout_query_movements_v1") {
|
||||
return "supplier_payouts_profile";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,6 +101,10 @@ function isDocumentPilot(pilot: AssistantMcpDiscoveryPilotExecutionContract): bo
|
|||
return pilot.pilot_scope === "counterparty_document_evidence_query_documents_v1";
|
||||
}
|
||||
|
||||
function isMovementPilot(pilot: AssistantMcpDiscoveryPilotExecutionContract): boolean {
|
||||
return pilot.pilot_scope === "counterparty_movement_evidence_query_movements_v1";
|
||||
}
|
||||
|
||||
function isMetadataPilot(pilot: AssistantMcpDiscoveryPilotExecutionContract): boolean {
|
||||
return pilot.pilot_scope === "metadata_inspection_v1";
|
||||
}
|
||||
|
|
@ -124,6 +128,9 @@ function headlineFor(mode: AssistantMcpDiscoveryAnswerMode, pilot: AssistantMcpD
|
|||
const askedMonthlyBreakdown =
|
||||
pilot.derived_bidirectional_value_flow?.aggregation_axis === "month" ||
|
||||
pilot.derived_value_flow?.aggregation_axis === "month";
|
||||
if (isMovementPilot(pilot) && mode === "confirmed_with_bounded_inference") {
|
||||
return "По данным 1С найдены строки движений; ответ ограничен проверенным периодом и найденными строками.";
|
||||
}
|
||||
if (pilot.derived_metadata_surface && mode === "confirmed_with_bounded_inference") {
|
||||
if (pilot.derived_metadata_surface.ambiguity_detected) {
|
||||
return "По метаданным 1С найдены конкурирующие schema-поверхности; перед следующим шагом нужно удержать неоднозначность явно.";
|
||||
|
|
@ -209,6 +216,10 @@ function buildMustNotClaim(pilot: AssistantMcpDiscoveryPilotExecutionContract):
|
|||
claims.push("Do not claim full document history outside the checked period.");
|
||||
claims.push("Do not present the confirmed document rows as a complete document universe.");
|
||||
}
|
||||
if (isMovementPilot(pilot)) {
|
||||
claims.push("Do not claim full movement history outside the checked period.");
|
||||
claims.push("Do not present the confirmed movement rows as a complete movement universe.");
|
||||
}
|
||||
if (isMetadataPilot(pilot)) {
|
||||
claims.push("Do not present metadata surface as confirmed business data rows.");
|
||||
claims.push("Do not claim a document/register exists outside the checked metadata probe results.");
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ interface AssistantMcpDiscoveryCoverageAwareQueryExecution {
|
|||
|
||||
export type AssistantMcpDiscoveryPilotScope =
|
||||
| "metadata_inspection_v1"
|
||||
| "counterparty_movement_evidence_query_movements_v1"
|
||||
| "counterparty_document_evidence_query_documents_v1"
|
||||
| "counterparty_lifecycle_query_documents_v1"
|
||||
| "counterparty_value_flow_query_movements_v1"
|
||||
|
|
@ -304,6 +305,23 @@ function isDocumentEvidencePilotEligible(planner: AssistantMcpDiscoveryPlannerCo
|
|||
);
|
||||
}
|
||||
|
||||
function isMovementEvidencePilotEligible(planner: AssistantMcpDiscoveryPlannerContract): boolean {
|
||||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||||
const domain = String(meaning?.asked_domain_family ?? "").toLowerCase();
|
||||
const action = String(meaning?.asked_action_family ?? "").toLowerCase();
|
||||
const unsupported = String(meaning?.unsupported_but_understood_family ?? "").toLowerCase();
|
||||
const semanticNeed = String(planner.semantic_data_need ?? "").toLowerCase();
|
||||
const combined = `${domain} ${action} ${unsupported} ${semanticNeed}`;
|
||||
return (
|
||||
planner.proposed_primitives.includes("query_movements") &&
|
||||
(combined.includes("movement") ||
|
||||
combined.includes("movements") ||
|
||||
combined.includes("bank_operations") ||
|
||||
combined.includes("movement_evidence") ||
|
||||
combined.includes("list_movements"))
|
||||
);
|
||||
}
|
||||
|
||||
function isValueFlowPilotEligible(planner: AssistantMcpDiscoveryPlannerContract): boolean {
|
||||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||||
const domain = String(meaning?.asked_domain_family ?? "").toLowerCase();
|
||||
|
|
@ -664,6 +682,16 @@ function summarizeDocumentRows(result: AddressMcpQueryExecutorResult): string |
|
|||
return `${result.fetched_rows} MCP document rows fetched, ${result.matched_rows} matched document scope`;
|
||||
}
|
||||
|
||||
function summarizeMovementRows(result: AddressMcpQueryExecutorResult): string | null {
|
||||
if (result.error) {
|
||||
return null;
|
||||
}
|
||||
if (result.fetched_rows <= 0) {
|
||||
return "0 MCP movement rows fetched";
|
||||
}
|
||||
return `${result.fetched_rows} MCP movement rows fetched, ${result.matched_rows} matched movement scope`;
|
||||
}
|
||||
|
||||
function summarizeValueFlowRows(result: AssistantMcpDiscoveryCoverageAwareQueryResult): string | null {
|
||||
if (result.error) {
|
||||
return null;
|
||||
|
|
@ -1344,6 +1372,17 @@ function buildDocumentConfirmedFacts(result: AddressMcpQueryExecutorResult, coun
|
|||
];
|
||||
}
|
||||
|
||||
function buildMovementConfirmedFacts(result: AddressMcpQueryExecutorResult, counterparty: string | null): string[] {
|
||||
if (result.error || result.matched_rows <= 0) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
counterparty
|
||||
? `1C movement rows were found for counterparty ${counterparty}`
|
||||
: "1C movement rows were found for the requested scope"
|
||||
];
|
||||
}
|
||||
|
||||
function buildValueFlowConfirmedFacts(
|
||||
result: AssistantMcpDiscoveryCoverageAwareQueryResult,
|
||||
counterparty: string | null,
|
||||
|
|
@ -1398,6 +1437,13 @@ function buildDocumentInferredFacts(result: AddressMcpQueryExecutorResult): stri
|
|||
return ["Counterparty document evidence is limited to confirmed 1C document rows in the checked scope"];
|
||||
}
|
||||
|
||||
function buildMovementInferredFacts(result: AddressMcpQueryExecutorResult): string[] {
|
||||
if (result.error || result.fetched_rows <= 0) {
|
||||
return [];
|
||||
}
|
||||
return ["Counterparty movement evidence is limited to confirmed 1C movement rows in the checked scope"];
|
||||
}
|
||||
|
||||
function buildValueFlowInferredFacts(derived: AssistantMcpDiscoveryDerivedValueFlow | null): string[] {
|
||||
if (!derived) {
|
||||
return [];
|
||||
|
|
@ -1449,6 +1495,14 @@ function buildDocumentUnknownFacts(periodScope: string | null): string[] {
|
|||
];
|
||||
}
|
||||
|
||||
function buildMovementUnknownFacts(periodScope: string | null): string[] {
|
||||
return [
|
||||
periodScope
|
||||
? "Full movement history outside the checked period is not proven by this MCP discovery pilot"
|
||||
: "Full movement history is not proven without an explicit checked period"
|
||||
];
|
||||
}
|
||||
|
||||
function buildValueFlowUnknownFacts(
|
||||
periodScope: string | null,
|
||||
direction: AssistantMcpDiscoveryDerivedValueFlow["value_flow_direction"],
|
||||
|
|
@ -1511,6 +1565,9 @@ function pilotScopeForPlanner(planner: AssistantMcpDiscoveryPlannerContract): As
|
|||
if (isMetadataPilotEligible(planner)) {
|
||||
return "metadata_inspection_v1";
|
||||
}
|
||||
if (isMovementEvidencePilotEligible(planner)) {
|
||||
return "counterparty_movement_evidence_query_movements_v1";
|
||||
}
|
||||
if (isValueFlowPilotEligible(planner)) {
|
||||
return valueFlowPilotProfile(planner).scope;
|
||||
}
|
||||
|
|
@ -1586,10 +1643,11 @@ export async function executeAssistantMcpDiscoveryPilot(
|
|||
|
||||
const metadataPilotEligible = isMetadataPilotEligible(planner);
|
||||
const documentPilotEligible = isDocumentEvidencePilotEligible(planner);
|
||||
const movementPilotEligible = isMovementEvidencePilotEligible(planner);
|
||||
const lifecyclePilotEligible = isLifecyclePilotEligible(planner);
|
||||
const valueFlowPilotEligible = isValueFlowPilotEligible(planner);
|
||||
|
||||
if (!metadataPilotEligible && !documentPilotEligible && !lifecyclePilotEligible && !valueFlowPilotEligible) {
|
||||
if (!metadataPilotEligible && !documentPilotEligible && !movementPilotEligible && !lifecyclePilotEligible && !valueFlowPilotEligible) {
|
||||
pushReason(reasonCodes, "pilot_scope_unsupported_for_live_execution");
|
||||
for (const step of dryRun.execution_steps) {
|
||||
skippedPrimitives.push(step.primitive_id);
|
||||
|
|
@ -1767,6 +1825,89 @@ export async function executeAssistantMcpDiscoveryPilot(
|
|||
};
|
||||
}
|
||||
|
||||
if (movementPilotEligible) {
|
||||
let queryResult: AddressMcpQueryExecutorResult | null = null;
|
||||
const filters = buildValueFlowFilters(planner);
|
||||
const selection = selectAddressRecipe("bank_operations_by_counterparty", filters);
|
||||
if (!selection.selected_recipe) {
|
||||
pushReason(reasonCodes, "pilot_movement_recipe_not_available");
|
||||
const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "Movement-evidence recipe is not available");
|
||||
return {
|
||||
schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||||
pilot_status: "unsupported",
|
||||
pilot_scope: "counterparty_movement_evidence_query_movements_v1",
|
||||
dry_run: dryRun,
|
||||
mcp_execution_performed: false,
|
||||
executed_primitives: executedPrimitives,
|
||||
skipped_primitives: skippedPrimitives,
|
||||
probe_results: probeResults,
|
||||
evidence,
|
||||
source_rows_summary: null,
|
||||
derived_metadata_surface: null,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
query_limitations: ["Movement-evidence recipe is not available"],
|
||||
reason_codes: reasonCodes
|
||||
};
|
||||
}
|
||||
|
||||
const recipePlan = buildAddressRecipePlan(selection.selected_recipe, filters);
|
||||
for (const step of dryRun.execution_steps) {
|
||||
if (step.primitive_id !== "query_movements") {
|
||||
skippedPrimitives.push(step.primitive_id);
|
||||
probeResults.push(skippedProbeResult(step, "pilot_only_executes_query_movements"));
|
||||
continue;
|
||||
}
|
||||
queryResult = await runtimeDeps.executeAddressMcpQuery({
|
||||
query: recipePlan.query,
|
||||
limit: recipePlan.limit,
|
||||
account_scope: recipePlan.account_scope
|
||||
});
|
||||
executedPrimitives.push(step.primitive_id);
|
||||
probeResults.push(queryResultToProbeResult(step.primitive_id, queryResult));
|
||||
if (queryResult.error) {
|
||||
pushUnique(queryLimitations, queryResult.error);
|
||||
pushReason(reasonCodes, "pilot_query_movements_mcp_error");
|
||||
} else {
|
||||
pushReason(reasonCodes, "pilot_query_movements_mcp_executed");
|
||||
}
|
||||
}
|
||||
|
||||
const sourceRowsSummary = queryResult ? summarizeMovementRows(queryResult) : null;
|
||||
const evidence = resolveAssistantMcpDiscoveryEvidence({
|
||||
plan: planner.discovery_plan,
|
||||
probeResults,
|
||||
confirmedFacts: queryResult ? buildMovementConfirmedFacts(queryResult, counterparty) : [],
|
||||
inferredFacts: queryResult ? buildMovementInferredFacts(queryResult) : [],
|
||||
unknownFacts: buildMovementUnknownFacts(dateScope),
|
||||
sourceRowsSummary,
|
||||
queryLimitations,
|
||||
recommendedNextProbe: "explain_evidence_basis"
|
||||
});
|
||||
|
||||
return {
|
||||
schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||||
pilot_status: "executed",
|
||||
pilot_scope: "counterparty_movement_evidence_query_movements_v1",
|
||||
dry_run: dryRun,
|
||||
mcp_execution_performed: executedPrimitives.length > 0,
|
||||
executed_primitives: executedPrimitives,
|
||||
skipped_primitives: skippedPrimitives,
|
||||
probe_results: probeResults,
|
||||
evidence,
|
||||
source_rows_summary: sourceRowsSummary,
|
||||
derived_metadata_surface: null,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
query_limitations: queryLimitations,
|
||||
reason_codes: reasonCodes
|
||||
};
|
||||
}
|
||||
|
||||
if (valueFlowPilotEligible) {
|
||||
let queryResult: AssistantMcpDiscoveryCoverageAwareQueryResult | null = null;
|
||||
const filters = buildValueFlowFilters(planner);
|
||||
|
|
|
|||
|
|
@ -170,6 +170,16 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
|
|||
};
|
||||
}
|
||||
|
||||
if (includesAny(combined, ["movement", "movements", "bank_operations", "movement_evidence", "list_movements"])) {
|
||||
pushUnique(axes, "coverage_target");
|
||||
return {
|
||||
semanticDataNeed: "movement evidence",
|
||||
primitives: ["resolve_entity_reference", "query_movements", "probe_coverage"],
|
||||
axes,
|
||||
reason: "planner_selected_movement_recipe"
|
||||
};
|
||||
}
|
||||
|
||||
if (includesAny(combined, ["document", "documents"])) {
|
||||
pushUnique(axes, "coverage_target");
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -112,6 +112,13 @@ function localizeLine(value: string): string {
|
|||
if (/^1C document rows were found for the requested scope$/i.test(value)) {
|
||||
return "В 1С найдены строки документов по запрошенному контуру.";
|
||||
}
|
||||
const movementRowsMatch = value.match(/^1C movement rows were found for counterparty\s+(.+)$/i);
|
||||
if (movementRowsMatch) {
|
||||
return `В 1С найдены строки движений по контрагенту ${movementRowsMatch[1]}.`;
|
||||
}
|
||||
if (/^1C movement rows were found for the requested scope$/i.test(value)) {
|
||||
return "В 1С найдены строки движений по запрошенному контуру.";
|
||||
}
|
||||
const supplierPayoutMatch = value.match(/^1C supplier-payout rows were found for counterparty\s+(.+)$/i);
|
||||
if (supplierPayoutMatch) {
|
||||
return `В 1С найдены строки исходящих платежей/списаний по контрагенту ${supplierPayoutMatch[1]}.`;
|
||||
|
|
@ -141,6 +148,9 @@ function localizeLine(value: string): string {
|
|||
if (/^Counterparty document evidence is limited to confirmed 1C document rows in the checked scope$/i.test(value)) {
|
||||
return "Срез документов ограничен только подтвержденными строками документов в проверенном окне.";
|
||||
}
|
||||
if (/^Counterparty movement evidence is limited to confirmed 1C movement rows in the checked scope$/i.test(value)) {
|
||||
return "Срез движений ограничен только подтвержденными строками движений в проверенном окне.";
|
||||
}
|
||||
if (/^Counterparty value-flow total was calculated from confirmed 1C movement rows$/i.test(value)) {
|
||||
return "Сумма рассчитана только по подтвержденным строкам денежных движений в 1С.";
|
||||
}
|
||||
|
|
@ -226,6 +236,12 @@ function localizeLine(value: string): string {
|
|||
if (/^Full document history is not proven without an explicit checked period$/i.test(value)) {
|
||||
return "Полный срез документов без явно проверенного периода не подтвержден.";
|
||||
}
|
||||
if (/^Full movement history outside the checked period is not proven by this MCP discovery pilot$/i.test(value)) {
|
||||
return "Полный исторический срез движений вне проверенного периода этим поиском не подтвержден.";
|
||||
}
|
||||
if (/^Full movement history is not proven without an explicit checked period$/i.test(value)) {
|
||||
return "Полный срез движений без явно проверенного периода не подтвержден.";
|
||||
}
|
||||
if (/^Full supplier-payout amount outside the checked period is not proven by this MCP discovery pilot$/i.test(value)) {
|
||||
return "Полный объем исходящих платежей вне проверенного периода этим поиском не подтвержден.";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,6 +169,13 @@ function mapPilotScopeToFollowupMeaning(
|
|||
unsupported: "counterparty_lifecycle"
|
||||
};
|
||||
}
|
||||
if (pilotScope === "counterparty_movement_evidence_query_movements_v1") {
|
||||
return {
|
||||
domain: "movements",
|
||||
action: "list_movements",
|
||||
unsupported: "movement_evidence"
|
||||
};
|
||||
}
|
||||
if (pilotScope === "counterparty_supplier_payout_query_movements_v1") {
|
||||
return {
|
||||
domain: "counterparty_value",
|
||||
|
|
@ -232,6 +239,13 @@ function mapAddressIntentToFollowupMeaning(
|
|||
unsupported: "counterparty_value_or_turnover"
|
||||
};
|
||||
}
|
||||
if (intent === "bank_operations_by_counterparty") {
|
||||
return {
|
||||
domain: "movements",
|
||||
action: "list_movements",
|
||||
unsupported: "movement_evidence"
|
||||
};
|
||||
}
|
||||
return {
|
||||
domain: null,
|
||||
action: null,
|
||||
|
|
@ -350,6 +364,18 @@ function hasDocumentEvidenceFollowupSignal(text: string): boolean {
|
|||
);
|
||||
}
|
||||
|
||||
function hasMovementEvidenceFollowupSignal(text: string): boolean {
|
||||
return /(?:\u043f\u043e\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f\u043c|\u0438\u044f)?|\u0434\u0430\u0432\u0430\u0439\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f|\u0438\u0435)|\u0438\u0449\u0438\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f|\u0438\u0435)|\u043f\u043e\u043a\u0430\u0436\u0438\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f|\u0438\u0435)|\u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a(?:\u0438\u0435|\u0438\u0439)\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f|\u0438\u0435)|movement(?:s)?\s+(?:then|next)?|(?:then|next)\s+movements?|go\s+to\s+movements?)/iu.test(
|
||||
text
|
||||
);
|
||||
}
|
||||
|
||||
function hasMetadataDownstreamContinuationSignal(text: string): boolean {
|
||||
return /(?:\u0434\u0430\u0432\u0430\u0439\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u0438\u0434(?:\u0435|\u0451)\u043c\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u043f\u043e\u0448\u043b(?:\u0438|\u0451\u043c)\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0439|\u0438\u0449\u0438\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u0438\u0449\u0438\s+\u0434\u0430\u043d\u043d\u044b\u0435|\u043f\u043e\u043a\u0430\u0436\u0438\s+\u0434\u0430\u043d\u043d\u044b\u0435|\u043f\u043e\u043a\u0430\u0436\u0438\s+\u0441\u0442\u0440\u043e\u043a\u0438|\u0433\u043b\u0443\u0431\u0436\u0435|\u0447\u0442\u043e\s+\u0434\u0430\u043b\u044c\u0448\u0435|continue|go\s+ahead|go\s+deeper|look\s+deeper|drill\s+down|show\s+(?:data|rows))/iu.test(
|
||||
text
|
||||
);
|
||||
}
|
||||
|
||||
function metadataActionFromRawText(text: string): string {
|
||||
if (/(?:\u043f\u043e\u043b(?:\u0435|\u044f)|field)/iu.test(text)) {
|
||||
return "inspect_fields";
|
||||
|
|
@ -404,6 +430,9 @@ function semanticNeedFor(input: {
|
|||
if (input.valueFlowSignal || /(?:turnover|revenue|payment|payout|value|net|netting|balance|cashflow)/iu.test(combined)) {
|
||||
return "counterparty value-flow evidence";
|
||||
}
|
||||
if (/(?:movement|movements|bank_operations|movement_evidence|list_movements)/iu.test(combined)) {
|
||||
return "movement evidence";
|
||||
}
|
||||
if (/(?:document|documents|list_documents)/iu.test(combined)) {
|
||||
return "document evidence";
|
||||
}
|
||||
|
|
@ -486,20 +515,56 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
|||
!rawValueFlowSignal &&
|
||||
hasDocumentEvidenceFollowupSignal(rawText)
|
||||
);
|
||||
const metadataGroundedMovementFollowupApplicable = Boolean(
|
||||
followupSeed.pilotScope === "metadata_inspection_v1" &&
|
||||
followupSeed.metadataRouteFamily === "movement_evidence" &&
|
||||
!followupSeed.metadataAmbiguityDetected &&
|
||||
followupSeed.counterparty &&
|
||||
!rawLifecycleSignal &&
|
||||
!rawValueFlowSignal &&
|
||||
hasMovementEvidenceFollowupSignal(rawText)
|
||||
);
|
||||
const metadataGroundedLaneContinuationApplicable = Boolean(
|
||||
followupSeed.pilotScope === "metadata_inspection_v1" &&
|
||||
(followupSeed.metadataRouteFamily === "document_evidence" ||
|
||||
followupSeed.metadataRouteFamily === "movement_evidence") &&
|
||||
!followupSeed.metadataAmbiguityDetected &&
|
||||
followupSeed.counterparty &&
|
||||
!rawLifecycleSignal &&
|
||||
!rawValueFlowSignal &&
|
||||
!rawMetadataSignal &&
|
||||
!hasDocumentEvidenceFollowupSignal(rawText) &&
|
||||
!hasMovementEvidenceFollowupSignal(rawText) &&
|
||||
hasMetadataDownstreamContinuationSignal(rawText)
|
||||
);
|
||||
const metadataGroundedDocumentLaneApplicable =
|
||||
metadataGroundedDocumentFollowupApplicable ||
|
||||
(metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "document_evidence");
|
||||
const metadataGroundedMovementLaneApplicable =
|
||||
metadataGroundedMovementFollowupApplicable ||
|
||||
(metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "movement_evidence");
|
||||
const effectiveMetadataFollowupSeedApplicable =
|
||||
metadataFollowupSeedApplicable && !metadataGroundedDocumentFollowupApplicable;
|
||||
const seededDomain = metadataGroundedDocumentFollowupApplicable
|
||||
metadataFollowupSeedApplicable &&
|
||||
!metadataGroundedDocumentLaneApplicable &&
|
||||
!metadataGroundedMovementLaneApplicable;
|
||||
const seededDomain = metadataGroundedDocumentLaneApplicable
|
||||
? "documents"
|
||||
: metadataGroundedMovementLaneApplicable
|
||||
? "movements"
|
||||
: followupDiscoverySeedApplicable || effectiveMetadataFollowupSeedApplicable
|
||||
? followupSeed.domain
|
||||
: null;
|
||||
const seededAction = metadataGroundedDocumentFollowupApplicable
|
||||
const seededAction = metadataGroundedDocumentLaneApplicable
|
||||
? "list_documents"
|
||||
: metadataGroundedMovementLaneApplicable
|
||||
? "list_movements"
|
||||
: followupDiscoverySeedApplicable || effectiveMetadataFollowupSeedApplicable
|
||||
? followupSeed.action
|
||||
: null;
|
||||
const seededUnsupported = metadataGroundedDocumentFollowupApplicable
|
||||
const seededUnsupported = metadataGroundedDocumentLaneApplicable
|
||||
? "document_evidence"
|
||||
: metadataGroundedMovementLaneApplicable
|
||||
? "movement_evidence"
|
||||
: followupDiscoverySeedApplicable || effectiveMetadataFollowupSeedApplicable
|
||||
? followupSeed.unsupported
|
||||
: null;
|
||||
|
|
@ -543,9 +608,11 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
|||
lifecycleSignal
|
||||
? "counterparty_lifecycle"
|
||||
: valueFlowSignal
|
||||
? "counterparty_value"
|
||||
: metadataGroundedDocumentFollowupApplicable
|
||||
? "documents"
|
||||
? "counterparty_value"
|
||||
: metadataGroundedMovementLaneApplicable
|
||||
? "movements"
|
||||
: metadataGroundedDocumentLaneApplicable
|
||||
? "documents"
|
||||
: rawMetadataSignal || effectiveMetadataFollowupSeedApplicable
|
||||
? "metadata"
|
||||
: rawDomain ?? seededDomain,
|
||||
|
|
@ -557,7 +624,9 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
|||
: payoutSignal
|
||||
? "payout"
|
||||
: rawAction ?? seededAction ?? "turnover"
|
||||
: metadataGroundedDocumentFollowupApplicable
|
||||
: metadataGroundedMovementLaneApplicable
|
||||
? "list_movements"
|
||||
: metadataGroundedDocumentLaneApplicable
|
||||
? "list_documents"
|
||||
: rawMetadataSignal || effectiveMetadataFollowupSeedApplicable
|
||||
? metadataActionFromRawText(rawText) ?? seededAction
|
||||
|
|
@ -576,7 +645,9 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
|||
: payoutSignal
|
||||
? "counterparty_payouts_or_outflow"
|
||||
: seededUnsupported ?? "counterparty_value_or_turnover"
|
||||
: metadataGroundedDocumentFollowupApplicable
|
||||
: metadataGroundedMovementLaneApplicable
|
||||
? "movement_evidence"
|
||||
: metadataGroundedDocumentLaneApplicable
|
||||
? "document_evidence"
|
||||
: rawMetadataSignal || effectiveMetadataFollowupSeedApplicable
|
||||
? "1c_metadata_surface"
|
||||
|
|
@ -588,7 +659,8 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
|||
unsupported ||
|
||||
lifecycleSignal ||
|
||||
valueFlowSignal ||
|
||||
metadataGroundedDocumentFollowupApplicable ||
|
||||
metadataGroundedMovementLaneApplicable ||
|
||||
metadataGroundedDocumentLaneApplicable ||
|
||||
rawMetadataSignal ||
|
||||
effectiveMetadataFollowupSeedApplicable ||
|
||||
followupDiscoverySeedApplicable
|
||||
|
|
@ -631,14 +703,17 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
|||
followupDiscoverySeedApplicable:
|
||||
followupDiscoverySeedApplicable ||
|
||||
effectiveMetadataFollowupSeedApplicable ||
|
||||
metadataGroundedDocumentFollowupApplicable
|
||||
metadataGroundedMovementLaneApplicable ||
|
||||
metadataGroundedDocumentLaneApplicable
|
||||
});
|
||||
const hasTurnMeaning = Object.keys(cleanTurnMeaning).length > 0;
|
||||
const sourceSignal: AssistantMcpDiscoveryTurnInputSource = assistantTurnMeaning
|
||||
? "assistant_turn_meaning"
|
||||
: followupDiscoverySeedApplicable || effectiveMetadataFollowupSeedApplicable
|
||||
? "followup_context"
|
||||
: metadataGroundedDocumentFollowupApplicable
|
||||
: metadataGroundedMovementLaneApplicable
|
||||
? "followup_context"
|
||||
: metadataGroundedDocumentLaneApplicable
|
||||
? "followup_context"
|
||||
: predecomposeContract
|
||||
? "predecompose_contract"
|
||||
|
|
@ -677,6 +752,12 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
|||
if (metadataGroundedDocumentFollowupApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_metadata_grounded_document_followup");
|
||||
}
|
||||
if (metadataGroundedMovementFollowupApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_metadata_grounded_movement_followup");
|
||||
}
|
||||
if (metadataGroundedLaneContinuationApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_metadata_grounded_lane_continuation");
|
||||
}
|
||||
if (unsupported) {
|
||||
pushReason(reasonCodes, "mcp_discovery_unsupported_but_understood_turn");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,6 +116,34 @@ describe("assistant MCP discovery answer adapter", () => {
|
|||
expect(draft.must_not_claim).toContain("Do not claim full document history outside the checked period.");
|
||||
});
|
||||
|
||||
it("turns generic movement evidence into a bounded movement answer draft", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
asked_domain_family: "movements",
|
||||
asked_action_family: "list_movements",
|
||||
explicit_entity_candidates: ["SVK"],
|
||||
explicit_date_scope: "2020",
|
||||
unsupported_but_understood_family: "movement_evidence"
|
||||
}
|
||||
});
|
||||
const pilot = await executeAssistantMcpDiscoveryPilot(
|
||||
planner,
|
||||
buildDeps([{ Period: "2020-01-15T00:00:00", Counterparty: "SVK", Registrar: "Move1" }])
|
||||
);
|
||||
|
||||
const draft = buildAssistantMcpDiscoveryAnswerDraft(pilot);
|
||||
|
||||
expect(draft.answer_mode).toBe("confirmed_with_bounded_inference");
|
||||
expect(draft.headline).toContain("движений");
|
||||
expect(draft.confirmed_lines).toContain("1C movement rows were found for counterparty SVK");
|
||||
expect(draft.inference_lines).toContain(
|
||||
"Counterparty movement evidence is limited to confirmed 1C movement rows in the checked scope"
|
||||
);
|
||||
expect(draft.unknown_lines).toContain("Full movement history outside the checked period is not proven by this MCP discovery pilot");
|
||||
expect(draft.must_not_claim).toContain("Do not claim full movement history outside the checked period.");
|
||||
expect(draft.must_not_claim).toContain("Do not present the confirmed movement rows as a complete movement universe.");
|
||||
});
|
||||
|
||||
it("asks for clarification when discovery did not execute due to missing scope", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
|
|
|
|||
|
|
@ -133,6 +133,44 @@ describe("assistant MCP discovery pilot executor", () => {
|
|||
expect(result.source_rows_summary).toBe("2 MCP document rows fetched, 2 matched document scope");
|
||||
});
|
||||
|
||||
it("executes generic movement evidence through query_movements without deriving turnover totals", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
asked_domain_family: "movements",
|
||||
asked_action_family: "list_movements",
|
||||
explicit_entity_candidates: ["SVK"],
|
||||
explicit_date_scope: "2020",
|
||||
unsupported_but_understood_family: "movement_evidence"
|
||||
}
|
||||
});
|
||||
const deps = buildDeps([
|
||||
{ Period: "2020-01-15T00:00:00", Amount: 1250, Counterparty: "SVK", Registrar: "Move1" },
|
||||
{ Period: "2020-03-20T00:00:00", Amount: "900,25", Counterparty: "SVK", Registrar: "Move2" }
|
||||
]);
|
||||
|
||||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||||
|
||||
expect(result.pilot_status).toBe("executed");
|
||||
expect(result.pilot_scope).toBe("counterparty_movement_evidence_query_movements_v1");
|
||||
expect(result.executed_primitives).toEqual(["query_movements"]);
|
||||
expect(result.derived_value_flow).toBeNull();
|
||||
expect(result.derived_bidirectional_value_flow).toBeNull();
|
||||
expect(result.evidence.confirmed_facts).toContain("1C movement rows were found for counterparty SVK");
|
||||
expect(result.evidence.inferred_facts).toContain(
|
||||
"Counterparty movement evidence is limited to confirmed 1C movement rows in the checked scope"
|
||||
);
|
||||
expect(result.evidence.unknown_facts).toContain(
|
||||
"Full movement history outside the checked period is not proven by this MCP discovery pilot"
|
||||
);
|
||||
expect(result.source_rows_summary).toBe("2 MCP movement rows fetched, 2 matched movement scope");
|
||||
|
||||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(1);
|
||||
const call = deps.executeAddressMcpQuery.mock.calls[0]?.[0];
|
||||
expect(String(call?.query ?? "")).toContain("Документ.СписаниеСРасчетногоСчета");
|
||||
expect(String(call?.query ?? "")).toContain("Документ.ПоступлениеНаРасчетныйСчет");
|
||||
expect(call?.limit).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("executes inspect_1c_metadata and derives a confirmed metadata surface", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
|
|
|
|||
|
|
@ -87,6 +87,25 @@ describe("assistant MCP discovery planner", () => {
|
|||
expect(result.required_axes).toEqual(["counterparty", "coverage_target"]);
|
||||
});
|
||||
|
||||
it("builds a movement discovery plan without aggregating value-flow totals", () => {
|
||||
const result = planAssistantMcpDiscovery({
|
||||
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.semantic_data_need).toBe("movement evidence");
|
||||
expect(result.proposed_primitives).toEqual(["resolve_entity_reference", "query_movements", "probe_coverage"]);
|
||||
expect(result.proposed_primitives).not.toContain("aggregate_by_axis");
|
||||
expect(result.required_axes).toEqual(["counterparty", "period", "coverage_target"]);
|
||||
expect(result.reason_codes).toContain("planner_selected_movement_recipe");
|
||||
});
|
||||
|
||||
it("builds an inference-safe lifecycle plan with evidence explanation", () => {
|
||||
const result = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
|
|
|
|||
|
|
@ -236,6 +236,33 @@ describe("assistant MCP discovery response candidate", () => {
|
|||
expect(candidate.reply_text).not.toContain("1C document rows were found");
|
||||
});
|
||||
|
||||
it("localizes movement evidence without leaking raw English facts", () => {
|
||||
const candidate = buildAssistantMcpDiscoveryResponseCandidate(
|
||||
entryPoint({
|
||||
bridge: {
|
||||
bridge_status: "answer_draft_ready",
|
||||
user_facing_response_allowed: true,
|
||||
business_fact_answer_allowed: true,
|
||||
requires_user_clarification: false,
|
||||
answer_draft: {
|
||||
answer_mode: "confirmed_with_bounded_inference",
|
||||
headline: "По данным 1С найдены строки движений; ответ ограничен проверенным периодом и найденными строками.",
|
||||
confirmed_lines: ["1C movement rows were found for counterparty SVK"],
|
||||
inference_lines: ["Counterparty movement evidence is limited to confirmed 1C movement rows in the checked scope"],
|
||||
unknown_lines: ["Full movement history outside the checked period is not proven by this MCP discovery pilot"],
|
||||
limitation_lines: [],
|
||||
next_step_line: null
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
expect(candidate.reply_text).toContain("В 1С найдены строки движений по контрагенту SVK.");
|
||||
expect(candidate.reply_text).toContain("Срез движений ограничен только подтвержденными строками движений");
|
||||
expect(candidate.reply_text).toContain("Полный исторический срез движений вне проверенного периода этим поиском не подтвержден.");
|
||||
expect(candidate.reply_text).not.toContain("1C movement rows were found");
|
||||
});
|
||||
|
||||
it("localizes metadata evidence without leaking raw MCP wording", () => {
|
||||
const candidate = buildAssistantMcpDiscoveryResponseCandidate(
|
||||
entryPoint({
|
||||
|
|
|
|||
|
|
@ -264,6 +264,103 @@ describe("assistant MCP discovery turn input adapter", () => {
|
|||
expect(result.reason_codes).toContain("mcp_discovery_counterparty_from_followup_context");
|
||||
});
|
||||
|
||||
it("pivots grounded metadata follow-up into movement evidence when the next lane is explicit", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage: "then movements",
|
||||
followupContext: {
|
||||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||||
previous_discovery_metadata_route_family: "movement_evidence",
|
||||
previous_discovery_metadata_selected_entity_set: "РегистрНакопления",
|
||||
previous_filters: {
|
||||
counterparty: "SVK",
|
||||
period_from: "2020-01-01",
|
||||
period_to: "2020-12-31"
|
||||
},
|
||||
previous_anchor_type: "counterparty",
|
||||
previous_anchor_value: "SVK"
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.adapter_status).toBe("ready");
|
||||
expect(result.should_run_discovery).toBe(true);
|
||||
expect(result.source_signal).toBe("followup_context");
|
||||
expect(result.semantic_data_need).toBe("movement evidence");
|
||||
expect(result.turn_meaning_ref).toMatchObject({
|
||||
asked_domain_family: "movements",
|
||||
asked_action_family: "list_movements",
|
||||
explicit_entity_candidates: ["SVK"],
|
||||
explicit_date_scope: "2020",
|
||||
unsupported_but_understood_family: "movement_evidence",
|
||||
stale_replay_forbidden: true
|
||||
});
|
||||
expect(result.reason_codes).toContain("mcp_discovery_metadata_grounded_movement_followup");
|
||||
expect(result.reason_codes).toContain("mcp_discovery_counterparty_from_followup_context");
|
||||
});
|
||||
|
||||
it("continues from grounded metadata into document evidence on a generic downstream follow-up", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage: "давай дальше",
|
||||
followupContext: {
|
||||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||||
previous_discovery_metadata_route_family: "document_evidence",
|
||||
previous_discovery_metadata_selected_entity_set: "Документ",
|
||||
previous_filters: {
|
||||
counterparty: "SVK",
|
||||
period_from: "2020-01-01",
|
||||
period_to: "2020-12-31"
|
||||
},
|
||||
previous_anchor_type: "counterparty",
|
||||
previous_anchor_value: "SVK"
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.adapter_status).toBe("ready");
|
||||
expect(result.should_run_discovery).toBe(true);
|
||||
expect(result.source_signal).toBe("followup_context");
|
||||
expect(result.semantic_data_need).toBe("document evidence");
|
||||
expect(result.turn_meaning_ref).toMatchObject({
|
||||
asked_domain_family: "documents",
|
||||
asked_action_family: "list_documents",
|
||||
explicit_entity_candidates: ["SVK"],
|
||||
explicit_date_scope: "2020",
|
||||
unsupported_but_understood_family: "document_evidence",
|
||||
stale_replay_forbidden: true
|
||||
});
|
||||
expect(result.reason_codes).toContain("mcp_discovery_metadata_grounded_lane_continuation");
|
||||
});
|
||||
|
||||
it("continues from grounded metadata into movement evidence on a generic downstream follow-up", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage: "continue with data",
|
||||
followupContext: {
|
||||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||||
previous_discovery_metadata_route_family: "movement_evidence",
|
||||
previous_discovery_metadata_selected_entity_set: "РегистрНакопления",
|
||||
previous_filters: {
|
||||
counterparty: "SVK",
|
||||
period_from: "2020-01-01",
|
||||
period_to: "2020-12-31"
|
||||
},
|
||||
previous_anchor_type: "counterparty",
|
||||
previous_anchor_value: "SVK"
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.adapter_status).toBe("ready");
|
||||
expect(result.should_run_discovery).toBe(true);
|
||||
expect(result.source_signal).toBe("followup_context");
|
||||
expect(result.semantic_data_need).toBe("movement evidence");
|
||||
expect(result.turn_meaning_ref).toMatchObject({
|
||||
asked_domain_family: "movements",
|
||||
asked_action_family: "list_movements",
|
||||
explicit_entity_candidates: ["SVK"],
|
||||
explicit_date_scope: "2020",
|
||||
unsupported_but_understood_family: "movement_evidence",
|
||||
stale_replay_forbidden: true
|
||||
});
|
||||
expect(result.reason_codes).toContain("mcp_discovery_metadata_grounded_lane_continuation");
|
||||
});
|
||||
|
||||
it("switches the checked year on a short payout follow-up while keeping prior discovery counterparty", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage: "а теперь за 2021?",
|
||||
|
|
|
|||
Loading…
Reference in New Issue