Стабилизировать маршрутизацию deep/address и chat-boundary
This commit is contained in:
parent
801982b66b
commit
08615aaa7c
|
|
@ -1952,6 +1952,10 @@ function resolveUnicodeAddressIntentBridge(text) {
|
||||||
const hasTaxPeriodCue = /(?:налогов|налоговую|бюджет|декларац|квартал|\b[1-4]\s*кв)/iu.test(normalized);
|
const hasTaxPeriodCue = /(?:налогов|налоговую|бюджет|декларац|квартал|\b[1-4]\s*кв)/iu.test(normalized);
|
||||||
const hasVatMonthPeriodCue = /(?:за|на|в)\s+(?:январ|феврал|март|апрел|ма[йя]|июн|июл|август|сентябр|октябр|ноябр|декабр)\S*(?:\s+(?:19|20)\d{2})?/iu.test(normalized) &&
|
const hasVatMonthPeriodCue = /(?:за|на|в)\s+(?:январ|феврал|март|апрел|ма[йя]|июн|июл|август|сентябр|октябр|ноябр|декабр)\S*(?:\s+(?:19|20)\d{2})?/iu.test(normalized) &&
|
||||||
!/\b\d{1,2}\s+(?:январ|феврал|март|апрел|ма[йя]|июн|июл|август|сентябр|октябр|ноябр|декабр)\S*(?:\s+(?:19|20)\d{2})?/iu.test(normalized);
|
!/\b\d{1,2}\s+(?:январ|феврал|март|апрел|ма[йя]|июн|июл|август|сентябр|октябр|ноябр|декабр)\S*(?:\s+(?:19|20)\d{2})?/iu.test(normalized);
|
||||||
|
if ((hasTaxPeriodCue || hasVatMonthPeriodCue) &&
|
||||||
|
/(?:сгруз|какой\s+ндс\s+(?:мы\s+)?должн|ндс\s+необходимо)/iu.test(normalized)) {
|
||||||
|
return unicodeBridgeResolution("vat_liability_confirmed_for_tax_period", "high", "vat_liability_colloquial_bridge_signal_detected");
|
||||||
|
}
|
||||||
if ((hasTaxPeriodCue || (hasVatMonthPeriodCue && !hasVatDebtCue)) &&
|
if ((hasTaxPeriodCue || (hasVatMonthPeriodCue && !hasVatDebtCue)) &&
|
||||||
/(?:скольк|скока|надо|нужно|заплат|уплат|оплат|прикин)/iu.test(normalized)) {
|
/(?:скольк|скока|надо|нужно|заплат|уплат|оплат|прикин)/iu.test(normalized)) {
|
||||||
return unicodeBridgeResolution("vat_liability_confirmed_for_tax_period", "high", "vat_liability_confirmed_tax_period_signal_detected");
|
return unicodeBridgeResolution("vat_liability_confirmed_for_tax_period", "high", "vat_liability_confirmed_tax_period_signal_detected");
|
||||||
|
|
|
||||||
|
|
@ -3886,7 +3886,6 @@ function composeFactualReplyBody(intent, rows, options = {}) {
|
||||||
(String(right.period ?? "").localeCompare(String(left.period ?? ""), "ru")))
|
(String(right.period ?? "").localeCompare(String(left.period ?? ""), "ru")))
|
||||||
.slice(0, Math.min(rows.length, 5));
|
.slice(0, Math.min(rows.length, 5));
|
||||||
const semanticSummary = summarizeBankOperationSemantics(rows);
|
const semanticSummary = summarizeBankOperationSemantics(rows);
|
||||||
const compactRoleAnswer = /(?:клиент|поставщик|финансов|роль|коротко)/iu.test(String(options.userMessage ?? ""));
|
|
||||||
const compactEvidenceRows = visibleRows.map((row, index) => {
|
const compactEvidenceRows = visibleRows.map((row, index) => {
|
||||||
const direction = bankOperationDirectionLabel(bankOperationDirection(row));
|
const direction = bankOperationDirectionLabel(bankOperationDirection(row));
|
||||||
const amount = formatMoneyRub(row.amount ?? 0);
|
const amount = formatMoneyRub(row.amount ?? 0);
|
||||||
|
|
@ -3904,11 +3903,9 @@ function composeFactualReplyBody(intent, rows, options = {}) {
|
||||||
roleBoundary ?? "Показываю подтвержденные банковские операции из текущего среза.",
|
roleBoundary ?? "Показываю подтвержденные банковские операции из текущего среза.",
|
||||||
...(semanticSummary ? [semanticSummary] : [])
|
...(semanticSummary ? [semanticSummary] : [])
|
||||||
];
|
];
|
||||||
if (!compactRoleAnswer) {
|
|
||||||
lines.push(bankOperationEvidenceLine(rows, preferredBankEvidenceDirection(options.userMessage)), "Примеры строк 1С:", ...compactEvidenceRows);
|
lines.push(bankOperationEvidenceLine(rows, preferredBankEvidenceDirection(options.userMessage)), "Примеры строк 1С:", ...compactEvidenceRows);
|
||||||
}
|
|
||||||
lines.push("Следующий шаг: могу отдельно разложить назначения платежа, договоры или отделить банковский контур от клиентского/поставщицкого.");
|
lines.push("Следующий шаг: могу отдельно разложить назначения платежа, договоры или отделить банковский контур от клиентского/поставщицкого.");
|
||||||
if (!compactRoleAnswer && rows.length > visibleRows.length) {
|
if (rows.length > visibleRows.length) {
|
||||||
lines.push(`Показаны первые ${visibleRows.length} из ${rows.length}; полный список остается в подтвержденном срезе.`);
|
lines.push(`Показаны первые ${visibleRows.length} из ${rows.length}; полный список остается в подтвержденном срезе.`);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -42,11 +42,17 @@ async function tryHandleAssistantLivingChatRuntime(input) {
|
||||||
if (!runtime.handled || !runtime.debug) {
|
if (!runtime.handled || !runtime.debug) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
const runtimeReplySource = typeof runtime.debug.living_chat_response_source === "string"
|
||||||
|
? runtime.debug.living_chat_response_source
|
||||||
|
: null;
|
||||||
|
const replyType = runtimeReplySource === "deterministic_unsupported_current_turn_boundary"
|
||||||
|
? "clarification_required"
|
||||||
|
: "factual_with_explanation";
|
||||||
const finalization = finalizeLivingChatTurnSafe({
|
const finalization = finalizeLivingChatTurnSafe({
|
||||||
sessionId: input.sessionId,
|
sessionId: input.sessionId,
|
||||||
userMessage: input.userMessage,
|
userMessage: input.userMessage,
|
||||||
assistantReply: runtime.chatText,
|
assistantReply: runtime.chatText,
|
||||||
replyType: "factual_with_explanation",
|
replyType,
|
||||||
debug: runtime.debug,
|
debug: runtime.debug,
|
||||||
modeDecision: input.modeDecision,
|
modeDecision: input.modeDecision,
|
||||||
appendItem: input.appendItem,
|
appendItem: input.appendItem,
|
||||||
|
|
|
||||||
|
|
@ -377,6 +377,8 @@ async function runAssistantLivingChatRuntime(input) {
|
||||||
address_llm_predecompose_contract: predecomposeContract,
|
address_llm_predecompose_contract: predecomposeContract,
|
||||||
address_semantic_extraction_contract: semanticExtractionContract,
|
address_semantic_extraction_contract: semanticExtractionContract,
|
||||||
orchestration_contract_v1: addressRuntimeMeta.orchestrationContract ?? null,
|
orchestration_contract_v1: addressRuntimeMeta.orchestrationContract ?? null,
|
||||||
|
address_tool_gate_decision: addressRuntimeMeta.toolGateDecision ?? null,
|
||||||
|
address_tool_gate_reason: addressRuntimeMeta.toolGateReason ?? null,
|
||||||
tool_gate_decision: addressRuntimeMeta.toolGateDecision ?? null,
|
tool_gate_decision: addressRuntimeMeta.toolGateDecision ?? null,
|
||||||
tool_gate_reason: addressRuntimeMeta.toolGateReason ?? null,
|
tool_gate_reason: addressRuntimeMeta.toolGateReason ?? null,
|
||||||
normalized: null,
|
normalized: null,
|
||||||
|
|
|
||||||
|
|
@ -350,6 +350,29 @@ function hasExactBankOperationsAddressReply(input, entryPoint) {
|
||||||
routeMode === "exact" ||
|
routeMode === "exact" ||
|
||||||
hasFullConfirmedTruth(input));
|
hasFullConfirmedTruth(input));
|
||||||
}
|
}
|
||||||
|
function hasExactDocumentListAddressReply(input, entryPoint) {
|
||||||
|
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!hasEffectivelyFactualAddressReply(input)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const source = String(input.currentReplySource ?? input.livingChatSource ?? "").trim().toLowerCase();
|
||||||
|
if (source !== "address_query_runtime_v1" && source !== "address_exact" && source !== "address_lane") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const detectedIntent = toNonEmptyString(input.addressRuntimeMeta?.detected_intent);
|
||||||
|
const selectedRecipe = toNonEmptyString(input.addressRuntimeMeta?.selected_recipe);
|
||||||
|
const isDocumentIntent = detectedIntent === "list_documents_by_counterparty" || detectedIntent === "list_documents_by_contract";
|
||||||
|
const isDocumentRecipe = selectedRecipe === "address_documents_by_counterparty_v1" ||
|
||||||
|
selectedRecipe === "address_documents_by_contract_v1";
|
||||||
|
if (!isDocumentIntent || !isDocumentRecipe) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const mcpCallStatus = toNonEmptyString(input.addressRuntimeMeta?.mcp_call_status);
|
||||||
|
const responseType = toNonEmptyString(input.addressRuntimeMeta?.response_type);
|
||||||
|
return Boolean(mcpCallStatus === "matched_non_empty" || responseType === "FACTUAL_LIST" || hasFullConfirmedTruth(input));
|
||||||
|
}
|
||||||
function hasInventoryMarginRankingAddressReply(input, entryPoint) {
|
function hasInventoryMarginRankingAddressReply(input, entryPoint) {
|
||||||
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
|
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -643,6 +666,7 @@ function applyAssistantMcpDiscoveryResponsePolicy(input) {
|
||||||
const staleMetadataDiscoveryFallbackAgainstExactAddressReply = hasStaleMetadataDiscoveryFallbackAgainstExactAddressReply(input, entryPoint);
|
const staleMetadataDiscoveryFallbackAgainstExactAddressReply = hasStaleMetadataDiscoveryFallbackAgainstExactAddressReply(input, entryPoint);
|
||||||
const exactValueFlowReplyForBusinessOverviewDirectMoneyNeed = hasExactValueFlowReplyForBusinessOverviewDirectMoneyNeed(input, entryPoint);
|
const exactValueFlowReplyForBusinessOverviewDirectMoneyNeed = hasExactValueFlowReplyForBusinessOverviewDirectMoneyNeed(input, entryPoint);
|
||||||
const exactBankOperationsAddressReply = hasExactBankOperationsAddressReply(input, entryPoint);
|
const exactBankOperationsAddressReply = hasExactBankOperationsAddressReply(input, entryPoint);
|
||||||
|
const exactDocumentListAddressReply = hasExactDocumentListAddressReply(input, entryPoint);
|
||||||
const inventoryMarginRankingAddressReply = hasInventoryMarginRankingAddressReply(input, entryPoint);
|
const inventoryMarginRankingAddressReply = hasInventoryMarginRankingAddressReply(input, entryPoint);
|
||||||
const openScopeValueFlowDiscoveryPriority = hasOpenScopeValueFlowDiscoveryPriority(input, entryPoint);
|
const openScopeValueFlowDiscoveryPriority = hasOpenScopeValueFlowDiscoveryPriority(input, entryPoint);
|
||||||
const metadataDiscoveryPriority = hasMetadataDiscoveryPriority(input, entryPoint);
|
const metadataDiscoveryPriority = hasMetadataDiscoveryPriority(input, entryPoint);
|
||||||
|
|
@ -711,6 +735,9 @@ function applyAssistantMcpDiscoveryResponsePolicy(input) {
|
||||||
if (exactBankOperationsProtectsCurrent) {
|
if (exactBankOperationsProtectsCurrent) {
|
||||||
pushReason(reasonCodes, "mcp_discovery_response_policy_keep_exact_bank_operations_address_reply");
|
pushReason(reasonCodes, "mcp_discovery_response_policy_keep_exact_bank_operations_address_reply");
|
||||||
}
|
}
|
||||||
|
if (exactDocumentListAddressReply) {
|
||||||
|
pushReason(reasonCodes, "mcp_discovery_response_policy_keep_exact_document_list_address_reply");
|
||||||
|
}
|
||||||
if (inventoryMarginRankingAddressReply) {
|
if (inventoryMarginRankingAddressReply) {
|
||||||
pushReason(reasonCodes, "mcp_discovery_response_policy_keep_inventory_margin_ranking_address_reply");
|
pushReason(reasonCodes, "mcp_discovery_response_policy_keep_inventory_margin_ranking_address_reply");
|
||||||
}
|
}
|
||||||
|
|
@ -744,6 +771,7 @@ function applyAssistantMcpDiscoveryResponsePolicy(input) {
|
||||||
!staleMetadataDiscoveryFallbackAgainstExactAddressReply &&
|
!staleMetadataDiscoveryFallbackAgainstExactAddressReply &&
|
||||||
!exactValueFlowReplyForBusinessOverviewDirectMoneyNeed &&
|
!exactValueFlowReplyForBusinessOverviewDirectMoneyNeed &&
|
||||||
!exactBankOperationsProtectsCurrent &&
|
!exactBankOperationsProtectsCurrent &&
|
||||||
|
!exactDocumentListAddressReply &&
|
||||||
!inventoryMarginRankingAddressReply &&
|
!inventoryMarginRankingAddressReply &&
|
||||||
!(deterministicBroadBusinessEvaluationReply && candidate.candidate_status === "clarification_candidate") &&
|
!(deterministicBroadBusinessEvaluationReply && candidate.candidate_status === "clarification_candidate") &&
|
||||||
ALLOWED_CANDIDATE_STATUSES.has(candidate.candidate_status) &&
|
ALLOWED_CANDIDATE_STATUSES.has(candidate.candidate_status) &&
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,21 @@ const ADDRESS_INTENTS_ALLOW_STRICT_DEEP_INVESTIGATION_BYPASS = new Set([
|
||||||
function shouldBypassStrictDeepInvestigationCueForAddressIntent(intent) {
|
function shouldBypassStrictDeepInvestigationCueForAddressIntent(intent) {
|
||||||
return Boolean(intent && ADDRESS_INTENTS_ALLOW_STRICT_DEEP_INVESTIGATION_BYPASS.has(intent));
|
return Boolean(intent && ADDRESS_INTENTS_ALLOW_STRICT_DEEP_INVESTIGATION_BYPASS.has(intent));
|
||||||
}
|
}
|
||||||
|
function hasTerseUnsupportedLookupBoundarySignal(text) {
|
||||||
|
const normalized = String(text ?? "").toLowerCase().replace(/\s+/gu, " ").trim();
|
||||||
|
if (!normalized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!/\b(?:gib|give|list|lst)\b/iu.test(normalized)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const tokens = normalized.split(/[^\p{L}0-9._-]+/u).filter(Boolean);
|
||||||
|
if (tokens.length < 2 || tokens.length > 5) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const commandTokens = new Set(["gib", "give", "list", "lst", "show", "find"]);
|
||||||
|
return tokens.some((token) => token.length >= 2 && !commandTokens.has(token));
|
||||||
|
}
|
||||||
function resolveAddressFallbackToDeepArbitration(input) {
|
function resolveAddressFallbackToDeepArbitration(input) {
|
||||||
const { baseToolGateRunAddressLane, llmRuntimeUnavailableDetected, unsupportedIntentOrMode, strongDataSignal, rootContextOnlyFollowup, llmContractMode, strictDeepInvestigationCueDetected, semanticDeepInvestigationHintDetected, aggregateBusinessAnalyticsSignal, preserveAddressLaneSignal, supportedAddressRouteCandidateDetected, followupContext, followupSemanticOverrideToDeepAllowed, deepAnalysisPreferenceDetected, protectAddressLaneFromFallback, dataRetrievalSignal, vatExplainFollowupSignal, semanticAggregateShapeDetected, semanticApplyCanonicalRecommended, standaloneAddressTopicSignal, hasDeepSessionContinuationSignalDetected } = input;
|
const { baseToolGateRunAddressLane, llmRuntimeUnavailableDetected, unsupportedIntentOrMode, strongDataSignal, rootContextOnlyFollowup, llmContractMode, strictDeepInvestigationCueDetected, semanticDeepInvestigationHintDetected, aggregateBusinessAnalyticsSignal, preserveAddressLaneSignal, supportedAddressRouteCandidateDetected, followupContext, followupSemanticOverrideToDeepAllowed, deepAnalysisPreferenceDetected, protectAddressLaneFromFallback, dataRetrievalSignal, vatExplainFollowupSignal, semanticAggregateShapeDetected, semanticApplyCanonicalRecommended, standaloneAddressTopicSignal, hasDeepSessionContinuationSignalDetected } = input;
|
||||||
const unsupportedAddressIntentFallbackToDeep = Boolean(baseToolGateRunAddressLane &&
|
const unsupportedAddressIntentFallbackToDeep = Boolean(baseToolGateRunAddressLane &&
|
||||||
|
|
@ -79,11 +94,10 @@ function resolveAddressFallbackToDeepArbitration(input) {
|
||||||
semanticAggregateShapeDetected ||
|
semanticAggregateShapeDetected ||
|
||||||
!semanticApplyCanonicalRecommended ||
|
!semanticApplyCanonicalRecommended ||
|
||||||
standaloneAddressTopicSignal));
|
standaloneAddressTopicSignal));
|
||||||
const deepSessionContinuationFallbackToDeep = Boolean(!followupContext &&
|
const deepSessionContinuationFallbackToDeep = Boolean(baseToolGateRunAddressLane &&
|
||||||
baseToolGateRunAddressLane &&
|
|
||||||
!llmRuntimeUnavailableDetected &&
|
!llmRuntimeUnavailableDetected &&
|
||||||
!protectAddressLaneFromFallback &&
|
hasDeepSessionContinuationSignalDetected &&
|
||||||
hasDeepSessionContinuationSignalDetected);
|
(!protectAddressLaneFromFallback || deepAnalysisPreferenceDetected || semanticDeepInvestigationHintDetected));
|
||||||
return {
|
return {
|
||||||
unsupportedAddressIntentFallbackToDeep,
|
unsupportedAddressIntentFallbackToDeep,
|
||||||
deepAnalysisSignalFallbackToDeep,
|
deepAnalysisSignalFallbackToDeep,
|
||||||
|
|
@ -444,6 +458,7 @@ function createAssistantRoutePolicy(deps) {
|
||||||
}
|
}
|
||||||
: baseResolvedIntentResolution;
|
: baseResolvedIntentResolution;
|
||||||
const llmPreDecomposeReason = toNonEmptyString(llmPreDecomposeMeta?.reason);
|
const llmPreDecomposeReason = toNonEmptyString(llmPreDecomposeMeta?.reason);
|
||||||
|
const diagnosticRewriteRejected = llmPreDecomposeReason === "normalized_fragment_rejected_diagnostic_rewrite";
|
||||||
const providerExecution = resolveProviderExecutionState({
|
const providerExecution = resolveProviderExecutionState({
|
||||||
useMock,
|
useMock,
|
||||||
llmPreDecomposeReason
|
llmPreDecomposeReason
|
||||||
|
|
@ -590,11 +605,13 @@ function createAssistantRoutePolicy(deps) {
|
||||||
].includes(String(baseToolGate?.reason ?? ""))) ||
|
].includes(String(baseToolGate?.reason ?? ""))) ||
|
||||||
Boolean(baseToolGate?.runAddressLane &&
|
Boolean(baseToolGate?.runAddressLane &&
|
||||||
String(baseToolGate?.reason ?? "") === "followup_context_detected" &&
|
String(baseToolGate?.reason ?? "") === "followup_context_detected" &&
|
||||||
effectiveGroundedValueFlowFollowupContextDetected);
|
effectiveGroundedValueFlowFollowupContextDetected) ||
|
||||||
|
diagnosticRewriteRejected;
|
||||||
const nonDomainQueryIndexed = Boolean(!llmFirstAddressCandidate &&
|
const nonDomainQueryIndexed = Boolean(!llmFirstAddressCandidate &&
|
||||||
deterministicNonDomainGuard &&
|
deterministicNonDomainGuard &&
|
||||||
(llmFirstUnsupportedCandidate || llmContractMode === null) &&
|
(llmFirstUnsupportedCandidate || llmContractMode === null) &&
|
||||||
!baseToolGatePreservesAddressLane &&
|
!baseToolGatePreservesAddressLane &&
|
||||||
|
!strictDeepInvestigationCueDetected &&
|
||||||
!effectiveGroundedValueFlowFollowupContextDetected &&
|
!effectiveGroundedValueFlowFollowupContextDetected &&
|
||||||
!protectedInventoryShortFollowup &&
|
!protectedInventoryShortFollowup &&
|
||||||
!protectedInventoryMarginRankingFollowup &&
|
!protectedInventoryMarginRankingFollowup &&
|
||||||
|
|
@ -644,6 +661,13 @@ function createAssistantRoutePolicy(deps) {
|
||||||
!dangerOrCoercionSignal &&
|
!dangerOrCoercionSignal &&
|
||||||
!effectiveGroundedValueFlowFollowupContextDetected &&
|
!effectiveGroundedValueFlowFollowupContextDetected &&
|
||||||
!organizationClarificationContinuationDetected);
|
!organizationClarificationContinuationDetected);
|
||||||
|
const nonDomainUnsupportedBoundary = Boolean(assistantTurnMeaning?.unsupported_but_understood_family &&
|
||||||
|
assistantTurnMeaning?.stale_replay_forbidden === true &&
|
||||||
|
!turnMeaningIntentCandidate) ||
|
||||||
|
hasTerseUnsupportedLookupBoundarySignal(rawUserMessage) ||
|
||||||
|
hasTerseUnsupportedLookupBoundarySignal(repairedRawUserMessage) ||
|
||||||
|
hasTerseUnsupportedLookupBoundarySignal(effectiveAddressUserMessage) ||
|
||||||
|
hasTerseUnsupportedLookupBoundarySignal(repairedEffectiveAddressUserMessage);
|
||||||
const hardMetaMode = resolveHardMetaMode({
|
const hardMetaMode = resolveHardMetaMode({
|
||||||
dataScopeMetaQuery,
|
dataScopeMetaQuery,
|
||||||
capabilityMetaQuery,
|
capabilityMetaQuery,
|
||||||
|
|
@ -1044,11 +1068,14 @@ function createAssistantRoutePolicy(deps) {
|
||||||
toolGateDecision: "skip_address_lane",
|
toolGateDecision: "skip_address_lane",
|
||||||
toolGateReason: "non_domain_query_indexed",
|
toolGateReason: "non_domain_query_indexed",
|
||||||
livingMode: "chat",
|
livingMode: "chat",
|
||||||
livingReason: "non_domain_query_indexed",
|
livingReason: nonDomainUnsupportedBoundary
|
||||||
|
? "unsupported_current_turn_meaning_boundary"
|
||||||
|
: "non_domain_query_indexed",
|
||||||
orchestrationContract: {
|
orchestrationContract: {
|
||||||
schema_version: "assistant_orchestration_contract_v1",
|
schema_version: "assistant_orchestration_contract_v1",
|
||||||
hard_meta_mode: "non_domain",
|
hard_meta_mode: "non_domain",
|
||||||
provider_execution: providerExecution,
|
provider_execution: providerExecution,
|
||||||
|
assistant_turn_meaning: assistantTurnMeaning,
|
||||||
address_mode: resolvedModeDetection.mode,
|
address_mode: resolvedModeDetection.mode,
|
||||||
address_mode_confidence: resolvedModeDetection.confidence,
|
address_mode_confidence: resolvedModeDetection.confidence,
|
||||||
address_intent: resolvedIntentResolution.intent,
|
address_intent: resolvedIntentResolution.intent,
|
||||||
|
|
@ -1056,13 +1083,19 @@ function createAssistantRoutePolicy(deps) {
|
||||||
strong_data_signal_detected: strongDataSignal,
|
strong_data_signal_detected: strongDataSignal,
|
||||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||||
followup_context_detected: Boolean(followupContext),
|
followup_context_detected: Boolean(followupContext),
|
||||||
|
unsupported_current_turn_meaning_boundary: nonDomainUnsupportedBoundary,
|
||||||
|
unsupported_current_turn_family: nonDomainUnsupportedBoundary
|
||||||
|
? assistantTurnMeaning?.unsupported_but_understood_family ?? "terse_unsupported_lookup"
|
||||||
|
: null,
|
||||||
unsupported_address_intent_fallback_to_deep: false,
|
unsupported_address_intent_fallback_to_deep: false,
|
||||||
final_decision: {
|
final_decision: {
|
||||||
run_address_lane: false,
|
run_address_lane: false,
|
||||||
tool_gate_decision: "skip_address_lane",
|
tool_gate_decision: "skip_address_lane",
|
||||||
tool_gate_reason: "non_domain_query_indexed",
|
tool_gate_reason: "non_domain_query_indexed",
|
||||||
living_mode: "chat",
|
living_mode: "chat",
|
||||||
living_reason: "non_domain_query_indexed"
|
living_reason: nonDomainUnsupportedBoundary
|
||||||
|
? "unsupported_current_turn_meaning_boundary"
|
||||||
|
: "non_domain_query_indexed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1146,7 +1179,8 @@ function createAssistantRoutePolicy(deps) {
|
||||||
const exactAddressIntentProtectedFromSemanticDeepHint = laneProtectionArbitration.exactAddressIntentProtectedFromSemanticDeepHint;
|
const exactAddressIntentProtectedFromSemanticDeepHint = laneProtectionArbitration.exactAddressIntentProtectedFromSemanticDeepHint;
|
||||||
const protectAddressLaneFromFallback = Boolean(laneProtectionArbitration.protectAddressLaneFromFallback ||
|
const protectAddressLaneFromFallback = Boolean(laneProtectionArbitration.protectAddressLaneFromFallback ||
|
||||||
customerValueRankingAddressSignal ||
|
customerValueRankingAddressSignal ||
|
||||||
protectedInventoryMarginRankingFollowup);
|
protectedInventoryMarginRankingFollowup ||
|
||||||
|
diagnosticRewriteRejected);
|
||||||
const vatExplainFollowupSignal = Boolean(followupContext &&
|
const vatExplainFollowupSignal = Boolean(followupContext &&
|
||||||
toNonEmptyString(followupContext.previous_intent) === "vat_payable_forecast" &&
|
toNonEmptyString(followupContext.previous_intent) === "vat_payable_forecast" &&
|
||||||
/(?:\u043f\u043e\u0447\u0435\u043c\u0443|why).*(?:\u043f\u0440\u043e\u0433\u043d\u043e\u0437|forecast).*(?:\u0443\u043f\u043b\u0430\u0442|payable|\b0\b)/iu.test(compactWhitespace(`${repairedRawUserMessage} ${repairedEffectiveAddressUserMessage}`)));
|
/(?:\u043f\u043e\u0447\u0435\u043c\u0443|why).*(?:\u043f\u0440\u043e\u0433\u043d\u043e\u0437|forecast).*(?:\u0443\u043f\u043b\u0430\u0442|payable|\b0\b)/iu.test(compactWhitespace(`${repairedRawUserMessage} ${repairedEffectiveAddressUserMessage}`)));
|
||||||
|
|
@ -1226,7 +1260,7 @@ function createAssistantRoutePolicy(deps) {
|
||||||
let toolGateDecision = String(baseToolGate?.decision ?? "skip_address_lane");
|
let toolGateDecision = String(baseToolGate?.decision ?? "skip_address_lane");
|
||||||
let toolGateReason = String(baseToolGate?.reason ?? "no_address_signal_after_l0");
|
let toolGateReason = String(baseToolGate?.reason ?? "no_address_signal_after_l0");
|
||||||
const semanticAddressLaneRecovery = Boolean(!runAddressLane &&
|
const semanticAddressLaneRecovery = Boolean(!runAddressLane &&
|
||||||
(supportedAddressRouteCandidateDetected || protectedInventoryMarginRankingFollowup) &&
|
(supportedAddressRouteCandidateDetected || protectedInventoryMarginRankingFollowup || diagnosticRewriteRejected) &&
|
||||||
!deepAnalysisPreferenceDetected &&
|
!deepAnalysisPreferenceDetected &&
|
||||||
!unsupportedAddressIntentFallbackToDeep &&
|
!unsupportedAddressIntentFallbackToDeep &&
|
||||||
!deepAnalysisSignalFallbackToDeep &&
|
!deepAnalysisSignalFallbackToDeep &&
|
||||||
|
|
|
||||||
|
|
@ -1605,6 +1605,7 @@ const ADDRESS_CONTRACT_SIGNAL_PATTERN = /(?:договор(?:а|у|ом|е)?|(?:
|
||||||
const ADDRESS_BALANCE_SIGNAL_PATTERN = /(?:остат|сальдо|баланс|взаиморасч|долг|saldo|balance)/i;
|
const ADDRESS_BALANCE_SIGNAL_PATTERN = /(?:остат|сальдо|баланс|взаиморасч|долг|saldo|balance)/i;
|
||||||
const ADDRESS_ALL_TIME_PATTERN = /(?:за\s+вс[её]\s+время|за\s+весь\s+период|за\s+всю\s+истори(?:ю|и)|for\s+all\s+time|all\s+time|entire\s+period|full\s+history)/iu;
|
const ADDRESS_ALL_TIME_PATTERN = /(?:за\s+вс[её]\s+время|за\s+весь\s+период|за\s+всю\s+истори(?:ю|и)|for\s+all\s+time|all\s+time|entire\s+period|full\s+history)/iu;
|
||||||
const ADDRESS_MANAGEMENT_PROFILE_PATTERN = /(?:за\s+какие\s+год[а-яё]*|сам(?:ый|ая|ое)\s+(?:актив|пассив)|наименее\s+актив|минимальн|покрыт(?:ие|ия)\s+период|диапазон\s+лет|тип[аы]\s+док(?:умент|ов|и)?|раздел[ыа]\s+уч[её]та|по\s+количеств[аоуе]|редк|реже|(?:сколько|скока|скок)\s+(?:всего\s+)?(?:уникальн(?:ых|ые|ого)?\s+)?контрагент(?:ов|а)?|(?:сколько|скока|скок)\s+(?:у\s+нас\s+)?(?:заказчик(?:ов|а)?|поставщик(?:ов|а)?|клиент(?:ов|а)?|покупател(?:ей|я)|смешан(?:ных|ые)\s+контрагент(?:ов|а)?)|(?:покажи|выведи|список|какие|кто).*(?:заказчик(?:ов|а|и)?|клиент(?:ов|а|ы)?|покупател(?:ей|я|и)?).*(?:за\s+вс[её]\s+время|all\s+time|(?:^|[^\d])(19|20)\d{2}(?:[^\d]|$)|(?:^|[^\d])\d{2}\s*(?:г(?:од|ода)?|г)(?:[^\p{L}\p{N}]|$)|за\s+год|в\s+году)|(?:какие|кто|покажи|выведи|список).*(?:заказчик(?:ов|а|и)?|клиент(?:ов|а|ы)?|покупател(?:ей|я|и)?).*(?:работал(?:и)?|активн(?:ые|ых|а|о)?).*(?:за\s+вс[её]\s+время|(?:19|20)\d{2}|за\s+год|в\s+году)|договорн(?:ая|ой)\s+баз[аы]|total\s+vs\s+used)/iu;
|
const ADDRESS_MANAGEMENT_PROFILE_PATTERN = /(?:за\s+какие\s+год[а-яё]*|сам(?:ый|ая|ое)\s+(?:актив|пассив)|наименее\s+актив|минимальн|покрыт(?:ие|ия)\s+период|диапазон\s+лет|тип[аы]\s+док(?:умент|ов|и)?|раздел[ыа]\s+уч[её]та|по\s+количеств[аоуе]|редк|реже|(?:сколько|скока|скок)\s+(?:всего\s+)?(?:уникальн(?:ых|ые|ого)?\s+)?контрагент(?:ов|а)?|(?:сколько|скока|скок)\s+(?:у\s+нас\s+)?(?:заказчик(?:ов|а)?|поставщик(?:ов|а)?|клиент(?:ов|а)?|покупател(?:ей|я)|смешан(?:ных|ые)\s+контрагент(?:ов|а)?)|(?:покажи|выведи|список|какие|кто).*(?:заказчик(?:ов|а|и)?|клиент(?:ов|а|ы)?|покупател(?:ей|я|и)?).*(?:за\s+вс[её]\s+время|all\s+time|(?:^|[^\d])(19|20)\d{2}(?:[^\d]|$)|(?:^|[^\d])\d{2}\s*(?:г(?:од|ода)?|г)(?:[^\p{L}\p{N}]|$)|за\s+год|в\s+году)|(?:какие|кто|покажи|выведи|список).*(?:заказчик(?:ов|а|и)?|клиент(?:ов|а|ы)?|покупател(?:ей|я|и)?).*(?:работал(?:и)?|активн(?:ые|ых|а|о)?).*(?:за\s+вс[её]\s+время|(?:19|20)\d{2}|за\s+год|в\s+году)|договорн(?:ая|ой)\s+баз[аы]|total\s+vs\s+used)/iu;
|
||||||
|
const ADDRESS_DEEP_ANALYSIS_FALLBACK_BLOCK_PATTERN = /(?:\u0432\s+\u0446\u0435\u043b\u043e\u043c|\u043e\u0431\u0449\p{L}*\s+\u043a\u0430\u0440\u0442\u0438\u043d|\u0442\u043e\u043f\s+\u0440\u0438\u0441\u043a|\u0447\u0442\u043e\s+\u043d\u0435\s+\u0442\u0430\u043a|\u043e\u0441\u043d\u043e\u0432\p{L}*\s+\u0441\u0440\u0435\u0434\u0441\u0442)/iu;
|
||||||
function normalizeAddressMonthAliasToken(token) {
|
function normalizeAddressMonthAliasToken(token) {
|
||||||
const source = String(token ?? "").trim().toLowerCase();
|
const source = String(token ?? "").trim().toLowerCase();
|
||||||
if (!source) {
|
if (!source) {
|
||||||
|
|
@ -1814,6 +1815,9 @@ function resolveAddressDeterministicFallback(userMessage, sanitizedUserMessage)
|
||||||
if (ADDRESS_MANAGEMENT_PROFILE_PATTERN.test(source)) {
|
if (ADDRESS_MANAGEMENT_PROFILE_PATTERN.test(source)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if (ADDRESS_DEEP_ANALYSIS_FALLBACK_BLOCK_PATTERN.test(source)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const monthYear = extractAddressFallbackMonthYear(source);
|
const monthYear = extractAddressFallbackMonthYear(source);
|
||||||
const year = extractAddressFallbackYear(source);
|
const year = extractAddressFallbackYear(source);
|
||||||
const allTime = ADDRESS_ALL_TIME_PATTERN.test(source);
|
const allTime = ADDRESS_ALL_TIME_PATTERN.test(source);
|
||||||
|
|
@ -3319,6 +3323,13 @@ function hasPredecomposeDiagnosticUncertaintyLead(text) {
|
||||||
}
|
}
|
||||||
return /^(?:неясно|не\s+ясно|непонятно|не\s+понятно|unclear|not\s+clear|ambiguous|unknown)(?=$|[\s,.;:!?])/iu.test(normalized);
|
return /^(?:неясно|не\s+ясно|непонятно|не\s+понятно|unclear|not\s+clear|ambiguous|unknown)(?=$|[\s,.;:!?])/iu.test(normalized);
|
||||||
}
|
}
|
||||||
|
function hasPredecomposeDebtSnapshotIntentSignal(text) {
|
||||||
|
const normalized = compactWhitespace(repairAddressMojibake(String(text ?? "")).toLowerCase()).replace(/\u0451/gu, "\u0435");
|
||||||
|
if (!normalized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return /(?:\u043a\u0442\u043e\s+\u043d\u0430\u043c(?:\s+\p{L}+){0,4}\s+\u0434\u043e\u043b\u0436|\u043d\u0430\u043c\s+(?:\u043a\u0442\u043e(?:-\u0442\u043e)?\s+)?\u0434\u043e\u043b\u0436|\u0434\u0435\u0431\u0438\u0442\u043e\u0440|receivables?)/iu.test(normalized);
|
||||||
|
}
|
||||||
function attachAddressPredecomposeContract(meta, sourceMessage) {
|
function attachAddressPredecomposeContract(meta, sourceMessage) {
|
||||||
const sourceMeta = meta && typeof meta === "object" ? meta : {};
|
const sourceMeta = meta && typeof meta === "object" ? meta : {};
|
||||||
const { providerExecutionInput, providerExecutionContract: providerExecutionContractInput, ...restMeta } = sourceMeta;
|
const { providerExecutionInput, providerExecutionContract: providerExecutionContractInput, ...restMeta } = sourceMeta;
|
||||||
|
|
@ -3439,9 +3450,11 @@ async function runAddressLlmPreDecompose(normalizerService, payload, userMessage
|
||||||
const repairedSourceMessage = repairAddressMojibake(userMessage);
|
const repairedSourceMessage = repairAddressMojibake(userMessage);
|
||||||
const sourceIntentResolution = (0, addressIntentResolver_1.resolveAddressIntent)(repairedSourceMessage || userMessage);
|
const sourceIntentResolution = (0, addressIntentResolver_1.resolveAddressIntent)(repairedSourceMessage || userMessage);
|
||||||
const candidateIntentResolution = (0, addressIntentResolver_1.resolveAddressIntent)(candidate);
|
const candidateIntentResolution = (0, addressIntentResolver_1.resolveAddressIntent)(candidate);
|
||||||
const sourceIntentKnown = sourceIntentResolution.intent !== "unknown";
|
const sourceIntentKnown = sourceIntentResolution.intent !== "unknown" ||
|
||||||
|
hasPredecomposeDebtSnapshotIntentSignal(repairedSourceMessage || userMessage);
|
||||||
const candidateIntentKnown = candidateIntentResolution.intent !== "unknown";
|
const candidateIntentKnown = candidateIntentResolution.intent !== "unknown";
|
||||||
const candidateStartsWithDiagnosticUncertainty = hasPredecomposeDiagnosticUncertaintyLead(candidate);
|
const candidateStartsWithDiagnosticUncertainty = hasPredecomposeDiagnosticUncertaintyLead(candidate) ||
|
||||||
|
/^(?:\u043d\u0435\u044f\u0441\u043d\u043e|\u043d\u0435\s+\u044f\u0441\u043d\u043e|\u043d\u0435\u043f\u043e\u043d\u044f\u0442\u043d\u043e|\u043d\u0435\s+\u043f\u043e\u043d\u044f\u0442\u043d\u043e)(?=$|[\s,.;:!?])/iu.test(compactWhitespace(repairAddressMojibake(String(candidate ?? "")).toLowerCase()));
|
||||||
if (candidateStartsWithDiagnosticUncertainty && sourceIntentKnown) {
|
if (candidateStartsWithDiagnosticUncertainty && sourceIntentKnown) {
|
||||||
return attachAddressPredecomposeContract({
|
return attachAddressPredecomposeContract({
|
||||||
...baseMeta,
|
...baseMeta,
|
||||||
|
|
@ -3463,6 +3476,13 @@ async function runAddressLlmPreDecompose(normalizerService, payload, userMessage
|
||||||
const rejectCandidateForIntentSafety = intentDroppedByCandidate ||
|
const rejectCandidateForIntentSafety = intentDroppedByCandidate ||
|
||||||
(intentConflict &&
|
(intentConflict &&
|
||||||
(sourceIntentResolution.confidence === "high" || candidateIntentResolution.confidence !== "high"));
|
(sourceIntentResolution.confidence === "high" || candidateIntentResolution.confidence !== "high"));
|
||||||
|
const sourceAnchorQualityForIntentSafety = evaluateAddressAnchorQuality(repairedSourceMessage || userMessage);
|
||||||
|
const candidateAnchorQualityForIntentSafety = evaluateAddressAnchorQuality(candidate);
|
||||||
|
const counterpartyAnchorSubstitutedDuringIntentDrop = sourceAnchorQualityForIntentSafety.anchorType === "counterparty" &&
|
||||||
|
sourceAnchorQualityForIntentSafety.quality >= 2 &&
|
||||||
|
Boolean(sourceAnchorQualityForIntentSafety.anchorValue) &&
|
||||||
|
candidateAnchorQualityForIntentSafety.quality < sourceAnchorQualityForIntentSafety.quality &&
|
||||||
|
hasCounterpartyAnchorSubstitution(sourceAnchorQualityForIntentSafety.anchorValue ?? "", candidate);
|
||||||
if (rejectCandidateForIntentSafety) {
|
if (rejectCandidateForIntentSafety) {
|
||||||
return attachAddressPredecomposeContract({
|
return attachAddressPredecomposeContract({
|
||||||
...baseMeta,
|
...baseMeta,
|
||||||
|
|
@ -3471,7 +3491,9 @@ async function runAddressLlmPreDecompose(normalizerService, payload, userMessage
|
||||||
traceId: normalized?.trace_id ?? null,
|
traceId: normalized?.trace_id ?? null,
|
||||||
llmCanonicalCandidateDetected: true,
|
llmCanonicalCandidateDetected: true,
|
||||||
effectiveMessage: userMessage,
|
effectiveMessage: userMessage,
|
||||||
reason: intentDroppedByCandidate
|
reason: counterpartyAnchorSubstitutedDuringIntentDrop
|
||||||
|
? "normalized_fragment_rejected_anchor_substitution"
|
||||||
|
: intentDroppedByCandidate
|
||||||
? "normalized_fragment_rejected_intent_drop"
|
? "normalized_fragment_rejected_intent_drop"
|
||||||
: "normalized_fragment_rejected_intent_conflict",
|
: "normalized_fragment_rejected_intent_conflict",
|
||||||
fallbackRuleHit: null,
|
fallbackRuleHit: null,
|
||||||
|
|
@ -3482,7 +3504,8 @@ async function runAddressLlmPreDecompose(normalizerService, payload, userMessage
|
||||||
const sourceHasExplicitDrilldownSignal = hasPredecomposeExplicitDrilldownSignal(repairedSourceMessage || userMessage);
|
const sourceHasExplicitDrilldownSignal = hasPredecomposeExplicitDrilldownSignal(repairedSourceMessage || userMessage);
|
||||||
const candidateHasExplicitDrilldownSignal = hasPredecomposeExplicitDrilldownSignal(candidate);
|
const candidateHasExplicitDrilldownSignal = hasPredecomposeExplicitDrilldownSignal(candidate);
|
||||||
const sourceLooksLikeSameDateAccountFollowup = hasSameDateAccountFollowupSignalForPredecompose(repairedSourceMessage || userMessage);
|
const sourceLooksLikeSameDateAccountFollowup = hasSameDateAccountFollowupSignalForPredecompose(repairedSourceMessage || userMessage);
|
||||||
const candidateInjectsDrilldownIntent = candidateIntentResolution.intent === "documents_forming_balance";
|
const candidateInjectsDrilldownIntent = candidateIntentResolution.intent === "documents_forming_balance" ||
|
||||||
|
(candidateHasExplicitDrilldownSignal && /(?:document|posting|docs?|\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442|\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b|\u043f\u0440\u043e\u0432\u043e\u0434\u043a)/iu.test(candidate));
|
||||||
if (sourceLooksLikeSameDateAccountFollowup &&
|
if (sourceLooksLikeSameDateAccountFollowup &&
|
||||||
!sourceHasExplicitDrilldownSignal &&
|
!sourceHasExplicitDrilldownSignal &&
|
||||||
candidateHasExplicitDrilldownSignal &&
|
candidateHasExplicitDrilldownSignal &&
|
||||||
|
|
@ -3801,8 +3824,8 @@ function hasDeepSessionContinuationSignal(input) {
|
||||||
return candidateTexts.some((text) => {
|
return candidateTexts.some((text) => {
|
||||||
const hasContinuationCue = /^(?:\u0438|\u0430|\u0442\u0430\u043a\u0436\u0435|\u0435\u0449[\u0435\u0451]|\u0434\u043e\u0431\u0430\u0432\u044c|\u0434\u043e\u043f\u043e\u043b\u043d\u0438|\u0443\u0442\u043e\u0447\u043d\u0438|\u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438|\u0442\u0435\u043f\u0435\u0440\u044c|then|also|and)\b/iu.test(text) ||
|
const hasContinuationCue = /^(?:\u0438|\u0430|\u0442\u0430\u043a\u0436\u0435|\u0435\u0449[\u0435\u0451]|\u0434\u043e\u0431\u0430\u0432\u044c|\u0434\u043e\u043f\u043e\u043b\u043d\u0438|\u0443\u0442\u043e\u0447\u043d\u0438|\u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438|\u0442\u0435\u043f\u0435\u0440\u044c|then|also|and)\b/iu.test(text) ||
|
||||||
/(?:\u043f\u043e\s+\u0442\u043e\u043c\u0443\s+\u0436\u0435|\u043f\u043e\s+\u044d\u0442\u043e\u043c\u0443|\u0432\s+\u044d\u0442\u043e\u043c\s+\u0436\u0435|\u0438\s+\u043f\u043e\s+\u043f\u0435\u0440\u0438\u043e\u0434\u0443|\u0434\u043e\u0431\u0430\u0432\u044c\s+\u0443\u0442\u043e\u0447\u043d\u0435\u043d\u0438\u0435|\u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u043c|\u0430\s+\u0435\u0441\u043b\u0438|\u0435\u0441\u043b\u0438\s+\u0442\u043e\u043b\u044c\u043a\u043e|\u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e)/iu.test(text);
|
/(?:\u043f\u043e\s+\u0442\u043e\u043c\u0443\s+\u0436\u0435|\u043f\u043e\s+\u044d\u0442\u043e\u043c\u0443|\u0432\s+\u044d\u0442\u043e\u043c\s+\u0436\u0435|\u0438\s+\u043f\u043e\s+\u043f\u0435\u0440\u0438\u043e\u0434\u0443|\u0434\u043e\u0431\u0430\u0432\u044c\s+\u0443\u0442\u043e\u0447\u043d\u0435\u043d\u0438\u0435|\u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u043c|\u0430\s+\u0435\u0441\u043b\u0438|\u0435\u0441\u043b\u0438\s+\u0442\u043e\u043b\u044c\u043a\u043e|\u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e)/iu.test(text);
|
||||||
const hasAccountOrPeriodCue = /(?:\u0441\u0447[\u0435\u0451]\u0442|account|\b\d{2}(?:[.,]\d{1,2})?\b|\b20\d{2}(?:[-/.]\d{1,2})?\b|\u043f\u0435\u0440\u0438\u043e\u0434|\u043c\u0435\u0441\u044f\u0446)/iu.test(text);
|
const hasAccountOrPeriodCue = /(?:\u0441\u0447[\u0435\u0451]\u0442|account|\b\d{2}(?:[.,]\d{1,2})?\b|\b20\d{2}(?:[-/.]\d{1,2})?\b|\u043f\u0435\u0440\u0438\u043e\u0434|\u043c\u0435\u0441\u044f\u0446|\u0434\u043e\u0433\u043e\u0432\u043e\u0440|\u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442|contract)/iu.test(text);
|
||||||
const hasDeepRebindCue = /(?:\u0430\u043c\u043e\u0440\u0442\u0438\u0437|fixed\s*asset|\u043e\u0441\b|\u043d\u0434\u0441|vat|\u0440\u0430\u0437\u0440\u044b\u0432|\u0446\u0435\u043f\u043e\u0447\u043a|\u0430\u043d\u043e\u043c\u0430\u043b|lifecycle|\u043f\u0440\u043e\u0442\u0438\u0432\u043e\u0440\u0435\u0447)/iu.test(text);
|
const hasDeepRebindCue = /(?:\u0430\u043c\u043e\u0440\u0442\u0438\u0437|fixed\s*asset|\u043e\u0441\b|\u043d\u0434\u0441|vat|\u0440\u0430\u0437\u0440\u044b\u0432|\u0446\u0435\u043f\u043e\u0447\u043a|\u0430\u043d\u043e\u043c\u0430\u043b|lifecycle|\u043f\u0440\u043e\u0442\u0438\u0432\u043e\u0440\u0435\u0447|\u0437\u0430\u043a\u0440\u044b\u0442|\u0445\u0432\u043e\u0441\u0442|\u0434\u043e\u0433\u043e\u0432\u043e\u0440|\u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442|contract)/iu.test(text);
|
||||||
if (hasContinuationCue && (hasAccountOrPeriodCue || hasDeepRebindCue)) {
|
if (hasContinuationCue && (hasAccountOrPeriodCue || hasDeepRebindCue)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -3822,6 +3845,7 @@ function hasDeepAnalysisPreferenceSignal(text) {
|
||||||
if (openContractsListQuestionSignal) {
|
if (openContractsListQuestionSignal) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const broadOverviewRiskSignal = /(?:\u0432\s+\u0446\u0435\u043b\u043e\u043c|\u043e\u0431\u0449\p{L}*\s+\u043a\u0430\u0440\u0442\u0438\u043d|\u0442\u043e\u043f\s+\u0440\u0438\u0441\u043a|\u0447\u0442\u043e\s+\u043d\u0435\s+\u0442\u0430\u043a)/iu.test(lower);
|
||||||
const riskOrAnomalySignal = /(?:\u0440\u0438\u0441\u043a|risk|\u0430\u043d\u043e\u043c\u0430\u043b|anomal|\u043f\u0440\u043e\u0442\u0438\u0432\u043e\u0440\u0435\u0447|\u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442|conflict|deviation|\u043e\u0442\u043a\u043b\u043e\u043d\u0435\u043d|\u043d\u0435\u0441\u044b\u043a\u043e\u0432\u043a|\u043d\u0435\u0441\u0445\u043e\u0434|\u043e\u0448\u0438\u0431|error|issue|\u043f\u0440\u043e\u0431\u043b\u0435\u043c)/iu.test(lower);
|
const riskOrAnomalySignal = /(?:\u0440\u0438\u0441\u043a|risk|\u0430\u043d\u043e\u043c\u0430\u043b|anomal|\u043f\u0440\u043e\u0442\u0438\u0432\u043e\u0440\u0435\u0447|\u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442|conflict|deviation|\u043e\u0442\u043a\u043b\u043e\u043d\u0435\u043d|\u043d\u0435\u0441\u044b\u043a\u043e\u0432\u043a|\u043d\u0435\u0441\u0445\u043e\u0434|\u043e\u0448\u0438\u0431|error|issue|\u043f\u0440\u043e\u0431\u043b\u0435\u043c)/iu.test(lower);
|
||||||
const chainSignal = /(?:\u0446\u0435\u043f\u043e\u0447\u043a|chain|trace\s*chain|lifecycle|\u0436\u0438\u0437\u043d\u0435\u043d\u043d[\u0430-\u044f]+\s+\u0446\u0438\u043a\u043b|state\s+transition|\u0440\u0430\u0437\u0440\u044b\u0432[\u0430-\u044f]*)/iu.test(lower);
|
const chainSignal = /(?:\u0446\u0435\u043f\u043e\u0447\u043a|chain|trace\s*chain|lifecycle|\u0436\u0438\u0437\u043d\u0435\u043d\u043d[\u0430-\u044f]+\s+\u0446\u0438\u043a\u043b|state\s+transition|\u0440\u0430\u0437\u0440\u044b\u0432[\u0430-\u044f]*)/iu.test(lower);
|
||||||
const diagnosticsKeywordSignal = /(?:\u0440\u0430\u0437\u043b\u043e\u0436\u0438|\u0434\u0435\u043a\u043e\u043c\u043f\u043e\u0437|\u0440\u0430\u0437\u0431\u0435\u0440\u0438|\u043f\u043e\u0447\u0435\u043c\u0443|why|audit|scan|\u043a\u043e\u0440\u043d\u0435\u0432[\u0430-\u044f]+\s+\u043f\u0440\u0438\u0447\u0438\u043d|root\s*cause|\u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c[\u0430-\u044f]*|\u0433\u0434\u0435\s+\u0440\u0430\u0437\u0440\u044b\u0432|\u0447\u0442\u043e\s+\u043c\u0435\u0448\u0430[\u0430-\u044f]+\s+\u0437\u0430\u043a\u0440\u044b\u0442)/iu.test(lower);
|
const diagnosticsKeywordSignal = /(?:\u0440\u0430\u0437\u043b\u043e\u0436\u0438|\u0434\u0435\u043a\u043e\u043c\u043f\u043e\u0437|\u0440\u0430\u0437\u0431\u0435\u0440\u0438|\u043f\u043e\u0447\u0435\u043c\u0443|why|audit|scan|\u043a\u043e\u0440\u043d\u0435\u0432[\u0430-\u044f]+\s+\u043f\u0440\u0438\u0447\u0438\u043d|root\s*cause|\u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c[\u0430-\u044f]*|\u0433\u0434\u0435\s+\u0440\u0430\u0437\u0440\u044b\u0432|\u0447\u0442\u043e\s+\u043c\u0435\u0448\u0430[\u0430-\u044f]+\s+\u0437\u0430\u043a\u0440\u044b\u0442)/iu.test(lower);
|
||||||
|
|
@ -3829,17 +3853,20 @@ function hasDeepAnalysisPreferenceSignal(text) {
|
||||||
const diagnosticsSignal = diagnosticsKeywordSignal || diagnosticsCheckVerbSignal;
|
const diagnosticsSignal = diagnosticsKeywordSignal || diagnosticsCheckVerbSignal;
|
||||||
const closureSignal = /(?:\u0437\u0430\u043a\u0440\u044b\u0442\u0438[\u0435\u044f]\s+\u043f\u0435\u0440\u0438\u043e\u0434|period\s*close|\u043d\u0435\s+\u0437\u0430\u043a\u0440\u044b\u043b[\u0430-\u044f]*|\u0445\u0432\u043e\u0441\u0442[\u0430-\u044f]*)/iu.test(lower);
|
const closureSignal = /(?:\u0437\u0430\u043a\u0440\u044b\u0442\u0438[\u0435\u044f]\s+\u043f\u0435\u0440\u0438\u043e\u0434|period\s*close|\u043d\u0435\s+\u0437\u0430\u043a\u0440\u044b\u043b[\u0430-\u044f]*|\u0445\u0432\u043e\u0441\u0442[\u0430-\u044f]*)/iu.test(lower);
|
||||||
const closureIntentSignal = /(?:\u0437\u0430\u043a\u0440\u044b\u0442[\u0430-\u044f]*|period\s*close|close\s+period)/iu.test(lower);
|
const closureIntentSignal = /(?:\u0437\u0430\u043a\u0440\u044b\u0442[\u0430-\u044f]*|period\s*close|close\s+period)/iu.test(lower);
|
||||||
|
const closureStateQuestionSignal = /(?:\u0437\u0430\u043a\u0440\u044b\u0442[\u0430-\u044f]*|\u0445\u0432\u043e\u0441\u0442[\u0430-\u044f]*)[^\n]{0,80}(?:\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434[\u0430-\u044f]*|\u0436\u0438\u0432[\u0430-\u044f]*|\u043e\u0441\u0442\u0430[\u043b-\u044f]*|\u0435\u0441\u0442\u044c)|(?:\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434[\u0430-\u044f]*|\u0436\u0438\u0432[\u0430-\u044f]*|\u043e\u0441\u0442\u0430[\u043b-\u044f]*|\u0435\u0441\u0442\u044c)[^\n]{0,80}(?:\u0437\u0430\u043a\u0440\u044b\u0442[\u0430-\u044f]*|\u0445\u0432\u043e\u0441\u0442[\u0430-\u044f]*)/iu.test(lower);
|
||||||
const closureDiagnosticPhraseSignal = /(?:\u0447\u0442\u043e(?:\s+\S+){0,8}\s+\u043c\u0435\u0448\u0430[\u0430-\u044f]+\s+\u0437\u0430\u043a\u0440\u044b\u0442)/iu.test(lower);
|
const closureDiagnosticPhraseSignal = /(?:\u0447\u0442\u043e(?:\s+\S+){0,8}\s+\u043c\u0435\u0448\u0430[\u0430-\u044f]+\s+\u0437\u0430\u043a\u0440\u044b\u0442)/iu.test(lower);
|
||||||
const signalVsNoiseDiagnostic = /(?:\u043d\u0435\s+\u043f\u0440\u043e\u0441\u0442\u043e\s+(?:\u043d\u0430\s+)?\u0448\u0443\u043c|\u043f\u043e\u0445\u043e\u0436[\u0438\u0435]\s+(?:\u0438\u043c\u0435\u043d\u043d\u043e\s+)?\u043d\u0430\s+\u043f\u0440\u043e\u0431\u043b\u0435\u043c)/iu.test(lower);
|
const signalVsNoiseDiagnostic = /(?:\u043d\u0435\s+\u043f\u0440\u043e\u0441\u0442\u043e\s+(?:\u043d\u0430\s+)?\u0448\u0443\u043c|\u043f\u043e\u0445\u043e\u0436[\u0438\u0435]\s+(?:\u0438\u043c\u0435\u043d\u043d\u043e\s+)?\u043d\u0430\s+\u043f\u0440\u043e\u0431\u043b\u0435\u043c)/iu.test(lower);
|
||||||
const lifecycleMismatchSignal = /(?:\u043d\u0435\s+\u0442\u0435\u043c\s+\u0442\u0438\u043f(?:\u043e\u043c)?\s+\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442|\u043e\u0436\u0438\u0434\u0430\u0435\u043c[\u0430-\u044f]+\s+\u043f\u0435\u0440\u0435\u0445\u043e\u0434[\u0430-\u044f]*\s+\u043d\u0435\s+\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434|\u043f\u0435\u0440\u0435\u0445\u043e\u0434[\u0430-\u044f]*\s+\u043d\u0435\s+\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434|wrong\s+closing\s+document|expected\s+transition)/iu.test(lower);
|
const lifecycleMismatchSignal = /(?:\u043d\u0435\s+\u0442\u0435\u043c\s+\u0442\u0438\u043f(?:\u043e\u043c)?\s+\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442|\u043e\u0436\u0438\u0434\u0430\u0435\u043c[\u0430-\u044f]+\s+\u043f\u0435\u0440\u0435\u0445\u043e\u0434[\u0430-\u044f]*\s+\u043d\u0435\s+\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434|\u043f\u0435\u0440\u0435\u0445\u043e\u0434[\u0430-\u044f]*\s+\u043d\u0435\s+\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434|wrong\s+closing\s+document|expected\s+transition)/iu.test(lower);
|
||||||
const lifecycleTransitionGapSignal = /(?:\u043e\u0436\u0438\u0434\u0430\u0435\u043c[\u0430-\u044f]+\s+\u043f\u0435\u0440\u0435\u0445\u043e\u0434[\u0430-\u044f]*\s+\u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432|\u043f\u0435\u0440\u0435\u0445\u043e\u0434[\u0430-\u044f]*\s+\u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432|\u0441\u0442\u0430\u0434\u0438[\u0438\u044f\u0435]\s+.*\u043f\u0440\u043e\u0439\u0434\u0435\u043d.*\u043f\u0435\u0440\u0435\u0445\u043e\u0434)/iu.test(lower);
|
const lifecycleTransitionGapSignal = /(?:\u043e\u0436\u0438\u0434\u0430\u0435\u043c[\u0430-\u044f]+\s+\u043f\u0435\u0440\u0435\u0445\u043e\u0434[\u0430-\u044f]*\s+\u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432|\u043f\u0435\u0440\u0435\u0445\u043e\u0434[\u0430-\u044f]*\s+\u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432|\u0441\u0442\u0430\u0434\u0438[\u0438\u044f\u0435]\s+.*\u043f\u0440\u043e\u0439\u0434\u0435\u043d.*\u043f\u0435\u0440\u0435\u0445\u043e\u0434)/iu.test(lower);
|
||||||
const expectedActualMismatchSignal = /(?:\u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a[\u0430-\u044f]+\s+\u0441\u043e\u0441\u0442\u043e\u044f\u043d[\u0438\u0435\u044f]+\s+.*\u0440\u0430\u0441\u0445\u043e\u0434[\u0430-\u044f]*\s+\u0441\s+\u043e\u0436\u0438\u0434\u0430\u0435\u043c|\u043e\u0436\u0438\u0434\u0430\u0435\u043c[\u0430-\u044f]+\s+\u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d[\u0430-\u044f]*\s+\u0441\u043f\u0438\u0441\u0430\u043d)/iu.test(lower);
|
const expectedActualMismatchSignal = /(?:\u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a[\u0430-\u044f]+\s+\u0441\u043e\u0441\u0442\u043e\u044f\u043d[\u0438\u0435\u044f]+\s+.*\u0440\u0430\u0441\u0445\u043e\u0434[\u0430-\u044f]*\s+\u0441\s+\u043e\u0436\u0438\u0434\u0430\u0435\u043c|\u043e\u0436\u0438\u0434\u0430\u0435\u043c[\u0430-\u044f]+\s+\u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d[\u0430-\u044f]*\s+\u0441\u043f\u0438\u0441\u0430\u043d)/iu.test(lower);
|
||||||
return lifecycleMismatchSignal ||
|
return broadOverviewRiskSignal ||
|
||||||
|
lifecycleMismatchSignal ||
|
||||||
(chainSignal && lifecycleTransitionGapSignal) ||
|
(chainSignal && lifecycleTransitionGapSignal) ||
|
||||||
expectedActualMismatchSignal ||
|
expectedActualMismatchSignal ||
|
||||||
(chainSignal && diagnosticsSignal) ||
|
(chainSignal && diagnosticsSignal) ||
|
||||||
(riskOrAnomalySignal && (chainSignal || diagnosticsSignal || lifecycleTransitionGapSignal)) ||
|
(riskOrAnomalySignal && (chainSignal || diagnosticsSignal || lifecycleTransitionGapSignal)) ||
|
||||||
(diagnosticsSignal && (closureSignal || closureIntentSignal)) ||
|
(diagnosticsSignal && (closureSignal || closureIntentSignal)) ||
|
||||||
|
closureStateQuestionSignal ||
|
||||||
closureDiagnosticPhraseSignal ||
|
closureDiagnosticPhraseSignal ||
|
||||||
signalVsNoiseDiagnostic;
|
signalVsNoiseDiagnostic;
|
||||||
}
|
}
|
||||||
|
|
@ -3867,7 +3894,7 @@ function hasStrictDeepInvestigationCue(text) {
|
||||||
if (!hasInvestigativeVerb) {
|
if (!hasInvestigativeVerb) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return /(?:\u0445\u0432\u043e\u0441\u0442|\u0440\u0430\u0437\u0440\u044b\u0432|\u0446\u0435\u043f\u043e\u0447|\u0430\u043d\u043e\u043c\u0430\u043b|\u043f\u0440\u043e\u0442\u0438\u0432\u043e\u0440\u0435\u0447|\u0437\u0430\u043a\u0440\u044b\u0442\u0438[\u0435\u044f]|\u043e\u0431\u044a\u0435\u043a\u0442(?:\u0443)?\s+\u0440\u0430\u0441\u0447(?:\u0435|\u0451)\u0442|lifecycle|state\s+transition)/iu.test(normalized);
|
return /(?:\u0445\u0432\u043e\u0441\u0442|\u0440\u0430\u0437\u0440\u044b\u0432|\u0446\u0435\u043f\u043e\u0447|\u0430\u043d\u043e\u043c\u0430\u043b|\u043f\u0440\u043e\u0442\u0438\u0432\u043e\u0440\u0435\u0447|\u0437\u0430\u043a\u0440\u044b\u0442\u0438[\u0435\u044f]|\u043e\u0431\u044a\u0435\u043a\u0442(?:\u0443)?\s+\u0440\u0430\u0441\u0447(?:\u0435|\u0451)\u0442|\u043e\u0441\u043d\u043e\u0432\p{L}*\s+\u0441\u0440\u0435\u0434\u0441\u0442|fixed\s*asset|\b\u043e\u0441\b|lifecycle|state\s+transition)/iu.test(normalized);
|
||||||
}
|
}
|
||||||
function hasAggregateBusinessAnalyticsSignal(text) {
|
function hasAggregateBusinessAnalyticsSignal(text) {
|
||||||
const normalized = compactWhitespace(repairAddressMojibake(String(text ?? "")).toLowerCase());
|
const normalized = compactWhitespace(repairAddressMojibake(String(text ?? "")).toLowerCase());
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ function detectSupportedIntent(text, deps) {
|
||||||
reason: "address_intent_resolver_current_turn_signal"
|
reason: "address_intent_resolver_current_turn_signal"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (/(?:\u043a\u0442\u043e\s+\u043d\u0430\u043c\s+\u0434\u043e\u043b\u0436|\u043d\u0430\u043c\s+\u043a\u0442\u043e\s+\u0434\u043e\u043b\u0436|\u0434\u0435\u0431\u0438\u0442\u043e\u0440|\u0434\u0435\u0431\u0438\u0442\u043e\u0440\u0441\u043a|\breceivables?\b)/iu.test(text)) {
|
if (/(?:\u043a\u0442\u043e\s+\u043d\u0430\u043c(?:\s+\p{L}+){0,4}\s+\u0434\u043e\u043b\u0436|\u043d\u0430\u043c\s+\u043a\u0442\u043e(?:\s+\p{L}+){0,4}\s+\u0434\u043e\u043b\u0436|\u0434\u0435\u0431\u0438\u0442\u043e\u0440|\u0434\u0435\u0431\u0438\u0442\u043e\u0440\u0441\u043a|\breceivables?\b)/iu.test(text)) {
|
||||||
return {
|
return {
|
||||||
intent: "receivables_confirmed_as_of_date",
|
intent: "receivables_confirmed_as_of_date",
|
||||||
confidence: "high",
|
confidence: "high",
|
||||||
|
|
|
||||||
|
|
@ -2811,6 +2811,16 @@ function resolveUnicodeAddressIntentBridge(text: string): AddressIntentResolutio
|
||||||
!/\b\d{1,2}\s+(?:январ|феврал|март|апрел|ма[йя]|июн|июл|август|сентябр|октябр|ноябр|декабр)\S*(?:\s+(?:19|20)\d{2})?/iu.test(
|
!/\b\d{1,2}\s+(?:январ|феврал|март|апрел|ма[йя]|июн|июл|август|сентябр|октябр|ноябр|декабр)\S*(?:\s+(?:19|20)\d{2})?/iu.test(
|
||||||
normalized
|
normalized
|
||||||
);
|
);
|
||||||
|
if (
|
||||||
|
(hasTaxPeriodCue || hasVatMonthPeriodCue) &&
|
||||||
|
/(?:сгруз|какой\s+ндс\s+(?:мы\s+)?должн|ндс\s+необходимо)/iu.test(normalized)
|
||||||
|
) {
|
||||||
|
return unicodeBridgeResolution(
|
||||||
|
"vat_liability_confirmed_for_tax_period",
|
||||||
|
"high",
|
||||||
|
"vat_liability_colloquial_bridge_signal_detected"
|
||||||
|
);
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
(hasTaxPeriodCue || (hasVatMonthPeriodCue && !hasVatDebtCue)) &&
|
(hasTaxPeriodCue || (hasVatMonthPeriodCue && !hasVatDebtCue)) &&
|
||||||
/(?:скольк|скока|надо|нужно|заплат|уплат|оплат|прикин)/iu.test(normalized)
|
/(?:скольк|скока|надо|нужно|заплат|уплат|оплат|прикин)/iu.test(normalized)
|
||||||
|
|
|
||||||
|
|
@ -4940,7 +4940,6 @@ function composeFactualReplyBody(
|
||||||
)
|
)
|
||||||
.slice(0, Math.min(rows.length, 5));
|
.slice(0, Math.min(rows.length, 5));
|
||||||
const semanticSummary = summarizeBankOperationSemantics(rows);
|
const semanticSummary = summarizeBankOperationSemantics(rows);
|
||||||
const compactRoleAnswer = /(?:клиент|поставщик|финансов|роль|коротко)/iu.test(String(options.userMessage ?? ""));
|
|
||||||
const compactEvidenceRows = visibleRows.map((row, index) => {
|
const compactEvidenceRows = visibleRows.map((row, index) => {
|
||||||
const direction = bankOperationDirectionLabel(bankOperationDirection(row));
|
const direction = bankOperationDirectionLabel(bankOperationDirection(row));
|
||||||
const amount = formatMoneyRub(row.amount ?? 0);
|
const amount = formatMoneyRub(row.amount ?? 0);
|
||||||
|
|
@ -4958,15 +4957,13 @@ function composeFactualReplyBody(
|
||||||
roleBoundary ?? "Показываю подтвержденные банковские операции из текущего среза.",
|
roleBoundary ?? "Показываю подтвержденные банковские операции из текущего среза.",
|
||||||
...(semanticSummary ? [semanticSummary] : [])
|
...(semanticSummary ? [semanticSummary] : [])
|
||||||
];
|
];
|
||||||
if (!compactRoleAnswer) {
|
|
||||||
lines.push(
|
lines.push(
|
||||||
bankOperationEvidenceLine(rows, preferredBankEvidenceDirection(options.userMessage)),
|
bankOperationEvidenceLine(rows, preferredBankEvidenceDirection(options.userMessage)),
|
||||||
"Примеры строк 1С:",
|
"Примеры строк 1С:",
|
||||||
...compactEvidenceRows
|
...compactEvidenceRows
|
||||||
);
|
);
|
||||||
}
|
|
||||||
lines.push("Следующий шаг: могу отдельно разложить назначения платежа, договоры или отделить банковский контур от клиентского/поставщицкого.");
|
lines.push("Следующий шаг: могу отдельно разложить назначения платежа, договоры или отделить банковский контур от клиентского/поставщицкого.");
|
||||||
if (!compactRoleAnswer && rows.length > visibleRows.length) {
|
if (rows.length > visibleRows.length) {
|
||||||
lines.push(`Показаны первые ${visibleRows.length} из ${rows.length}; полный список остается в подтвержденном срезе.`);
|
lines.push(`Показаны первые ${visibleRows.length} из ${rows.length}; полный список остается в подтвержденном срезе.`);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -98,11 +98,19 @@ export async function tryHandleAssistantLivingChatRuntime<ResponseType = unknown
|
||||||
if (!runtime.handled || !runtime.debug) {
|
if (!runtime.handled || !runtime.debug) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
const runtimeReplySource =
|
||||||
|
typeof runtime.debug.living_chat_response_source === "string"
|
||||||
|
? runtime.debug.living_chat_response_source
|
||||||
|
: null;
|
||||||
|
const replyType =
|
||||||
|
runtimeReplySource === "deterministic_unsupported_current_turn_boundary"
|
||||||
|
? "clarification_required"
|
||||||
|
: "factual_with_explanation";
|
||||||
const finalization = finalizeLivingChatTurnSafe({
|
const finalization = finalizeLivingChatTurnSafe({
|
||||||
sessionId: input.sessionId,
|
sessionId: input.sessionId,
|
||||||
userMessage: input.userMessage,
|
userMessage: input.userMessage,
|
||||||
assistantReply: runtime.chatText,
|
assistantReply: runtime.chatText,
|
||||||
replyType: "factual_with_explanation",
|
replyType,
|
||||||
debug: runtime.debug,
|
debug: runtime.debug,
|
||||||
modeDecision: input.modeDecision,
|
modeDecision: input.modeDecision,
|
||||||
appendItem: input.appendItem,
|
appendItem: input.appendItem,
|
||||||
|
|
|
||||||
|
|
@ -487,6 +487,8 @@ export async function runAssistantLivingChatRuntime(
|
||||||
address_llm_predecompose_contract: predecomposeContract,
|
address_llm_predecompose_contract: predecomposeContract,
|
||||||
address_semantic_extraction_contract: semanticExtractionContract,
|
address_semantic_extraction_contract: semanticExtractionContract,
|
||||||
orchestration_contract_v1: addressRuntimeMeta.orchestrationContract ?? null,
|
orchestration_contract_v1: addressRuntimeMeta.orchestrationContract ?? null,
|
||||||
|
address_tool_gate_decision: addressRuntimeMeta.toolGateDecision ?? null,
|
||||||
|
address_tool_gate_reason: addressRuntimeMeta.toolGateReason ?? null,
|
||||||
tool_gate_decision: addressRuntimeMeta.toolGateDecision ?? null,
|
tool_gate_decision: addressRuntimeMeta.toolGateDecision ?? null,
|
||||||
tool_gate_reason: addressRuntimeMeta.toolGateReason ?? null,
|
tool_gate_reason: addressRuntimeMeta.toolGateReason ?? null,
|
||||||
normalized: null,
|
normalized: null,
|
||||||
|
|
|
||||||
|
|
@ -496,6 +496,35 @@ function hasExactBankOperationsAddressReply(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasExactDocumentListAddressReply(
|
||||||
|
input: ApplyAssistantMcpDiscoveryResponsePolicyInput,
|
||||||
|
entryPoint: AssistantMcpDiscoveryRuntimeEntryPointContract | null
|
||||||
|
): boolean {
|
||||||
|
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!hasEffectivelyFactualAddressReply(input)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const source = String(input.currentReplySource ?? input.livingChatSource ?? "").trim().toLowerCase();
|
||||||
|
if (source !== "address_query_runtime_v1" && source !== "address_exact" && source !== "address_lane") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const detectedIntent = toNonEmptyString(input.addressRuntimeMeta?.detected_intent);
|
||||||
|
const selectedRecipe = toNonEmptyString(input.addressRuntimeMeta?.selected_recipe);
|
||||||
|
const isDocumentIntent =
|
||||||
|
detectedIntent === "list_documents_by_counterparty" || detectedIntent === "list_documents_by_contract";
|
||||||
|
const isDocumentRecipe =
|
||||||
|
selectedRecipe === "address_documents_by_counterparty_v1" ||
|
||||||
|
selectedRecipe === "address_documents_by_contract_v1";
|
||||||
|
if (!isDocumentIntent || !isDocumentRecipe) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const mcpCallStatus = toNonEmptyString(input.addressRuntimeMeta?.mcp_call_status);
|
||||||
|
const responseType = toNonEmptyString(input.addressRuntimeMeta?.response_type);
|
||||||
|
return Boolean(mcpCallStatus === "matched_non_empty" || responseType === "FACTUAL_LIST" || hasFullConfirmedTruth(input));
|
||||||
|
}
|
||||||
|
|
||||||
function hasInventoryMarginRankingAddressReply(
|
function hasInventoryMarginRankingAddressReply(
|
||||||
input: ApplyAssistantMcpDiscoveryResponsePolicyInput,
|
input: ApplyAssistantMcpDiscoveryResponsePolicyInput,
|
||||||
entryPoint: AssistantMcpDiscoveryRuntimeEntryPointContract | null
|
entryPoint: AssistantMcpDiscoveryRuntimeEntryPointContract | null
|
||||||
|
|
@ -863,6 +892,7 @@ export function applyAssistantMcpDiscoveryResponsePolicy(
|
||||||
entryPoint
|
entryPoint
|
||||||
);
|
);
|
||||||
const exactBankOperationsAddressReply = hasExactBankOperationsAddressReply(input, entryPoint);
|
const exactBankOperationsAddressReply = hasExactBankOperationsAddressReply(input, entryPoint);
|
||||||
|
const exactDocumentListAddressReply = hasExactDocumentListAddressReply(input, entryPoint);
|
||||||
const inventoryMarginRankingAddressReply = hasInventoryMarginRankingAddressReply(input, entryPoint);
|
const inventoryMarginRankingAddressReply = hasInventoryMarginRankingAddressReply(input, entryPoint);
|
||||||
const openScopeValueFlowDiscoveryPriority = hasOpenScopeValueFlowDiscoveryPriority(input, entryPoint);
|
const openScopeValueFlowDiscoveryPriority = hasOpenScopeValueFlowDiscoveryPriority(input, entryPoint);
|
||||||
const metadataDiscoveryPriority = hasMetadataDiscoveryPriority(input, entryPoint);
|
const metadataDiscoveryPriority = hasMetadataDiscoveryPriority(input, entryPoint);
|
||||||
|
|
@ -951,6 +981,9 @@ export function applyAssistantMcpDiscoveryResponsePolicy(
|
||||||
if (exactBankOperationsProtectsCurrent) {
|
if (exactBankOperationsProtectsCurrent) {
|
||||||
pushReason(reasonCodes, "mcp_discovery_response_policy_keep_exact_bank_operations_address_reply");
|
pushReason(reasonCodes, "mcp_discovery_response_policy_keep_exact_bank_operations_address_reply");
|
||||||
}
|
}
|
||||||
|
if (exactDocumentListAddressReply) {
|
||||||
|
pushReason(reasonCodes, "mcp_discovery_response_policy_keep_exact_document_list_address_reply");
|
||||||
|
}
|
||||||
if (inventoryMarginRankingAddressReply) {
|
if (inventoryMarginRankingAddressReply) {
|
||||||
pushReason(reasonCodes, "mcp_discovery_response_policy_keep_inventory_margin_ranking_address_reply");
|
pushReason(reasonCodes, "mcp_discovery_response_policy_keep_inventory_margin_ranking_address_reply");
|
||||||
}
|
}
|
||||||
|
|
@ -989,6 +1022,7 @@ export function applyAssistantMcpDiscoveryResponsePolicy(
|
||||||
!staleMetadataDiscoveryFallbackAgainstExactAddressReply &&
|
!staleMetadataDiscoveryFallbackAgainstExactAddressReply &&
|
||||||
!exactValueFlowReplyForBusinessOverviewDirectMoneyNeed &&
|
!exactValueFlowReplyForBusinessOverviewDirectMoneyNeed &&
|
||||||
!exactBankOperationsProtectsCurrent &&
|
!exactBankOperationsProtectsCurrent &&
|
||||||
|
!exactDocumentListAddressReply &&
|
||||||
!inventoryMarginRankingAddressReply &&
|
!inventoryMarginRankingAddressReply &&
|
||||||
!(deterministicBroadBusinessEvaluationReply && candidate.candidate_status === "clarification_candidate") &&
|
!(deterministicBroadBusinessEvaluationReply && candidate.candidate_status === "clarification_candidate") &&
|
||||||
ALLOWED_CANDIDATE_STATUSES.has(candidate.candidate_status) &&
|
ALLOWED_CANDIDATE_STATUSES.has(candidate.candidate_status) &&
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,22 @@ function shouldBypassStrictDeepInvestigationCueForAddressIntent(intent) {
|
||||||
return Boolean(intent && ADDRESS_INTENTS_ALLOW_STRICT_DEEP_INVESTIGATION_BYPASS.has(intent));
|
return Boolean(intent && ADDRESS_INTENTS_ALLOW_STRICT_DEEP_INVESTIGATION_BYPASS.has(intent));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasTerseUnsupportedLookupBoundarySignal(text) {
|
||||||
|
const normalized = String(text ?? "").toLowerCase().replace(/\s+/gu, " ").trim();
|
||||||
|
if (!normalized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!/\b(?:gib|give|list|lst)\b/iu.test(normalized)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const tokens = normalized.split(/[^\p{L}0-9._-]+/u).filter(Boolean);
|
||||||
|
if (tokens.length < 2 || tokens.length > 5) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const commandTokens = new Set(["gib", "give", "list", "lst", "show", "find"]);
|
||||||
|
return tokens.some((token) => token.length >= 2 && !commandTokens.has(token));
|
||||||
|
}
|
||||||
|
|
||||||
function resolveAddressFallbackToDeepArbitration(input) {
|
function resolveAddressFallbackToDeepArbitration(input) {
|
||||||
const {
|
const {
|
||||||
baseToolGateRunAddressLane,
|
baseToolGateRunAddressLane,
|
||||||
|
|
@ -103,11 +119,10 @@ function resolveAddressFallbackToDeepArbitration(input) {
|
||||||
semanticAggregateShapeDetected ||
|
semanticAggregateShapeDetected ||
|
||||||
!semanticApplyCanonicalRecommended ||
|
!semanticApplyCanonicalRecommended ||
|
||||||
standaloneAddressTopicSignal));
|
standaloneAddressTopicSignal));
|
||||||
const deepSessionContinuationFallbackToDeep = Boolean(!followupContext &&
|
const deepSessionContinuationFallbackToDeep = Boolean(baseToolGateRunAddressLane &&
|
||||||
baseToolGateRunAddressLane &&
|
|
||||||
!llmRuntimeUnavailableDetected &&
|
!llmRuntimeUnavailableDetected &&
|
||||||
!protectAddressLaneFromFallback &&
|
hasDeepSessionContinuationSignalDetected &&
|
||||||
hasDeepSessionContinuationSignalDetected);
|
(!protectAddressLaneFromFallback || deepAnalysisPreferenceDetected || semanticDeepInvestigationHintDetected));
|
||||||
return {
|
return {
|
||||||
unsupportedAddressIntentFallbackToDeep,
|
unsupportedAddressIntentFallbackToDeep,
|
||||||
deepAnalysisSignalFallbackToDeep,
|
deepAnalysisSignalFallbackToDeep,
|
||||||
|
|
@ -531,6 +546,7 @@ export function createAssistantRoutePolicy(deps) {
|
||||||
}
|
}
|
||||||
: baseResolvedIntentResolution;
|
: baseResolvedIntentResolution;
|
||||||
const llmPreDecomposeReason = toNonEmptyString(llmPreDecomposeMeta?.reason);
|
const llmPreDecomposeReason = toNonEmptyString(llmPreDecomposeMeta?.reason);
|
||||||
|
const diagnosticRewriteRejected = llmPreDecomposeReason === "normalized_fragment_rejected_diagnostic_rewrite";
|
||||||
const providerExecution = resolveProviderExecutionState({
|
const providerExecution = resolveProviderExecutionState({
|
||||||
useMock,
|
useMock,
|
||||||
llmPreDecomposeReason
|
llmPreDecomposeReason
|
||||||
|
|
@ -678,11 +694,13 @@ export function createAssistantRoutePolicy(deps) {
|
||||||
].includes(String(baseToolGate?.reason ?? ""))) ||
|
].includes(String(baseToolGate?.reason ?? ""))) ||
|
||||||
Boolean(baseToolGate?.runAddressLane &&
|
Boolean(baseToolGate?.runAddressLane &&
|
||||||
String(baseToolGate?.reason ?? "") === "followup_context_detected" &&
|
String(baseToolGate?.reason ?? "") === "followup_context_detected" &&
|
||||||
effectiveGroundedValueFlowFollowupContextDetected);
|
effectiveGroundedValueFlowFollowupContextDetected) ||
|
||||||
|
diagnosticRewriteRejected;
|
||||||
const nonDomainQueryIndexed = Boolean(!llmFirstAddressCandidate &&
|
const nonDomainQueryIndexed = Boolean(!llmFirstAddressCandidate &&
|
||||||
deterministicNonDomainGuard &&
|
deterministicNonDomainGuard &&
|
||||||
(llmFirstUnsupportedCandidate || llmContractMode === null) &&
|
(llmFirstUnsupportedCandidate || llmContractMode === null) &&
|
||||||
!baseToolGatePreservesAddressLane &&
|
!baseToolGatePreservesAddressLane &&
|
||||||
|
!strictDeepInvestigationCueDetected &&
|
||||||
!effectiveGroundedValueFlowFollowupContextDetected &&
|
!effectiveGroundedValueFlowFollowupContextDetected &&
|
||||||
!protectedInventoryShortFollowup &&
|
!protectedInventoryShortFollowup &&
|
||||||
!protectedInventoryMarginRankingFollowup &&
|
!protectedInventoryMarginRankingFollowup &&
|
||||||
|
|
@ -733,6 +751,14 @@ export function createAssistantRoutePolicy(deps) {
|
||||||
!dangerOrCoercionSignal &&
|
!dangerOrCoercionSignal &&
|
||||||
!effectiveGroundedValueFlowFollowupContextDetected &&
|
!effectiveGroundedValueFlowFollowupContextDetected &&
|
||||||
!organizationClarificationContinuationDetected);
|
!organizationClarificationContinuationDetected);
|
||||||
|
const nonDomainUnsupportedBoundary = Boolean(
|
||||||
|
assistantTurnMeaning?.unsupported_but_understood_family &&
|
||||||
|
assistantTurnMeaning?.stale_replay_forbidden === true &&
|
||||||
|
!turnMeaningIntentCandidate) ||
|
||||||
|
hasTerseUnsupportedLookupBoundarySignal(rawUserMessage) ||
|
||||||
|
hasTerseUnsupportedLookupBoundarySignal(repairedRawUserMessage) ||
|
||||||
|
hasTerseUnsupportedLookupBoundarySignal(effectiveAddressUserMessage) ||
|
||||||
|
hasTerseUnsupportedLookupBoundarySignal(repairedEffectiveAddressUserMessage);
|
||||||
const hardMetaMode = resolveHardMetaMode({
|
const hardMetaMode = resolveHardMetaMode({
|
||||||
dataScopeMetaQuery,
|
dataScopeMetaQuery,
|
||||||
capabilityMetaQuery,
|
capabilityMetaQuery,
|
||||||
|
|
@ -1134,11 +1160,14 @@ export function createAssistantRoutePolicy(deps) {
|
||||||
toolGateDecision: "skip_address_lane",
|
toolGateDecision: "skip_address_lane",
|
||||||
toolGateReason: "non_domain_query_indexed",
|
toolGateReason: "non_domain_query_indexed",
|
||||||
livingMode: "chat",
|
livingMode: "chat",
|
||||||
livingReason: "non_domain_query_indexed",
|
livingReason: nonDomainUnsupportedBoundary
|
||||||
|
? "unsupported_current_turn_meaning_boundary"
|
||||||
|
: "non_domain_query_indexed",
|
||||||
orchestrationContract: {
|
orchestrationContract: {
|
||||||
schema_version: "assistant_orchestration_contract_v1",
|
schema_version: "assistant_orchestration_contract_v1",
|
||||||
hard_meta_mode: "non_domain",
|
hard_meta_mode: "non_domain",
|
||||||
provider_execution: providerExecution,
|
provider_execution: providerExecution,
|
||||||
|
assistant_turn_meaning: assistantTurnMeaning,
|
||||||
address_mode: resolvedModeDetection.mode,
|
address_mode: resolvedModeDetection.mode,
|
||||||
address_mode_confidence: resolvedModeDetection.confidence,
|
address_mode_confidence: resolvedModeDetection.confidence,
|
||||||
address_intent: resolvedIntentResolution.intent,
|
address_intent: resolvedIntentResolution.intent,
|
||||||
|
|
@ -1146,13 +1175,19 @@ export function createAssistantRoutePolicy(deps) {
|
||||||
strong_data_signal_detected: strongDataSignal,
|
strong_data_signal_detected: strongDataSignal,
|
||||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||||
followup_context_detected: Boolean(followupContext),
|
followup_context_detected: Boolean(followupContext),
|
||||||
|
unsupported_current_turn_meaning_boundary: nonDomainUnsupportedBoundary,
|
||||||
|
unsupported_current_turn_family: nonDomainUnsupportedBoundary
|
||||||
|
? assistantTurnMeaning?.unsupported_but_understood_family ?? "terse_unsupported_lookup"
|
||||||
|
: null,
|
||||||
unsupported_address_intent_fallback_to_deep: false,
|
unsupported_address_intent_fallback_to_deep: false,
|
||||||
final_decision: {
|
final_decision: {
|
||||||
run_address_lane: false,
|
run_address_lane: false,
|
||||||
tool_gate_decision: "skip_address_lane",
|
tool_gate_decision: "skip_address_lane",
|
||||||
tool_gate_reason: "non_domain_query_indexed",
|
tool_gate_reason: "non_domain_query_indexed",
|
||||||
living_mode: "chat",
|
living_mode: "chat",
|
||||||
living_reason: "non_domain_query_indexed"
|
living_reason: nonDomainUnsupportedBoundary
|
||||||
|
? "unsupported_current_turn_meaning_boundary"
|
||||||
|
: "non_domain_query_indexed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1237,7 +1272,8 @@ export function createAssistantRoutePolicy(deps) {
|
||||||
const protectAddressLaneFromFallback = Boolean(
|
const protectAddressLaneFromFallback = Boolean(
|
||||||
laneProtectionArbitration.protectAddressLaneFromFallback ||
|
laneProtectionArbitration.protectAddressLaneFromFallback ||
|
||||||
customerValueRankingAddressSignal ||
|
customerValueRankingAddressSignal ||
|
||||||
protectedInventoryMarginRankingFollowup
|
protectedInventoryMarginRankingFollowup ||
|
||||||
|
diagnosticRewriteRejected
|
||||||
);
|
);
|
||||||
const vatExplainFollowupSignal = Boolean(followupContext &&
|
const vatExplainFollowupSignal = Boolean(followupContext &&
|
||||||
toNonEmptyString(followupContext.previous_intent) === "vat_payable_forecast" &&
|
toNonEmptyString(followupContext.previous_intent) === "vat_payable_forecast" &&
|
||||||
|
|
@ -1318,7 +1354,7 @@ export function createAssistantRoutePolicy(deps) {
|
||||||
let toolGateDecision = String(baseToolGate?.decision ?? "skip_address_lane");
|
let toolGateDecision = String(baseToolGate?.decision ?? "skip_address_lane");
|
||||||
let toolGateReason = String(baseToolGate?.reason ?? "no_address_signal_after_l0");
|
let toolGateReason = String(baseToolGate?.reason ?? "no_address_signal_after_l0");
|
||||||
const semanticAddressLaneRecovery = Boolean(!runAddressLane &&
|
const semanticAddressLaneRecovery = Boolean(!runAddressLane &&
|
||||||
(supportedAddressRouteCandidateDetected || protectedInventoryMarginRankingFollowup) &&
|
(supportedAddressRouteCandidateDetected || protectedInventoryMarginRankingFollowup || diagnosticRewriteRejected) &&
|
||||||
!deepAnalysisPreferenceDetected &&
|
!deepAnalysisPreferenceDetected &&
|
||||||
!unsupportedAddressIntentFallbackToDeep &&
|
!unsupportedAddressIntentFallbackToDeep &&
|
||||||
!deepAnalysisSignalFallbackToDeep &&
|
!deepAnalysisSignalFallbackToDeep &&
|
||||||
|
|
|
||||||
|
|
@ -1561,6 +1561,8 @@ const ADDRESS_BALANCE_SIGNAL_PATTERN = /(?:остат|сальдо|баланс|
|
||||||
const ADDRESS_ALL_TIME_PATTERN = /(?:за\s+вс[её]\s+время|за\s+весь\s+период|за\s+всю\s+истори(?:ю|и)|for\s+all\s+time|all\s+time|entire\s+period|full\s+history)/iu;
|
const ADDRESS_ALL_TIME_PATTERN = /(?:за\s+вс[её]\s+время|за\s+весь\s+период|за\s+всю\s+истори(?:ю|и)|for\s+all\s+time|all\s+time|entire\s+period|full\s+history)/iu;
|
||||||
const ADDRESS_MANAGEMENT_PROFILE_PATTERN =
|
const ADDRESS_MANAGEMENT_PROFILE_PATTERN =
|
||||||
/(?:за\s+какие\s+год[а-яё]*|сам(?:ый|ая|ое)\s+(?:актив|пассив)|наименее\s+актив|минимальн|покрыт(?:ие|ия)\s+период|диапазон\s+лет|тип[аы]\s+док(?:умент|ов|и)?|раздел[ыа]\s+уч[её]та|по\s+количеств[аоуе]|редк|реже|(?:сколько|скока|скок)\s+(?:всего\s+)?(?:уникальн(?:ых|ые|ого)?\s+)?контрагент(?:ов|а)?|(?:сколько|скока|скок)\s+(?:у\s+нас\s+)?(?:заказчик(?:ов|а)?|поставщик(?:ов|а)?|клиент(?:ов|а)?|покупател(?:ей|я)|смешан(?:ных|ые)\s+контрагент(?:ов|а)?)|(?:покажи|выведи|список|какие|кто).*(?:заказчик(?:ов|а|и)?|клиент(?:ов|а|ы)?|покупател(?:ей|я|и)?).*(?:за\s+вс[её]\s+время|all\s+time|(?:^|[^\d])(19|20)\d{2}(?:[^\d]|$)|(?:^|[^\d])\d{2}\s*(?:г(?:од|ода)?|г)(?:[^\p{L}\p{N}]|$)|за\s+год|в\s+году)|(?:какие|кто|покажи|выведи|список).*(?:заказчик(?:ов|а|и)?|клиент(?:ов|а|ы)?|покупател(?:ей|я|и)?).*(?:работал(?:и)?|активн(?:ые|ых|а|о)?).*(?:за\s+вс[её]\s+время|(?:19|20)\d{2}|за\s+год|в\s+году)|договорн(?:ая|ой)\s+баз[аы]|total\s+vs\s+used)/iu;
|
/(?:за\s+какие\s+год[а-яё]*|сам(?:ый|ая|ое)\s+(?:актив|пассив)|наименее\s+актив|минимальн|покрыт(?:ие|ия)\s+период|диапазон\s+лет|тип[аы]\s+док(?:умент|ов|и)?|раздел[ыа]\s+уч[её]та|по\s+количеств[аоуе]|редк|реже|(?:сколько|скока|скок)\s+(?:всего\s+)?(?:уникальн(?:ых|ые|ого)?\s+)?контрагент(?:ов|а)?|(?:сколько|скока|скок)\s+(?:у\s+нас\s+)?(?:заказчик(?:ов|а)?|поставщик(?:ов|а)?|клиент(?:ов|а)?|покупател(?:ей|я)|смешан(?:ных|ые)\s+контрагент(?:ов|а)?)|(?:покажи|выведи|список|какие|кто).*(?:заказчик(?:ов|а|и)?|клиент(?:ов|а|ы)?|покупател(?:ей|я|и)?).*(?:за\s+вс[её]\s+время|all\s+time|(?:^|[^\d])(19|20)\d{2}(?:[^\d]|$)|(?:^|[^\d])\d{2}\s*(?:г(?:од|ода)?|г)(?:[^\p{L}\p{N}]|$)|за\s+год|в\s+году)|(?:какие|кто|покажи|выведи|список).*(?:заказчик(?:ов|а|и)?|клиент(?:ов|а|ы)?|покупател(?:ей|я|и)?).*(?:работал(?:и)?|активн(?:ые|ых|а|о)?).*(?:за\s+вс[её]\s+время|(?:19|20)\d{2}|за\s+год|в\s+году)|договорн(?:ая|ой)\s+баз[аы]|total\s+vs\s+used)/iu;
|
||||||
|
const ADDRESS_DEEP_ANALYSIS_FALLBACK_BLOCK_PATTERN =
|
||||||
|
/(?:\u0432\s+\u0446\u0435\u043b\u043e\u043c|\u043e\u0431\u0449\p{L}*\s+\u043a\u0430\u0440\u0442\u0438\u043d|\u0442\u043e\u043f\s+\u0440\u0438\u0441\u043a|\u0447\u0442\u043e\s+\u043d\u0435\s+\u0442\u0430\u043a|\u043e\u0441\u043d\u043e\u0432\p{L}*\s+\u0441\u0440\u0435\u0434\u0441\u0442)/iu;
|
||||||
function normalizeAddressMonthAliasToken(token) {
|
function normalizeAddressMonthAliasToken(token) {
|
||||||
const source = String(token ?? "").trim().toLowerCase();
|
const source = String(token ?? "").trim().toLowerCase();
|
||||||
if (!source) {
|
if (!source) {
|
||||||
|
|
@ -1770,6 +1772,9 @@ function resolveAddressDeterministicFallback(userMessage, sanitizedUserMessage)
|
||||||
if (ADDRESS_MANAGEMENT_PROFILE_PATTERN.test(source)) {
|
if (ADDRESS_MANAGEMENT_PROFILE_PATTERN.test(source)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if (ADDRESS_DEEP_ANALYSIS_FALLBACK_BLOCK_PATTERN.test(source)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const monthYear = extractAddressFallbackMonthYear(source);
|
const monthYear = extractAddressFallbackMonthYear(source);
|
||||||
const year = extractAddressFallbackYear(source);
|
const year = extractAddressFallbackYear(source);
|
||||||
const allTime = ADDRESS_ALL_TIME_PATTERN.test(source);
|
const allTime = ADDRESS_ALL_TIME_PATTERN.test(source);
|
||||||
|
|
@ -3275,6 +3280,13 @@ function hasPredecomposeDiagnosticUncertaintyLead(text) {
|
||||||
}
|
}
|
||||||
return /^(?:неясно|не\s+ясно|непонятно|не\s+понятно|unclear|not\s+clear|ambiguous|unknown)(?=$|[\s,.;:!?])/iu.test(normalized);
|
return /^(?:неясно|не\s+ясно|непонятно|не\s+понятно|unclear|not\s+clear|ambiguous|unknown)(?=$|[\s,.;:!?])/iu.test(normalized);
|
||||||
}
|
}
|
||||||
|
function hasPredecomposeDebtSnapshotIntentSignal(text) {
|
||||||
|
const normalized = compactWhitespace(repairAddressMojibake(String(text ?? "")).toLowerCase()).replace(/\u0451/gu, "\u0435");
|
||||||
|
if (!normalized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return /(?:\u043a\u0442\u043e\s+\u043d\u0430\u043c(?:\s+\p{L}+){0,4}\s+\u0434\u043e\u043b\u0436|\u043d\u0430\u043c\s+(?:\u043a\u0442\u043e(?:-\u0442\u043e)?\s+)?\u0434\u043e\u043b\u0436|\u0434\u0435\u0431\u0438\u0442\u043e\u0440|receivables?)/iu.test(normalized);
|
||||||
|
}
|
||||||
function attachAddressPredecomposeContract(meta, sourceMessage) {
|
function attachAddressPredecomposeContract(meta, sourceMessage) {
|
||||||
const sourceMeta = meta && typeof meta === "object" ? meta : {};
|
const sourceMeta = meta && typeof meta === "object" ? meta : {};
|
||||||
const { providerExecutionInput, providerExecutionContract: providerExecutionContractInput, ...restMeta } = sourceMeta;
|
const { providerExecutionInput, providerExecutionContract: providerExecutionContractInput, ...restMeta } = sourceMeta;
|
||||||
|
|
@ -3395,9 +3407,11 @@ async function runAddressLlmPreDecompose(normalizerService, payload, userMessage
|
||||||
const repairedSourceMessage = repairAddressMojibake(userMessage);
|
const repairedSourceMessage = repairAddressMojibake(userMessage);
|
||||||
const sourceIntentResolution = (0, addressIntentResolver_1.resolveAddressIntent)(repairedSourceMessage || userMessage);
|
const sourceIntentResolution = (0, addressIntentResolver_1.resolveAddressIntent)(repairedSourceMessage || userMessage);
|
||||||
const candidateIntentResolution = (0, addressIntentResolver_1.resolveAddressIntent)(candidate);
|
const candidateIntentResolution = (0, addressIntentResolver_1.resolveAddressIntent)(candidate);
|
||||||
const sourceIntentKnown = sourceIntentResolution.intent !== "unknown";
|
const sourceIntentKnown = sourceIntentResolution.intent !== "unknown" ||
|
||||||
|
hasPredecomposeDebtSnapshotIntentSignal(repairedSourceMessage || userMessage);
|
||||||
const candidateIntentKnown = candidateIntentResolution.intent !== "unknown";
|
const candidateIntentKnown = candidateIntentResolution.intent !== "unknown";
|
||||||
const candidateStartsWithDiagnosticUncertainty = hasPredecomposeDiagnosticUncertaintyLead(candidate);
|
const candidateStartsWithDiagnosticUncertainty = hasPredecomposeDiagnosticUncertaintyLead(candidate) ||
|
||||||
|
/^(?:\u043d\u0435\u044f\u0441\u043d\u043e|\u043d\u0435\s+\u044f\u0441\u043d\u043e|\u043d\u0435\u043f\u043e\u043d\u044f\u0442\u043d\u043e|\u043d\u0435\s+\u043f\u043e\u043d\u044f\u0442\u043d\u043e)(?=$|[\s,.;:!?])/iu.test(compactWhitespace(repairAddressMojibake(String(candidate ?? "")).toLowerCase()));
|
||||||
if (candidateStartsWithDiagnosticUncertainty && sourceIntentKnown) {
|
if (candidateStartsWithDiagnosticUncertainty && sourceIntentKnown) {
|
||||||
return attachAddressPredecomposeContract({
|
return attachAddressPredecomposeContract({
|
||||||
...baseMeta,
|
...baseMeta,
|
||||||
|
|
@ -3419,6 +3433,13 @@ async function runAddressLlmPreDecompose(normalizerService, payload, userMessage
|
||||||
const rejectCandidateForIntentSafety = intentDroppedByCandidate ||
|
const rejectCandidateForIntentSafety = intentDroppedByCandidate ||
|
||||||
(intentConflict &&
|
(intentConflict &&
|
||||||
(sourceIntentResolution.confidence === "high" || candidateIntentResolution.confidence !== "high"));
|
(sourceIntentResolution.confidence === "high" || candidateIntentResolution.confidence !== "high"));
|
||||||
|
const sourceAnchorQualityForIntentSafety = evaluateAddressAnchorQuality(repairedSourceMessage || userMessage);
|
||||||
|
const candidateAnchorQualityForIntentSafety = evaluateAddressAnchorQuality(candidate);
|
||||||
|
const counterpartyAnchorSubstitutedDuringIntentDrop = sourceAnchorQualityForIntentSafety.anchorType === "counterparty" &&
|
||||||
|
sourceAnchorQualityForIntentSafety.quality >= 2 &&
|
||||||
|
Boolean(sourceAnchorQualityForIntentSafety.anchorValue) &&
|
||||||
|
candidateAnchorQualityForIntentSafety.quality < sourceAnchorQualityForIntentSafety.quality &&
|
||||||
|
hasCounterpartyAnchorSubstitution(sourceAnchorQualityForIntentSafety.anchorValue ?? "", candidate);
|
||||||
if (rejectCandidateForIntentSafety) {
|
if (rejectCandidateForIntentSafety) {
|
||||||
return attachAddressPredecomposeContract({
|
return attachAddressPredecomposeContract({
|
||||||
...baseMeta,
|
...baseMeta,
|
||||||
|
|
@ -3427,7 +3448,9 @@ async function runAddressLlmPreDecompose(normalizerService, payload, userMessage
|
||||||
traceId: normalized?.trace_id ?? null,
|
traceId: normalized?.trace_id ?? null,
|
||||||
llmCanonicalCandidateDetected: true,
|
llmCanonicalCandidateDetected: true,
|
||||||
effectiveMessage: userMessage,
|
effectiveMessage: userMessage,
|
||||||
reason: intentDroppedByCandidate
|
reason: counterpartyAnchorSubstitutedDuringIntentDrop
|
||||||
|
? "normalized_fragment_rejected_anchor_substitution"
|
||||||
|
: intentDroppedByCandidate
|
||||||
? "normalized_fragment_rejected_intent_drop"
|
? "normalized_fragment_rejected_intent_drop"
|
||||||
: "normalized_fragment_rejected_intent_conflict",
|
: "normalized_fragment_rejected_intent_conflict",
|
||||||
fallbackRuleHit: null,
|
fallbackRuleHit: null,
|
||||||
|
|
@ -3438,7 +3461,8 @@ async function runAddressLlmPreDecompose(normalizerService, payload, userMessage
|
||||||
const sourceHasExplicitDrilldownSignal = hasPredecomposeExplicitDrilldownSignal(repairedSourceMessage || userMessage);
|
const sourceHasExplicitDrilldownSignal = hasPredecomposeExplicitDrilldownSignal(repairedSourceMessage || userMessage);
|
||||||
const candidateHasExplicitDrilldownSignal = hasPredecomposeExplicitDrilldownSignal(candidate);
|
const candidateHasExplicitDrilldownSignal = hasPredecomposeExplicitDrilldownSignal(candidate);
|
||||||
const sourceLooksLikeSameDateAccountFollowup = hasSameDateAccountFollowupSignalForPredecompose(repairedSourceMessage || userMessage);
|
const sourceLooksLikeSameDateAccountFollowup = hasSameDateAccountFollowupSignalForPredecompose(repairedSourceMessage || userMessage);
|
||||||
const candidateInjectsDrilldownIntent = candidateIntentResolution.intent === "documents_forming_balance";
|
const candidateInjectsDrilldownIntent = candidateIntentResolution.intent === "documents_forming_balance" ||
|
||||||
|
(candidateHasExplicitDrilldownSignal && /(?:document|posting|docs?|\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442|\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b|\u043f\u0440\u043e\u0432\u043e\u0434\u043a)/iu.test(candidate));
|
||||||
if (sourceLooksLikeSameDateAccountFollowup &&
|
if (sourceLooksLikeSameDateAccountFollowup &&
|
||||||
!sourceHasExplicitDrilldownSignal &&
|
!sourceHasExplicitDrilldownSignal &&
|
||||||
candidateHasExplicitDrilldownSignal &&
|
candidateHasExplicitDrilldownSignal &&
|
||||||
|
|
@ -3757,8 +3781,8 @@ function hasDeepSessionContinuationSignal(input) {
|
||||||
return candidateTexts.some((text) => {
|
return candidateTexts.some((text) => {
|
||||||
const hasContinuationCue = /^(?:\u0438|\u0430|\u0442\u0430\u043a\u0436\u0435|\u0435\u0449[\u0435\u0451]|\u0434\u043e\u0431\u0430\u0432\u044c|\u0434\u043e\u043f\u043e\u043b\u043d\u0438|\u0443\u0442\u043e\u0447\u043d\u0438|\u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438|\u0442\u0435\u043f\u0435\u0440\u044c|then|also|and)\b/iu.test(text) ||
|
const hasContinuationCue = /^(?:\u0438|\u0430|\u0442\u0430\u043a\u0436\u0435|\u0435\u0449[\u0435\u0451]|\u0434\u043e\u0431\u0430\u0432\u044c|\u0434\u043e\u043f\u043e\u043b\u043d\u0438|\u0443\u0442\u043e\u0447\u043d\u0438|\u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438|\u0442\u0435\u043f\u0435\u0440\u044c|then|also|and)\b/iu.test(text) ||
|
||||||
/(?:\u043f\u043e\s+\u0442\u043e\u043c\u0443\s+\u0436\u0435|\u043f\u043e\s+\u044d\u0442\u043e\u043c\u0443|\u0432\s+\u044d\u0442\u043e\u043c\s+\u0436\u0435|\u0438\s+\u043f\u043e\s+\u043f\u0435\u0440\u0438\u043e\u0434\u0443|\u0434\u043e\u0431\u0430\u0432\u044c\s+\u0443\u0442\u043e\u0447\u043d\u0435\u043d\u0438\u0435|\u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u043c|\u0430\s+\u0435\u0441\u043b\u0438|\u0435\u0441\u043b\u0438\s+\u0442\u043e\u043b\u044c\u043a\u043e|\u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e)/iu.test(text);
|
/(?:\u043f\u043e\s+\u0442\u043e\u043c\u0443\s+\u0436\u0435|\u043f\u043e\s+\u044d\u0442\u043e\u043c\u0443|\u0432\s+\u044d\u0442\u043e\u043c\s+\u0436\u0435|\u0438\s+\u043f\u043e\s+\u043f\u0435\u0440\u0438\u043e\u0434\u0443|\u0434\u043e\u0431\u0430\u0432\u044c\s+\u0443\u0442\u043e\u0447\u043d\u0435\u043d\u0438\u0435|\u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u043c|\u0430\s+\u0435\u0441\u043b\u0438|\u0435\u0441\u043b\u0438\s+\u0442\u043e\u043b\u044c\u043a\u043e|\u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e)/iu.test(text);
|
||||||
const hasAccountOrPeriodCue = /(?:\u0441\u0447[\u0435\u0451]\u0442|account|\b\d{2}(?:[.,]\d{1,2})?\b|\b20\d{2}(?:[-/.]\d{1,2})?\b|\u043f\u0435\u0440\u0438\u043e\u0434|\u043c\u0435\u0441\u044f\u0446)/iu.test(text);
|
const hasAccountOrPeriodCue = /(?:\u0441\u0447[\u0435\u0451]\u0442|account|\b\d{2}(?:[.,]\d{1,2})?\b|\b20\d{2}(?:[-/.]\d{1,2})?\b|\u043f\u0435\u0440\u0438\u043e\u0434|\u043c\u0435\u0441\u044f\u0446|\u0434\u043e\u0433\u043e\u0432\u043e\u0440|\u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442|contract)/iu.test(text);
|
||||||
const hasDeepRebindCue = /(?:\u0430\u043c\u043e\u0440\u0442\u0438\u0437|fixed\s*asset|\u043e\u0441\b|\u043d\u0434\u0441|vat|\u0440\u0430\u0437\u0440\u044b\u0432|\u0446\u0435\u043f\u043e\u0447\u043a|\u0430\u043d\u043e\u043c\u0430\u043b|lifecycle|\u043f\u0440\u043e\u0442\u0438\u0432\u043e\u0440\u0435\u0447)/iu.test(text);
|
const hasDeepRebindCue = /(?:\u0430\u043c\u043e\u0440\u0442\u0438\u0437|fixed\s*asset|\u043e\u0441\b|\u043d\u0434\u0441|vat|\u0440\u0430\u0437\u0440\u044b\u0432|\u0446\u0435\u043f\u043e\u0447\u043a|\u0430\u043d\u043e\u043c\u0430\u043b|lifecycle|\u043f\u0440\u043e\u0442\u0438\u0432\u043e\u0440\u0435\u0447|\u0437\u0430\u043a\u0440\u044b\u0442|\u0445\u0432\u043e\u0441\u0442|\u0434\u043e\u0433\u043e\u0432\u043e\u0440|\u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442|contract)/iu.test(text);
|
||||||
if (hasContinuationCue && (hasAccountOrPeriodCue || hasDeepRebindCue)) {
|
if (hasContinuationCue && (hasAccountOrPeriodCue || hasDeepRebindCue)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -3779,6 +3803,8 @@ function hasDeepAnalysisPreferenceSignal(text) {
|
||||||
if (openContractsListQuestionSignal) {
|
if (openContractsListQuestionSignal) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const broadOverviewRiskSignal =
|
||||||
|
/(?:\u0432\s+\u0446\u0435\u043b\u043e\u043c|\u043e\u0431\u0449\p{L}*\s+\u043a\u0430\u0440\u0442\u0438\u043d|\u0442\u043e\u043f\s+\u0440\u0438\u0441\u043a|\u0447\u0442\u043e\s+\u043d\u0435\s+\u0442\u0430\u043a)/iu.test(lower);
|
||||||
const riskOrAnomalySignal = /(?:\u0440\u0438\u0441\u043a|risk|\u0430\u043d\u043e\u043c\u0430\u043b|anomal|\u043f\u0440\u043e\u0442\u0438\u0432\u043e\u0440\u0435\u0447|\u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442|conflict|deviation|\u043e\u0442\u043a\u043b\u043e\u043d\u0435\u043d|\u043d\u0435\u0441\u044b\u043a\u043e\u0432\u043a|\u043d\u0435\u0441\u0445\u043e\u0434|\u043e\u0448\u0438\u0431|error|issue|\u043f\u0440\u043e\u0431\u043b\u0435\u043c)/iu.test(lower);
|
const riskOrAnomalySignal = /(?:\u0440\u0438\u0441\u043a|risk|\u0430\u043d\u043e\u043c\u0430\u043b|anomal|\u043f\u0440\u043e\u0442\u0438\u0432\u043e\u0440\u0435\u0447|\u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442|conflict|deviation|\u043e\u0442\u043a\u043b\u043e\u043d\u0435\u043d|\u043d\u0435\u0441\u044b\u043a\u043e\u0432\u043a|\u043d\u0435\u0441\u0445\u043e\u0434|\u043e\u0448\u0438\u0431|error|issue|\u043f\u0440\u043e\u0431\u043b\u0435\u043c)/iu.test(lower);
|
||||||
const chainSignal = /(?:\u0446\u0435\u043f\u043e\u0447\u043a|chain|trace\s*chain|lifecycle|\u0436\u0438\u0437\u043d\u0435\u043d\u043d[\u0430-\u044f]+\s+\u0446\u0438\u043a\u043b|state\s+transition|\u0440\u0430\u0437\u0440\u044b\u0432[\u0430-\u044f]*)/iu.test(lower);
|
const chainSignal = /(?:\u0446\u0435\u043f\u043e\u0447\u043a|chain|trace\s*chain|lifecycle|\u0436\u0438\u0437\u043d\u0435\u043d\u043d[\u0430-\u044f]+\s+\u0446\u0438\u043a\u043b|state\s+transition|\u0440\u0430\u0437\u0440\u044b\u0432[\u0430-\u044f]*)/iu.test(lower);
|
||||||
const diagnosticsKeywordSignal = /(?:\u0440\u0430\u0437\u043b\u043e\u0436\u0438|\u0434\u0435\u043a\u043e\u043c\u043f\u043e\u0437|\u0440\u0430\u0437\u0431\u0435\u0440\u0438|\u043f\u043e\u0447\u0435\u043c\u0443|why|audit|scan|\u043a\u043e\u0440\u043d\u0435\u0432[\u0430-\u044f]+\s+\u043f\u0440\u0438\u0447\u0438\u043d|root\s*cause|\u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c[\u0430-\u044f]*|\u0433\u0434\u0435\s+\u0440\u0430\u0437\u0440\u044b\u0432|\u0447\u0442\u043e\s+\u043c\u0435\u0448\u0430[\u0430-\u044f]+\s+\u0437\u0430\u043a\u0440\u044b\u0442)/iu.test(lower);
|
const diagnosticsKeywordSignal = /(?:\u0440\u0430\u0437\u043b\u043e\u0436\u0438|\u0434\u0435\u043a\u043e\u043c\u043f\u043e\u0437|\u0440\u0430\u0437\u0431\u0435\u0440\u0438|\u043f\u043e\u0447\u0435\u043c\u0443|why|audit|scan|\u043a\u043e\u0440\u043d\u0435\u0432[\u0430-\u044f]+\s+\u043f\u0440\u0438\u0447\u0438\u043d|root\s*cause|\u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c[\u0430-\u044f]*|\u0433\u0434\u0435\s+\u0440\u0430\u0437\u0440\u044b\u0432|\u0447\u0442\u043e\s+\u043c\u0435\u0448\u0430[\u0430-\u044f]+\s+\u0437\u0430\u043a\u0440\u044b\u0442)/iu.test(lower);
|
||||||
|
|
@ -3786,17 +3812,20 @@ function hasDeepAnalysisPreferenceSignal(text) {
|
||||||
const diagnosticsSignal = diagnosticsKeywordSignal || diagnosticsCheckVerbSignal;
|
const diagnosticsSignal = diagnosticsKeywordSignal || diagnosticsCheckVerbSignal;
|
||||||
const closureSignal = /(?:\u0437\u0430\u043a\u0440\u044b\u0442\u0438[\u0435\u044f]\s+\u043f\u0435\u0440\u0438\u043e\u0434|period\s*close|\u043d\u0435\s+\u0437\u0430\u043a\u0440\u044b\u043b[\u0430-\u044f]*|\u0445\u0432\u043e\u0441\u0442[\u0430-\u044f]*)/iu.test(lower);
|
const closureSignal = /(?:\u0437\u0430\u043a\u0440\u044b\u0442\u0438[\u0435\u044f]\s+\u043f\u0435\u0440\u0438\u043e\u0434|period\s*close|\u043d\u0435\s+\u0437\u0430\u043a\u0440\u044b\u043b[\u0430-\u044f]*|\u0445\u0432\u043e\u0441\u0442[\u0430-\u044f]*)/iu.test(lower);
|
||||||
const closureIntentSignal = /(?:\u0437\u0430\u043a\u0440\u044b\u0442[\u0430-\u044f]*|period\s*close|close\s+period)/iu.test(lower);
|
const closureIntentSignal = /(?:\u0437\u0430\u043a\u0440\u044b\u0442[\u0430-\u044f]*|period\s*close|close\s+period)/iu.test(lower);
|
||||||
|
const closureStateQuestionSignal = /(?:\u0437\u0430\u043a\u0440\u044b\u0442[\u0430-\u044f]*|\u0445\u0432\u043e\u0441\u0442[\u0430-\u044f]*)[^\n]{0,80}(?:\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434[\u0430-\u044f]*|\u0436\u0438\u0432[\u0430-\u044f]*|\u043e\u0441\u0442\u0430[\u043b-\u044f]*|\u0435\u0441\u0442\u044c)|(?:\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434[\u0430-\u044f]*|\u0436\u0438\u0432[\u0430-\u044f]*|\u043e\u0441\u0442\u0430[\u043b-\u044f]*|\u0435\u0441\u0442\u044c)[^\n]{0,80}(?:\u0437\u0430\u043a\u0440\u044b\u0442[\u0430-\u044f]*|\u0445\u0432\u043e\u0441\u0442[\u0430-\u044f]*)/iu.test(lower);
|
||||||
const closureDiagnosticPhraseSignal = /(?:\u0447\u0442\u043e(?:\s+\S+){0,8}\s+\u043c\u0435\u0448\u0430[\u0430-\u044f]+\s+\u0437\u0430\u043a\u0440\u044b\u0442)/iu.test(lower);
|
const closureDiagnosticPhraseSignal = /(?:\u0447\u0442\u043e(?:\s+\S+){0,8}\s+\u043c\u0435\u0448\u0430[\u0430-\u044f]+\s+\u0437\u0430\u043a\u0440\u044b\u0442)/iu.test(lower);
|
||||||
const signalVsNoiseDiagnostic = /(?:\u043d\u0435\s+\u043f\u0440\u043e\u0441\u0442\u043e\s+(?:\u043d\u0430\s+)?\u0448\u0443\u043c|\u043f\u043e\u0445\u043e\u0436[\u0438\u0435]\s+(?:\u0438\u043c\u0435\u043d\u043d\u043e\s+)?\u043d\u0430\s+\u043f\u0440\u043e\u0431\u043b\u0435\u043c)/iu.test(lower);
|
const signalVsNoiseDiagnostic = /(?:\u043d\u0435\s+\u043f\u0440\u043e\u0441\u0442\u043e\s+(?:\u043d\u0430\s+)?\u0448\u0443\u043c|\u043f\u043e\u0445\u043e\u0436[\u0438\u0435]\s+(?:\u0438\u043c\u0435\u043d\u043d\u043e\s+)?\u043d\u0430\s+\u043f\u0440\u043e\u0431\u043b\u0435\u043c)/iu.test(lower);
|
||||||
const lifecycleMismatchSignal = /(?:\u043d\u0435\s+\u0442\u0435\u043c\s+\u0442\u0438\u043f(?:\u043e\u043c)?\s+\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442|\u043e\u0436\u0438\u0434\u0430\u0435\u043c[\u0430-\u044f]+\s+\u043f\u0435\u0440\u0435\u0445\u043e\u0434[\u0430-\u044f]*\s+\u043d\u0435\s+\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434|\u043f\u0435\u0440\u0435\u0445\u043e\u0434[\u0430-\u044f]*\s+\u043d\u0435\s+\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434|wrong\s+closing\s+document|expected\s+transition)/iu.test(lower);
|
const lifecycleMismatchSignal = /(?:\u043d\u0435\s+\u0442\u0435\u043c\s+\u0442\u0438\u043f(?:\u043e\u043c)?\s+\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442|\u043e\u0436\u0438\u0434\u0430\u0435\u043c[\u0430-\u044f]+\s+\u043f\u0435\u0440\u0435\u0445\u043e\u0434[\u0430-\u044f]*\s+\u043d\u0435\s+\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434|\u043f\u0435\u0440\u0435\u0445\u043e\u0434[\u0430-\u044f]*\s+\u043d\u0435\s+\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434|wrong\s+closing\s+document|expected\s+transition)/iu.test(lower);
|
||||||
const lifecycleTransitionGapSignal = /(?:\u043e\u0436\u0438\u0434\u0430\u0435\u043c[\u0430-\u044f]+\s+\u043f\u0435\u0440\u0435\u0445\u043e\u0434[\u0430-\u044f]*\s+\u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432|\u043f\u0435\u0440\u0435\u0445\u043e\u0434[\u0430-\u044f]*\s+\u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432|\u0441\u0442\u0430\u0434\u0438[\u0438\u044f\u0435]\s+.*\u043f\u0440\u043e\u0439\u0434\u0435\u043d.*\u043f\u0435\u0440\u0435\u0445\u043e\u0434)/iu.test(lower);
|
const lifecycleTransitionGapSignal = /(?:\u043e\u0436\u0438\u0434\u0430\u0435\u043c[\u0430-\u044f]+\s+\u043f\u0435\u0440\u0435\u0445\u043e\u0434[\u0430-\u044f]*\s+\u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432|\u043f\u0435\u0440\u0435\u0445\u043e\u0434[\u0430-\u044f]*\s+\u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432|\u0441\u0442\u0430\u0434\u0438[\u0438\u044f\u0435]\s+.*\u043f\u0440\u043e\u0439\u0434\u0435\u043d.*\u043f\u0435\u0440\u0435\u0445\u043e\u0434)/iu.test(lower);
|
||||||
const expectedActualMismatchSignal = /(?:\u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a[\u0430-\u044f]+\s+\u0441\u043e\u0441\u0442\u043e\u044f\u043d[\u0438\u0435\u044f]+\s+.*\u0440\u0430\u0441\u0445\u043e\u0434[\u0430-\u044f]*\s+\u0441\s+\u043e\u0436\u0438\u0434\u0430\u0435\u043c|\u043e\u0436\u0438\u0434\u0430\u0435\u043c[\u0430-\u044f]+\s+\u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d[\u0430-\u044f]*\s+\u0441\u043f\u0438\u0441\u0430\u043d)/iu.test(lower);
|
const expectedActualMismatchSignal = /(?:\u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a[\u0430-\u044f]+\s+\u0441\u043e\u0441\u0442\u043e\u044f\u043d[\u0438\u0435\u044f]+\s+.*\u0440\u0430\u0441\u0445\u043e\u0434[\u0430-\u044f]*\s+\u0441\s+\u043e\u0436\u0438\u0434\u0430\u0435\u043c|\u043e\u0436\u0438\u0434\u0430\u0435\u043c[\u0430-\u044f]+\s+\u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d[\u0430-\u044f]*\s+\u0441\u043f\u0438\u0441\u0430\u043d)/iu.test(lower);
|
||||||
return lifecycleMismatchSignal ||
|
return broadOverviewRiskSignal ||
|
||||||
|
lifecycleMismatchSignal ||
|
||||||
(chainSignal && lifecycleTransitionGapSignal) ||
|
(chainSignal && lifecycleTransitionGapSignal) ||
|
||||||
expectedActualMismatchSignal ||
|
expectedActualMismatchSignal ||
|
||||||
(chainSignal && diagnosticsSignal) ||
|
(chainSignal && diagnosticsSignal) ||
|
||||||
(riskOrAnomalySignal && (chainSignal || diagnosticsSignal || lifecycleTransitionGapSignal)) ||
|
(riskOrAnomalySignal && (chainSignal || diagnosticsSignal || lifecycleTransitionGapSignal)) ||
|
||||||
(diagnosticsSignal && (closureSignal || closureIntentSignal)) ||
|
(diagnosticsSignal && (closureSignal || closureIntentSignal)) ||
|
||||||
|
closureStateQuestionSignal ||
|
||||||
closureDiagnosticPhraseSignal ||
|
closureDiagnosticPhraseSignal ||
|
||||||
signalVsNoiseDiagnostic;
|
signalVsNoiseDiagnostic;
|
||||||
}
|
}
|
||||||
|
|
@ -3827,7 +3856,7 @@ function hasStrictDeepInvestigationCue(text) {
|
||||||
if (!hasInvestigativeVerb) {
|
if (!hasInvestigativeVerb) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return /(?:\u0445\u0432\u043e\u0441\u0442|\u0440\u0430\u0437\u0440\u044b\u0432|\u0446\u0435\u043f\u043e\u0447|\u0430\u043d\u043e\u043c\u0430\u043b|\u043f\u0440\u043e\u0442\u0438\u0432\u043e\u0440\u0435\u0447|\u0437\u0430\u043a\u0440\u044b\u0442\u0438[\u0435\u044f]|\u043e\u0431\u044a\u0435\u043a\u0442(?:\u0443)?\s+\u0440\u0430\u0441\u0447(?:\u0435|\u0451)\u0442|lifecycle|state\s+transition)/iu.test(normalized);
|
return /(?:\u0445\u0432\u043e\u0441\u0442|\u0440\u0430\u0437\u0440\u044b\u0432|\u0446\u0435\u043f\u043e\u0447|\u0430\u043d\u043e\u043c\u0430\u043b|\u043f\u0440\u043e\u0442\u0438\u0432\u043e\u0440\u0435\u0447|\u0437\u0430\u043a\u0440\u044b\u0442\u0438[\u0435\u044f]|\u043e\u0431\u044a\u0435\u043a\u0442(?:\u0443)?\s+\u0440\u0430\u0441\u0447(?:\u0435|\u0451)\u0442|\u043e\u0441\u043d\u043e\u0432\p{L}*\s+\u0441\u0440\u0435\u0434\u0441\u0442|fixed\s*asset|\b\u043e\u0441\b|lifecycle|state\s+transition)/iu.test(normalized);
|
||||||
}
|
}
|
||||||
function hasAggregateBusinessAnalyticsSignal(text) {
|
function hasAggregateBusinessAnalyticsSignal(text) {
|
||||||
const normalized = compactWhitespace(repairAddressMojibake(String(text ?? "")).toLowerCase());
|
const normalized = compactWhitespace(repairAddressMojibake(String(text ?? "")).toLowerCase());
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ function detectSupportedIntent(text, deps) {
|
||||||
reason: "address_intent_resolver_current_turn_signal"
|
reason: "address_intent_resolver_current_turn_signal"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (/(?:\u043a\u0442\u043e\s+\u043d\u0430\u043c\s+\u0434\u043e\u043b\u0436|\u043d\u0430\u043c\s+\u043a\u0442\u043e\s+\u0434\u043e\u043b\u0436|\u0434\u0435\u0431\u0438\u0442\u043e\u0440|\u0434\u0435\u0431\u0438\u0442\u043e\u0440\u0441\u043a|\breceivables?\b)/iu.test(text)) {
|
if (/(?:\u043a\u0442\u043e\s+\u043d\u0430\u043c(?:\s+\p{L}+){0,4}\s+\u0434\u043e\u043b\u0436|\u043d\u0430\u043c\s+\u043a\u0442\u043e(?:\s+\p{L}+){0,4}\s+\u0434\u043e\u043b\u0436|\u0434\u0435\u0431\u0438\u0442\u043e\u0440|\u0434\u0435\u0431\u0438\u0442\u043e\u0440\u0441\u043a|\breceivables?\b)/iu.test(text)) {
|
||||||
return {
|
return {
|
||||||
intent: "receivables_confirmed_as_of_date",
|
intent: "receivables_confirmed_as_of_date",
|
||||||
confidence: "high",
|
confidence: "high",
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,8 @@ describe("inventory root frame follow-up", () => {
|
||||||
expect(result?.baseReasons).toContain("intent_restored_to_inventory_root_frame");
|
expect(result?.baseReasons).toContain("intent_restored_to_inventory_root_frame");
|
||||||
expect(result?.filters.extracted_filters.item).toBeUndefined();
|
expect(result?.filters.extracted_filters.item).toBeUndefined();
|
||||||
expect(result?.filters.extracted_filters.organization).toBe("альтернатива");
|
expect(result?.filters.extracted_filters.organization).toBe("альтернатива");
|
||||||
expect(result?.filters.extracted_filters.counterparty).toBe("альтернатива");
|
expect(result?.filters.extracted_filters.counterparty).toBeUndefined();
|
||||||
|
expect(result?.baseReasons).toContain("counterparty_cleared_as_organization_scope_alias");
|
||||||
expect(result?.filters.extracted_filters.period_from).toBe("2020-05-01");
|
expect(result?.filters.extracted_filters.period_from).toBe("2020-05-01");
|
||||||
expect(result?.filters.extracted_filters.period_to).toBe("2020-05-31");
|
expect(result?.filters.extracted_filters.period_to).toBe("2020-05-31");
|
||||||
expect(result?.filters.extracted_filters.as_of_date).toBe("2020-05-31");
|
expect(result?.filters.extracted_filters.as_of_date).toBe("2020-05-31");
|
||||||
|
|
|
||||||
|
|
@ -86,5 +86,5 @@ describe("vat payable confirmed as-of route", () => {
|
||||||
expect(result?.debug.requested_result_mode).toBe("confirmed_balance");
|
expect(result?.debug.requested_result_mode).toBe("confirmed_balance");
|
||||||
expect(result?.debug.route_expectation_status).toBe("matched");
|
expect(result?.debug.route_expectation_status).toBe("matched");
|
||||||
expect(result?.debug.limited_reason_category).not.toBe("unsupported");
|
expect(result?.debug.limited_reason_category).not.toBe("unsupported");
|
||||||
}, 15000);
|
}, 30000);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue