АДРЕСНЫЙ РЕЖИМ -ADDRESS: стабилизация контекстных цепочек и live-якорей (договор 19/15)
This commit is contained in:
@@ -378,7 +378,20 @@ function extractLooseByAnchorValue(text) {
|
||||
"документам",
|
||||
"докам",
|
||||
"взаиморасчетам",
|
||||
"взаиморасчётам"
|
||||
"взаиморасчётам",
|
||||
"теперь",
|
||||
"сейчас",
|
||||
"вернись",
|
||||
"вернуться",
|
||||
"вернуть",
|
||||
"раскрой",
|
||||
"раскрыть",
|
||||
"раскройте",
|
||||
"нему",
|
||||
"ней",
|
||||
"ним",
|
||||
"этому",
|
||||
"тому"
|
||||
]);
|
||||
if (stopWords.has(lowered)) {
|
||||
return undefined;
|
||||
@@ -466,6 +479,19 @@ function isLikelyCounterpartyToken(rawToken) {
|
||||
"show",
|
||||
"list",
|
||||
"please",
|
||||
"теперь",
|
||||
"сейчас",
|
||||
"вернись",
|
||||
"вернуться",
|
||||
"вернуть",
|
||||
"раскрой",
|
||||
"раскрыть",
|
||||
"раскройте",
|
||||
"нему",
|
||||
"ней",
|
||||
"ним",
|
||||
"этому",
|
||||
"тому",
|
||||
"vse",
|
||||
"all",
|
||||
"kakie",
|
||||
|
||||
@@ -352,7 +352,7 @@ function hasContractAnchorSignal(text) {
|
||||
if (hasContractAnchorMention(text)) {
|
||||
return true;
|
||||
}
|
||||
// Allow short forms like "15/24" for follow-up prompts if document/bank signal exists.
|
||||
// Allow short forms like "19/15" for follow-up prompts if document/bank signal exists.
|
||||
return hasContractNumberLikeToken(text) && hasDocsOrBankSignal(text);
|
||||
}
|
||||
function hasLooseByAnchorMention(text) {
|
||||
|
||||
@@ -24,6 +24,78 @@ function hasAllTimeHint(text) {
|
||||
function hasSameDateHint(text) {
|
||||
return /(?:на\s+ту\s+же\s+дат[ауеы]|на\s+эту\s+же\s+дат[ауеы]|та\s+же\s+дата|same\s+date|as\s+of\s+same\s+date|the\s+same\s+date)/iu.test(String(text ?? ""));
|
||||
}
|
||||
function hasOpenItemsHint(text) {
|
||||
return /(?:open\s+items|unclosed\s+items|хвост|висят|незакрыт|не\s+закрыт|открыт|долг|задолж|позиц)/iu.test(String(text ?? ""));
|
||||
}
|
||||
function hasDocumentSignal(text) {
|
||||
return /(?:док(?:и|умент|ументы|ументов|ументами)|docs?|documents?)/iu.test(String(text ?? ""));
|
||||
}
|
||||
function hasBankSignal(text) {
|
||||
return /(?:банк|банков|операц|опер|выписк|платеж|платёж|оплат|списан|поступлен|движени|bank|payment|payments|transaction|transactions|statement|wire)/iu.test(String(text ?? ""));
|
||||
}
|
||||
function hasAccountSignal(text) {
|
||||
const normalized = String(text ?? "");
|
||||
return (/(?:сч[её]т|account)\D{0,12}\d{2}(?:[.,]\d{1,2})?/iu.test(normalized) ||
|
||||
/\b\d{2}(?:[.,]\d{1,2})\b/u.test(normalized) ||
|
||||
/(?:^|\s)по\s+\d{2}(?:[.,]\d{1,2})?(?=$|[\s,.;:!?])/iu.test(normalized));
|
||||
}
|
||||
function hasExplicitCounterpartyMention(text) {
|
||||
return /(?:контраг|counterparty|компан|организац|supplier|vendor|customer|client|partner|поставщик|клиент|покупател|партнер)/iu.test(String(text ?? ""));
|
||||
}
|
||||
function hasExplicitContractMention(text) {
|
||||
return /(?:договор|контракт|contract|\bдог\.?\b)/iu.test(String(text ?? ""));
|
||||
}
|
||||
function hasContractNumberLikeToken(text) {
|
||||
if (/(?:^|[\s([{])(?:№|#|n)\s*[a-zа-яё0-9][a-zа-яё0-9./_-]{1,}(?=$|[\s,.;:!?)\]}])/iu.test(String(text ?? ""))) {
|
||||
return true;
|
||||
}
|
||||
return /\b\d{1,6}[./_-]\d{1,6}(?:[./_-]\d{1,6})?\b/iu.test(String(text ?? ""));
|
||||
}
|
||||
function hasExplicitLooseByAnchorToken(text) {
|
||||
const match = String(text ?? "").match(/(?:^|\s)по\s+([a-zа-яё][a-zа-яё0-9._-]{1,})(?=[\s,.;:!?)]|$)/iu);
|
||||
if (!match) {
|
||||
return false;
|
||||
}
|
||||
const token = String(match[1] ?? "").trim().toLowerCase();
|
||||
if (!token) {
|
||||
return false;
|
||||
}
|
||||
const pronounTokens = new Set([
|
||||
"нему",
|
||||
"ней",
|
||||
"ним",
|
||||
"немуже",
|
||||
"нейже",
|
||||
"этому",
|
||||
"этомуже",
|
||||
"этому-то",
|
||||
"тому",
|
||||
"томуже",
|
||||
"этому",
|
||||
"этой",
|
||||
"той"
|
||||
]);
|
||||
const genericTokens = new Set([
|
||||
"контрагенту",
|
||||
"контрагента",
|
||||
"договору",
|
||||
"договора",
|
||||
"счету",
|
||||
"счёту",
|
||||
"дате",
|
||||
"периоду"
|
||||
]);
|
||||
return !pronounTokens.has(token) && !genericTokens.has(token);
|
||||
}
|
||||
function mapCounterpartyIntentToContractIntent(intent) {
|
||||
if (intent === "list_documents_by_counterparty") {
|
||||
return "list_documents_by_contract";
|
||||
}
|
||||
if (intent === "bank_operations_by_counterparty") {
|
||||
return "bank_operations_by_contract";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function hasAddressFollowupContextSignal(text) {
|
||||
const normalized = String(text ?? "").trim();
|
||||
if (!normalized) {
|
||||
@@ -153,14 +225,76 @@ function deriveIntentWithFollowupContext(detectedIntent, userMessage, followupCo
|
||||
if (!followupContext || !followupContext.previous_intent) {
|
||||
return detectedIntent;
|
||||
}
|
||||
const normalizedMessage = String(userMessage ?? "");
|
||||
const hasFollowupSignal = hasAddressFollowupContextSignal(normalizedMessage);
|
||||
if (!hasFollowupSignal) {
|
||||
return detectedIntent;
|
||||
}
|
||||
const previousIntent = followupContext.previous_intent;
|
||||
const previousFilters = followupContext.previous_filters ?? {};
|
||||
const previousContract = toNonEmptyString(previousFilters.contract);
|
||||
const previousCounterparty = toNonEmptyString(previousFilters.counterparty);
|
||||
const previousContractFromAnchor = followupContext.previous_anchor_type === "contract" ? toNonEmptyString(followupContext.previous_anchor_value) : null;
|
||||
const previousCounterpartyFromAnchor = followupContext.previous_anchor_type === "counterparty" ? toNonEmptyString(followupContext.previous_anchor_value) : null;
|
||||
const hasPreviousContract = Boolean(previousContract ?? previousContractFromAnchor);
|
||||
const hasPreviousCounterparty = Boolean(previousCounterparty ?? previousCounterpartyFromAnchor);
|
||||
const hasAnyPartyAnchor = hasPreviousContract || hasPreviousCounterparty;
|
||||
if (hasOpenItemsHint(normalizedMessage) && hasAnyPartyAnchor) {
|
||||
return {
|
||||
intent: "open_items_by_counterparty_or_contract",
|
||||
confidence: "low",
|
||||
reasons: [...detectedIntent.reasons, "open_items_from_followup_context"]
|
||||
};
|
||||
}
|
||||
const previousIsBalanceFamily = previousIntent === "account_balance_snapshot" || previousIntent === "documents_forming_balance";
|
||||
if (previousIsBalanceFamily &&
|
||||
hasAccountSignal(normalizedMessage) &&
|
||||
(detectedIntent.intent === "unknown" ||
|
||||
detectedIntent.intent === "list_documents_by_counterparty" ||
|
||||
detectedIntent.intent === "bank_operations_by_counterparty")) {
|
||||
const preferDocumentsForming = hasDocumentSignal(normalizedMessage) &&
|
||||
/(?:раскрой|раскры|формир|документами|по\s+документ)/iu.test(normalizedMessage);
|
||||
return {
|
||||
intent: preferDocumentsForming ? "documents_forming_balance" : "account_balance_snapshot",
|
||||
confidence: "low",
|
||||
reasons: [...detectedIntent.reasons, "intent_adjusted_to_balance_followup_context"]
|
||||
};
|
||||
}
|
||||
if (hasPreviousContract) {
|
||||
if (detectedIntent.intent === "unknown") {
|
||||
if (hasBankSignal(normalizedMessage)) {
|
||||
return {
|
||||
intent: "bank_operations_by_contract",
|
||||
confidence: "low",
|
||||
reasons: [...detectedIntent.reasons, "intent_adjusted_to_contract_followup_context"]
|
||||
};
|
||||
}
|
||||
if (hasDocumentSignal(normalizedMessage)) {
|
||||
return {
|
||||
intent: "list_documents_by_contract",
|
||||
confidence: "low",
|
||||
reasons: [...detectedIntent.reasons, "intent_adjusted_to_contract_followup_context"]
|
||||
};
|
||||
}
|
||||
}
|
||||
const mappedIntent = mapCounterpartyIntentToContractIntent(detectedIntent.intent);
|
||||
if (mappedIntent) {
|
||||
const explicitCounterpartyTarget = hasExplicitCounterpartyMention(normalizedMessage) || hasExplicitLooseByAnchorToken(normalizedMessage);
|
||||
const explicitContractTarget = hasExplicitContractMention(normalizedMessage) || hasContractNumberLikeToken(normalizedMessage);
|
||||
if (!explicitCounterpartyTarget || explicitContractTarget) {
|
||||
return {
|
||||
intent: mappedIntent,
|
||||
confidence: "low",
|
||||
reasons: [...detectedIntent.reasons, "intent_adjusted_to_contract_followup_context"]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
if (detectedIntent.intent !== "unknown") {
|
||||
return detectedIntent;
|
||||
}
|
||||
if (!hasAddressFollowupContextSignal(userMessage)) {
|
||||
return detectedIntent;
|
||||
}
|
||||
return {
|
||||
intent: followupContext.previous_intent,
|
||||
intent: previousIntent,
|
||||
confidence: "low",
|
||||
reasons: [...detectedIntent.reasons, "intent_from_followup_context"]
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user