ГЛОБАЛЬНЫЙ РЕФАКТОРИНГ АРХИТЕКТУРЫ - Этап 4.8: референциальная continuity для follow-up по контрагентам и pivot операции

This commit is contained in:
2026-04-12 10:36:44 +03:00
parent a49212608f
commit ca2feab893
7 changed files with 584 additions and 17 deletions
@@ -15,6 +15,7 @@ export interface AddressFollowupContext {
previous_filters?: AddressFilterSet;
previous_anchor_type?: "account" | "counterparty" | "contract" | "document_ref" | "unknown" | null;
previous_anchor_value?: string | null;
resolved_counterparty_from_display?: boolean;
}
export interface AddressDecomposeStageResult {
@@ -321,6 +322,24 @@ export function hasAddressFollowupContextSignal(text: string): boolean {
return tokenCount <= 6;
}
function isValueCounterpartyIntent(intent: AddressIntent): boolean {
return (
intent === "customer_revenue_and_payments" ||
intent === "supplier_payouts_profile" ||
intent === "contract_usage_and_value"
);
}
function hasBroadCounterpartyRankingCue(text: string): boolean {
const normalized = String(text ?? "").toLowerCase();
if (!normalized) {
return false;
}
return /(?:\bкто\b|\bкакие\b|\bкакой\b|\bтоп\b|\bсписок\b|\bвсе\b|\bвсех\b|\bвсего\b|\bclients?\b|\bcounterpart(?:y|ies)\b|контрагент|клиент|заказчик)/iu.test(
normalized
);
}
function mergeFollowupFilters(
current: AddressFilterSet,
intent: AddressIntent,
@@ -369,6 +388,28 @@ function mergeFollowupFilters(
}
}
if (isValueCounterpartyIntent(intent)) {
const inheritedCounterparty =
previousCounterparty ??
(followupContext.previous_anchor_type === "counterparty" ? previousAnchorValue : null);
const currentCounterparty = toNonEmptyString(merged.counterparty);
const previousIntentIsValueCounterparty = isValueCounterpartyIntent(followupContext.previous_intent ?? "unknown");
const resolvedCounterpartyFromDisplay = followupContext.resolved_counterparty_from_display === true;
const allowCarryover =
!hasBroadCounterpartyRankingCue(userMessage) &&
(resolvedCounterpartyFromDisplay || previousIntentIsValueCounterparty);
const shouldInheritCounterparty =
allowCarryover &&
(!currentCounterparty ||
(Boolean(inheritedCounterparty) &&
isLowQualityCounterpartyAnchor(currentCounterparty) &&
!isLowQualityCounterpartyAnchor(inheritedCounterparty)));
if (inheritedCounterparty && shouldInheritCounterparty) {
merged.counterparty = inheritedCounterparty;
reasons.push(currentCounterparty ? "counterparty_replaced_from_followup_context" : "counterparty_from_followup_context");
}
}
if (intent === "list_documents_by_contract" || intent === "bank_operations_by_contract") {
const inheritedContract = previousContract ?? (followupContext.previous_anchor_type === "contract" ? previousAnchorValue : null);
const currentContract = toNonEmptyString(merged.contract);