Архитектура: перенести base address tool gate из assistantService в assistantRoutePolicy и выровнять top-level orchestration owner

This commit is contained in:
2026-04-19 13:42:55 +03:00
parent d1ad8e2c1b
commit ac3757bc59
6 changed files with 329 additions and 279 deletions
@@ -78,7 +78,8 @@ export function createAssistantRoutePolicy(deps) {
isGroundedInventoryContextDebug,
resolveRouteMemorySignals,
findLastAddressAssistantItem,
resolveAddressToolGateDecision,
resolveAddressToolGateDecision: resolveAddressToolGateDecisionOverride,
hasAddressLlmPreDecomposeCandidate,
hasSameDateAccountFollowupSignalForPredecompose,
hasLooseAllTimeAddressLookupSignal,
hasDeepAnalysisPreferenceSignal,
@@ -88,6 +89,148 @@ export function createAssistantRoutePolicy(deps) {
resolveLivingAssistantModeDecision,
resolveProviderExecutionState
} = deps;
function resolveBaseAddressToolGateDecision(addressInputMessage, followupContext, llmPreDecomposeMeta = null, rawUserMessage = null) {
const repairedInputMessage = repairAddressMojibake(String(addressInputMessage ?? ""));
const rawMessageForGate = String(rawUserMessage ?? addressInputMessage ?? "");
const repairedRawMessageForGate = repairAddressMojibake(rawMessageForGate);
const rawMetaSignals = resolveMetaSignalSet({
rawUserMessage: rawMessageForGate,
repairedRawUserMessage: repairedRawMessageForGate,
effectiveAddressUserMessage: rawMessageForGate,
repairedEffectiveAddressUserMessage: repairedRawMessageForGate
});
const effectiveMetaSignals = resolveMetaSignalSet({
rawUserMessage: rawMessageForGate,
repairedRawUserMessage: repairedRawMessageForGate,
effectiveAddressUserMessage: String(addressInputMessage ?? ""),
repairedEffectiveAddressUserMessage: repairedInputMessage
});
const dataScopeMetaQuery = Boolean(rawMetaSignals.dataScopeMetaQuery || effectiveMetaSignals.dataScopeMetaQuery);
const rawCapabilityMetaQuery = Boolean(rawMetaSignals.capabilityMetaQuery);
const capabilityMetaQuery = Boolean(rawCapabilityMetaQuery || effectiveMetaSignals.capabilityMetaQuery);
const rawDataRetrievalSignal = hasDataRetrievalRequestSignal(rawMessageForGate) ||
hasDataRetrievalRequestSignal(repairedRawMessageForGate);
const dataRetrievalSignal = rawDataRetrievalSignal || hasDataRetrievalRequestSignal(repairedInputMessage);
const rawCapabilityMetaOverride = rawCapabilityMetaQuery && !rawDataRetrievalSignal;
if (dataScopeMetaQuery || rawCapabilityMetaOverride || (capabilityMetaQuery && !dataRetrievalSignal)) {
return {
runAddressLane: false,
decision: "skip_address_lane",
reason: dataScopeMetaQuery ? "assistant_data_scope_query_detected" : "assistant_capability_query_detected"
};
}
const modeDetection = detectAddressQuestionMode(repairedInputMessage || addressInputMessage);
const modeDetectionRaw = detectAddressQuestionMode(String(addressInputMessage ?? ""));
const hasClassifierSignal = modeDetection.mode === "address_query" || modeDetectionRaw.mode === "address_query";
const intentResolution = resolveAddressIntent(repairedInputMessage || addressInputMessage);
const intentResolutionRaw = resolveAddressIntent(String(addressInputMessage ?? ""));
const hasIntentSignal = intentResolution.intent !== "unknown" || intentResolutionRaw.intent !== "unknown";
const llmContractMode = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.mode);
const llmContractModeConfidence = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.mode_confidence);
const llmContractIntent = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
const semanticExtractionContract = llmPreDecomposeMeta?.semanticExtractionContract &&
typeof llmPreDecomposeMeta.semanticExtractionContract === "object"
? llmPreDecomposeMeta.semanticExtractionContract
: null;
const semanticCanonicalRecommended = semanticExtractionContract?.apply_canonical_recommended !== false;
const llmSupportedDeepAddressIntentSignal = llmContractMode === "deep_analysis" &&
/^(?:inventory_purchase_provenance_for_item|inventory_purchase_documents_for_item|inventory_sale_trace_for_item|inventory_profitability_for_item|inventory_purchase_to_sale_chain)$/u.test(llmContractIntent ?? "") &&
semanticCanonicalRecommended;
const llmCanonicalEntitySignal = /(?:заказчик|поставщик|контрагент|компан|customer|supplier|counterparty|company|vendor|client)/iu.test(compactWhitespace(repairedInputMessage.toLowerCase()));
const llmCanonicalAppliedSignal = Boolean(llmPreDecomposeMeta?.applied) && llmContractMode !== "deep_analysis";
const hasLlmCanonicalSignal = semanticCanonicalRecommended &&
Boolean(llmPreDecomposeMeta?.llmCanonicalCandidateDetected) &&
((llmContractMode === "address_query" && llmContractModeConfidence !== "low") ||
(llmCanonicalAppliedSignal &&
(hasStrongDataIntentSignal(repairedInputMessage) || llmCanonicalEntitySignal)));
const hasLlmCanonicalDataSignal = semanticCanonicalRecommended &&
Boolean(llmPreDecomposeMeta?.llmCanonicalCandidateDetected) &&
Boolean(llmPreDecomposeMeta?.applied) &&
(llmContractMode === "address_query" || llmContractMode === "unsupported" || llmContractMode === null) &&
hasStrongDataIntentSignal(repairedInputMessage);
const hasBusinessRankingAddressSignal = /(?:кто\s+(?:нам\s+)?(?:больше(?:\s+всего)?\s+принес|принес\s+больше(?:\s+всего)?).*(?:денег)?|who\s+brought\s+(?:us\s+)?(?:the\s+)?most\s+money)/iu.test(compactWhitespace(repairedInputMessage.toLowerCase()));
const sameDateAccountFollowupSignal = hasSameDateAccountFollowupSignalForPredecompose(rawMessageForGate) ||
hasSameDateAccountFollowupSignalForPredecompose(repairedInputMessage);
const hasLexicalAddressSignal = hasAddressLlmPreDecomposeCandidate(addressInputMessage) ||
hasAddressLlmPreDecomposeCandidate(repairedInputMessage) ||
hasAccountingSignal(addressInputMessage) ||
hasAccountingSignal(repairedInputMessage) ||
hasShortDebtMirrorFollowupSignal(rawMessageForGate) ||
hasShortDebtMirrorFollowupSignal(repairedInputMessage) ||
hasBusinessRankingAddressSignal ||
sameDateAccountFollowupSignal;
const hasUnsupportedLowConfidencePredecomposeSignal = llmContractMode === "unsupported" &&
(llmContractModeConfidence === "low" || llmContractModeConfidence === "medium") &&
llmContractIntent === "unknown";
const hasAnyAddressSignal = hasClassifierSignal ||
hasIntentSignal ||
hasLlmCanonicalSignal ||
hasLlmCanonicalDataSignal ||
hasLexicalAddressSignal ||
llmSupportedDeepAddressIntentSignal;
const strongDataSignalFromRawMessage = hasStrongDataIntentSignal(rawMessageForGate) ||
hasDataRetrievalRequestSignal(rawMessageForGate) ||
hasAccountingSignal(rawMessageForGate) ||
hasSameDateAccountFollowupSignalForPredecompose(rawMessageForGate);
const strongDataSignalFromEffectiveMessage = hasStrongDataIntentSignal(repairedInputMessage) ||
hasAccountingSignal(repairedInputMessage) ||
hasDataRetrievalRequestSignal(repairedInputMessage);
if (!semanticCanonicalRecommended &&
llmContractIntent === "unknown" &&
!followupContext &&
!hasClassifierSignal &&
!hasIntentSignal &&
!hasLexicalAddressSignal &&
!llmSupportedDeepAddressIntentSignal &&
!strongDataSignalFromRawMessage &&
!strongDataSignalFromEffectiveMessage) {
return {
runAddressLane: false,
decision: "skip_address_lane",
reason: "llm_predecompose_semantic_guard_rejected"
};
}
if (hasUnsupportedLowConfidencePredecomposeSignal &&
!followupContext &&
!hasAnyAddressSignal &&
!llmSupportedDeepAddressIntentSignal &&
!strongDataSignalFromRawMessage &&
!strongDataSignalFromEffectiveMessage) {
return {
runAddressLane: false,
decision: "skip_address_lane",
reason: "llm_predecompose_unsupported_mode"
};
}
const hasMessageSignal = hasAnyAddressSignal || strongDataSignalFromRawMessage || strongDataSignalFromEffectiveMessage;
if (hasMessageSignal) {
return {
runAddressLane: true,
decision: "run_address_lane",
reason: hasClassifierSignal
? "address_mode_classifier_detected"
: hasIntentSignal
? "address_intent_resolver_detected"
: hasLlmCanonicalSignal
? "llm_canonical_candidate_detected"
: hasLlmCanonicalDataSignal
? "llm_canonical_data_signal_detected"
: "address_signal_detected"
};
}
if (followupContext) {
return {
runAddressLane: true,
decision: "run_address_lane",
reason: "followup_context_detected"
};
}
return {
runAddressLane: false,
decision: "skip_address_lane",
reason: "no_address_signal_after_l0"
};
}
function hasInventoryRootRestatementFollowupSignal(text) {
const normalized = compactWhitespace(repairAddressMojibake(String(text ?? "")).toLowerCase()).replace(/ё/g, "е");
if (!normalized) {
@@ -250,7 +393,9 @@ export function createAssistantRoutePolicy(deps) {
!capabilityMetaQuery &&
!dataRetrievalSignal);
const effectiveAddressFollowupSignal = explicitAddressFollowupSignal && !dangerOrCoercionSignal;
const baseToolGate = resolveAddressToolGateDecision(effectiveAddressUserMessage, followupContext, llmPreDecomposeMeta, rawUserMessage);
const baseToolGate = typeof resolveAddressToolGateDecisionOverride === "function"
? resolveAddressToolGateDecisionOverride(effectiveAddressUserMessage, followupContext, llmPreDecomposeMeta, rawUserMessage)
: resolveBaseAddressToolGateDecision(effectiveAddressUserMessage, followupContext, llmPreDecomposeMeta, rawUserMessage);
const deterministicNonDomainGuard = Boolean(!dataScopeMetaQuery &&
!capabilityMetaQuery &&
!dataRetrievalSignal &&
@@ -3649,143 +3649,6 @@ async function runAddressLlmPreDecompose(normalizerService, payload, userMessage
}, userMessage);
}
}
function resolveAddressToolGateDecision(addressInputMessage, followupContext, llmPreDecomposeMeta = null, rawUserMessage = null) {
const repairedInputMessage = repairAddressMojibake(String(addressInputMessage ?? ""));
const rawMessageForGate = String(rawUserMessage ?? addressInputMessage ?? "");
const repairedRawMessageForGate = repairAddressMojibake(rawMessageForGate);
const dataScopeMetaQuery = hasAssistantDataScopeMetaQuestionSignal(rawMessageForGate) ||
hasAssistantDataScopeMetaQuestionSignal(repairedRawMessageForGate) ||
hasAssistantDataScopeMetaQuestionSignal(repairedInputMessage);
const rawCapabilityMetaQuery = shouldHandleAsAssistantCapabilityMetaQuery(rawMessageForGate) ||
shouldHandleAsAssistantCapabilityMetaQuery(repairedRawMessageForGate);
const capabilityMetaQuery = rawCapabilityMetaQuery ||
shouldHandleAsAssistantCapabilityMetaQuery(repairedInputMessage);
const rawDataRetrievalSignal = hasDataRetrievalRequestSignal(rawMessageForGate) ||
hasDataRetrievalRequestSignal(repairedRawMessageForGate);
const dataRetrievalSignal = rawDataRetrievalSignal ||
hasDataRetrievalRequestSignal(repairedInputMessage);
const rawCapabilityMetaOverride = rawCapabilityMetaQuery && !rawDataRetrievalSignal;
if (dataScopeMetaQuery || rawCapabilityMetaOverride || (capabilityMetaQuery && !dataRetrievalSignal)) {
return {
runAddressLane: false,
decision: "skip_address_lane",
reason: dataScopeMetaQuery ? "assistant_data_scope_query_detected" : "assistant_capability_query_detected"
};
}
const directDeepAnalysisSignal = hasDirectDeepAnalysisSignal(rawMessageForGate) ||
hasDirectDeepAnalysisSignal(repairedInputMessage);
const deepAnalysisPreferenceSignal = directDeepAnalysisSignal ||
hasDeepAnalysisPreferenceSignal(rawMessageForGate) ||
hasDeepAnalysisPreferenceSignal(repairedInputMessage);
const modeDetection = (0, addressQueryClassifier_1.detectAddressQuestionMode)(repairedInputMessage || addressInputMessage);
const modeDetectionRaw = (0, addressQueryClassifier_1.detectAddressQuestionMode)(String(addressInputMessage ?? ""));
const hasClassifierSignal = modeDetection.mode === "address_query" || modeDetectionRaw.mode === "address_query";
const intentResolution = (0, addressIntentResolver_1.resolveAddressIntent)(repairedInputMessage || addressInputMessage);
const intentResolutionRaw = (0, addressIntentResolver_1.resolveAddressIntent)(String(addressInputMessage ?? ""));
const hasIntentSignal = intentResolution.intent !== "unknown" || intentResolutionRaw.intent !== "unknown";
const llmContractMode = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.mode);
const llmContractModeConfidence = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.mode_confidence);
const llmContractIntent = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
const semanticExtractionContract = llmPreDecomposeMeta?.semanticExtractionContract &&
typeof llmPreDecomposeMeta.semanticExtractionContract === "object"
? llmPreDecomposeMeta.semanticExtractionContract
: null;
const semanticCanonicalRecommended = semanticExtractionContract?.apply_canonical_recommended !== false;
const llmSupportedDeepAddressIntentSignal = llmContractMode === "deep_analysis" &&
/^(?:inventory_purchase_provenance_for_item|inventory_purchase_documents_for_item|inventory_sale_trace_for_item|inventory_profitability_for_item|inventory_purchase_to_sale_chain)$/u.test(llmContractIntent ?? "") &&
semanticCanonicalRecommended;
const llmCanonicalEntitySignal = /(?:\u0437\u0430\u043a\u0430\u0437\u0447\u0438\u043a|\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442|\u043a\u043e\u043c\u043f\u0430\u043d|customer|supplier|counterparty|company|vendor|client)/iu.test(compactWhitespace(repairedInputMessage.toLowerCase()));
const llmCanonicalAppliedSignal = Boolean(llmPreDecomposeMeta?.applied) && llmContractMode !== "deep_analysis";
const hasLlmCanonicalSignal = semanticCanonicalRecommended &&
Boolean(llmPreDecomposeMeta?.llmCanonicalCandidateDetected) &&
((llmContractMode === "address_query" && llmContractModeConfidence !== "low") ||
(llmCanonicalAppliedSignal &&
(hasStrongDataIntentSignal(repairedInputMessage) || llmCanonicalEntitySignal)));
const hasLlmCanonicalDataSignal = semanticCanonicalRecommended &&
Boolean(llmPreDecomposeMeta?.llmCanonicalCandidateDetected) &&
Boolean(llmPreDecomposeMeta?.applied) &&
(llmContractMode === "address_query" || llmContractMode === "unsupported" || llmContractMode === null) &&
hasStrongDataIntentSignal(repairedInputMessage);
const hasBusinessRankingAddressSignal = /(?:\u043a\u0442\u043e\s+(?:\u043d\u0430\u043c\s+)?(?:\u0431\u043e\u043b\u044c\u0448\u0435(?:\s+\u0432\u0441\u0435\u0433\u043e)?\s+\u043f\u0440\u0438\u043d\u0435\u0441|\u043f\u0440\u0438\u043d\u0435\u0441\s+\u0431\u043e\u043b\u044c\u0448\u0435(?:\s+\u0432\u0441\u0435\u0433\u043e)?).*(?:\u0434\u0435\u043d\u0435\u0433)?|who\s+brought\s+(?:us\s+)?(?:the\s+)?most\s+money)/iu.test(compactWhitespace(repairedInputMessage.toLowerCase()));
const sameDateAccountFollowupSignal = hasSameDateAccountFollowupSignalForPredecompose(rawMessageForGate) ||
hasSameDateAccountFollowupSignalForPredecompose(repairedInputMessage);
const hasLexicalAddressSignal = isAddressLlmPreDecomposeCandidate(addressInputMessage) ||
isAddressLlmPreDecomposeCandidate(repairedInputMessage) ||
hasAccountingSignal(addressInputMessage) ||
hasAccountingSignal(repairedInputMessage) ||
hasShortDebtMirrorFollowupSignal(rawMessageForGate) ||
hasShortDebtMirrorFollowupSignal(repairedInputMessage) ||
hasBusinessRankingAddressSignal ||
sameDateAccountFollowupSignal;
const hasUnsupportedLowConfidencePredecomposeSignal = llmContractMode === "unsupported" &&
(llmContractModeConfidence === "low" || llmContractModeConfidence === "medium") &&
llmContractIntent === "unknown";
const hasAnyAddressSignal =
hasClassifierSignal || hasIntentSignal || hasLlmCanonicalSignal || hasLlmCanonicalDataSignal || hasLexicalAddressSignal || llmSupportedDeepAddressIntentSignal;
const strongDataSignalFromRawMessage = hasStrongDataIntentSignal(rawMessageForGate) ||
hasDataRetrievalRequestSignal(rawMessageForGate) ||
hasAccountingSignal(rawMessageForGate) ||
hasSameDateAccountFollowupSignalForPredecompose(rawMessageForGate);
const strongDataSignalFromEffectiveMessage = hasStrongDataIntentSignal(repairedInputMessage) ||
hasAccountingSignal(repairedInputMessage) ||
hasDataRetrievalRequestSignal(repairedInputMessage);
if (!semanticCanonicalRecommended &&
llmContractIntent === "unknown" &&
!followupContext &&
!hasClassifierSignal &&
!hasIntentSignal &&
!hasLexicalAddressSignal &&
!llmSupportedDeepAddressIntentSignal &&
!strongDataSignalFromRawMessage &&
!strongDataSignalFromEffectiveMessage) {
return {
runAddressLane: false,
decision: "skip_address_lane",
reason: "llm_predecompose_semantic_guard_rejected"
};
}
if (hasUnsupportedLowConfidencePredecomposeSignal && !followupContext &&
!hasAnyAddressSignal &&
!llmSupportedDeepAddressIntentSignal &&
!strongDataSignalFromRawMessage &&
!strongDataSignalFromEffectiveMessage) {
return {
runAddressLane: false,
decision: "skip_address_lane",
reason: "llm_predecompose_unsupported_mode"
};
}
const hasMessageSignal = hasAnyAddressSignal || strongDataSignalFromRawMessage || strongDataSignalFromEffectiveMessage;
if (hasMessageSignal) {
return {
runAddressLane: true,
decision: "run_address_lane",
reason: hasClassifierSignal
? "address_mode_classifier_detected"
: hasIntentSignal
? "address_intent_resolver_detected"
: hasLlmCanonicalSignal
? "llm_canonical_candidate_detected"
: hasLlmCanonicalDataSignal
? "llm_canonical_data_signal_detected"
: llmSupportedDeepAddressIntentSignal
? "address_signal_detected"
: "address_signal_detected"
};
}
if (followupContext) {
return {
runAddressLane: true,
decision: "run_address_lane",
reason: "followup_context_detected"
};
}
return {
runAddressLane: false,
decision: "skip_address_lane",
reason: "no_address_signal_after_l0"
};
}
function hasLooseAllTimeAddressLookupSignal(text) {
const repaired = repairAddressMojibake(String(text ?? ""));
const normalized = compactWhitespace(repaired.toLowerCase());
@@ -4181,7 +4044,7 @@ const assistantRoutePolicy = (0, assistantRoutePolicy_1.createAssistantRoutePoli
hasShortInventoryObjectFollowupSignal,
resolveRouteMemorySignals: assistantMemoryRecapPolicy.resolveRouteMemorySignals,
findLastAddressAssistantItem,
resolveAddressToolGateDecision,
hasAddressLlmPreDecomposeCandidate: isAddressLlmPreDecomposeCandidate,
hasSameDateAccountFollowupSignalForPredecompose,
hasLooseAllTimeAddressLookupSignal,
hasDeepAnalysisPreferenceSignal,