ARCH: стабилизировать continuity и защитить exact-ответы от discovery

This commit is contained in:
2026-04-21 18:43:05 +03:00
parent b542b65b81
commit 429bd3d8ec
8 changed files with 603 additions and 18 deletions
@@ -13,6 +13,7 @@ export type AssistantMcpDiscoveryResponsePolicyDecision = "apply_candidate" | "k
export interface ApplyAssistantMcpDiscoveryResponsePolicyInput {
currentReply: string;
currentReplySource?: string | null;
currentReplyType?: string | null;
livingChatSource?: string | null;
modeDecisionReason?: string | null;
addressRuntimeMeta?: Record<string, unknown> | null;
@@ -151,6 +152,69 @@ function isDiscoveryReadyAddressCandidate(
);
}
function hasAlignedFactualAddressReply(
input: ApplyAssistantMcpDiscoveryResponsePolicyInput,
entryPoint: AssistantMcpDiscoveryRuntimeEntryPointContract | null
): boolean {
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
return false;
}
if (toNonEmptyString(input.currentReplyType) !== "factual") {
return false;
}
const detectedIntent = toNonEmptyString(input.addressRuntimeMeta?.detected_intent);
const turnInput = toRecordObject(entryPoint?.turn_input);
const turnMeaning = toRecordObject(turnInput?.turn_meaning_ref);
const askedDomain = toNonEmptyString(turnMeaning?.asked_domain_family);
const askedAction = toNonEmptyString(turnMeaning?.asked_action_family);
if (detectedIntent === "counterparty_activity_lifecycle") {
return askedDomain === "counterparty_lifecycle" || askedAction === "activity_duration";
}
if (detectedIntent === "supplier_payouts_profile") {
return askedDomain === "counterparty_value" && askedAction === "payout";
}
if (detectedIntent === "customer_revenue_and_payments") {
return askedDomain === "counterparty_value" && askedAction === "turnover";
}
return false;
}
function hasMatchedFactualAddressContinuationTarget(
input: ApplyAssistantMcpDiscoveryResponsePolicyInput
): boolean {
if (toNonEmptyString(input.currentReplyType) !== "factual") {
return false;
}
const detectedIntent = toNonEmptyString(input.addressRuntimeMeta?.detected_intent);
const dialogContinuationContract = toRecordObject(input.addressRuntimeMeta?.dialogContinuationContract);
const targetIntent = toNonEmptyString(dialogContinuationContract?.target_intent);
return Boolean(detectedIntent && targetIntent && detectedIntent === targetIntent);
}
function hasFullConfirmedFactualAddressReply(
input: ApplyAssistantMcpDiscoveryResponsePolicyInput,
entryPoint: AssistantMcpDiscoveryRuntimeEntryPointContract | null
): boolean {
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
return false;
}
if (toNonEmptyString(input.currentReplyType) !== "factual") {
return false;
}
const truthGateStatus = toNonEmptyString(input.addressRuntimeMeta?.truth_gate_contract_status);
if (truthGateStatus === "full_confirmed") {
return true;
}
const truthAnswerPolicy = toRecordObject(input.addressRuntimeMeta?.assistant_truth_answer_policy_v1);
const truthGate = toRecordObject(truthAnswerPolicy?.truth_gate);
const sourceTruthGateStatus = toNonEmptyString(truthGate?.source_truth_gate_status);
const coverageStatus = toNonEmptyString(truthGate?.coverage_status);
const groundingStatus = toNonEmptyString(truthGate?.grounding_status);
return sourceTruthGateStatus === "full_confirmed" || (coverageStatus === "full" && groundingStatus === "grounded");
}
export function applyAssistantMcpDiscoveryResponsePolicy(
input: ApplyAssistantMcpDiscoveryResponsePolicyInput
): AssistantMcpDiscoveryResponsePolicyResult {
@@ -164,6 +228,9 @@ export function applyAssistantMcpDiscoveryResponsePolicy(
const discoveryReadyChatCandidate = isDiscoveryReadyChatCandidate(input, entryPoint);
const discoveryReadyDeepCandidate = isDiscoveryReadyDeepCandidate(input, entryPoint);
const discoveryReadyAddressCandidate = isDiscoveryReadyAddressCandidate(input, entryPoint);
const alignedFactualAddressReply = hasAlignedFactualAddressReply(input, entryPoint);
const matchedFactualAddressContinuationTarget = hasMatchedFactualAddressContinuationTarget(input);
const fullConfirmedFactualAddressReply = hasFullConfirmedFactualAddressReply(input, entryPoint);
if (!entryPoint) {
pushReason(reasonCodes, "mcp_discovery_response_policy_no_entry_point");
@@ -180,6 +247,15 @@ export function applyAssistantMcpDiscoveryResponsePolicy(
if (!discoveryReadyAddressCandidate) {
pushReason(reasonCodes, "mcp_discovery_response_policy_not_discovery_ready_address_candidate");
}
if (alignedFactualAddressReply) {
pushReason(reasonCodes, "mcp_discovery_response_policy_keep_aligned_factual_address_reply");
}
if (matchedFactualAddressContinuationTarget) {
pushReason(reasonCodes, "mcp_discovery_response_policy_keep_factual_address_continuation_target");
}
if (fullConfirmedFactualAddressReply) {
pushReason(reasonCodes, "mcp_discovery_response_policy_keep_full_confirmed_factual_address_reply");
}
if (!ALLOWED_CANDIDATE_STATUSES.has(candidate.candidate_status)) {
pushReason(reasonCodes, "mcp_discovery_response_policy_candidate_status_not_allowed");
}
@@ -196,6 +272,9 @@ export function applyAssistantMcpDiscoveryResponsePolicy(
const canApply =
Boolean(entryPoint) &&
(unsupportedBoundary || discoveryReadyChatCandidate || discoveryReadyDeepCandidate || discoveryReadyAddressCandidate) &&
!alignedFactualAddressReply &&
!matchedFactualAddressContinuationTarget &&
!fullConfirmedFactualAddressReply &&
ALLOWED_CANDIDATE_STATUSES.has(candidate.candidate_status) &&
candidate.eligible_for_future_hot_runtime &&
Boolean(toNonEmptyString(candidate.reply_text)) &&
@@ -5,7 +5,10 @@ const SUPPORTED_ADDRESS_INTENTS = new Set([
"payables_confirmed_as_of_date",
"list_documents_by_counterparty",
"customer_revenue_and_payments",
"inventory_on_hand_as_of_date"
"inventory_on_hand_as_of_date",
"vat_liability_confirmed_for_tax_period",
"vat_payable_confirmed_as_of_date",
"vat_payable_forecast"
]);
function fallbackCompactWhitespace(value) {
@@ -89,8 +92,23 @@ function detectCounterpartyTurnoverFamily(text) {
"\u0434\u043e\u0445\u043e\u0434",
"\u0431\u044b\u043b",
"\u0431\u044b\u043b\u0430",
"\u0432\u0440\u0435\u043c\u044f",
"\u0432\u0440\u0435\u043c\u0435\u043d\u0438",
"\u0433\u043e\u0434",
"\u0433\u043e\u0434\u0430",
"\u043f\u0435\u0440\u0438\u043e\u0434",
"\u043f\u0435\u0440\u0438\u043e\u0434\u0430",
"\u043c\u0435\u0441\u044f\u0446",
"\u043c\u0435\u0441\u044f\u0446\u0430",
"\u043a\u0432\u0430\u0440\u0442\u0430\u043b",
"\u043a\u0432\u0430\u0440\u0442\u0430\u043b\u0430",
"turnover",
"revenue"
"revenue",
"time",
"year",
"period",
"month",
"quarter"
]);
const entity = rawEntity && !ignored.has(rawEntity) ? rawEntity : null;
return {
@@ -146,6 +164,8 @@ export function createAssistantTurnMeaningPolicy(deps = {}) {
? "receivables"
: explicitIntentCandidate?.startsWith("payables_")
? "payables"
: explicitIntentCandidate?.startsWith("vat_")
? "vat"
: explicitIntentCandidate?.startsWith("inventory_")
? "inventory"
: explicitIntentCandidate?.includes("counterparty")
@@ -158,6 +178,12 @@ export function createAssistantTurnMeaningPolicy(deps = {}) {
explicitIntentCandidate === "payables_confirmed_as_of_date" ||
explicitIntentCandidate === "inventory_on_hand_as_of_date"
? "confirmed_snapshot"
: explicitIntentCandidate === "vat_liability_confirmed_for_tax_period"
? "confirmed_tax_period"
: explicitIntentCandidate === "vat_payable_confirmed_as_of_date"
? "confirmed_snapshot"
: explicitIntentCandidate === "vat_payable_forecast"
? "forecast"
: explicitIntentCandidate === "list_documents_by_counterparty"
? "list_documents"
: counterpartyTurnover?.family