ГЛОБАЛЬНЫЙ РЕФАКТОРИНГ АРХИТЕКТУРЫ - Рефакторинг этапов Stage 3.7 ХВОСТЫ Убрана часть чат-дрифта и зафиксировал роутинг в deep \ Ослаблена шаблонность коротких блоков ответа (детерминированные вариативные формулировки вместо одной и той же фразы)

This commit is contained in:
2026-04-11 20:14:38 +03:00
parent 351993430f
commit 160ed18fe5
9 changed files with 594 additions and 86 deletions
@@ -1108,6 +1108,29 @@ function buildLimitedScopeLine(filters: AddressFilterSet): string | null {
return `Контекст запроса: ${scopeParts.join(", ")}.`;
}
function buildLimitedVariantSeedFingerprint(filters: AddressFilterSet): string {
const seedParts: string[] = [];
const keys: Array<keyof AddressFilterSet> = [
"organization",
"counterparty",
"contract",
"account",
"document_ref",
"as_of_date",
"period_from",
"period_to"
];
for (const key of keys) {
const raw = filters[key];
const value = typeof raw === "string" ? raw.trim() : "";
if (!value) {
continue;
}
seedParts.push(`${key}:${value.toLowerCase()}`);
}
return seedParts.length > 0 ? seedParts.join("|") : "no_filter_seed";
}
function buildLimitedOffers(input: {
category: AddressLimitedReasonCategory;
shape: AddressQueryShapeDetection;
@@ -1238,12 +1261,25 @@ function composeLimitedReply(input: {
missingRequiredFilters: string[];
}): string {
const reason = normalizeLimitedReason(input.reason);
const headingSeed = `${input.category}|${input.shape.shape}|${reason}`;
const filterSeed = buildLimitedVariantSeedFingerprint(input.filters);
const missingSeed = Array.from(new Set(input.missingRequiredFilters.map((item) => String(item ?? "").trim())))
.filter((item) => item.length > 0)
.sort()
.join(",");
const headingSeed = `${input.category}|${input.shape.shape}|${input.intent}|${reason}|${filterSeed}|${missingSeed}`;
const aggregateLimitedSignal = hasAggregateLimitedSignal({
shape: input.shape,
intent: input.intent,
reason: input.reason
});
const missingAnchorLabels = Array.from(
new Set(
(Array.isArray(input.missingRequiredFilters) ? input.missingRequiredFilters : [])
.map((item) => normalizeMissingAnchorLabel(String(item ?? "").trim()))
.filter((item) => item.length > 0)
)
);
const missingAnchorPhrase = missingAnchorLabels.length > 0 ? missingAnchorLabels.join(", ") : "контрагент, договор, счет или период";
const heading =
input.category === "empty_match"
? pickDeterministicVariant(headingSeed, [
@@ -1282,12 +1318,14 @@ function composeLimitedReply(input: {
])
: pickDeterministicVariant(reasonSeed, [
"Сценарий пока не закрыт текущими адресными маршрутами без потери точности.",
"Для этого запроса пока нет надежного ответа внутри текущего address-контура."
"Для этого запроса пока нет надежного ответа в текущем адресном режиме.",
"Надежный ответ здесь требует более широкого анализа, чем текущий адресный контур."
])
: input.category === "missing_anchor"
? pickDeterministicVariant(reasonSeed, [
"Нужно чуть точнее заякорить запрос: не хватает конкретного ориентира (контрагент, договор, счет или период).",
"Для точного ответа нужен хотя бы один явный якорь: контрагент, договор, счет или период."
`Нужно чуть точнее заякорить запрос: не хватает конкретного ориентира (${missingAnchorPhrase}).`,
`Для точного ответа нужен хотя бы один явный ориентир: ${missingAnchorPhrase}.`,
`Смысл запроса понятен, но без уточнения (${missingAnchorPhrase}) риск ошибки слишком высокий.`
])
: input.category === "recipe_visibility_gap"
? "Для уверенного ответа нужен более специализированный сценарий выборки."
@@ -3209,28 +3209,75 @@ function buildShortSectionLine(structure: AnswerStructureV11): string {
const broken = sanitizeUserText(structure.direct_answer) ?? "";
const domain = inferNarrativeDomainFromText(broken);
const incomplete = isIncompleteEvidence(structure);
const shortSeed = `${domain}|${incomplete ? "partial" : "grounded"}|${broken}`;
if (/вне доступного учетного контура/i.test(broken)) {
return "Запрос вне доступного учетного контура.";
}
if (/не совпал с предметом вопроса|более точный фокус/i.test(broken)) {
return "Требуется уточнение фокуса, чтобы ответить по нужному участку учета.";
return pickDeterministicBoundaryVariant(shortSeed, [
"Требуется уточнить фокус, чтобы ответить по нужному участку учета.",
"Сейчас не хватает точного фокуса вопроса для надежного вывода.",
"Нужна чуть более точная формулировка фокуса запроса."
]);
}
if (/ненадежен|уточнен/i.test(broken)) {
return "Проблема подтверждается частично; для уверенного вывода нужны уточнения.";
return pickDeterministicBoundaryVariant(shortSeed, [
"Сигналы есть, но для уверенного вывода нужны уточнения.",
"Картина пока частичная: подтверждения есть, но не хватает уточнений.",
"Промежуточный вывод собран, однако без уточнений он остается ограниченным."
]);
}
if (domain === "settlements_60_62") {
return incomplete
? "Проблема с закрытием расчета подтверждается частично."
: "Проблема с закрытием расчета подтверждена.";
? pickDeterministicBoundaryVariant(shortSeed, [
"По взаиморасчетам видны признаки неполного закрытия, но картина пока частичная.",
"Есть подтвержденные сигналы разрыва закрытия расчетов, часть вывода остается ограниченной.",
"Риск незакрытых взаиморасчетов подтвержден частично и требует уточнения."
])
: pickDeterministicBoundaryVariant(shortSeed, [
"Проблема с закрытием расчетов подтверждена на текущей опоре.",
"Разрыв в закрытии взаиморасчетов подтвержден.",
"По текущей опоре несходимость в закрытии расчетов подтверждена."
]);
}
if (domain === "vat_document_register_book") {
return incomplete ? "Проблема в цепочке НДС подтверждается частично." : "Проблема в цепочке НДС подтверждена.";
return incomplete
? pickDeterministicBoundaryVariant(shortSeed, [
"В цепочке НДС есть подтвержденные отклонения, но пока не по всем звеньям.",
"Сигналы по НДС подтверждены частично; для полного вывода нужна дополнительная проверка.",
"Есть частичное подтверждение разрыва в НДС-контуре."
])
: pickDeterministicBoundaryVariant(shortSeed, [
"Проблема в цепочке НДС подтверждена.",
"Разрыв в НДС-контуре подтвержден на текущей опоре.",
"По НДС выявлен и подтвержден проблемный переход в цепочке."
]);
}
if (domain === "month_close_costs_20_44") {
return incomplete ? "Проблема в контуре закрытия месяца подтверждается частично." : "Проблема в контуре закрытия месяца подтверждена.";
return incomplete
? pickDeterministicBoundaryVariant(shortSeed, [
"По закрытию месяца есть проблемные сигналы, но они подтверждены частично.",
"Контур 20/44 показывает частично подтвержденные отклонения закрытия.",
"Есть признаки сбоя в закрытии месяца, однако опора пока неполная."
])
: pickDeterministicBoundaryVariant(shortSeed, [
"Проблема в контуре закрытия месяца подтверждена.",
"Сбой в закрытии месяца подтвержден на текущей выборке.",
"Разрыв в контуре 20/44 подтвержден."
]);
}
return incomplete ? "Проблема подтверждается частично на текущей опоре." : "Проблема подтверждена на текущей опоре.";
return incomplete
? pickDeterministicBoundaryVariant(shortSeed, [
"Есть подтвержденные сигналы, но вывод пока частичный.",
"Промежуточная проверка выявила проблему, однако опора еще неполная.",
"Часть признаков подтверждена, для полного вывода нужна допроверка."
])
: pickDeterministicBoundaryVariant(shortSeed, [
"Проблема подтверждена на текущей опоре.",
"Текущая опора подтверждает наличие проблемы.",
"Подтверждение проблемы по текущей выборке получено."
]);
}
function humanizeCompositeDirectAnswer(value: string): string | null {
@@ -3888,7 +3888,7 @@ function hasLivingChatSignal(text) {
if (!lower) {
return false;
}
if (/^(ага|угу|ок|окей|ясно|понял|поняла|принято|спасибо|благодарю|супер|класс|норм|го|давай|погнали|привет|хай|йо|hello|hi|thanks?)$/i.test(lower)) {
if (/^(ага|угу|ок|окей|ясно|понял|поняла|принято|спасибо|благодарю|супер|класс|норм|го|давай|погнали|привет|хай|йо|yo|че\s+там|ч[её]\s+как|че\s+как|hello|hi|thanks?)$/i.test(lower)) {
return true;
}
if (/(как дела|как ты|что нового|расскажи о себе|чем можешь помочь|давай поговорим|поговорим|обсудим|посоветуй|что думаешь)/i.test(lower)) {
@@ -4757,6 +4757,12 @@ export function resolveLivingAssistantModeDecision(input) {
reason: "assistant_capability_query_detected"
};
}
if (hasOrganizationFactLookupSignal(userMessage) || hasOrganizationFactFollowupSignal(userMessage)) {
return {
mode: "chat",
reason: "organization_fact_lookup_signal_detected"
};
}
if (hasStrongDataIntentSignal(userMessage)) {
return {
mode: "deep_analysis",
@@ -4773,8 +4779,8 @@ export function resolveLivingAssistantModeDecision(input) {
const predecomposeConfidence = toNonEmptyString(input?.predecomposeModeConfidence);
if (predecomposeMode === "unsupported" && (predecomposeConfidence === "low" || predecomposeConfidence === "medium")) {
return {
mode: "chat",
reason: "predecompose_unsupported_mode"
mode: "deep_analysis",
reason: "predecompose_unsupported_mode_fallback_to_deep"
};
}
return {