АДРЕСНЫЙ РЕЖИМ -

This commit is contained in:
2026-04-02 12:02:30 +03:00
parent da8a4eb872
commit 4dff069ae3
43 changed files with 7289 additions and 138 deletions
@@ -17,7 +17,7 @@ import { buildAddressRecipePlan, selectAddressRecipe } from "./addressRecipeCata
import { executeAddressMcpQuery } from "./addressMcpClient";
import { runAddressDecomposeStage, type AddressFollowupContext } from "./address_runtime/decomposeStage";
import { resolvePrimaryAnchor, refineAnchorFromRows, type AnchorResolutionDebug } from "./address_runtime/resolveStage";
import { composeFactualReply, contractCandidatesFromRows, inferReplyType } from "./address_runtime/composeStage";
import { composeFactualReply, inferReplyType } from "./address_runtime/composeStage";
interface NormalizedAddressRow {
period: string | null;
@@ -464,12 +464,14 @@ function canAutoBroadenPeriodWindow(intent: AddressIntent, filters: AddressFilte
);
}
function isDocumentOrBankIntent(intent: AddressIntent): boolean {
function isAnchorRecoveryIntent(intent: AddressIntent): boolean {
return (
intent === "list_documents_by_counterparty" ||
intent === "bank_operations_by_counterparty" ||
intent === "list_documents_by_contract" ||
intent === "bank_operations_by_contract"
intent === "bank_operations_by_contract" ||
intent === "open_items_by_counterparty_or_contract" ||
intent === "list_open_contracts"
);
}
@@ -1058,40 +1060,9 @@ export class AddressQueryService {
}
}
if (intent.intent === "list_open_contracts" && filteredRows.length > 0 && contractCandidatesFromRows(filteredRows).length === 0) {
return buildLimitedExecutionResult({
mode,
shape,
intent,
filters: filters.extracted_filters,
missingRequiredFilters: [],
selectedRecipe: recipeSelection.selected_recipe.recipe_id,
accountScopeMode: plan.account_scope_mode,
accountScopeFallbackApplied,
accountScopeAudit,
anchor,
matchFailureStage,
matchFailureReason,
mcpCallStatus: stageStatus,
rowsFetched: mcp.fetched_rows,
rawRowsReceived: mcp.raw_rows.length,
rowsAfterAccountScope: normalizedRows.length,
rowsAfterRecipeFilter: filterByAnchors.length,
rowsMaterialized: normalizedRows.length,
rowsMatched: filteredRows.length,
rawRowKeysSample: rowDiagnostics.rawRowKeysSample,
materializationDropReason: rowDiagnostics.materializationDropReason,
category: "recipe_visibility_gap",
reasonText: "в live строках нет договорных якорей для уверенного списка незакрытых договоров",
nextStep: "сузьте запрос по контрагенту или добавьте номер договора",
limitations: ["no_contract_anchors_in_live_rows"],
reasons: baseReasons
});
}
if (
filteredRows.length === 0 &&
isDocumentOrBankIntent(intent.intent) &&
isAnchorRecoveryIntent(intent.intent) &&
(stageStatus === "materialized_but_not_anchor_matched" || stageStatus === "materialized_but_filtered_out_by_recipe")
) {
const currentLimit =
@@ -1201,6 +1172,82 @@ export class AddressQueryService {
}
};
}
if (
intent.intent === "open_items_by_counterparty_or_contract" &&
expandedFilteredRows.length === 0 &&
expandedRowsByAnchor.length === 0 &&
expandedNormalizedRows.length > 0
) {
const expandedFallbackRows = applyIntentSpecificFilter(intent.intent, expandedNormalizedRows);
if (expandedFallbackRows.length > 0) {
const expandedRowDiagnostics = deriveRowStageDiagnostics(
expandedMcp.raw_rows,
expandedNormalizedRows.length,
expandedNormalizedRows.length
);
const expandedFactual = composeFactualReply(intent.intent, expandedFallbackRows);
const expandedLimitations = [
...filters.warnings,
"query_limit_auto_expanded_for_anchor_recovery",
"open_items_anchor_not_matched_fallback_rows"
];
const expandedReasons = [
...baseReasons,
"query_limit_auto_expanded_for_anchor_recovery",
"open_items_anchor_not_matched_fallback_rows"
];
return {
handled: true,
reply_text:
`Точный якорь не подтвердился в live-строках даже после расширения до ${expandedPlan.limit}; ` +
"показаны ближайшие доступные позиции по счетам 60/62/76.\n" +
expandedFactual.text,
reply_type: inferReplyType(expandedFactual.responseType),
response_type: expandedFactual.responseType,
debug: {
detected_mode: mode.mode,
detected_mode_confidence: mode.confidence,
query_shape: shape.shape,
query_shape_confidence: shape.confidence,
detected_intent: intent.intent,
detected_intent_confidence: intent.confidence,
extracted_filters: filters.extracted_filters,
missing_required_filters: [],
selected_recipe: expandedSelection.selected_recipe.recipe_id,
mcp_call_status_legacy: "matched_non_empty",
account_scope_mode: expandedPlan.account_scope_mode,
account_scope_fallback_applied: expandedAccountScopeFallbackApplied,
anchor_type: expandedAnchor.anchor_type,
anchor_value_raw: expandedAnchor.anchor_value_raw,
anchor_value_resolved: expandedAnchor.anchor_value_resolved,
resolver_confidence: expandedAnchor.resolver_confidence,
ambiguity_count: expandedAnchor.ambiguity_count,
match_failure_stage: "materialized_but_not_anchor_matched",
match_failure_reason:
expandedAnchorFilter.mismatchReason ?? "anchor_not_matched_after_query_limit_expansion",
mcp_call_status: "matched_non_empty",
rows_fetched: expandedMcp.fetched_rows,
raw_rows_received: expandedMcp.raw_rows.length,
rows_after_account_scope: expandedNormalizedRows.length,
rows_after_recipe_filter: expandedRowsByAnchor.length,
rows_materialized: expandedNormalizedRows.length,
rows_matched: expandedFallbackRows.length,
raw_row_keys_sample: expandedRowDiagnostics.rawRowKeysSample,
materialization_drop_reason: expandedRowDiagnostics.materializationDropReason,
account_token_raw: expandedAccountScopeAudit.accountTokenRaw,
account_token_normalized: expandedAccountScopeAudit.accountTokenNormalized,
account_scope_fields_checked: expandedAccountScopeAudit.accountScopeFieldsChecked,
account_scope_match_strategy: expandedAccountScopeAudit.accountScopeMatchStrategy,
account_scope_drop_reason: expandedAccountScopeAudit.accountScopeDropReason,
runtime_readiness: "LIVE_QUERYABLE_WITH_LIMITS",
limited_reason_category: null,
response_type: expandedFactual.responseType,
limitations: expandedLimitations,
reasons: expandedReasons
}
};
}
}
}
}
}
@@ -1313,6 +1360,67 @@ export class AddressQueryService {
}
}
if (
filteredRows.length === 0 &&
intent.intent === "open_items_by_counterparty_or_contract" &&
normalizedRows.length > 0
) {
const openItemsFallbackRows = applyIntentSpecificFilter(intent.intent, normalizedRows);
if (openItemsFallbackRows.length > 0) {
const fallbackFactual = composeFactualReply(intent.intent, openItemsFallbackRows);
const fallbackLimitations = [...filters.warnings, "open_items_anchor_not_matched_fallback_rows"];
const fallbackReasons = [...baseReasons, "open_items_anchor_not_matched_fallback_rows"];
return {
handled: true,
reply_text:
"Точный якорь не подтвердился в текущем окне live-данных; показаны ближайшие доступные позиции по счетам 60/62/76.\n" +
fallbackFactual.text,
reply_type: inferReplyType(fallbackFactual.responseType),
response_type: fallbackFactual.responseType,
debug: {
detected_mode: mode.mode,
detected_mode_confidence: mode.confidence,
query_shape: shape.shape,
query_shape_confidence: shape.confidence,
detected_intent: intent.intent,
detected_intent_confidence: intent.confidence,
extracted_filters: filters.extracted_filters,
missing_required_filters: [],
selected_recipe: recipeSelection.selected_recipe.recipe_id,
mcp_call_status_legacy: "matched_non_empty",
account_scope_mode: plan.account_scope_mode,
account_scope_fallback_applied: accountScopeFallbackApplied,
anchor_type: anchor.anchor_type,
anchor_value_raw: anchor.anchor_value_raw,
anchor_value_resolved: anchor.anchor_value_resolved,
resolver_confidence: anchor.resolver_confidence,
ambiguity_count: anchor.ambiguity_count,
match_failure_stage: matchFailureStage,
match_failure_reason: matchFailureReason,
mcp_call_status: "matched_non_empty",
rows_fetched: mcp.fetched_rows,
raw_rows_received: mcp.raw_rows.length,
rows_after_account_scope: normalizedRows.length,
rows_after_recipe_filter: filterByAnchors.length,
rows_materialized: normalizedRows.length,
rows_matched: openItemsFallbackRows.length,
raw_row_keys_sample: rowDiagnostics.rawRowKeysSample,
materialization_drop_reason: rowDiagnostics.materializationDropReason,
account_token_raw: accountScopeAudit.accountTokenRaw,
account_token_normalized: accountScopeAudit.accountTokenNormalized,
account_scope_fields_checked: accountScopeAudit.accountScopeFieldsChecked,
account_scope_match_strategy: accountScopeAudit.accountScopeMatchStrategy,
account_scope_drop_reason: accountScopeAudit.accountScopeDropReason,
runtime_readiness: "LIVE_QUERYABLE_WITH_LIMITS",
limited_reason_category: null,
response_type: fallbackFactual.responseType,
limitations: fallbackLimitations,
reasons: fallbackReasons
}
};
}
}
if (filteredRows.length === 0) {
const hadBaseRows = normalizedRows.length > 0 || mcp.fetched_rows > 0;
const hadAnchorMatchedRows = filterByAnchors.length > 0;