АДРЕСНЫЙ РЕЖИМ - ADDRESS: стабилизация интерпретации сленга и mixed RU/EN, расширение intent/mode-роутинга, регресс-тесты и live stress 102/102
This commit is contained in:
@@ -28,14 +28,20 @@ const ACCOUNT_BALANCE_HINTS = [
|
||||
"account balance",
|
||||
"balance by account",
|
||||
"saldo",
|
||||
"баланс",
|
||||
"остаток по счет",
|
||||
"сальдо по счет",
|
||||
"по счету"
|
||||
"по счету",
|
||||
"что на счете",
|
||||
"что на счёте",
|
||||
"на конец"
|
||||
];
|
||||
|
||||
const DOCUMENTS_FORMING_BALANCE_HINTS = [
|
||||
"documents forming balance",
|
||||
"docs forming balance",
|
||||
"documents form balance",
|
||||
"docs form balance",
|
||||
"balance documents",
|
||||
"documents for balance",
|
||||
"which documents form balance",
|
||||
@@ -61,6 +67,8 @@ const OPEN_ITEMS_HINTS = [
|
||||
"висят",
|
||||
"незакрыт",
|
||||
"открыт",
|
||||
"долг",
|
||||
"задолж",
|
||||
"позици"
|
||||
];
|
||||
|
||||
@@ -93,14 +101,21 @@ const BANK_OPERATIONS_BY_COUNTERPARTY_HINTS = [
|
||||
"bank operations by customer",
|
||||
"show bank operations by counterparty",
|
||||
"bank ops",
|
||||
"bank oper",
|
||||
"transactions by counterparty",
|
||||
"транзак",
|
||||
"банк",
|
||||
"банков",
|
||||
"по банку",
|
||||
"опер",
|
||||
"выписк",
|
||||
"платеж",
|
||||
"платёж",
|
||||
"оплат",
|
||||
"списан",
|
||||
"списани",
|
||||
"поступлен",
|
||||
"поступлени",
|
||||
"движени"
|
||||
];
|
||||
const DOCUMENTS_BY_CONTRACT_HINTS = [
|
||||
@@ -126,7 +141,10 @@ const BANK_OPERATIONS_BY_CONTRACT_HINTS = [
|
||||
];
|
||||
|
||||
const BANK_OPERATION_CORE_HINTS = [
|
||||
"банк",
|
||||
"банков",
|
||||
"операц",
|
||||
"опер",
|
||||
"выписк",
|
||||
"платеж",
|
||||
"платёж",
|
||||
@@ -148,18 +166,62 @@ function hasAny(text: string, patterns: string[]): boolean {
|
||||
return patterns.some((item) => text.includes(item));
|
||||
}
|
||||
|
||||
function hasCompactAccountCodeToken(text: string): boolean {
|
||||
// Match compact account tokens like 60.01 / 62, while avoiding date fragments.
|
||||
return /(?<![\d-])\d{2}(?:[.,]\d{1,2})(?![\d-])/u.test(text);
|
||||
}
|
||||
|
||||
function hasDocumentsFormingBalanceSignal(text: string): boolean {
|
||||
if (hasAny(text, DOCUMENTS_FORMING_BALANCE_HINTS)) {
|
||||
return true;
|
||||
}
|
||||
const hasDocLexeme = text.includes("документ") || text.includes("доки");
|
||||
const hasLooseAccountCodeToken = hasCompactAccountCodeToken(text);
|
||||
const hasDocLexeme = /(?:документ|док(?:и|ам|ах|ов|а)?)/u.test(text);
|
||||
const hasFormingLexeme = text.includes("формир");
|
||||
const hasBalanceLexeme = text.includes("остат");
|
||||
const hasAccountLexeme = text.includes("счет") || text.includes("счёт") || hasAccountNumberAnchor(text);
|
||||
const hasAccountLexeme =
|
||||
text.includes("счет") || text.includes("счёт") || hasAccountNumberAnchor(text) || hasLooseAccountCodeToken;
|
||||
if (hasDocLexeme && hasFormingLexeme && hasBalanceLexeme && hasAccountLexeme) {
|
||||
return true;
|
||||
}
|
||||
return hasBalanceLexeme && hasAccountLexeme && text.includes("из чего состоит");
|
||||
if (
|
||||
hasDocLexeme &&
|
||||
hasBalanceLexeme &&
|
||||
hasAccountLexeme &&
|
||||
(text.includes("раскрой") || text.includes("раскид") || text.includes("под остатк"))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (hasBalanceLexeme && hasAccountLexeme && text.includes("из чего состоит")) {
|
||||
return true;
|
||||
}
|
||||
return hasBalanceLexeme && hasAccountLexeme && /из\s+чего\s+остат/u.test(text);
|
||||
}
|
||||
|
||||
function hasDocumentsFormingBalanceAccountAnchor(text: string): boolean {
|
||||
if (hasAccountNumberAnchor(text) || text.includes("счет") || text.includes("счёт")) {
|
||||
return true;
|
||||
}
|
||||
// Allow compact account mentions like "60.01" in slang prompts without explicit "счет".
|
||||
return hasCompactAccountCodeToken(text);
|
||||
}
|
||||
|
||||
function hasAccountBalanceSignal(text: string): boolean {
|
||||
if (hasAny(text, ACCOUNT_BALANCE_HINTS)) {
|
||||
return true;
|
||||
}
|
||||
const hasAccountLexeme =
|
||||
hasAccountNumberAnchor(text) || hasCompactAccountCodeToken(text) || /(?:^|\s)по\s+\d{2}(?:[.,]\d{1,2})?(?=$|[\s,.;:!?])/u.test(text);
|
||||
const hasBalanceLexeme =
|
||||
text.includes("баланс") ||
|
||||
text.includes("остат") ||
|
||||
text.includes("сальд") ||
|
||||
text.includes("saldo") ||
|
||||
text.includes("balance") ||
|
||||
text.includes("скока") ||
|
||||
text.includes("сколько") ||
|
||||
/на\s+конец/u.test(text);
|
||||
return hasAccountLexeme && hasBalanceLexeme;
|
||||
}
|
||||
|
||||
function isLikelyCounterpartyToken(rawToken: string): boolean {
|
||||
@@ -241,6 +303,8 @@ function hasPartyAnchorMention(text: string): boolean {
|
||||
function hasContractAnchorMention(text: string): boolean {
|
||||
return (
|
||||
text.includes("договор") ||
|
||||
text.includes("контракт") ||
|
||||
/\bдог\.?\b/iu.test(text) ||
|
||||
text.includes("дог.") ||
|
||||
text.includes("contract") ||
|
||||
text.includes("dogovor")
|
||||
@@ -265,6 +329,10 @@ function hasContractNumberLikeToken(text: string): boolean {
|
||||
if (!token) {
|
||||
continue;
|
||||
}
|
||||
if (/^\d{1,2}\.\d{1,2}$/u.test(token)) {
|
||||
// Likely an account code like 60.01/51.00, not a contract number.
|
||||
continue;
|
||||
}
|
||||
const parts = token.split(/[./_-]+/u).map((part) => Number(part));
|
||||
if (!parts.every((part) => Number.isFinite(part))) {
|
||||
return true;
|
||||
@@ -381,7 +449,7 @@ function hasDocumentSignal(text: string): boolean {
|
||||
}
|
||||
|
||||
function hasHeuristicCounterpartyAnchor(text: string): boolean {
|
||||
if (!hasDocsOrBankSignal(text)) {
|
||||
if (!hasDocsOrBankSignal(text) && !hasBankOperationSignal(text)) {
|
||||
return false;
|
||||
}
|
||||
const tokens = String(text ?? "")
|
||||
@@ -441,7 +509,7 @@ export function resolveAddressIntent(userMessage: string): AddressIntentResoluti
|
||||
};
|
||||
}
|
||||
|
||||
if (hasDocumentsFormingBalanceSignal(text) && (hasAccountNumberAnchor(text) || text.includes("счет"))) {
|
||||
if (hasDocumentsFormingBalanceSignal(text) && hasDocumentsFormingBalanceAccountAnchor(text)) {
|
||||
return {
|
||||
intent: "documents_forming_balance",
|
||||
confidence: "high",
|
||||
@@ -449,6 +517,17 @@ export function resolveAddressIntent(userMessage: string): AddressIntentResoluti
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
hasAny(text, OPEN_ITEMS_HINTS) &&
|
||||
(text.includes("контраг") || text.includes("договор") || text.includes("counterparty") || text.includes("contract"))
|
||||
) {
|
||||
return {
|
||||
intent: "open_items_by_counterparty_or_contract",
|
||||
confidence: "medium",
|
||||
reasons: ["open_items_signal_detected"]
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
hasContractAnchorSignal(text) &&
|
||||
hasBankOperationSignal(text)
|
||||
@@ -496,7 +575,7 @@ export function resolveAddressIntent(userMessage: string): AddressIntentResoluti
|
||||
};
|
||||
}
|
||||
|
||||
if (hasAny(text, ACCOUNT_BALANCE_HINTS) || hasAccountNumberAnchor(text)) {
|
||||
if (hasAccountBalanceSignal(text)) {
|
||||
return {
|
||||
intent: "account_balance_snapshot",
|
||||
confidence: "high",
|
||||
@@ -512,14 +591,6 @@ export function resolveAddressIntent(userMessage: string): AddressIntentResoluti
|
||||
};
|
||||
}
|
||||
|
||||
if (hasAny(text, OPEN_ITEMS_HINTS) && (text.includes("контраг") || text.includes("договор") || text.includes("counterparty") || text.includes("contract"))) {
|
||||
return {
|
||||
intent: "open_items_by_counterparty_or_contract",
|
||||
confidence: "medium",
|
||||
reasons: ["open_items_signal_detected"]
|
||||
};
|
||||
}
|
||||
|
||||
if (hasAny(text, OPEN_CONTRACTS_HINTS) && (text.includes("договор") || text.includes("contract"))) {
|
||||
return {
|
||||
intent: "list_open_contracts",
|
||||
|
||||
Reference in New Issue
Block a user