ГЛОБАЛЬНЫЙ РЕФАКТОРИНГ АРХИТЕКТУРЫ - Рефакторинг этапов Stage 3.6 Усилина оркестрация и детект data-scope для живого сленга. Убрана шаблонность soft-refusal в limited-ответах. Подрезана словарнаю жирность в фильтр-экстракторе
This commit is contained in:
@@ -1111,6 +1111,7 @@ function buildLimitedScopeLine(filters: AddressFilterSet): string | null {
|
||||
function buildLimitedOffers(input: {
|
||||
category: AddressLimitedReasonCategory;
|
||||
shape: AddressQueryShapeDetection;
|
||||
intent: AddressIntent;
|
||||
filters: AddressFilterSet;
|
||||
missingRequiredFilters: string[];
|
||||
reason: string;
|
||||
@@ -1139,6 +1140,14 @@ function buildLimitedOffers(input: {
|
||||
}
|
||||
}
|
||||
|
||||
if (input.intent === "list_receivables_counterparties") {
|
||||
offers.push("показать контрагентов с максимальными хвостами дебиторки по 62/76");
|
||||
} else if (input.intent === "list_payables_counterparties") {
|
||||
offers.push("показать контрагентов с максимальными хвостами кредиторки по 60/76");
|
||||
} else if (input.intent === "open_items_by_counterparty_or_contract" || input.intent === "list_open_contracts") {
|
||||
offers.push("показать незакрытые договоры и хвосты взаиморасчетов на дату");
|
||||
}
|
||||
|
||||
if (counterparty) {
|
||||
offers.push(`показать документы и платежи по контрагенту ${counterparty}`);
|
||||
} else if (contract) {
|
||||
@@ -1170,49 +1179,128 @@ function buildLimitedOffers(input: {
|
||||
return Array.from(new Set(offers)).slice(0, 3);
|
||||
}
|
||||
|
||||
function buildLimitedIntentSignalLine(input: {
|
||||
intent: AddressIntent;
|
||||
shape: AddressQueryShapeDetection;
|
||||
}): string | null {
|
||||
const byIntent: Partial<Record<AddressIntent, string>> = {
|
||||
list_documents_by_counterparty: "Сигнал запроса: нужен срез документов/платежей по контрагенту.",
|
||||
list_documents_by_contract: "Сигнал запроса: нужен срез документов/платежей по договору.",
|
||||
bank_operations_by_counterparty: "Сигнал запроса: нужен срез банковских операций по контрагенту.",
|
||||
bank_operations_by_contract: "Сигнал запроса: нужен срез банковских операций по договору.",
|
||||
open_items_by_counterparty_or_contract: "Сигнал запроса: нужен контроль незакрытых взаиморасчетов.",
|
||||
list_open_contracts: "Сигнал запроса: нужен список незакрытых договоров на дату.",
|
||||
list_receivables_counterparties: "Сигнал запроса: нужен ранжированный список должников.",
|
||||
list_payables_counterparties: "Сигнал запроса: нужен ранжированный список кредиторов."
|
||||
};
|
||||
|
||||
const byShape: Partial<Record<AddressQueryShapeDetection["shape"], string>> = {
|
||||
AGGREGATE_LOOKUP: "Сигнал запроса: агрегатный вопрос по периоду/срезу.",
|
||||
DOCUMENT_LIST: "Сигнал запроса: список документов/операций.",
|
||||
OBJECT_LOOKUP: "Сигнал запроса: поиск конкретных объектов.",
|
||||
VERIFY_FACTUAL: "Сигнал запроса: проверка фактического состояния по данным.",
|
||||
COMPOUND_FACTUAL_QUERY: "Сигнал запроса: комбинированная проверка взаимосвязанных фактов."
|
||||
};
|
||||
|
||||
return byIntent[input.intent] ?? byShape[input.shape.shape] ?? null;
|
||||
}
|
||||
|
||||
function hasAggregateLimitedSignal(input: {
|
||||
shape: AddressQueryShapeDetection;
|
||||
intent: AddressIntent;
|
||||
reason: string;
|
||||
}): boolean {
|
||||
if (input.shape.shape === "AGGREGATE_LOOKUP") {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
input.intent === "counterparty_population_and_roles" ||
|
||||
input.intent === "counterparty_activity_lifecycle" ||
|
||||
input.intent === "contract_usage_overview" ||
|
||||
input.intent === "supplier_payouts_profile" ||
|
||||
input.intent === "customer_revenue_and_payments" ||
|
||||
input.intent === "contract_usage_and_value"
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return /(?:оборот|выруч|доход|прибыл|марж|рентабел|тренд|динам|самый|топ|ranking|revenue|profit|margin|year)/iu.test(
|
||||
String(input.reason ?? "")
|
||||
);
|
||||
}
|
||||
|
||||
function composeLimitedReply(input: {
|
||||
category: AddressLimitedReasonCategory;
|
||||
reason: string;
|
||||
nextStep?: string;
|
||||
shape: AddressQueryShapeDetection;
|
||||
intent: AddressIntent;
|
||||
filters: AddressFilterSet;
|
||||
missingRequiredFilters: string[];
|
||||
}): string {
|
||||
const reason = normalizeLimitedReason(input.reason);
|
||||
const headingSeed = `${input.category}|${input.shape.shape}|${reason}`;
|
||||
const aggregateLimitedSignal = hasAggregateLimitedSignal({
|
||||
shape: input.shape,
|
||||
intent: input.intent,
|
||||
reason: input.reason
|
||||
});
|
||||
const heading =
|
||||
input.category === "empty_match"
|
||||
? pickDeterministicVariant(headingSeed, [
|
||||
"По текущим условиям в доступном срезе данных совпадений не нашлось.",
|
||||
"В текущем срезе данных по этому запросу совпадения не найдены."
|
||||
"В текущем срезе данных по этому запросу совпадения не найдены.",
|
||||
"По заданным фильтрам в текущем срезе совпадений пока нет."
|
||||
])
|
||||
: input.category === "missing_anchor"
|
||||
? pickDeterministicVariant(headingSeed, [
|
||||
"Чтобы ответ был точным, нужно чуть сильнее заякорить запрос.",
|
||||
"Запрос понятен, но для надежного ответа не хватает опорного ориентира."
|
||||
"Запрос понятен, но для надежного ответа не хватает опорного ориентира.",
|
||||
"Вопрос по смыслу ясен, но пока не хватает конкретной опоры для выборки."
|
||||
])
|
||||
: input.category === "recipe_visibility_gap"
|
||||
? pickDeterministicVariant(headingSeed, [
|
||||
"Запрос понятен, но текущий сценарий выборки не дает нужной детализации.",
|
||||
"Смысл запроса ясен, но в этом контуре не хватает глубины выборки."
|
||||
"Смысл запроса ясен, но в этом контуре не хватает глубины выборки.",
|
||||
"Сценарий запроса корректный, но текущая витрина не дает нужной детализации."
|
||||
])
|
||||
: input.category === "unsupported"
|
||||
? pickDeterministicVariant(headingSeed, [
|
||||
"По этому вопросу в текущем адресном контуре пока нет надежного маршрута ответа.",
|
||||
"Сейчас в адресном режиме такой сценарий не закрыт без риска ошибочного вывода."
|
||||
"Сейчас не дам прямой адресный ответ, чтобы не ошибиться в выводах.",
|
||||
"В текущем адресном контуре этот запрос лучше не закрывать «в лоб» — риск неверной трактовки высок.",
|
||||
"Для такого формата запроса нужен более широкий аналитический контур, иначе ответ будет ненадежным."
|
||||
])
|
||||
: "Не удалось завершить проверку в адресном режиме.";
|
||||
|
||||
const reasonSeed = `${headingSeed}|reason`;
|
||||
const reasonLine =
|
||||
input.category === "unsupported"
|
||||
? "Коротко: сценарий пока не покрыт текущими адресными маршрутами."
|
||||
? aggregateLimitedSignal
|
||||
? pickDeterministicVariant(reasonSeed, [
|
||||
"Это агрегатный/сравнительный вопрос: без расширенного анализа здесь легко дать ложную метрику.",
|
||||
"Запрос про сводную аналитику или ранжирование, поэтому в address-контуре ответ сейчас будет ненадежным.",
|
||||
"Нужна расширенная аналитическая обработка: адресный режим в этом кейсе не гарантирует корректный расчет."
|
||||
])
|
||||
: pickDeterministicVariant(reasonSeed, [
|
||||
"Сценарий пока не закрыт текущими адресными маршрутами без потери точности.",
|
||||
"Для этого запроса пока нет надежного ответа внутри текущего address-контура."
|
||||
])
|
||||
: input.category === "missing_anchor"
|
||||
? "Коротко: не хватает конкретного ориентира (контрагент, договор, счет или период)."
|
||||
? pickDeterministicVariant(reasonSeed, [
|
||||
"Нужно чуть точнее заякорить запрос: не хватает конкретного ориентира (контрагент, договор, счет или период).",
|
||||
"Для точного ответа нужен хотя бы один явный якорь: контрагент, договор, счет или период."
|
||||
])
|
||||
: input.category === "recipe_visibility_gap"
|
||||
? "Коротко: для уверенного ответа нужен более специализированный сценарий выборки."
|
||||
: `Коротко: ${reason}.`;
|
||||
? "Для уверенного ответа нужен более специализированный сценарий выборки."
|
||||
: `${reason}.`;
|
||||
|
||||
const lines = [heading, reasonLine];
|
||||
const signalLine = buildLimitedIntentSignalLine({
|
||||
intent: input.intent,
|
||||
shape: input.shape
|
||||
});
|
||||
if (signalLine && !(input.category === "unsupported" && aggregateLimitedSignal)) {
|
||||
lines.push(signalLine);
|
||||
}
|
||||
const scopeLine = buildLimitedScopeLine(input.filters);
|
||||
if (scopeLine) {
|
||||
lines.push(scopeLine);
|
||||
@@ -1221,6 +1309,7 @@ function composeLimitedReply(input: {
|
||||
const offers = buildLimitedOffers({
|
||||
category: input.category,
|
||||
shape: input.shape,
|
||||
intent: input.intent,
|
||||
filters: input.filters,
|
||||
missingRequiredFilters: input.missingRequiredFilters,
|
||||
reason: input.reason,
|
||||
@@ -1275,6 +1364,7 @@ function buildLimitedExecutionResult(input: {
|
||||
reason: input.reasonText,
|
||||
nextStep: input.nextStep,
|
||||
shape: input.shape,
|
||||
intent: input.intent.intent,
|
||||
filters: input.filters,
|
||||
missingRequiredFilters: input.missingRequiredFilters
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user