АДРЕСНЫЙ РЕЖИМ -ADDRESS:Шаг 0 (Pre-Prod Rails): Предпродакшн-постановка ADDRESS Query V1 на рельсы референсный домен, nightly-автоматизация и подтверждённый global gate (102/102 + 25/25)

This commit is contained in:
2026-04-02 17:53:43 +03:00
parent 4cb9fc7021
commit 53716e548e
113 changed files with 89499 additions and 549 deletions
@@ -413,6 +413,7 @@ function extractLooseByAnchorValue(text: string): string | undefined {
"периоду",
"период",
"документам",
"документами",
"докам",
"взаиморасчетам",
"взаиморасчётам",
@@ -424,6 +425,13 @@ function extractLooseByAnchorValue(text: string): string | undefined {
"раскрой",
"раскрыть",
"раскройте",
"связанный",
"связанные",
"связанных",
"связанным",
"связанному",
"related",
"linked",
"нему",
"ней",
"ним",
@@ -484,6 +492,13 @@ function isLikelyCounterpartyToken(rawToken: string): boolean {
"документ",
"документы",
"документов",
"документами",
"документу",
"документе",
"документа",
"документах",
"докам",
"доками",
"банк",
"банковские",
"операции",
@@ -546,7 +561,14 @@ function isLikelyCounterpartyToken(rawToken: string): boolean {
"dokument",
"dokumenty",
"documents",
"docs"
"docs",
"связанный",
"связанные",
"связанных",
"связанным",
"связанному",
"related",
"linked"
]);
return !stopWords.has(lowered);
}
@@ -168,7 +168,7 @@ function hasAny(text: string, patterns: string[]): boolean {
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);
return /(?<![\d-])\d{2}(?:[.,]\d{1,2})?(?![\d-])/u.test(text);
}
function hasDocumentsFormingBalanceSignal(text: string): boolean {
@@ -221,7 +221,22 @@ function hasAccountBalanceSignal(text: string): boolean {
text.includes("скока") ||
text.includes("сколько") ||
/на\s+конец/u.test(text);
return hasAccountLexeme && hasBalanceLexeme;
if (hasAccountLexeme && hasBalanceLexeme) {
return true;
}
const hasAsOfStyleDate =
/\b(19|20)\d{2}[./-](0?[1-9]|1[0-2])(?:[./-](0?[1-9]|[12]\d|3[01]))\b/u.test(text) ||
/(?:на\s+ту\s+же\s+дат[ауеы]|same\s+date|the\s+same\s+date)/iu.test(text);
const hasFollowupBalanceVerb = /(?:вернись|вернуться|вернуть|back|return)/iu.test(text);
return hasAccountLexeme && hasAsOfStyleDate && hasFollowupBalanceVerb;
}
function hasDocumentsByAccountDrilldownSignal(text: string): boolean {
const hasAccountLexeme = hasAccountNumberAnchor(text) || hasCompactAccountCodeToken(text);
const hasDocLexeme = /(?:документ|док(?:и|ам|ах|ов|а)?|docs?|documents?)/iu.test(text);
const hasDrilldownVerb = /(?:раскрой|раскры|разлож|разверн|документами|по\s+документ)/iu.test(text);
const hasSameDate = /(?:на\s+ту\s+же\s+дат[ауеы]|same\s+date|the\s+same\s+date)/iu.test(text);
return hasAccountLexeme && hasDocLexeme && (hasDrilldownVerb || hasSameDate);
}
function hasOpenContractsListSignal(text: string): boolean {
@@ -265,6 +280,13 @@ function isLikelyCounterpartyToken(rawToken: string): boolean {
"документ",
"документы",
"документов",
"документами",
"документу",
"документе",
"документа",
"документах",
"докам",
"доками",
"банк",
"банковские",
"операции",
@@ -292,7 +314,13 @@ function isLikelyCounterpartyToken(rawToken: string): boolean {
"ёпт",
"епта",
"нах",
"нахуй"
"нахуй",
"связанным",
"связанные",
"связанных",
"связанному",
"related",
"linked"
]);
return !stopWords.has(token);
}
@@ -534,6 +562,14 @@ export function resolveAddressIntent(userMessage: string): AddressIntentResoluti
};
}
if (hasDocumentsByAccountDrilldownSignal(text)) {
return {
intent: "documents_forming_balance",
confidence: "medium",
reasons: ["documents_by_account_drilldown_signal_detected"]
};
}
if (hasOpenContractsListSignal(text)) {
return {
intent: "list_open_contracts",
@@ -332,7 +332,8 @@ function deriveIntentWithFollowupContext(
hasAccountSignal(normalizedMessage) &&
(detectedIntent.intent === "unknown" ||
detectedIntent.intent === "list_documents_by_counterparty" ||
detectedIntent.intent === "bank_operations_by_counterparty")
detectedIntent.intent === "bank_operations_by_counterparty" ||
detectedIntent.intent === "account_balance_snapshot")
) {
const preferDocumentsForming =
hasDocumentSignal(normalizedMessage) &&
@@ -2388,8 +2388,12 @@ function hasAddressFollowupContextSignal(userMessage) {
}
return false;
}
function resolveAddressFollowupCarryoverContext(userMessage, items) {
if (!hasAddressFollowupContextSignal(userMessage)) {
function resolveAddressFollowupCarryoverContext(userMessage, items, alternateMessage = null) {
const hasPrimaryFollowupSignal = hasAddressFollowupContextSignal(userMessage);
const hasAlternateFollowupSignal = toNonEmptyString(alternateMessage)
? hasAddressFollowupContextSignal(alternateMessage)
: false;
if (!hasPrimaryFollowupSignal && !hasAlternateFollowupSignal) {
return null;
}
const previousAddressDebug = findLastAddressAssistantDebug(items);
@@ -2913,7 +2917,7 @@ export class AssistantService {
toolGateReason: null
};
const addressInputMessage = toNonEmptyString(addressPreDecompose?.effectiveMessage) ?? userMessage;
const carryover = resolveAddressFollowupCarryoverContext(userMessage, session.items);
const carryover = resolveAddressFollowupCarryoverContext(userMessage, session.items, addressInputMessage);
const toolGate = resolveAddressToolGateDecision(addressInputMessage, carryover?.followupContext ?? null);
const addressRuntimeMeta = {
...addressPreDecompose,