Укрепить debt-boundary ответы MCP discovery

This commit is contained in:
2026-06-15 23:46:56 +03:00
parent ee98b04706
commit fa1731a5b4
14 changed files with 418 additions and 24 deletions
@@ -4317,11 +4317,10 @@ class AddressQueryService {
rowsMatched: expandedFilteredRows.length
});
const expandedFactual = (0, composeStage_1.composeFactualReply)(intent.intent, expandedFilteredRows, composeRuntimeOptions(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"];
return buildFactualExecutionResult({
replyText: `${expandedPrefix}\n${expandedFactual.text}`,
replyText: expandedFactual.text,
responseType: expandedFactual.responseType,
responseSemantics: expandedFactual.semantics,
selectedRecipe: expandedSelection.selected_recipe.recipe_id,
@@ -582,7 +582,8 @@ function runAssistantAddressLaneResponseRuntime(input) {
mcp_discovery_response_candidate_v1: mcpDiscoveryResponsePolicy.candidate,
mcp_discovery_response_applied: mcpDiscoveryResponsePolicy.applied,
comparison_scope_response_augmentation_v1: comparisonScopeProofReply?.audit ?? null,
...buildAppliedMcpDiscoveryRoutePatch(debugWithResponseGuard, mcpDiscoveryResponsePolicy.applied)
...buildAppliedMcpDiscoveryRoutePatch(debugWithResponseGuard, mcpDiscoveryResponsePolicy.applied),
...(0, assistantMcpDiscoveryResponsePolicy_1.buildAppliedMcpDiscoveryTruthPatch)(mcpDiscoveryResponsePolicy, debugWithResponseGuard)
};
const finalization = finalizeAddressTurnSafe({
sessionId: input.sessionId,
@@ -142,7 +142,8 @@ function runAssistantDeepTurnResponseRuntime(input) {
...packagingRuntime.debug,
mcp_discovery_response_policy_v1: mcpDiscoveryResponsePolicy,
mcp_discovery_response_candidate_v1: mcpDiscoveryResponsePolicy.candidate,
mcp_discovery_response_applied: mcpDiscoveryResponsePolicy.applied
mcp_discovery_response_applied: mcpDiscoveryResponsePolicy.applied,
...(0, assistantMcpDiscoveryResponsePolicy_1.buildAppliedMcpDiscoveryTruthPatch)(mcpDiscoveryResponsePolicy, packagingRuntime.debug)
};
const assistantItem = mcpDiscoveryResponsePolicy.applied
? {
@@ -362,6 +362,7 @@ async function runAssistantLivingChatRuntime(input) {
mcp_discovery_response_policy_v1: mcpDiscoveryResponsePolicy,
mcp_discovery_response_candidate_v1: mcpDiscoveryResponsePolicy.candidate,
mcp_discovery_response_applied: mcpDiscoveryResponsePolicy.applied,
...(0, assistantMcpDiscoveryResponsePolicy_1.buildAppliedMcpDiscoveryTruthPatch)(mcpDiscoveryResponsePolicy, addressRuntimeMeta),
living_chat_script_guard_applied: livingChatScriptGuardApplied,
living_chat_script_guard_reason: livingChatScriptGuardReason,
living_chat_grounding_guard_applied: livingChatGroundingGuardApplied,
@@ -73,6 +73,18 @@ function requestsBusinessOverviewNextChecks(turnMeaning, graph) {
const text = normalizedTurnAndGraphText(turnMeaning, graph);
return /(?:что\s+(?:дальше\s+)?провер\w*|что\s+провер\w*\s+дальше|следующ\w*\s+шаг|в\s+первую\s+очередь\s+провер\w*|next\s+checks?|next\s+steps?)/iu.test(text);
}
function requestsShortDebtBoundaryFollowup(turnMeaning, graph) {
const text = normalizeQuestionText([
turnMeaning?.raw_message,
turnMeaning?.effective_message,
graph?.source_message,
graph?.question
].join(" "));
if (!/\u043f\u0440\u043e\u0441\u0440\u043e\u0447/iu.test(text)) {
return false;
}
return /(?:\u043a\u043e\u0440\u043e\u0442\u043a|\u043f\u043e\u0447\u0435\u043c\u0443|\u0434\u043e\u043a\u0430\u0437\p{L}*)/iu.test(text);
}
function requestsCompactCashflowAnswer(turnMeaning, graph) {
const text = normalizeQuestionText([
turnMeaning?.raw_message,
@@ -902,6 +914,7 @@ function buildCompactBusinessOverviewReply(entryPoint, draft) {
const debtDueDateBoundary = actionFamily === "debt_due_date_boundary" || unsupportedFamily === "debt_due_date_boundary";
const vendorRiskBoundary = actionFamily === "vendor_risk_procurement_boundary" || unsupportedFamily === "vendor_risk_procurement_boundary";
const inventoryReserveBoundary = actionFamily === "inventory_reserve_boundary" || unsupportedFamily === "inventory_reserve_liquidation_boundary";
const shortDebtBoundaryFollowup = debtDueDateBoundary && requestsShortDebtBoundaryFollowup(turnMeaning, graph);
const compactCashflowRequested = directMoneyOnlyAnswer && requestsCompactCashflowAnswer(turnMeaning, graph);
const cashflowPolarityRequested = compactCashflowRequested && requestsCashflowPolarityAnswer(turnMeaning, graph);
const directAccountingProfitRequested = requestsDirectAccountingProfitAnswer(turnMeaning, graph);
@@ -1035,19 +1048,20 @@ function buildCompactBusinessOverviewReply(entryPoint, draft) {
const rowsWithPaymentTerms = typeof dueDateAging.rows_with_payment_terms === "number" && Number.isFinite(dueDateAging.rows_with_payment_terms)
? dueDateAging.rows_with_payment_terms
: null;
const rowsWithAmount = typeof dueDateAging.rows_with_amount === "number" && Number.isFinite(dueDateAging.rows_with_amount)
? dueDateAging.rows_with_amount
: null;
const dueDateScopePrefix = organizationScope ? `по компании ${organizationScope} ` : "";
if (status === "confirmed_overdue") {
lines.push(`Коротко: ${dueDateScopePrefix}на ${asOfDate} подтвержденная просрочка есть: ${overdueAmount ?? "сумма не распознана"} по ${dueDateAging.overdue_rows ?? "найденным"} строкам.`);
lines.push("Основа ответа: открытые расчеты 60/62/76, договорный срок оплаты и дата расчетного документа; это проверка просрочки по срокам оплаты, а не просто возраст договора.");
}
else if (status === "no_payment_terms_configured") {
if (shortDebtBoundaryFollowup) {
lines.push(`Коротко: да, доказать просрочку ${dueDateScopePrefix}на ${asOfDate} нельзя, потому что в подтвержденных данных не установлен срок оплаты.`);
lines.push(`Открытый долг${grossAmount ? ` на ${grossAmount}` : ""} виден, но без due date это задолженность без доказанной договорной просрочки.`);
return joinBusinessReplyLines(lines);
}
lines.push(`Коротко: ${dueDateScopePrefix}на ${asOfDate} подтвержденной просрочки нет: открытые расчеты проверены${grossAmount ? ` на ${grossAmount}` : ""}, но в найденных договорах срок оплаты не установлен.`);
lines.push(rowsWithAmount !== null
? `Проверено строк с суммой: ${rowsWithAmount}. Без установленного срока оплаты нельзя честно назвать эти остатки просрочкой.`
: "Без установленного срока оплаты нельзя честно назвать эти остатки просрочкой.");
lines.push("Без установленного срока оплаты нельзя честно назвать эти остатки просрочкой.");
lines.push("Что проверить дальше: сроки оплаты и даты документов-оснований в договорах/расчетах; без них просрочку доказать нельзя.");
}
else if (status === "insufficient_due_date_basis") {
lines.push(`Коротко: ${dueDateScopePrefix}на ${asOfDate} просрочка не подтверждена: по строкам с установленным сроком оплаты не хватило даты расчетного документа.`);
@@ -1,9 +1,111 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ASSISTANT_MCP_DISCOVERY_RESPONSE_POLICY_SCHEMA_VERSION = void 0;
exports.buildAppliedMcpDiscoveryTruthPatch = buildAppliedMcpDiscoveryTruthPatch;
exports.applyAssistantMcpDiscoveryResponsePolicy = applyAssistantMcpDiscoveryResponsePolicy;
const assistantMcpDiscoveryResponseCandidate_1 = require("./assistantMcpDiscoveryResponseCandidate");
const assistantRuntimeContracts_1 = require("../types/assistantRuntimeContracts");
exports.ASSISTANT_MCP_DISCOVERY_RESPONSE_POLICY_SCHEMA_VERSION = "assistant_mcp_discovery_response_policy_v1";
function uniqueReasonCodes(values) {
const result = [];
values.forEach((value) => {
const text = toNonEmptyString(value);
if (!text) {
return;
}
const normalized = normalizeReasonCode(text);
if (normalized && !result.includes(normalized)) {
result.push(normalized);
}
});
return result;
}
function patchCoverageGateState(value, gate) {
const source = toRecordObject(value);
if (!source) {
return value;
}
const currentGate = toRecordObject(source.coverage_gate_state);
return {
...source,
coverage_gate_state: {
...(currentGate ?? {}),
coverage_status: gate.coverage_status,
evidence_grade: gate.evidence_grade,
grounding_status: gate.grounding_status,
truth_mode: gate.truth_mode,
carryover_eligibility: gate.carryover_eligibility,
reason_codes: gate.reason_codes
}
};
}
function buildAppliedMcpDiscoveryTruthPatch(policy, currentDebug) {
if (!policy.applied || !ALLOWED_CANDIDATE_STATUSES.has(policy.candidate.candidate_status)) {
return {};
}
const reasonCodes = uniqueReasonCodes([
...policy.reason_codes,
"mcp_discovery_response_policy_candidate_applied",
"mcp_discovery_response_candidate_guarded_truth_patch"
]);
const truthGate = {
schema_version: assistantRuntimeContracts_1.ASSISTANT_TRUTH_ANSWER_POLICY_RUNTIME_SCHEMA_VERSION,
policy_owner: "assistantTruthAnswerPolicyRuntimeAdapter",
coverage_status: "partial",
evidence_grade: "medium",
grounding_status: "partial",
truth_mode: "limited",
carryover_eligibility: "root_only",
reason_codes: reasonCodes,
source_truth_gate_status: "partial_supported",
blocked_or_limited_explanation: "mcp_discovery_guarded_bounded_answer"
};
const answerShape = {
schema_version: assistantRuntimeContracts_1.ASSISTANT_TRUTH_ANSWER_POLICY_RUNTIME_SCHEMA_VERSION,
policy_owner: "assistantTruthAnswerPolicyRuntimeAdapter",
answer_shape: "limited_with_reason",
reply_type: "partial_coverage",
capability_contract_id: null,
transition_contract_id: null,
may_state_confirmed_facts: true,
must_include_limitation: true,
may_power_followup: true,
required_sections: ["direct_answer", "evidence_window", "limitations"],
downgrade_only: true
};
const truthPolicy = {
schema_version: assistantRuntimeContracts_1.ASSISTANT_TRUTH_ANSWER_POLICY_RUNTIME_SCHEMA_VERSION,
policy_owner: "assistantTruthAnswerPolicyRuntimeAdapter",
truth_gate: truthGate,
answer_shape: answerShape
};
const debug = currentDebug ?? {};
const patch = {
assistant_truth_answer_policy_v1: truthPolicy,
coverage_gate_contract: truthGate,
answer_shape_contract: answerShape,
truth_gate_contract_status: "partial_supported",
truth_mode: "limited",
carryover_eligibility: "root_only",
answer_shape: "limited_with_reason"
};
const stateTransition = patchCoverageGateState(debug.assistant_state_transition_v1, truthGate);
if (stateTransition !== debug.assistant_state_transition_v1) {
patch.assistant_state_transition_v1 = stateTransition;
}
const stateTransitionContract = patchCoverageGateState(debug.state_transition_contract, truthGate);
if (stateTransitionContract !== debug.state_transition_contract) {
patch.state_transition_contract = stateTransitionContract;
}
const runtimeContract = toRecordObject(debug.assistant_runtime_contract_v1);
if (runtimeContract) {
patch.assistant_runtime_contract_v1 = {
...runtimeContract,
truth_gate_contract_status: "partial_supported"
};
}
return patch;
}
const ALLOWED_CANDIDATE_STATUSES = new Set([
"ready_for_guarded_use",
"checked_sources_only_candidate",