Удержать контекст СВК в follow-up ассистента
This commit is contained in:
+73
-15
@@ -127,6 +127,48 @@ function readNavigationDiscoveryCounterparty(debug) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function readDiscoveryPilot(debug) {
|
||||
const entry = toObject(debug.assistant_mcp_discovery_entry_point_v1);
|
||||
const bridge = toObject(entry?.bridge);
|
||||
return toObject(bridge?.pilot);
|
||||
}
|
||||
function readCurrentDiscoveryCounterparty(debug) {
|
||||
const pilot = readDiscoveryPilot(debug);
|
||||
const entityResolution = toObject(pilot?.derived_entity_resolution);
|
||||
const bidirectionalValueFlow = toObject(pilot?.derived_bidirectional_value_flow);
|
||||
const valueFlow = toObject(pilot?.derived_value_flow);
|
||||
return (toNonEmptyString(entityResolution?.resolved_entity) ??
|
||||
toNonEmptyString(bidirectionalValueFlow?.counterparty) ??
|
||||
toNonEmptyString(valueFlow?.counterparty) ??
|
||||
(0, assistantContinuityPolicy_1.readAddressDebugCounterparty)(debug, toNonEmptyString) ??
|
||||
readNavigationDiscoveryCounterparty(debug));
|
||||
}
|
||||
function isCounterpartyDiscoveryChain(value) {
|
||||
return (value === "entity_resolution" ||
|
||||
value === "value_flow" ||
|
||||
value === "value_flow_comparison" ||
|
||||
value === "document_evidence" ||
|
||||
value === "movement_evidence" ||
|
||||
value === "lifecycle");
|
||||
}
|
||||
function intentFromDiscoveryChain(value) {
|
||||
if (value === "business_overview") {
|
||||
return "business_overview";
|
||||
}
|
||||
if (value === "value_flow" || value === "value_flow_comparison") {
|
||||
return "customer_revenue_and_payments";
|
||||
}
|
||||
if (value === "document_evidence") {
|
||||
return "list_documents_by_counterparty";
|
||||
}
|
||||
if (value === "movement_evidence") {
|
||||
return "bank_operations_by_counterparty";
|
||||
}
|
||||
if (value === "lifecycle") {
|
||||
return "counterparty_activity_lifecycle";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
function toAddressFocusObjectType(value) {
|
||||
const normalized = toNonEmptyString(value);
|
||||
if (!normalized) {
|
||||
@@ -364,8 +406,7 @@ function readBusinessOverviewComparisonProofBundles(debug) {
|
||||
const entryPoint = toObject(debug.assistant_mcp_discovery_entry_point_v1);
|
||||
const turnInput = toObject(entryPoint?.turn_input);
|
||||
const turnMeaning = toObject(turnInput?.turn_meaning_ref);
|
||||
const bridge = toObject(entryPoint?.bridge);
|
||||
const pilot = toObject(bridge?.pilot);
|
||||
const pilot = readDiscoveryPilot(debug);
|
||||
const counterpartyValueFlowBundle = cloneRecord(turnMeaning?.previous_counterparty_value_flow_bundle) ??
|
||||
cloneRecord(pilot?.derived_bidirectional_value_flow);
|
||||
const counterpartyDocumentBundle = cloneRecord(turnMeaning?.previous_counterparty_document_bundle);
|
||||
@@ -383,6 +424,18 @@ function readBusinessOverviewComparisonProofBundles(debug) {
|
||||
counterparty_document_bundle: counterpartyDocumentBundle
|
||||
};
|
||||
}
|
||||
function readCounterpartyValueFlowProofBundles(debug) {
|
||||
const pilot = readDiscoveryPilot(debug);
|
||||
const counterpartyValueFlowBundle = cloneRecord(pilot?.derived_bidirectional_value_flow);
|
||||
const counterparty = toNonEmptyString(counterpartyValueFlowBundle?.counterparty);
|
||||
if (!counterparty || !counterpartyValueFlowBundle) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
counterparty_value_flow_bundle: counterpartyValueFlowBundle,
|
||||
counterparty_document_bundle: null
|
||||
};
|
||||
}
|
||||
function buildFocusObjectFromDebug(debug, resultSetId, createdAt) {
|
||||
const extractedFilters = toObject(debug.extracted_filters) ?? {};
|
||||
const selectedDiscoveryChain = toNonEmptyString(debug.mcp_discovery_selected_chain_id);
|
||||
@@ -396,8 +449,8 @@ function buildFocusObjectFromDebug(debug, resultSetId, createdAt) {
|
||||
return buildFocusObject("organization", organization, resultSetId, createdAt);
|
||||
}
|
||||
}
|
||||
if (selectedDiscoveryChain === "value_flow_comparison" && debug.mcp_discovery_response_applied === true) {
|
||||
const counterparty = (0, assistantContinuityPolicy_1.readAddressDebugCounterparty)(debug, toNonEmptyString) ?? readNavigationDiscoveryCounterparty(debug);
|
||||
if (isCounterpartyDiscoveryChain(selectedDiscoveryChain) && debug.mcp_discovery_response_applied === true) {
|
||||
const counterparty = readCurrentDiscoveryCounterparty(debug);
|
||||
if (counterparty) {
|
||||
return buildFocusObject("counterparty", counterparty, resultSetId, createdAt);
|
||||
}
|
||||
@@ -587,14 +640,13 @@ function evolveAddressNavigationStateWithAssistantItem(state, item, turnIndex) {
|
||||
}
|
||||
const debug = item.debug;
|
||||
const selectedDiscoveryChain = toNonEmptyString(debug.mcp_discovery_selected_chain_id);
|
||||
const discoveryIntent = selectedDiscoveryChain === "business_overview"
|
||||
? "business_overview"
|
||||
: selectedDiscoveryChain === "value_flow_comparison"
|
||||
? "customer_revenue_and_payments"
|
||||
: "unknown";
|
||||
const detectedIntent = toNonEmptyString(debug.detected_intent);
|
||||
const intent = toAddressIntent(detectedIntent && detectedIntent !== "unknown" ? detectedIntent : discoveryIntent);
|
||||
const discoveryIntent = intentFromDiscoveryChain(selectedDiscoveryChain);
|
||||
const trackableDiscoveryTurn = debug.mcp_discovery_response_applied === true && Boolean(selectedDiscoveryChain);
|
||||
const shouldPreferDiscoveryIntent = trackableDiscoveryTurn &&
|
||||
discoveryIntent !== "unknown" &&
|
||||
selectedDiscoveryChain !== "business_overview";
|
||||
const intent = toAddressIntent(shouldPreferDiscoveryIntent ? discoveryIntent : detectedIntent && detectedIntent !== "unknown" ? detectedIntent : discoveryIntent);
|
||||
if (intent === "unknown" && !trackableDiscoveryTurn) {
|
||||
return state;
|
||||
}
|
||||
@@ -603,8 +655,8 @@ function evolveAddressNavigationStateWithAssistantItem(state, item, turnIndex) {
|
||||
const routeId = toNonEmptyString(debug.selected_recipe) ?? selectedDiscoveryChain;
|
||||
const filters = sanitizeBusinessOverviewNavigationFilters(normalizeFilters(debug.extracted_filters), debug);
|
||||
const derivedOrganizationScope = resolveDerivedOrganizationScope(debug, filters, item.text) ?? (0, assistantContinuityPolicy_1.readAddressDebugOrganization)(debug, toNonEmptyString);
|
||||
const derivedCounterpartyScope = selectedDiscoveryChain === "value_flow_comparison" && debug.mcp_discovery_response_applied === true
|
||||
? (0, assistantContinuityPolicy_1.readAddressDebugCounterparty)(debug, toNonEmptyString) ?? readNavigationDiscoveryCounterparty(debug)
|
||||
const derivedCounterpartyScope = isCounterpartyDiscoveryChain(selectedDiscoveryChain) && debug.mcp_discovery_response_applied === true
|
||||
? readCurrentDiscoveryCounterparty(debug)
|
||||
: null;
|
||||
const filtersWithDerivedScope = derivedOrganizationScope && !toNonEmptyString(filters.organization)
|
||||
? {
|
||||
@@ -637,8 +689,12 @@ function evolveAddressNavigationStateWithAssistantItem(state, item, turnIndex) {
|
||||
const debugFocusObject = buildFocusObjectFromDebug(debug, resultSetId, createdAt);
|
||||
const primaryEntityFocusObject = buildFocusObjectFromPrimaryEntityRef(resultSet, createdAt);
|
||||
const focusObject = resolveFocusObjectForNavigation(state, intent, resultSet, debugFocusObject, primaryEntityFocusObject);
|
||||
const rawComparisonCounterparty = selectedDiscoveryChain === "business_overview" && debug.mcp_discovery_response_applied === true
|
||||
? readNavigationDiscoveryCounterparty(debug)
|
||||
const rawComparisonCounterparty = debug.mcp_discovery_response_applied === true
|
||||
? selectedDiscoveryChain === "business_overview"
|
||||
? readNavigationDiscoveryCounterparty(debug)
|
||||
: isCounterpartyDiscoveryChain(selectedDiscoveryChain)
|
||||
? readCurrentDiscoveryCounterparty(debug)
|
||||
: null
|
||||
: null;
|
||||
const comparisonCounterparty = rawComparisonCounterparty && !shouldSuppressBusinessOverviewCounterpartyFocus(debug, rawComparisonCounterparty)
|
||||
? rawComparisonCounterparty
|
||||
@@ -648,7 +704,9 @@ function evolveAddressNavigationStateWithAssistantItem(state, item, turnIndex) {
|
||||
: null;
|
||||
const currentComparisonProofBundles = selectedDiscoveryChain === "business_overview" && debug.mcp_discovery_response_applied === true
|
||||
? readBusinessOverviewComparisonProofBundles(debug)
|
||||
: null;
|
||||
: selectedDiscoveryChain === "value_flow_comparison" && debug.mcp_discovery_response_applied === true
|
||||
? readCounterpartyValueFlowProofBundles(debug)
|
||||
: null;
|
||||
const inheritedComparisonScope = state.session_context.comparison_scope;
|
||||
const inheritedComparisonProofBundles = comparisonCounterparty &&
|
||||
sameBusinessLabel(inheritedComparisonScope?.counterparty?.label, comparisonCounterparty)
|
||||
|
||||
@@ -1313,6 +1313,15 @@ function mergeFollowupFilters(current, intent, userMessage, followupContext) {
|
||||
intent === "vat_payable_confirmed_as_of_date";
|
||||
const currentHasPeriod = hasExplicitPeriodWindow(merged);
|
||||
const previousHasPeriod = hasExplicitPeriodWindow(previous);
|
||||
const currentPeriodFromForCarryover = toNonEmptyString(merged.period_from);
|
||||
const currentPeriodToForCarryover = toNonEmptyString(merged.period_to);
|
||||
const todayIsoForPeriodCarryover = new Date().toISOString().slice(0, 10);
|
||||
const currentValueFlowPeriodDefaultsToToday = isValueCounterpartyIntent(intent) &&
|
||||
hasFollowupSignal &&
|
||||
!hasExplicitPeriodInMessage &&
|
||||
!hasExplicitCurrentDateInMessage &&
|
||||
!currentPeriodFromForCarryover &&
|
||||
currentPeriodToForCarryover === todayIsoForPeriodCarryover;
|
||||
const currentCounterpartyExplicit = toNonEmptyString(merged.counterparty);
|
||||
const currentContractExplicit = toNonEmptyString(merged.contract);
|
||||
const currentItemExplicit = toNonEmptyString(merged.item);
|
||||
@@ -1387,7 +1396,7 @@ function mergeFollowupFilters(current, intent, userMessage, followupContext) {
|
||||
}
|
||||
reasons.push("period_from_followup_context");
|
||||
}
|
||||
if (!currentHasPeriod &&
|
||||
if ((!currentHasPeriod || currentValueFlowPeriodDefaultsToToday) &&
|
||||
previousHasPeriod &&
|
||||
hasFollowupSignal &&
|
||||
!hasExplicitPeriodInMessage &&
|
||||
|
||||
@@ -778,7 +778,11 @@ function resolveFollowupTargetIntent(inventoryPurchaseDateVatBridge, selectedObj
|
||||
}
|
||||
function shouldUseNavigationTemporalCarryover(sourceIntentHint) {
|
||||
const normalizedIntent = fallbackToNonEmptyString(sourceIntentHint);
|
||||
return (normalizedIntent === "inventory_on_hand_as_of_date" ||
|
||||
return (normalizedIntent === "customer_revenue_and_payments" ||
|
||||
normalizedIntent === "supplier_payouts_profile" ||
|
||||
normalizedIntent === "contract_usage_and_value" ||
|
||||
normalizedIntent === "counterparty_activity_lifecycle" ||
|
||||
normalizedIntent === "inventory_on_hand_as_of_date" ||
|
||||
normalizedIntent === "inventory_supplier_stock_overlap_as_of_date" ||
|
||||
normalizedIntent === "inventory_purchase_provenance_for_item" ||
|
||||
normalizedIntent === "inventory_purchase_documents_for_item" ||
|
||||
|
||||
+28
-13
@@ -212,7 +212,7 @@ function valueFlowDirectionLabelRu(pilot) {
|
||||
? "исходящих платежей/списаний"
|
||||
: "входящих денежных поступлений";
|
||||
}
|
||||
function valueFlowZeroResultConfirmedLine(pilot) {
|
||||
function valueFlowZeroResultInsufficiencyLine(pilot) {
|
||||
if (!hasExecutedZeroValueFlowRows(pilot)) {
|
||||
return null;
|
||||
}
|
||||
@@ -224,7 +224,22 @@ function valueFlowZeroResultConfirmedLine(pilot) {
|
||||
const period = explicitDateScope(pilot);
|
||||
const organizationPart = organization ? ` по организации ${organization}` : "";
|
||||
const periodPart = period ? ` за период ${period}` : " в проверенном окне";
|
||||
return `В проверенном срезе 1С по контрагенту ${counterparty}${organizationPart}${periodPart}: 0 руб.; ${valueFlowDirectionLabelRu(pilot)} не найдено.`;
|
||||
const direction = valueFlowDirectionLabelRu(pilot);
|
||||
return `Точную сумму ${direction} по контрагенту ${counterparty}${organizationPart}${periodPart} не подтверждаю: в выполненной проверке 1С строки ${direction} не найдены; это не доказывает отсутствие операций вне доступного банковского контура.`;
|
||||
}
|
||||
function valueFlowZeroResultCheckedLine(pilot) {
|
||||
if (!hasExecutedZeroValueFlowRows(pilot)) {
|
||||
return null;
|
||||
}
|
||||
const counterparty = firstEntityCandidate(pilot);
|
||||
if (!counterparty) {
|
||||
return null;
|
||||
}
|
||||
const organization = explicitOrganizationScope(pilot);
|
||||
const period = explicitDateScope(pilot);
|
||||
const organizationPart = organization ? ` по организации ${organization}` : "";
|
||||
const periodPart = period ? ` за период ${period}` : " в проверенном окне";
|
||||
return `В проверенном срезе 1С по контрагенту ${counterparty}${organizationPart}${periodPart} строки ${valueFlowDirectionLabelRu(pilot)} не найдено.`;
|
||||
}
|
||||
function valueFlowZeroResultUnknownLine(pilot) {
|
||||
if (!hasExecutedZeroValueFlowRows(pilot)) {
|
||||
@@ -239,11 +254,11 @@ function valueFlowZeroResultUnknownLine(pilot) {
|
||||
return `Это не доказывает отсутствие операций с контрагентом ${counterparty}${periodPart} или вне доступного банковского контура.`;
|
||||
}
|
||||
function valueFlowZeroResultHeadline(pilot) {
|
||||
const confirmedLine = valueFlowZeroResultConfirmedLine(pilot);
|
||||
if (!confirmedLine) {
|
||||
const insufficiencyLine = valueFlowZeroResultInsufficiencyLine(pilot);
|
||||
if (!insufficiencyLine) {
|
||||
return null;
|
||||
}
|
||||
return confirmedLine;
|
||||
return insufficiencyLine;
|
||||
}
|
||||
function hasAllTimeScope(pilot) {
|
||||
return (dryRunHasAxis(pilot, "all_time_scope") ||
|
||||
@@ -1846,20 +1861,20 @@ function buildAssistantMcpDiscoveryAnswerDraft(pilot) {
|
||||
? [derivedValueLine]
|
||||
: derivedValueLine
|
||||
? [derivedValueLine, ...monthlyConfirmedLines]
|
||||
: valueFlowZeroResultConfirmedLine(pilot)
|
||||
? [valueFlowZeroResultConfirmedLine(pilot)]
|
||||
: derivedEntityResolutionLine
|
||||
? [...pilot.evidence.confirmed_facts, derivedEntityResolutionLine]
|
||||
: derivedMetadataLine
|
||||
? [derivedMetadataLine]
|
||||
: pilot.evidence.confirmed_facts;
|
||||
: derivedEntityResolutionLine
|
||||
? [...pilot.evidence.confirmed_facts, derivedEntityResolutionLine]
|
||||
: derivedMetadataLine
|
||||
? [derivedMetadataLine]
|
||||
: pilot.evidence.confirmed_facts;
|
||||
const unknownLines = pilot.derived_business_overview
|
||||
? businessOverviewUnknownLines(pilot)
|
||||
: pilot.derived_metadata_surface
|
||||
? pilot.derived_metadata_surface.available_fields.length > 0
|
||||
? userFacingUnknowns(pilot.evidence.unknown_facts)
|
||||
: ["Детальный список полей этих объектов этим шагом не получен."]
|
||||
: appendValueFlowZeroResultUnknown(rankedValueFlowUnknownLines(pilot), pilot);
|
||||
: appendValueFlowZeroResultUnknown(valueFlowZeroResultCheckedLine(pilot)
|
||||
? [valueFlowZeroResultCheckedLine(pilot), ...rankedValueFlowUnknownLines(pilot)]
|
||||
: rankedValueFlowUnknownLines(pilot), pilot);
|
||||
return {
|
||||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_ANSWER_DRAFT_SCHEMA_VERSION,
|
||||
policy_owner: "assistantMcpDiscoveryAnswerAdapter",
|
||||
|
||||
+41
-2
@@ -2209,9 +2209,28 @@ function deriveBusinessOverviewContractUsageProfile(result, periodScope) {
|
||||
};
|
||||
}
|
||||
function deriveValueFlow(result, counterparty, periodScope, direction, aggregationAxis) {
|
||||
if (!result || result.error || result.matched_rows <= 0) {
|
||||
if (!result || result.error) {
|
||||
return null;
|
||||
}
|
||||
if (result.matched_rows <= 0) {
|
||||
return {
|
||||
value_flow_direction: direction,
|
||||
counterparty,
|
||||
period_scope: periodScope,
|
||||
aggregation_axis: aggregationAxis,
|
||||
rows_matched: 0,
|
||||
rows_with_amount: 0,
|
||||
total_amount: 0,
|
||||
total_amount_human_ru: formatAmountHumanRu(0),
|
||||
first_movement_date: null,
|
||||
latest_movement_date: null,
|
||||
coverage_limited_by_probe_limit: result.coverage_limited_by_probe_limit,
|
||||
coverage_recovered_by_period_chunking: result.coverage_recovered_by_period_chunking,
|
||||
period_chunking_granularity: result.period_chunking_granularity,
|
||||
monthly_breakdown: [],
|
||||
inference_basis: "sum_of_confirmed_1c_value_flow_rows"
|
||||
};
|
||||
}
|
||||
let totalAmount = 0;
|
||||
let rowsWithAmount = 0;
|
||||
for (const row of result.rows) {
|
||||
@@ -3926,16 +3945,30 @@ function buildMovementConfirmedFacts(result, counterparty, periodScope) {
|
||||
return [`В 1С найдены строки движений${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)}.`];
|
||||
}
|
||||
function buildValueFlowConfirmedFacts(result, counterparty, direction) {
|
||||
if (result.error || result.matched_rows <= 0) {
|
||||
if (result.error) {
|
||||
return [];
|
||||
}
|
||||
if (direction === "outgoing_supplier_payout") {
|
||||
if (result.matched_rows <= 0) {
|
||||
return [
|
||||
counterparty
|
||||
? `1C supplier-payout rows were checked for counterparty ${counterparty}: not_found`
|
||||
: "1C supplier-payout rows were checked for the requested counterparty scope: not_found"
|
||||
];
|
||||
}
|
||||
return [
|
||||
counterparty
|
||||
? `1C supplier-payout rows were found for counterparty ${counterparty}`
|
||||
: "1C supplier-payout rows were found for the requested counterparty scope"
|
||||
];
|
||||
}
|
||||
if (result.matched_rows <= 0) {
|
||||
return [
|
||||
counterparty
|
||||
? `1C value-flow rows were checked for counterparty ${counterparty}: not_found`
|
||||
: "1C value-flow rows were checked for the requested counterparty scope: not_found"
|
||||
];
|
||||
}
|
||||
return [
|
||||
counterparty
|
||||
? `1C value-flow rows were found for counterparty ${counterparty}`
|
||||
@@ -4115,6 +4148,12 @@ function buildValueFlowInferredFacts(derived) {
|
||||
return [];
|
||||
}
|
||||
const facts = [];
|
||||
if (derived.rows_matched <= 0) {
|
||||
facts.push(derived.value_flow_direction === "outgoing_supplier_payout"
|
||||
? "Counterparty supplier-payout not_found result was treated as zero only inside the scoped checked 1C result set"
|
||||
: "Counterparty incoming value-flow not_found result was treated as zero only inside the scoped checked 1C result set");
|
||||
return facts;
|
||||
}
|
||||
if (derived.value_flow_direction === "outgoing_supplier_payout") {
|
||||
facts.push("Counterparty supplier-payout total was calculated from confirmed 1C outgoing payment rows");
|
||||
}
|
||||
|
||||
@@ -261,13 +261,14 @@ function resolveAssistantMcpDiscoveryEvidence(input) {
|
||||
const rowsMatched = probeRowsMatched(probeResults);
|
||||
const rowsReceived = probeRowsReceived(probeResults);
|
||||
const bypassDetected = hasProbeBypass(input.plan, probeResults);
|
||||
const confirmedNoMatchFact = confirmedFacts.some((fact) => /\bnot_found\b/iu.test(String(fact ?? "")));
|
||||
if (bypassDetected) {
|
||||
pushReason(reasonCodes, "probe_result_used_primitive_outside_runtime_plan");
|
||||
}
|
||||
if (input.plan.plan_status !== "allowed") {
|
||||
pushReason(reasonCodes, "plan_not_allowed_by_runtime");
|
||||
}
|
||||
if (confirmedFacts.length > 0 && rowsMatched <= 0) {
|
||||
if (confirmedFacts.length > 0 && rowsMatched <= 0 && !confirmedNoMatchFact) {
|
||||
pushReason(reasonCodes, "confirmed_facts_without_matched_probe_rows");
|
||||
}
|
||||
if (!sourceRowsSummary && rowsReceived > 0) {
|
||||
@@ -287,6 +288,12 @@ function resolveAssistantMcpDiscoveryEvidence(input) {
|
||||
answerPermission = "confirmed_answer";
|
||||
pushReason(reasonCodes, "confirmed_facts_with_allowed_mcp_evidence");
|
||||
}
|
||||
else if (confirmedFacts.length > 0 && confirmedNoMatchFact && rowsMatched <= 0 && sourceRowsSummary) {
|
||||
evidenceStatus = "confirmed";
|
||||
coverageStatus = "full";
|
||||
answerPermission = "confirmed_answer";
|
||||
pushReason(reasonCodes, "confirmed_no_match_fact_with_allowed_mcp_evidence");
|
||||
}
|
||||
else if (inferredFacts.length > 0 && rowsReceived > 0) {
|
||||
evidenceStatus = "inferred_only";
|
||||
coverageStatus = "partial";
|
||||
|
||||
+22
-2
@@ -211,9 +211,15 @@ function localizeLine(value) {
|
||||
if (/^1C value-flow rows were found for the requested counterparty scope$/i.test(value)) {
|
||||
return "В 1С найдены строки входящих денежных поступлений в запрошенном срезе.";
|
||||
}
|
||||
if (/^1C value-flow rows were checked for the requested counterparty scope: not_found$/i.test(value)) {
|
||||
return "В 1С проверены входящие денежные строки в запрошенном срезе: строки не найдены.";
|
||||
}
|
||||
if (/^1C supplier-payout rows were found for the requested counterparty scope$/i.test(value)) {
|
||||
return "В 1С найдены строки исходящих платежей и списаний в запрошенном срезе.";
|
||||
}
|
||||
if (/^1C supplier-payout rows were checked for the requested counterparty scope: not_found$/i.test(value)) {
|
||||
return "В 1С проверены исходящие платежи/списания в запрошенном срезе: строки не найдены.";
|
||||
}
|
||||
const openScopeBidirectionalMatch = value.match(/^1C bidirectional value-flow rows were checked for the requested counterparty scope: incoming=(found|not_found), outgoing=(found|not_found)$/i);
|
||||
if (openScopeBidirectionalMatch) {
|
||||
const incoming = openScopeBidirectionalMatch[1] === "found"
|
||||
@@ -238,6 +244,10 @@ function localizeLine(value) {
|
||||
if (valueFlowMatch) {
|
||||
return `В 1С найдены строки входящих денежных поступлений по контрагенту ${valueFlowMatch[1]}.`;
|
||||
}
|
||||
const valueFlowNotFoundMatch = value.match(/^1C value-flow rows were checked for counterparty\s+(.+): not_found$/i);
|
||||
if (valueFlowNotFoundMatch) {
|
||||
return `В 1С проверены входящие денежные строки по контрагенту ${valueFlowNotFoundMatch[1]}: строки не найдены.`;
|
||||
}
|
||||
if (/^1C value-flow rows were found for the requested counterparty scope$/i.test(value)) {
|
||||
return "В 1С найдены строки входящих денежных поступлений по запрошенному контрагентскому контуру.";
|
||||
}
|
||||
@@ -259,6 +269,10 @@ function localizeLine(value) {
|
||||
if (supplierPayoutMatch) {
|
||||
return `В 1С найдены строки исходящих платежей/списаний по контрагенту ${supplierPayoutMatch[1]}.`;
|
||||
}
|
||||
const supplierPayoutNotFoundMatch = value.match(/^1C supplier-payout rows were checked for counterparty\s+(.+): not_found$/i);
|
||||
if (supplierPayoutNotFoundMatch) {
|
||||
return `В 1С проверены исходящие платежи/списания по контрагенту ${supplierPayoutNotFoundMatch[1]}: строки не найдены.`;
|
||||
}
|
||||
if (/^1C supplier-payout rows were found for the requested counterparty scope$/i.test(value)) {
|
||||
return "В 1С найдены строки исходящих платежей/списаний по запрошенному контрагентскому контуру.";
|
||||
}
|
||||
@@ -292,6 +306,12 @@ function localizeLine(value) {
|
||||
if (/^Counterparty supplier-payout total was calculated from confirmed 1C outgoing payment rows$/i.test(value)) {
|
||||
return "Сумма исходящих платежей рассчитана только по подтвержденным строкам списаний в 1С.";
|
||||
}
|
||||
if (/^Counterparty supplier-payout not_found result was treated as zero only inside the scoped checked 1C result set$/i.test(value)) {
|
||||
return "Нулевую сумму исходящих платежей можно трактовать только внутри этого проверенного среза 1С, где строки списаний не найдены.";
|
||||
}
|
||||
if (/^Counterparty incoming value-flow not_found result was treated as zero only inside the scoped checked 1C result set$/i.test(value)) {
|
||||
return "Нулевую сумму входящих поступлений можно трактовать только внутри этого проверенного среза 1С, где строки поступлений не найдены.";
|
||||
}
|
||||
if (/^Counterparty net value-flow was calculated as incoming confirmed 1C rows minus outgoing confirmed 1C rows$/i.test(value)) {
|
||||
return "Нетто денежного потока рассчитано как подтвержденные входящие платежи минус подтвержденные исходящие платежи в 1С.";
|
||||
}
|
||||
@@ -1159,7 +1179,7 @@ function buildCompactBusinessOverviewReply(entryPoint, draft) {
|
||||
outgoingAmount ? `исходящие ${outgoingAmount}` : null,
|
||||
netAmount ? `${netDirection} ${sentenceAmount(netAmount) ?? netAmount}` : null
|
||||
].filter((value) => Boolean(value));
|
||||
lines.push(`По подтвержденным строкам 1С ${subject} за ${periodWithoutPrefix}: ${executiveFacts.length > 0 ? executiveFacts.join(", ") : "денежные метрики не подтверждены"}; это ограниченный проверенный срез, не аудиторское заключение и не подтвержденная чистая прибыль.`);
|
||||
lines.push(`По подтвержденным строкам 1С ${subject} за ${periodWithoutPrefix}: ${executiveFacts.length > 0 ? executiveFacts.join(", ") : "денежные метрики не подтверждены"}; это ограниченный проверенный срез, а не финансовый аудит и не подтверждение чистой прибыли.`);
|
||||
lines.push("Интерпретация: по этому срезу видны крупные контрактные денежные потоки и заметная зависимость от нескольких крупных контрагентов, а не равномерный поток мелких продаж.");
|
||||
lines.push("Что видно в ограниченном денежном срезе:");
|
||||
if (incomingAmount) {
|
||||
@@ -1194,7 +1214,7 @@ function buildCompactBusinessOverviewReply(entryPoint, draft) {
|
||||
lines.push(`- ${localizeLine(debtLine)}`);
|
||||
}
|
||||
if (!limitLine) {
|
||||
lines.push("Ограничение: это оценка по денежным потокам и найденным срезам 1С, не аудиторское заключение и не подтвержденная чистая прибыль.");
|
||||
lines.push("Ограничение: это оценка по денежным потокам и найденным срезам 1С, а не финансовый аудит и не подтверждение чистой прибыли.");
|
||||
}
|
||||
const missingOverviewFamilies = [];
|
||||
if (!taxLine) {
|
||||
|
||||
+58
-5
@@ -615,6 +615,53 @@ function hasExactBankOperationsAddressReply(input, entryPoint) {
|
||||
routeMode === "exact" ||
|
||||
hasFullConfirmedTruth(input));
|
||||
}
|
||||
function extractExplicitDateScopeYear(value) {
|
||||
const source = toNonEmptyString(value);
|
||||
if (!source) {
|
||||
return null;
|
||||
}
|
||||
const match = source.match(/\b((?:19|20)\d{2})\b/u);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
function extractDocumentReplyDateYears(value) {
|
||||
const source = String(value ?? "");
|
||||
if (!source) {
|
||||
return [];
|
||||
}
|
||||
const years = new Set();
|
||||
for (const match of source.matchAll(/\b((?:19|20)\d{2})-\d{2}-\d{2}(?:T|\b)/gu)) {
|
||||
years.add(match[1]);
|
||||
}
|
||||
for (const match of source.matchAll(/\b\d{1,2}\.\d{1,2}\.((?:19|20)\d{2})\b/gu)) {
|
||||
years.add(match[1]);
|
||||
}
|
||||
return [...years];
|
||||
}
|
||||
function hasDocumentListTemporalConflictWithDiscovery(input, entryPoint) {
|
||||
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
|
||||
return false;
|
||||
}
|
||||
const detectedIntent = toNonEmptyString(input.addressRuntimeMeta?.detected_intent);
|
||||
const selectedRecipe = toNonEmptyString(input.addressRuntimeMeta?.selected_recipe);
|
||||
const isDocumentIntent = detectedIntent === "list_documents_by_counterparty" || detectedIntent === "list_documents_by_contract";
|
||||
const isDocumentRecipe = selectedRecipe === "address_documents_by_counterparty_v1" ||
|
||||
selectedRecipe === "address_documents_by_contract_v1";
|
||||
if (!isDocumentIntent || !isDocumentRecipe) {
|
||||
return false;
|
||||
}
|
||||
const turnMeaning = readDiscoveryTurnMeaning(entryPoint);
|
||||
const expectedYear = extractExplicitDateScopeYear(turnMeaning?.explicit_date_scope);
|
||||
if (!expectedYear) {
|
||||
return false;
|
||||
}
|
||||
const filters = toRecordObject(input.addressRuntimeMeta?.extracted_filters);
|
||||
const periodFrom = toNonEmptyString(filters?.period_from);
|
||||
const periodTo = toNonEmptyString(filters?.period_to);
|
||||
const hasMatchingPeriod = Boolean(periodFrom?.startsWith(`${expectedYear}-`)) && Boolean(periodTo?.startsWith(`${expectedYear}-`));
|
||||
const replyDateYears = extractDocumentReplyDateYears(input.currentReply);
|
||||
const hasOutOfScopeReplyDates = replyDateYears.some((year) => year !== expectedYear);
|
||||
return hasOutOfScopeReplyDates || (!hasMatchingPeriod && replyDateYears.length > 0);
|
||||
}
|
||||
function hasExactDocumentListAddressReply(input, entryPoint) {
|
||||
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
|
||||
return false;
|
||||
@@ -968,7 +1015,8 @@ function applyAssistantMcpDiscoveryResponsePolicy(input) {
|
||||
const staleMetadataDiscoveryFallbackAgainstExactAddressReply = hasStaleMetadataDiscoveryFallbackAgainstExactAddressReply(input, entryPoint);
|
||||
const exactValueFlowReplyForBusinessOverviewDirectMoneyNeed = hasExactValueFlowReplyForBusinessOverviewDirectMoneyNeed(input, entryPoint);
|
||||
const exactBankOperationsAddressReply = hasExactBankOperationsAddressReply(input, entryPoint);
|
||||
const exactDocumentListAddressReply = hasExactDocumentListAddressReply(input, entryPoint);
|
||||
const documentListTemporalConflictWithDiscovery = hasDocumentListTemporalConflictWithDiscovery(input, entryPoint);
|
||||
const exactDocumentListAddressReply = hasExactDocumentListAddressReply(input, entryPoint) && !documentListTemporalConflictWithDiscovery;
|
||||
const inventoryMarginRankingAddressReply = hasInventoryMarginRankingAddressReply(input, entryPoint);
|
||||
const exactInventoryPurchaseToSaleChainAddressReply = hasExactInventoryPurchaseToSaleChainAddressReply(input, entryPoint);
|
||||
const openScopeValueFlowDiscoveryPriority = hasOpenScopeValueFlowDiscoveryPriority(input, entryPoint);
|
||||
@@ -987,10 +1035,12 @@ function applyAssistantMcpDiscoveryResponsePolicy(input) {
|
||||
!groundedMovementCandidatePriority &&
|
||||
!businessOverviewBoundaryCandidatePriority &&
|
||||
!groundedValueFlowRankingCandidatePriority;
|
||||
const alignedFactualAddressReplyProtectsCurrent = alignedFactualAddressReply && !groundedValueFlowRankingCandidatePriority;
|
||||
const matchedFactualAddressContinuationTargetProtectsCurrent = matchedFactualAddressContinuationTarget && !groundedValueFlowRankingCandidatePriority;
|
||||
const fullConfirmedFactualAddressReplyProtectsCurrent = fullConfirmedFactualAddressReply && !groundedValueFlowRankingCandidatePriority;
|
||||
const exactMatchedFactualAddressReplyProtectsCurrent = exactMatchedFactualAddressReply && !groundedValueFlowRankingCandidatePriority;
|
||||
const alignedFactualAddressReplyProtectsCurrent = alignedFactualAddressReply && !groundedValueFlowRankingCandidatePriority && !documentListTemporalConflictWithDiscovery;
|
||||
const matchedFactualAddressContinuationTargetProtectsCurrent = matchedFactualAddressContinuationTarget &&
|
||||
!groundedValueFlowRankingCandidatePriority &&
|
||||
!documentListTemporalConflictWithDiscovery;
|
||||
const fullConfirmedFactualAddressReplyProtectsCurrent = fullConfirmedFactualAddressReply && !groundedValueFlowRankingCandidatePriority && !documentListTemporalConflictWithDiscovery;
|
||||
const exactMatchedFactualAddressReplyProtectsCurrent = exactMatchedFactualAddressReply && !groundedValueFlowRankingCandidatePriority && !documentListTemporalConflictWithDiscovery;
|
||||
const exactBankOperationsProtectsCurrent = exactBankOperationsAddressReply &&
|
||||
!semanticConflictWithDiscoveryTurnMeaning &&
|
||||
!valueFlowActionConflictWithDiscoveryTurnMeaning;
|
||||
@@ -1024,6 +1074,9 @@ function applyAssistantMcpDiscoveryResponsePolicy(input) {
|
||||
if (evidenceLaneConflictWithDiscoveryTurnMeaning) {
|
||||
pushReason(reasonCodes, "mcp_discovery_response_policy_evidence_lane_conflict_allows_candidate_override");
|
||||
}
|
||||
if (documentListTemporalConflictWithDiscovery) {
|
||||
pushReason(reasonCodes, "mcp_discovery_response_policy_document_list_temporal_conflict_allows_candidate_override");
|
||||
}
|
||||
if (currentClarificationProtectsCurrentReply) {
|
||||
pushReason(reasonCodes, "mcp_discovery_response_policy_keep_current_clarification_required_reply");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user