АДРЕСНЫЙ РЕЖИМ - стабилизация LLM fallback якорей и периода

This commit is contained in:
2026-04-02 00:30:38 +03:00
parent 2d95ce6332
commit 7dd6607ded
19 changed files with 2727 additions and 49 deletions
@@ -103,6 +103,46 @@ const BANK_OPERATIONS_BY_COUNTERPARTY_HINTS = [
"поступлен",
"движени"
];
const DOCUMENTS_BY_CONTRACT_HINTS = [
"documents by contract",
"docs by contract",
"show documents by contract",
"list documents by contract",
"документы по договору",
"доки по договору",
"док по договору",
"документы договор",
"договор"
];
const BANK_OPERATIONS_BY_CONTRACT_HINTS = [
"bank operations by contract",
"bank payments by contract",
"payment orders by contract",
"transactions by contract",
"bank ops by contract",
"банковские операции по договору",
"платежи по договору",
"выписка по договору"
];
const BANK_OPERATION_CORE_HINTS = [
"банков",
"выписк",
"платеж",
"платёж",
"оплат",
"списан",
"поступлен",
"движени",
"транзак",
"bank",
"payment",
"payments",
"transaction",
"transactions",
"statement",
"wire"
];
function hasAny(text: string, patterns: string[]): boolean {
return patterns.some((item) => text.includes(item));
@@ -198,6 +238,72 @@ function hasPartyAnchorMention(text: string): boolean {
);
}
function hasContractAnchorMention(text: string): boolean {
return (
text.includes("договор") ||
text.includes("дог.") ||
text.includes("contract") ||
text.includes("dogovor")
);
}
function hasContractNumberLikeToken(text: string): boolean {
if (/(?:^|[\s([{])(?:№|#|n)\s*[a-zа-яё0-9][a-zа-яё0-9./_-]{1,}(?=$|[\s,.;:!?)\]}])/iu.test(text)) {
return true;
}
const rawTokens = text
.split(/[\s,;:!?()[\]{}"«»]+/u)
.map((token) => token.replace(/^[^\p{L}\p{N}#№]+|[^\p{L}\p{N}./_-]+$/gu, "").trim())
.filter((token) => token.length > 0);
for (const rawToken of rawTokens) {
const token = String(rawToken ?? "").trim();
if (!/^\d{1,6}[./_-]\d{1,6}(?:[./_-]\d{1,6})?$/u.test(token)) {
continue;
}
if (!token) {
continue;
}
const parts = token.split(/[./_-]+/u).map((part) => Number(part));
if (!parts.every((part) => Number.isFinite(part))) {
return true;
}
if (parts.length === 2) {
const [a, b] = parts;
const yearFirst = a >= 1900 && a <= 2099 && b >= 1 && b <= 12;
const yearSecond = b >= 1900 && b <= 2099 && a >= 1 && a <= 12;
if (yearFirst || yearSecond) {
continue;
}
return true;
}
if (parts.length === 3) {
const [a, b, c] = parts;
const ymd = a >= 1900 && a <= 2099 && b >= 1 && b <= 12 && c >= 1 && c <= 31;
const dmy = c >= 1900 && c <= 2099 && a >= 1 && a <= 31 && b >= 1 && b <= 12;
if (ymd || dmy) {
continue;
}
return true;
}
return true;
}
return false;
}
function hasContractAnchorSignal(text: string): boolean {
if (hasContractAnchorMention(text)) {
return true;
}
// Allow short forms like "15/24" for follow-up prompts if document/bank signal exists.
return hasContractNumberLikeToken(text) && hasDocsOrBankSignal(text);
}
function hasLooseByAnchorMention(text: string): boolean {
const match = text.match(/(?:^|\s)по\s+([a-zа-яё][a-zа-яё0-9._-]{1,})(?=[\s,.;:!?)]|$)/iu);
if (!match) {
@@ -260,6 +366,20 @@ function hasDocsOrBankSignal(text: string): boolean {
);
}
function hasBankOperationSignal(text: string): boolean {
return hasAny(text, BANK_OPERATION_CORE_HINTS) || hasAny(text, BANK_OPERATIONS_BY_COUNTERPARTY_HINTS) || hasAny(text, BANK_OPERATIONS_BY_CONTRACT_HINTS);
}
function hasDocumentSignal(text: string): boolean {
return (
text.includes("док") ||
text.includes("доки") ||
text.includes("документ") ||
text.includes("docs") ||
text.includes("documents")
);
}
function hasHeuristicCounterpartyAnchor(text: string): boolean {
if (!hasDocsOrBankSignal(text)) {
return false;
@@ -329,6 +449,28 @@ export function resolveAddressIntent(userMessage: string): AddressIntentResoluti
};
}
if (
hasContractAnchorSignal(text) &&
hasBankOperationSignal(text)
) {
return {
intent: "bank_operations_by_contract",
confidence: "medium",
reasons: ["bank_ops_by_contract_signal_detected"]
};
}
if (
hasContractAnchorSignal(text) &&
(hasAny(text, DOCUMENTS_BY_CONTRACT_HINTS) || hasDocumentSignal(text))
) {
return {
intent: "list_documents_by_contract",
confidence: "medium",
reasons: ["documents_by_contract_signal_detected"]
};
}
if (
hasAny(text, BANK_OPERATIONS_BY_COUNTERPARTY_HINTS) &&
(hasPartyAnchorMention(text) || hasLooseByAnchorMention(text) || hasHeuristicCounterpartyAnchor(text))