ОРРКЕСТРАЦИЯ - Усилить domain-loop business-first каноном для analyst и orchestrator

This commit is contained in:
2026-04-14 17:33:57 +03:00
parent f6a2c8e0a3
commit cb0eb450d7
19 changed files with 813 additions and 66 deletions
@@ -1340,6 +1340,17 @@ function hasInventoryPurchaseDocumentsSignal(text) {
function hasInventorySaleTraceSignal(text) {
return /(?:продаж|покупател|buyer|sale trace|purchase[\s-]?to[\s-]?sale|purchase -> warehouse -> sale|закупка.*продаж)/iu.test(text);
}
function hasSelectedObjectInventoryCue(text) {
return /(?:по\s+выбранному\s+объекту|selected\s+object)/iu.test(text);
}
function hasSelectedObjectInventoryProvenanceSignal(text) {
return (hasSelectedObjectInventoryCue(text) &&
/(?:кто\s+(?:(?:это|этот\s+товар|эту\s+позицию)\s+)?(?:нам\s+)?поставил|кто\s+(?:нам\s+)?поставил\s+(?:это|этот\s+товар|эту\s+позицию)|от\s+какого\s+поставщика|у\s+какого\s+поставщика|от\s+кого\s+куплен|supplier|vendor|поставщик)/iu.test(text));
}
function hasSelectedObjectInventoryPurchaseDocumentsSignal(text) {
return (hasSelectedObjectInventoryCue(text) &&
/(?:по\s+каким\s+документам\s+(?:это|его|этот\s+товар|эту\s+позицию)\s+купили|по\s+каким\s+документам\s+(?:был\s+)?куплен|какими\s+документами\s+(?:это|его|этот\s+товар|эту\s+позицию)\s+купили|какими\s+документами\s+(?:был\s+)?куплен|purchase\s+documents|documents\s+of\s+purchase|through\s+which\s+documents)/iu.test(text));
}
function hasInventoryProvenanceSignalV2(text) {
const hasItemCue = /(?:товар|номенклатур|sku|item|product|остат(?:ок|ки)|склад)/iu.test(text);
const hasSupplierCue = /(?:от\s+какого\s+поставщика|у\s+какого\s+поставщика|от\s+кого\s+куплен|кто\s+(?:нам\s+)?поставил|кем\s+поставлен|поставщик|supplier|vendor)/iu.test(text);
@@ -1541,6 +1552,13 @@ function resolveAddressIntent(userMessage) {
reasons: ["inventory_aging_signal_detected"]
};
}
if (hasSelectedObjectInventoryProvenanceSignal(text)) {
return {
intent: "inventory_purchase_provenance_for_item",
confidence: "medium",
reasons: ["inventory_selected_object_provenance_signal_detected"]
};
}
if (hasInventoryProvenanceSignalV2(text)) {
return {
intent: "inventory_purchase_provenance_for_item",
@@ -1555,6 +1573,13 @@ function resolveAddressIntent(userMessage) {
reasons: ["inventory_purchase_date_signal_detected"]
};
}
if (hasSelectedObjectInventoryPurchaseDocumentsSignal(text)) {
return {
intent: "inventory_purchase_documents_for_item",
confidence: "medium",
reasons: ["inventory_selected_object_purchase_documents_signal_detected"]
};
}
if (hasInventoryPurchaseDocumentsSignalV2(text)) {
return {
intent: "inventory_purchase_documents_for_item",
+27 -7
View File
@@ -2879,6 +2879,19 @@ class AddressQueryService {
const broadenedFactual = (0, composeStage_1.composeFactualReply)(intent.intent, broadenedFilteredRows, composeOptionsFromFilters(autoBroadenedFilters));
const broadenedLimitations = [...filters.warnings, "period_window_auto_broadened_to_available_data"];
const broadenedReasons = [...baseReasons, "period_window_auto_broadened_to_available_data"];
const broadenedResultSemantics = mergeAddressResultSemantics(deriveAddressResultSemantics({
intent: intent.intent,
selectedRecipe: broadenedSelection.selected_recipe.recipe_id,
filters: filters.extracted_filters,
responseType: broadenedFactual.responseType,
rowsMatched: broadenedFilteredRows.length
}), broadenedFactual.semantics);
const broadenedRouteExpectationAudit = buildRouteExpectationAudit({
intent: routeExpectationIntent,
selectedRecipe: broadenedSelection.selected_recipe.recipe_id,
requestedResultMode,
resultMode: broadenedResultSemantics.result_mode
});
return {
handled: true,
reply_text: injectNoticeAfterLeadLine(broadenedFactual.text, broadenedPrefix),
@@ -2921,13 +2934,20 @@ class AddressQueryService {
runtime_readiness: "LIVE_QUERYABLE_WITH_LIMITS",
limited_reason_category: null,
response_type: broadenedFactual.responseType,
...mergeAddressResultSemantics(deriveAddressResultSemantics({
intent: intent.intent,
selectedRecipe: broadenedSelection.selected_recipe.recipe_id,
filters: filters.extracted_filters,
responseType: broadenedFactual.responseType,
rowsMatched: broadenedFilteredRows.length
}), broadenedFactual.semantics),
capability_id: capabilityAudit.capabilityId,
capability_layer: capabilityAudit.layer,
capability_route_mode: capabilityAudit.routeMode,
capability_route_enabled: capabilityAudit.enabled,
capability_route_reason: capabilityAudit.reason,
shadow_route_intent: shadowRouteAudit.intent,
shadow_route_selected_recipe: shadowRouteAudit.selectedRecipe,
shadow_route_status: shadowRouteAudit.status,
route_expectation_status: broadenedRouteExpectationAudit.status,
route_expectation_reason: broadenedRouteExpectationAudit.reason,
route_expectation_expected_selected_recipes: broadenedRouteExpectationAudit.expectedSelectedRecipes,
route_expectation_expected_requested_result_modes: broadenedRouteExpectationAudit.expectedRequestedResultModes,
route_expectation_expected_result_modes: broadenedRouteExpectationAudit.expectedResultModes,
...broadenedResultSemantics,
limitations: broadenedLimitations,
reasons: withConfirmedBalanceFallbackReason(broadenedReasons, requestedResultMode, broadenedFactual.semantics)
}
@@ -244,6 +244,24 @@ function mapCounterpartyIntentToContractIntent(intent) {
}
return null;
}
function isInventoryIntent(intent) {
return (intent === "inventory_on_hand_as_of_date" ||
intent === "inventory_purchase_provenance_for_item" ||
intent === "inventory_purchase_documents_for_item" ||
intent === "inventory_supplier_stock_overlap_as_of_date" ||
intent === "inventory_sale_trace_for_item" ||
intent === "inventory_purchase_to_sale_chain" ||
intent === "inventory_aging_by_purchase_date");
}
function hasSelectedObjectInventorySignal(text) {
return /(?:по\s+выбранному\s+объекту|for\s+selected\s+object)/iu.test(String(text ?? ""));
}
function hasInventorySupplierFollowupCue(text) {
return /(?:кто\s+(?:(?:это|этот\s+товар|эту\s+позицию)\s+)?(?:нам\s+)?поставил|кто\s+(?:нам\s+)?поставил\s+(?:это|этот\s+товар|эту\s+позицию)|от\s+какого\s+поставщика|у\s+какого\s+поставщика|от\s+кого\s+куплен|supplier|vendor|поставщик)/iu.test(String(text ?? ""));
}
function hasInventoryPurchaseDocumentsFollowupCue(text) {
return /(?:по\s+каким\s+документам\s+(?:это|его|этот\s+товар|эту\s+позицию)\s+купили|по\s+каким\s+документам\s+(?:был\s+)?куплен|какими\s+документами\s+(?:это|его|этот\s+товар|эту\s+позицию)\s+купили|какими\s+документами\s+(?:был\s+)?куплен|purchase\s+documents|documents\s+of\s+purchase|through\s+which\s+documents)/iu.test(String(text ?? ""));
}
function hasAddressFollowupContextSignal(text) {
const normalized = String(text ?? "").trim();
if (!normalized) {
@@ -612,6 +630,32 @@ function deriveIntentWithFollowupContext(detectedIntent, userMessage, followupCo
reasons: [...detectedIntent.reasons, "intent_adjusted_to_balance_followup_context"]
};
}
const previousIsInventoryFamily = isInventoryIntent(previousIntent);
const inventorySelectedObjectFollowup = hasSelectedObjectInventorySignal(normalizedMessage) || (previousIsInventoryFamily && hasFollowupSignal);
if (inventorySelectedObjectFollowup && hasInventorySupplierFollowupCue(normalizedMessage)) {
if (detectedIntent.intent === "unknown" ||
detectedIntent.intent === "inventory_on_hand_as_of_date" ||
detectedIntent.intent === previousIntent) {
return {
intent: "inventory_purchase_provenance_for_item",
confidence: "low",
reasons: [...detectedIntent.reasons, "intent_adjusted_to_inventory_followup_context"]
};
}
}
if (inventorySelectedObjectFollowup && hasInventoryPurchaseDocumentsFollowupCue(normalizedMessage)) {
if (detectedIntent.intent === "unknown" ||
detectedIntent.intent === "list_documents_by_counterparty" ||
detectedIntent.intent === "list_documents_by_contract" ||
detectedIntent.intent === "inventory_on_hand_as_of_date" ||
detectedIntent.intent === previousIntent) {
return {
intent: "inventory_purchase_documents_for_item",
confidence: "low",
reasons: [...detectedIntent.reasons, "intent_adjusted_to_inventory_followup_context"]
};
}
}
if (hasPreviousContract) {
if (detectedIntent.intent === "list_contracts_by_counterparty") {
if (hasBankSignal(normalizedMessage)) {