Post-F: укрепить VAT, даты остатков и SVK follow-up
This commit is contained in:
@@ -73,6 +73,15 @@ const COUNTERPARTY_TOKEN_NOISE = new Set([
|
||||
"показать",
|
||||
"скажи",
|
||||
"выведи",
|
||||
"видно",
|
||||
"документам",
|
||||
"документами",
|
||||
"движение",
|
||||
"движения",
|
||||
"движениям",
|
||||
"операциям",
|
||||
"проверить",
|
||||
"проверь",
|
||||
"show",
|
||||
"list",
|
||||
"контра",
|
||||
@@ -99,7 +108,10 @@ function isCounterpartyFillerToken(token) {
|
||||
if (/^(?:бл[яе]|блять|нах|нахуй|епт|ёпт|епта)$/iu.test(normalized)) {
|
||||
return true;
|
||||
}
|
||||
if (/^(?:док(?:и|ам|ами|умент(?:ы|ов)?)?|docs?|docy|doci|doki|dokument(?:y|ov|am|a)?)$/iu.test(normalized)) {
|
||||
if (/^(?:док(?:и|ам|ами|умент(?:ы|ов|ам|ами)?)?|docs?|docy|doci|doki|dokument(?:y|ov|am|a)?)$/iu.test(normalized)) {
|
||||
return true;
|
||||
}
|
||||
if (/^(?:движени[еяям]*|операци[яиюеям]*|проверить|проверь|видно)$/iu.test(normalized)) {
|
||||
return true;
|
||||
}
|
||||
if (/^(?:pokazh?|pokazhi|pokaji|pokezh|kakie|kakoi|kakaya|est|za|po|na|s|vse|all|poka)$/iu.test(normalized)) {
|
||||
|
||||
@@ -1661,7 +1661,18 @@ function hasBidirectionalValueFlowComparisonSignal(text) {
|
||||
const hasOutgoingCue = /(?:\u0438\u0441\u0445\u043e\u0434\u044f\u0449|\u0441\u043f\u0438\u0441\u0430\u043d|\u0437\u0430\u043f\u043b\u0430\u0442|\u043f\u043b\u0430\u0442\u0438\u043b|\u043e\u043f\u043b\u0430\u0442|outflow|outgoing|payout)/iu.test(normalized);
|
||||
const hasComparisonCue = /(?:\u0431\u043e\u043b\u044c\u0448|\u043c\u0435\u043d\u044c\u0448|\u0441\u0440\u0430\u0432|\u0438\u043b\u0438|\u043d\u0435\u0442\u0442\u043e|\u0441\u0430\u043b\u044c\u0434\u043e|vs|versus)/iu.test(normalized);
|
||||
const hasValueFlowCue = /(?:\u0434\u0435\u043d\u044c\u0433|\u0434\u0435\u043d\u0435\u0433|\u0434\u0435\u043d\u0435\u0436|\u043f\u043e\u0442\u043e\u043a|\u043e\u0431\u043e\u0440\u043e\u0442|money|cash|flow)/iu.test(normalized);
|
||||
return hasIncomingCue && hasOutgoingCue && hasComparisonCue && hasValueFlowCue;
|
||||
const hasNetAmountCue = /(?:сколько|сумм|итог|нетто|сальдо|минус|net|total|sum)/iu.test(normalized);
|
||||
return hasIncomingCue && hasOutgoingCue && hasComparisonCue && (hasValueFlowCue || hasNetAmountCue);
|
||||
}
|
||||
function hasVatPeriodInspectionBridgeSignal(text) {
|
||||
const normalized = String(text ?? "").trim().toLowerCase();
|
||||
if (!/(?:ндс|vat)/iu.test(normalized)) {
|
||||
return false;
|
||||
}
|
||||
const hasPeriodCue = /(?:\b(?:19|20)\d{2}\b|за\s+(?:\d{4}|год|период|квартал|месяц|январ|феврал|март|апрел|ма[йя]|июн|июл|август|сентябр|октябр|ноябр|декабр)|\b[1-4]\s*(?:кв|квартал))/iu.test(normalized);
|
||||
const hasInspectionCue = /(?:что\s+с|позици|основан|не\s+хватает|налогов[а-яё]*\s+вывод|вывод|декларац|книга\s+(?:продаж|покупок)|расшифр|разбор)/iu.test(normalized);
|
||||
const forecastOnlyCue = /(?:прогноз|план|примерн|ориентировочн)/iu.test(normalized) && !hasInspectionCue;
|
||||
return hasPeriodCue && hasInspectionCue && !forecastOnlyCue;
|
||||
}
|
||||
function resolveUnicodeAddressIntentBridge(text) {
|
||||
const normalized = String(text ?? "").trim().toLowerCase();
|
||||
@@ -1780,6 +1791,9 @@ function resolveUnicodeAddressIntentBridge(text) {
|
||||
/(?:покупател|клиент|заказ|отгрузк|товар|услуг|задолженн|сальдо|не\s+плат|не\s+оплат|не\s+оплачен|неоплачен|просроч)/iu.test(normalized)) {
|
||||
return unicodeBridgeResolution("list_receivables_counterparties", "high", "receivables_debt_lifecycle_signal_detected");
|
||||
}
|
||||
if (hasVatPeriodInspectionBridgeSignal(normalized)) {
|
||||
return unicodeBridgeResolution("vat_liability_confirmed_for_tax_period", "high", "vat_period_inspection_bridge_signal_detected");
|
||||
}
|
||||
const inventoryBridgeIntent = (0, addressInventoryIntentSignals_1.resolveInventoryAddressIntent)(normalized);
|
||||
if (inventoryBridgeIntent) {
|
||||
if (inventoryBridgeIntent.intent === "inventory_aging_by_purchase_date") {
|
||||
|
||||
@@ -189,7 +189,15 @@ const FOLLOWUP_LOW_QUALITY_COUNTERPARTY_TOKENS = new Set([
|
||||
"сводную",
|
||||
"сводном",
|
||||
"сводного",
|
||||
"сводному"
|
||||
"сводному",
|
||||
"видно",
|
||||
"документам",
|
||||
"документами",
|
||||
"движение",
|
||||
"движения",
|
||||
"движениям",
|
||||
"проверить",
|
||||
"проверь"
|
||||
]);
|
||||
const FOLLOWUP_LOW_QUALITY_CONTRACT_TOKENS = new Set([
|
||||
"за",
|
||||
@@ -661,6 +669,10 @@ function mergeFollowupFilters(current, intent, userMessage, followupContext) {
|
||||
const hasFollowupSignal = hasAddressFollowupContextSignal(userMessage);
|
||||
const hasExplicitPeriodInMessage = hasExplicitPeriodLiteral(userMessage);
|
||||
const hasExplicitCurrentDateInMessage = hasExplicitCurrentDateHint(userMessage);
|
||||
const currentHasExplicitTemporalScope = hasExplicitPeriodWindow(merged) ||
|
||||
Boolean(toNonEmptyString(merged.as_of_date)) ||
|
||||
hasExplicitPeriodInMessage ||
|
||||
hasExplicitCurrentDateInMessage;
|
||||
const explicitQuotedItem = extractSelectedObjectItemFromFollowupText(userMessage);
|
||||
if (!toNonEmptyString(merged.organization) && previousOrganization) {
|
||||
merged.organization = previousOrganization;
|
||||
@@ -945,6 +957,7 @@ function mergeFollowupFilters(current, intent, userMessage, followupContext) {
|
||||
}
|
||||
if (!sameDateRequested &&
|
||||
(intent === "inventory_on_hand_as_of_date" || intent === "inventory_supplier_stock_overlap_as_of_date") &&
|
||||
!currentHasExplicitTemporalScope &&
|
||||
hasOpenItemsHint(userMessage)) {
|
||||
const inheritedAsOfDate = previousAsOfDate ?? previousPeriodTo ?? previousPeriodFrom;
|
||||
if (inheritedAsOfDate && merged.as_of_date !== inheritedAsOfDate) {
|
||||
|
||||
+50
-5
@@ -119,7 +119,28 @@ function isGarbageSemanticAnchorCandidate(value) {
|
||||
"прокси",
|
||||
"proxy",
|
||||
"summary",
|
||||
"overall"
|
||||
"overall",
|
||||
"деньги",
|
||||
"денег",
|
||||
"деньгам",
|
||||
"деньгами",
|
||||
"ооо",
|
||||
"ип",
|
||||
"ао",
|
||||
"пао",
|
||||
"зао",
|
||||
"llc",
|
||||
"inc",
|
||||
"corp",
|
||||
"документам",
|
||||
"документами",
|
||||
"движение",
|
||||
"движения",
|
||||
"движениям",
|
||||
"операциям",
|
||||
"проверить",
|
||||
"проверь",
|
||||
"видно"
|
||||
]).has(compact)) {
|
||||
return true;
|
||||
}
|
||||
@@ -756,6 +777,20 @@ function rawEntityResolutionCandidate(text) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function rawScopedEntityCandidateFromText(text) {
|
||||
const source = (0, addressTextRepair_1.repairAddressMojibakeText)(String(text ?? ""));
|
||||
const patterns = [
|
||||
/(?:^|[\s,.;:!?])(?:по|у|для|for|by)\s+([\p{L}\d._-]{2,})(?=$|[\s,.;:!?])/iu,
|
||||
/(?:документ(?:ам|ы)?|движени(?:ям|я)?|операци(?:ям|и)?|плат[её]ж(?:ам|и)?)\s+([\p{L}\d._-]{2,})(?=$|[\s,.;:!?])/iu
|
||||
];
|
||||
for (const pattern of patterns) {
|
||||
const candidate = normalizeEntityResolutionCandidate(source.match(pattern)?.[1] ?? "");
|
||||
if (candidate.length >= 2 && !isInvalidEntityCandidate(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function resolveEntityResolutionAmbiguityChoice(text, candidates) {
|
||||
const normalizedText = canonicalizeEntityResolutionCandidate(text);
|
||||
if (!normalizedText || candidates.length <= 0) {
|
||||
@@ -1013,6 +1048,11 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
const rawMetadataScopeHint = rawMetadataSignal ? metadataScopeHintFromRawText(rawText) : null;
|
||||
const rawTopicSwitchSignal = hasExplicitTopicSwitchSignal(rawText);
|
||||
const rawEntityCandidate = rawEntityResolutionSignal ? rawEntityResolutionCandidate(rawEntitySourceText) : null;
|
||||
const rawScopedEntityCandidate = !predecomposeEntities.counterparty &&
|
||||
!predecomposeEntities.organization &&
|
||||
(rawValueFlowSignal || rawLifecycleSignal || rawMetadataSignal || rawBusinessOverviewSignal)
|
||||
? rawScopedEntityCandidateFromText(rawEntitySourceText)
|
||||
: null;
|
||||
const entityResolutionClarificationCandidate = followupSeed.pilotScope === "entity_resolution_search_v1" &&
|
||||
followupSeed.entityResolutionStatus === "ambiguous"
|
||||
? resolveEntityResolutionAmbiguityChoice(rawEntitySourceText, followupSeed.entityResolutionAmbiguityCandidates)
|
||||
@@ -1379,8 +1419,9 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
!metadataGroundedDocumentLaneApplicable &&
|
||||
!metadataGroundedMovementLaneApplicable
|
||||
});
|
||||
const explicitCurrentCounterpartyCandidate = normalizedPredecomposeCounterparty && !isReferentialEntityPlaceholder(normalizedPredecomposeCounterparty)
|
||||
? normalizedPredecomposeCounterparty
|
||||
const explicitCurrentCounterpartyCandidate = (normalizedPredecomposeCounterparty ?? rawScopedEntityCandidate) &&
|
||||
!isReferentialEntityPlaceholder(normalizedPredecomposeCounterparty ?? rawScopedEntityCandidate ?? "")
|
||||
? normalizedPredecomposeCounterparty ?? rawScopedEntityCandidate
|
||||
: null;
|
||||
const explicitCurrentCounterpartyOverridesFollowupEntity = Boolean(explicitCurrentCounterpartyCandidate &&
|
||||
(effectiveFollowupCounterparty || followupSeed.discoveryEntity) &&
|
||||
@@ -1434,6 +1475,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
pushScopedEntityCandidate(entityCandidates, candidate, groundedFollowupEntity);
|
||||
}
|
||||
pushScopedEntityCandidate(entityCandidates, normalizedPredecomposeCounterparty, groundedFollowupEntity);
|
||||
pushScopedEntityCandidate(entityCandidates, rawScopedEntityCandidate, groundedFollowupEntity);
|
||||
if (!groundedFollowupEntity) {
|
||||
if (!rawMetadataScopeOverridesFollowupEntity) {
|
||||
pushScopedEntityCandidate(entityCandidates, effectiveFollowupCounterparty, null);
|
||||
@@ -1452,7 +1494,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
}
|
||||
pushUnique(entityCandidates, rawMetadataScopeHint);
|
||||
}
|
||||
const openScopeValueFlowWithoutCounterparty = valueFlowSignal && !normalizedPredecomposeCounterparty && !effectiveFollowupCounterparty;
|
||||
const openScopeValueFlowWithoutCounterparty = valueFlowSignal && !explicitCurrentCounterpartyCandidate && !effectiveFollowupCounterparty;
|
||||
const valueFlowOrganizationStaysScope = openScopeValueFlowWithoutCounterparty &&
|
||||
Boolean(bidirectionalValueFlowSignal ||
|
||||
hasValueRankingSignal(rawText) ||
|
||||
@@ -1460,7 +1502,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
explicitOrganizationScopeSignal ||
|
||||
organizationClarificationFollowupApplicable ||
|
||||
followupSeed.organization);
|
||||
const openScopeValueFlowWithoutResolvedCounterparty = Boolean(valueFlowSignal && !normalizedPredecomposeCounterparty && !effectiveFollowupCounterparty);
|
||||
const openScopeValueFlowWithoutResolvedCounterparty = Boolean(valueFlowSignal && !explicitCurrentCounterpartyCandidate && !effectiveFollowupCounterparty);
|
||||
if (openScopeValueFlowWithoutCounterparty && !valueFlowOrganizationStaysScope) {
|
||||
pushUnique(entityCandidates, predecomposeEntities.organization);
|
||||
pushUnique(entityCandidates, followupSeed.organization);
|
||||
@@ -1864,6 +1906,9 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
normalizedPredecomposeCounterparty) {
|
||||
pushReason(reasonCodes, "mcp_discovery_counterparty_from_predecompose");
|
||||
}
|
||||
if (rawScopedEntityCandidate && !normalizedPredecomposeCounterparty) {
|
||||
pushReason(reasonCodes, "mcp_discovery_counterparty_from_raw_scope");
|
||||
}
|
||||
if (effectiveFollowupCounterparty &&
|
||||
!rawEntitySearchOverridesStaleScope &&
|
||||
!rawMetadataScopeOverridesFollowupEntity) {
|
||||
|
||||
+12
-3
@@ -1429,13 +1429,22 @@ const ADDRESS_PREDECOMPOSE_NOISE_TOKENS = new Set([
|
||||
"dokumenty",
|
||||
"документ",
|
||||
"документы",
|
||||
"документам",
|
||||
"документами",
|
||||
"документов",
|
||||
"банк",
|
||||
"банковские",
|
||||
"операции",
|
||||
"операциям",
|
||||
"движение",
|
||||
"движения",
|
||||
"движениям",
|
||||
"платеж",
|
||||
"платёж",
|
||||
"платежи",
|
||||
"проверить",
|
||||
"проверь",
|
||||
"видно",
|
||||
"контрагент",
|
||||
"контрагенту",
|
||||
"контрагента",
|
||||
@@ -2615,10 +2624,10 @@ function hasAddressFollowupContextSignal(userMessage) {
|
||||
if (ultraShortFollowup && hasAny(/^(?:давай|показывай|показывыай|ещ[её]|also|again|go|ok|okay)(?=$|[\s,.;:!?])/iu)) {
|
||||
return true;
|
||||
}
|
||||
const shortValueFlowRetargetCue = shortFollowup &&
|
||||
const valueFlowRetargetFollowup = minTokens <= 14 &&
|
||||
(hasMarker() || hasPointer() || hasAny(/^(?:Р°|a|Рё|i|also|then|now)(?=$|[\s,.;:!?])/iu)) &&
|
||||
hasAny(/(?:нетто|сальдо|разниц|получил|заплатил|поступ|входящ|исходящ|оборот|выручк|денеж)/iu);
|
||||
if (shortValueFlowRetargetCue) {
|
||||
hasAny(/(?:нетто|сальдо|разниц|получил|заплатил|поступ|входящ|исходящ|оборот|выручк|денеж|нетто|сальдо|разниц|получил|заплатил|поступ|РІС…РѕРґСЏС‰|РёСЃС…РѕРґСЏС‰|РѕР±РѕСЂРѕС‚|выручк|денеж)/iu);
|
||||
if (valueFlowRetargetFollowup) {
|
||||
return true;
|
||||
}
|
||||
if (hasStandaloneAddressTopicSignal(rawText || repairedText)) {
|
||||
|
||||
Reference in New Issue
Block a user