АДРЕСНЫЙ РЕЖИМ -Step-5 - feat(assistant): стабилизация свободного LLM-роутинга, прическа маршрутов chat/address, прототип прогноза НДС
This commit is contained in:
@@ -1037,6 +1037,11 @@ export class AddressQueryService {
|
||||
return null;
|
||||
}
|
||||
const { mode, shape, intent, filters, baseReasons } = decompose;
|
||||
const composeOptionsFromFilters = (filterSet: AddressFilterSet) => ({
|
||||
userMessage,
|
||||
periodFrom: typeof filterSet.period_from === "string" ? filterSet.period_from : undefined,
|
||||
periodTo: typeof filterSet.period_to === "string" ? filterSet.period_to : undefined
|
||||
});
|
||||
let anchor = resolvePrimaryAnchor(intent.intent, filters.extracted_filters);
|
||||
const recipeSelection = selectAddressRecipe(intent.intent, filters.extracted_filters);
|
||||
|
||||
@@ -1273,7 +1278,7 @@ export class AddressQueryService {
|
||||
const recoveredBankRows = applyIntentSpecificFilter("bank_operations_by_contract", filterByAnchors);
|
||||
const recoveredRows = recoveredBankRows.length > 0 ? recoveredBankRows : filterByAnchors;
|
||||
if (recoveredRows.length > 0) {
|
||||
const factual = composeFactualReply(intent.intent, recoveredRows, { userMessage });
|
||||
const factual = composeFactualReply(intent.intent, recoveredRows, composeOptionsFromFilters(filters.extracted_filters));
|
||||
const recoveryReason =
|
||||
recoveredBankRows.length > 0
|
||||
? "contract_docs_recovered_via_bank_fallback"
|
||||
@@ -1392,7 +1397,11 @@ export class AddressQueryService {
|
||||
rowsAnchorMatched: expandedRowsByAnchor.length,
|
||||
rowsMatched: expandedFilteredRows.length
|
||||
});
|
||||
const expandedFactual = composeFactualReply(intent.intent, expandedFilteredRows, { userMessage });
|
||||
const expandedFactual = composeFactualReply(
|
||||
intent.intent,
|
||||
expandedFilteredRows,
|
||||
composeOptionsFromFilters(expandedLimitFilters)
|
||||
);
|
||||
const expandedPrefix = `Период сохранен. Глубина live-выборки автоматически расширена до ${expandedPlan.limit} строк.`;
|
||||
const expandedLimitations = [...filters.warnings, "query_limit_auto_expanded_for_anchor_recovery"];
|
||||
const expandedReasons = [...baseReasons, "query_limit_auto_expanded_for_anchor_recovery"];
|
||||
@@ -1501,7 +1510,11 @@ export class AddressQueryService {
|
||||
});
|
||||
const observedWindow = deriveObservedPeriodWindow(broadenedFilteredRows);
|
||||
const broadenedPrefix = composeAutoBroadenedPeriodPrefix(filters.extracted_filters, observedWindow);
|
||||
const broadenedFactual = composeFactualReply(intent.intent, broadenedFilteredRows, { userMessage });
|
||||
const broadenedFactual = composeFactualReply(
|
||||
intent.intent,
|
||||
broadenedFilteredRows,
|
||||
composeOptionsFromFilters(autoBroadenedFilters)
|
||||
);
|
||||
const broadenedLimitations = [...filters.warnings, "period_window_auto_broadened_to_available_data"];
|
||||
const broadenedReasons = [...baseReasons, "period_window_auto_broadened_to_available_data"];
|
||||
return {
|
||||
@@ -1616,14 +1629,21 @@ export class AddressQueryService {
|
||||
rowsAnchorMatched: historicalRowsByAnchor.length,
|
||||
rowsMatched: historicalFilteredRows.length
|
||||
});
|
||||
const historicalFactual = composeFactualReply(intent.intent, historicalFilteredRows, { userMessage });
|
||||
const historicalPrefix =
|
||||
"В последних доступных записях якорь не подтвердился; показаны найденные строки по историческому окну.";
|
||||
const historicalFactual = composeFactualReply(
|
||||
intent.intent,
|
||||
historicalFilteredRows,
|
||||
composeOptionsFromFilters(historicalFilters)
|
||||
);
|
||||
const historicalPrefix = "Найдены данные в историческом срезе базы по вашему запросу.";
|
||||
const historicalSuggestion =
|
||||
intent.intent === "list_documents_by_counterparty"
|
||||
? "\nЕсли нужно, могу дополнительно показать платежи и договоры по этому контрагенту."
|
||||
: "";
|
||||
const historicalLimitations = [...filters.warnings, "historical_window_sort_recovery_applied"];
|
||||
const historicalReasons = [...baseReasons, "historical_window_sort_recovery_applied"];
|
||||
return {
|
||||
handled: true,
|
||||
reply_text: `${historicalPrefix}\n${historicalFactual.text}`,
|
||||
reply_text: `${historicalPrefix}\n${historicalFactual.text}${historicalSuggestion}`,
|
||||
reply_type: inferReplyType(historicalFactual.responseType),
|
||||
response_type: historicalFactual.responseType,
|
||||
debug: {
|
||||
@@ -1681,14 +1701,21 @@ export class AddressQueryService {
|
||||
) {
|
||||
const documentBankFallbackRows = applyIntentSpecificFilter(intent.intent, normalizedRows);
|
||||
if (documentBankFallbackRows.length > 0) {
|
||||
const fallbackFactual = composeFactualReply(intent.intent, documentBankFallbackRows, { userMessage });
|
||||
const fallbackFactual = composeFactualReply(
|
||||
intent.intent,
|
||||
documentBankFallbackRows,
|
||||
composeOptionsFromFilters(filters.extracted_filters)
|
||||
);
|
||||
const fallbackPrefix = "По вашему запросу показываю найденные документы и операции в доступном срезе базы.";
|
||||
const fallbackSuggestion =
|
||||
intent.intent === "list_documents_by_counterparty"
|
||||
? "\nЕсли нужно, могу дополнительно сузить период или показать только платежи."
|
||||
: "";
|
||||
const fallbackLimitations = [...filters.warnings, "anchor_not_matched_fallback_rows"];
|
||||
const fallbackReasons = [...baseReasons, "anchor_not_matched_fallback_rows"];
|
||||
return {
|
||||
handled: true,
|
||||
reply_text:
|
||||
"Точный якорь не подтвердился в текущем окне live-данных; показаны ближайшие доступные документы/операции по выбранному типу.\n" +
|
||||
fallbackFactual.text,
|
||||
reply_text: `${fallbackPrefix}\n${fallbackFactual.text}${fallbackSuggestion}`,
|
||||
reply_type: inferReplyType(fallbackFactual.responseType),
|
||||
response_type: fallbackFactual.responseType,
|
||||
debug: {
|
||||
@@ -1751,11 +1778,24 @@ export class AddressQueryService {
|
||||
Array.isArray(filters.warnings) &&
|
||||
(filters.warnings.includes("counterparty_from_followup_context") ||
|
||||
filters.warnings.includes("contract_from_followup_context"));
|
||||
const anchorMismatchByCounterparty =
|
||||
isAnchorMismatch && String(matchFailureReason ?? "").includes("counterparty_anchor_not_matched");
|
||||
const anchorMismatchByContract = isAnchorMismatch && String(matchFailureReason ?? "").includes("contract_anchor_not_matched");
|
||||
const isLowQualityPartyAnchor =
|
||||
(anchor.anchor_type === "counterparty" || anchor.anchor_type === "contract") &&
|
||||
isLikelyLowQualityPartyAnchor(anchor.anchor_value_raw);
|
||||
const anchorMismatchCategory: AddressLimitedReasonCategory =
|
||||
isFollowupAnchorCarryover || !isLowQualityPartyAnchor ? "empty_match" : "missing_anchor";
|
||||
const requestedPeriodFrom =
|
||||
typeof filters.extracted_filters.period_from === "string" ? filters.extracted_filters.period_from : null;
|
||||
const requestedPeriodTo = typeof filters.extracted_filters.period_to === "string" ? filters.extracted_filters.period_to : null;
|
||||
const requestedPeriodHint =
|
||||
requestedPeriodFrom && requestedPeriodTo ? ` (период ${requestedPeriodFrom}..${requestedPeriodTo} сохранен)` : "";
|
||||
const anchorMismatchCategory: AddressLimitedReasonCategory = isFollowupAnchorCarryover
|
||||
? "empty_match"
|
||||
: anchorMismatchByCounterparty || anchorMismatchByContract
|
||||
? "missing_anchor"
|
||||
: !isLowQualityPartyAnchor
|
||||
? "empty_match"
|
||||
: "missing_anchor";
|
||||
const category: AddressLimitedReasonCategory = isAnchorMismatch
|
||||
? anchorMismatchCategory
|
||||
: isRecipeFilteredOut
|
||||
@@ -1764,18 +1804,26 @@ export class AddressQueryService {
|
||||
? "recipe_visibility_gap"
|
||||
: "empty_match";
|
||||
const reasonText = isAnchorMismatch
|
||||
? anchorMismatchCategory === "missing_anchor"
|
||||
? "якорь контрагента/договора не найден в материализованных live-строках"
|
||||
: "по указанному якорю и фильтрам в live-выборке нет строк"
|
||||
? anchorMismatchByCounterparty
|
||||
? "контрагент по указанному имени/алиасу не найден в materialized live-строках"
|
||||
: anchorMismatchByContract
|
||||
? "договор по указанному номеру/названию не найден в materialized live-строках"
|
||||
: anchorMismatchCategory === "missing_anchor"
|
||||
? "якорь контрагента/договора не найден в materialized live-строках"
|
||||
: "по указанному якорю и фильтрам в live-выборке нет строк"
|
||||
: isRecipeFilteredOut
|
||||
? "строки по якорю найдены, но отфильтрованы intent-specific recipe"
|
||||
: isVisibilityGapCandidate
|
||||
? "в текущем live recipe нет достаточной document/bank видимости после фильтрации"
|
||||
: "по выбранным фильтрам в live-выборке нет строк";
|
||||
const nextStep = isAnchorMismatch
|
||||
? anchorMismatchCategory === "missing_anchor"
|
||||
? "уточните контрагента точным именем или добавьте ИНН/договор"
|
||||
: "уточните период или снимите часть фильтров"
|
||||
? anchorMismatchByCounterparty
|
||||
? `уточните точное имя контрагента или добавьте ИНН${requestedPeriodHint}`
|
||||
: anchorMismatchByContract
|
||||
? `уточните номер/наименование договора${requestedPeriodHint}`
|
||||
: anchorMismatchCategory === "missing_anchor"
|
||||
? "уточните контрагента точным именем или добавьте ИНН/договор"
|
||||
: "уточните период или снимите часть фильтров"
|
||||
: isRecipeFilteredOut
|
||||
? "сузьте период, уточните контрагента или документный тип"
|
||||
: isVisibilityGapCandidate
|
||||
@@ -1783,9 +1831,13 @@ export class AddressQueryService {
|
||||
: "уточните период, контрагента, договор или снимите часть фильтров";
|
||||
const limitations = isAnchorMismatch
|
||||
? [
|
||||
anchorMismatchCategory === "missing_anchor"
|
||||
? "anchor_not_matched_after_materialization"
|
||||
: "no_rows_for_anchor_after_materialization"
|
||||
anchorMismatchByCounterparty
|
||||
? "counterparty_anchor_not_matched_after_materialization"
|
||||
: anchorMismatchByContract
|
||||
? "contract_anchor_not_matched_after_materialization"
|
||||
: anchorMismatchCategory === "missing_anchor"
|
||||
? "anchor_not_matched_after_materialization"
|
||||
: "no_rows_for_anchor_after_materialization"
|
||||
]
|
||||
: isRecipeFilteredOut
|
||||
? ["rows_filtered_out_by_recipe_after_anchor_match"]
|
||||
@@ -1824,7 +1876,7 @@ export class AddressQueryService {
|
||||
});
|
||||
}
|
||||
|
||||
const factual = composeFactualReply(intent.intent, filteredRows, { userMessage });
|
||||
const factual = composeFactualReply(intent.intent, filteredRows, composeOptionsFromFilters(filters.extracted_filters));
|
||||
return {
|
||||
handled: true,
|
||||
reply_text: factual.text,
|
||||
|
||||
Reference in New Issue
Block a user