Закрепить phase83 carryover и движения ассистента
This commit is contained in:
parent
5ed25bad81
commit
4f078d649e
|
|
@ -149,6 +149,7 @@
|
||||||
"expected_catalog_selected_matches_top": true,
|
"expected_catalog_selected_matches_top": true,
|
||||||
"required_answer_patterns_all": [
|
"required_answer_patterns_all": [
|
||||||
"(?i)2020",
|
"(?i)2020",
|
||||||
|
"12[ .\\u00a0]?093[ .\\u00a0]?465|12093465",
|
||||||
"(?i)нетто|сальдо",
|
"(?i)нетто|сальдо",
|
||||||
"(?i)руб"
|
"(?i)руб"
|
||||||
],
|
],
|
||||||
|
|
@ -161,7 +162,12 @@
|
||||||
"forbidden_answer_patterns": [
|
"forbidden_answer_patterns": [
|
||||||
"(?i)не найден контрагент",
|
"(?i)не найден контрагент",
|
||||||
"(?i)уточните, какого контрагента",
|
"(?i)уточните, какого контрагента",
|
||||||
"(?i)по какому контрагенту"
|
"(?i)по какому контрагенту",
|
||||||
|
"2026-06-03",
|
||||||
|
"2020-01-01\\.\\.2026-06-03",
|
||||||
|
"20[ .\\u00a0]?653[ .\\u00a0]?490|20653490",
|
||||||
|
"2[ .\\u00a0]?129[ .\\u00a0]?651|2129651",
|
||||||
|
"18[ .\\u00a0]?523[ .\\u00a0]?839|18523839"
|
||||||
],
|
],
|
||||||
"criticality": "critical",
|
"criticality": "critical",
|
||||||
"semantic_tags": [
|
"semantic_tags": [
|
||||||
|
|
@ -398,7 +404,14 @@
|
||||||
"expected_catalog_selected_matches_top": true,
|
"expected_catalog_selected_matches_top": true,
|
||||||
"required_answer_patterns_all": [
|
"required_answer_patterns_all": [
|
||||||
"(?i)2020",
|
"(?i)2020",
|
||||||
"(?i)кто|контрагент|клиент|принес|доход"
|
"(?i)кто|контрагент|клиент|принес|доход",
|
||||||
|
"(?i)группа\\s+свк",
|
||||||
|
"(?i)12[ .\\u00a0]?093[ .\\u00a0]?465|12093465"
|
||||||
|
],
|
||||||
|
"forbidden_answer_patterns": [
|
||||||
|
"(?i)самый\\s+доходный\\s+год",
|
||||||
|
"(?i)топ-\\d+\\s+лет",
|
||||||
|
"(?i)лет\\s+по\\s+сумме\\s+поступлений"
|
||||||
],
|
],
|
||||||
"criticality": "critical",
|
"criticality": "critical",
|
||||||
"semantic_tags": [
|
"semantic_tags": [
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ exports.readAssistantMcpDiscoveryLoopProvidedAxes = readAssistantMcpDiscoveryLoo
|
||||||
exports.readAssistantMcpDiscoveryLoopAskedDomainFamily = readAssistantMcpDiscoveryLoopAskedDomainFamily;
|
exports.readAssistantMcpDiscoveryLoopAskedDomainFamily = readAssistantMcpDiscoveryLoopAskedDomainFamily;
|
||||||
exports.readAssistantMcpDiscoveryLoopAskedActionFamily = readAssistantMcpDiscoveryLoopAskedActionFamily;
|
exports.readAssistantMcpDiscoveryLoopAskedActionFamily = readAssistantMcpDiscoveryLoopAskedActionFamily;
|
||||||
exports.readAssistantMcpDiscoveryLoopUnsupportedFamily = readAssistantMcpDiscoveryLoopUnsupportedFamily;
|
exports.readAssistantMcpDiscoveryLoopUnsupportedFamily = readAssistantMcpDiscoveryLoopUnsupportedFamily;
|
||||||
|
exports.readAssistantMcpDiscoveryLoopExplicitDateScope = readAssistantMcpDiscoveryLoopExplicitDateScope;
|
||||||
exports.readAssistantMcpDiscoveryLoopMetadataScopeHint = readAssistantMcpDiscoveryLoopMetadataScopeHint;
|
exports.readAssistantMcpDiscoveryLoopMetadataScopeHint = readAssistantMcpDiscoveryLoopMetadataScopeHint;
|
||||||
exports.readAssistantMcpDiscoveryLoopSubjectResolutionOptional = readAssistantMcpDiscoveryLoopSubjectResolutionOptional;
|
exports.readAssistantMcpDiscoveryLoopSubjectResolutionOptional = readAssistantMcpDiscoveryLoopSubjectResolutionOptional;
|
||||||
exports.readAssistantMcpDiscoveryMetadataRouteFamily = readAssistantMcpDiscoveryMetadataRouteFamily;
|
exports.readAssistantMcpDiscoveryMetadataRouteFamily = readAssistantMcpDiscoveryMetadataRouteFamily;
|
||||||
|
|
@ -197,6 +198,11 @@ function readAssistantMcpDiscoveryLoopAskedActionFamily(debug, toNonEmptyString
|
||||||
function readAssistantMcpDiscoveryLoopUnsupportedFamily(debug, toNonEmptyString = fallbackToNonEmptyString) {
|
function readAssistantMcpDiscoveryLoopUnsupportedFamily(debug, toNonEmptyString = fallbackToNonEmptyString) {
|
||||||
return toNonEmptyString(readAssistantMcpDiscoveryLoopState(debug)?.unsupported_but_understood_family);
|
return toNonEmptyString(readAssistantMcpDiscoveryLoopState(debug)?.unsupported_but_understood_family);
|
||||||
}
|
}
|
||||||
|
function readAssistantMcpDiscoveryLoopExplicitDateScope(debug, toNonEmptyString = fallbackToNonEmptyString) {
|
||||||
|
return (toNonEmptyString(readAssistantMcpDiscoveryLoopState(debug)?.explicit_date_scope) ??
|
||||||
|
toNonEmptyString(readAssistantMcpDiscoveryTurnMeaning(debug)?.explicit_date_scope) ??
|
||||||
|
toNonEmptyString(readAssistantMcpDiscoveryDataNeedGraph(debug)?.explicit_date_scope));
|
||||||
|
}
|
||||||
function readAssistantMcpDiscoveryLoopMetadataScopeHint(debug, toNonEmptyString = fallbackToNonEmptyString) {
|
function readAssistantMcpDiscoveryLoopMetadataScopeHint(debug, toNonEmptyString = fallbackToNonEmptyString) {
|
||||||
return (toNonEmptyString(readAssistantMcpDiscoveryLoopState(debug)?.metadata_scope_hint) ??
|
return (toNonEmptyString(readAssistantMcpDiscoveryLoopState(debug)?.metadata_scope_hint) ??
|
||||||
toNonEmptyString(readAssistantMcpDiscoveryTurnMeaning(debug)?.metadata_scope_hint) ??
|
toNonEmptyString(readAssistantMcpDiscoveryTurnMeaning(debug)?.metadata_scope_hint) ??
|
||||||
|
|
|
||||||
|
|
@ -239,6 +239,26 @@ function isOpenScopeValueFlowRanking(entryPoint) {
|
||||||
const subjectCandidates = Array.isArray(graph?.subject_candidates) ? graph.subject_candidates : [];
|
const subjectCandidates = Array.isArray(graph?.subject_candidates) ? graph.subject_candidates : [];
|
||||||
return businessFactFamily === "value_flow" && subjectCandidates.length === 0 && Boolean(toNonEmptyString(graph?.ranking_need));
|
return businessFactFamily === "value_flow" && subjectCandidates.length === 0 && Boolean(toNonEmptyString(graph?.ranking_need));
|
||||||
}
|
}
|
||||||
|
function readDiscoverySelectedChainId(entryPoint) {
|
||||||
|
const bridge = toRecordObject(entryPoint?.bridge);
|
||||||
|
const pilot = toRecordObject(bridge?.pilot);
|
||||||
|
const routeCandidate = toRecordObject(bridge?.route_candidate);
|
||||||
|
return (toNonEmptyString(routeCandidate?.selected_chain_id) ??
|
||||||
|
toNonEmptyString(bridge?.selected_chain_id) ??
|
||||||
|
toNonEmptyString(pilot?.selected_chain_id));
|
||||||
|
}
|
||||||
|
function isValueFlowRankingDiscoveryTurn(entryPoint) {
|
||||||
|
const graph = readDiscoveryDataNeedGraph(entryPoint);
|
||||||
|
const bridge = toRecordObject(entryPoint?.bridge);
|
||||||
|
const routeCandidate = toRecordObject(bridge?.route_candidate);
|
||||||
|
const selectedChainId = readDiscoverySelectedChainId(entryPoint);
|
||||||
|
const graphReasonCodes = readStringArray(graph?.reason_codes);
|
||||||
|
return Boolean(toNonEmptyString(graph?.business_fact_family) === "value_flow" &&
|
||||||
|
(selectedChainId === "value_flow_ranking" ||
|
||||||
|
toNonEmptyString(routeCandidate?.nearest_catalog_chain_template) === "value_flow_ranking" ||
|
||||||
|
toNonEmptyString(graph?.ranking_need) === "top_desc" ||
|
||||||
|
graphReasonCodes.includes("data_need_graph_ranking_top_desc")));
|
||||||
|
}
|
||||||
function readTruthAnswerShape(input) {
|
function readTruthAnswerShape(input) {
|
||||||
const directShape = toRecordObject(input.addressRuntimeMeta?.answer_shape_contract);
|
const directShape = toRecordObject(input.addressRuntimeMeta?.answer_shape_contract);
|
||||||
if (directShape) {
|
if (directShape) {
|
||||||
|
|
@ -296,6 +316,54 @@ function hasDiscoveryClarificationCandidatePriorityOverCurrentClarification(inpu
|
||||||
capabilityId?.includes("inventory_")));
|
capabilityId?.includes("inventory_")));
|
||||||
return asksForMovements && staleInventoryItemClarification;
|
return asksForMovements && staleInventoryItemClarification;
|
||||||
}
|
}
|
||||||
|
function hasGroundedMovementCandidatePriorityOverCurrentInventoryItemClarification(input, entryPoint, candidate) {
|
||||||
|
if (!hasCurrentClarificationRequiredAddressReply(input)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (candidate.candidate_status !== "ready_for_guarded_use") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!candidate.eligible_for_future_hot_runtime || !toNonEmptyString(candidate.reply_text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (candidate.reply_text && hasInternalMechanics(candidate.reply_text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const graph = readDiscoveryDataNeedGraph(entryPoint);
|
||||||
|
const turnMeaning = readDiscoveryTurnMeaning(entryPoint);
|
||||||
|
const bridge = toRecordObject(entryPoint?.bridge);
|
||||||
|
const pilot = toRecordObject(bridge?.pilot);
|
||||||
|
const handoff = toRecordObject(bridge?.execution_handoff);
|
||||||
|
const selectedChainId = readDiscoverySelectedChainId(entryPoint);
|
||||||
|
const discoveryFamily = toNonEmptyString(graph?.business_fact_family);
|
||||||
|
const askedDomain = toNonEmptyString(turnMeaning?.asked_domain_family);
|
||||||
|
const askedAction = toNonEmptyString(turnMeaning?.asked_action_family) ?? toNonEmptyString(graph?.action_family);
|
||||||
|
const asksForMovements = Boolean(selectedChainId === "movement_evidence" ||
|
||||||
|
discoveryFamily === "movement_evidence" ||
|
||||||
|
askedDomain === "movements" ||
|
||||||
|
askedAction === "list_movements");
|
||||||
|
const truthAnswerShape = readTruthAnswerShape(input);
|
||||||
|
const detectedIntent = toNonEmptyString(input.addressRuntimeMeta?.detected_intent);
|
||||||
|
const selectedRecipe = toNonEmptyString(input.addressRuntimeMeta?.selected_recipe);
|
||||||
|
const capabilityId = toNonEmptyString(input.addressRuntimeMeta?.capability_id) ??
|
||||||
|
toNonEmptyString(input.addressRuntimeMeta?.capability_contract_id) ??
|
||||||
|
toNonEmptyString(truthAnswerShape?.capability_contract_id);
|
||||||
|
const missingFilters = [
|
||||||
|
...readStringArray(input.addressRuntimeMeta?.missing_required_filters),
|
||||||
|
...readStringArray(input.addressRuntimeMeta?.missing_anchors),
|
||||||
|
...readStringArray(toRecordObject(input.addressRuntimeMeta?.capability_binding_contract)?.missing_anchors)
|
||||||
|
];
|
||||||
|
const staleInventoryItemClarification = Boolean(missingFilters.includes("item") &&
|
||||||
|
(detectedIntent?.startsWith("inventory_") ||
|
||||||
|
selectedRecipe?.includes("inventory_") ||
|
||||||
|
capabilityId?.includes("inventory_")));
|
||||||
|
const mcpExecutionPerformed = handoff?.mcp_execution_performed === true || pilot?.mcp_execution_performed === true;
|
||||||
|
const boundedMovementAnswerAllowed = bridge?.user_facing_response_allowed === true && bridge?.business_fact_answer_allowed === true;
|
||||||
|
return asksForMovements && staleInventoryItemClarification && mcpExecutionPerformed && boundedMovementAnswerAllowed;
|
||||||
|
}
|
||||||
function hasGroundedValueFlowCandidatePriorityOverCurrentClarification(input, entryPoint, candidate) {
|
function hasGroundedValueFlowCandidatePriorityOverCurrentClarification(input, entryPoint, candidate) {
|
||||||
if (!hasCurrentClarificationRequiredAddressReply(input)) {
|
if (!hasCurrentClarificationRequiredAddressReply(input)) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -313,10 +381,7 @@ function hasGroundedValueFlowCandidatePriorityOverCurrentClarification(input, en
|
||||||
const bridge = toRecordObject(entryPoint?.bridge);
|
const bridge = toRecordObject(entryPoint?.bridge);
|
||||||
const pilot = toRecordObject(bridge?.pilot);
|
const pilot = toRecordObject(bridge?.pilot);
|
||||||
const handoff = toRecordObject(bridge?.execution_handoff);
|
const handoff = toRecordObject(bridge?.execution_handoff);
|
||||||
const routeCandidate = toRecordObject(bridge?.route_candidate);
|
const selectedChainId = readDiscoverySelectedChainId(entryPoint);
|
||||||
const selectedChainId = toNonEmptyString(routeCandidate?.selected_chain_id) ??
|
|
||||||
toNonEmptyString(bridge?.selected_chain_id) ??
|
|
||||||
toNonEmptyString(pilot?.selected_chain_id);
|
|
||||||
const pilotScope = toNonEmptyString(pilot?.pilot_scope);
|
const pilotScope = toNonEmptyString(pilot?.pilot_scope);
|
||||||
const actionFamily = toNonEmptyString(graph?.action_family);
|
const actionFamily = toNonEmptyString(graph?.action_family);
|
||||||
const comparisonNeed = toNonEmptyString(graph?.comparison_need);
|
const comparisonNeed = toNonEmptyString(graph?.comparison_need);
|
||||||
|
|
@ -329,6 +394,70 @@ function hasGroundedValueFlowCandidatePriorityOverCurrentClarification(input, en
|
||||||
(handoff?.can_use_guarded_response === true || toNonEmptyString(bridge?.bridge_status) === "answer_draft_ready");
|
(handoff?.can_use_guarded_response === true || toNonEmptyString(bridge?.bridge_status) === "answer_draft_ready");
|
||||||
return valueFlowComparison && mcpExecutionPerformed && guardedBusinessAnswerAllowed;
|
return valueFlowComparison && mcpExecutionPerformed && guardedBusinessAnswerAllowed;
|
||||||
}
|
}
|
||||||
|
function hasCounterpartyRankingQuestionSignal(entryPoint) {
|
||||||
|
const graph = readDiscoveryDataNeedGraph(entryPoint);
|
||||||
|
const turnMeaning = readDiscoveryTurnMeaning(entryPoint);
|
||||||
|
const rawMessage = toNonEmptyString(turnMeaning?.raw_message) ?? "";
|
||||||
|
const effectiveMessage = toNonEmptyString(turnMeaning?.effective_message) ?? "";
|
||||||
|
const askedDomain = toNonEmptyString(turnMeaning?.asked_domain_family);
|
||||||
|
const askedAction = toNonEmptyString(turnMeaning?.asked_action_family);
|
||||||
|
const text = `${rawMessage}\n${effectiveMessage}`.toLowerCase();
|
||||||
|
return Boolean(isValueFlowRankingDiscoveryTurn(entryPoint) &&
|
||||||
|
(askedDomain === "counterparty_value" || askedDomain === "counterparty") &&
|
||||||
|
(askedAction === "turnover" || askedAction === "counterparty_value_or_turnover") &&
|
||||||
|
(/\bкто\b/u.test(text) ||
|
||||||
|
text.includes("контрагент") ||
|
||||||
|
text.includes("клиент") ||
|
||||||
|
text.includes("покупател") ||
|
||||||
|
text.includes("заказчик") ||
|
||||||
|
text.includes("принес") ||
|
||||||
|
text.includes("принёс") ||
|
||||||
|
text.includes("больше всего") ||
|
||||||
|
toNonEmptyString(graph?.ranking_need) === "top_desc"));
|
||||||
|
}
|
||||||
|
function hasWrongPeriodRankingCurrentReply(input) {
|
||||||
|
const currentReply = String(input.currentReply ?? "").toLowerCase();
|
||||||
|
return (currentReply.includes("самый доходный год") ||
|
||||||
|
currentReply.includes("топ-1 лет") ||
|
||||||
|
currentReply.includes("топ-5 лет") ||
|
||||||
|
currentReply.includes("топ-10 лет") ||
|
||||||
|
/топ-\d+\s+лет/u.test(currentReply) ||
|
||||||
|
currentReply.includes("лет по сумме поступлений") ||
|
||||||
|
currentReply.includes("год по подтвержденным поступлениям"));
|
||||||
|
}
|
||||||
|
function hasConfirmedValueFlowRankingEvidence(entryPoint) {
|
||||||
|
const bridge = toRecordObject(entryPoint?.bridge);
|
||||||
|
const pilot = toRecordObject(bridge?.pilot);
|
||||||
|
const handoff = toRecordObject(bridge?.execution_handoff);
|
||||||
|
const evidence = toRecordObject(pilot?.evidence);
|
||||||
|
const confirmedFacts = readStringArray(evidence?.confirmed_facts);
|
||||||
|
const mcpExecutionPerformed = handoff?.mcp_execution_performed === true || pilot?.mcp_execution_performed === true;
|
||||||
|
const businessAnswerAllowed = bridge?.user_facing_response_allowed === true && bridge?.business_fact_answer_allowed === true;
|
||||||
|
const confirmedRankingEvidence = Boolean(toNonEmptyString(evidence?.evidence_status) === "confirmed" ||
|
||||||
|
toNonEmptyString(evidence?.answer_permission) === "confirmed_answer" ||
|
||||||
|
confirmedFacts.some((fact) => /ranked by counterparty|рейтинг.*контрагент/iu.test(fact)));
|
||||||
|
return mcpExecutionPerformed && businessAnswerAllowed && confirmedRankingEvidence;
|
||||||
|
}
|
||||||
|
function hasGroundedValueFlowRankingCandidatePriorityOverCurrentExactReply(input, entryPoint, candidate) {
|
||||||
|
if (candidate.candidate_status !== "ready_for_guarded_use") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!candidate.eligible_for_future_hot_runtime || !toNonEmptyString(candidate.reply_text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (candidate.reply_text && hasInternalMechanics(candidate.reply_text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!hasEffectivelyFactualAddressReply(input)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Boolean(hasCounterpartyRankingQuestionSignal(entryPoint) &&
|
||||||
|
hasWrongPeriodRankingCurrentReply(input) &&
|
||||||
|
hasConfirmedValueFlowRankingEvidence(entryPoint));
|
||||||
|
}
|
||||||
function hasEffectivelyFactualAddressReply(input) {
|
function hasEffectivelyFactualAddressReply(input) {
|
||||||
if (toNonEmptyString(input.currentReplyType) === "factual") {
|
if (toNonEmptyString(input.currentReplyType) === "factual") {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -804,9 +933,17 @@ function applyAssistantMcpDiscoveryResponsePolicy(input) {
|
||||||
const currentClarificationRequiredAddressReply = hasCurrentClarificationRequiredAddressReply(input);
|
const currentClarificationRequiredAddressReply = hasCurrentClarificationRequiredAddressReply(input);
|
||||||
const discoveryClarificationCandidatePriority = hasDiscoveryClarificationCandidatePriorityOverCurrentClarification(input, entryPoint, candidate);
|
const discoveryClarificationCandidatePriority = hasDiscoveryClarificationCandidatePriorityOverCurrentClarification(input, entryPoint, candidate);
|
||||||
const groundedValueFlowCandidatePriority = hasGroundedValueFlowCandidatePriorityOverCurrentClarification(input, entryPoint, candidate);
|
const groundedValueFlowCandidatePriority = hasGroundedValueFlowCandidatePriorityOverCurrentClarification(input, entryPoint, candidate);
|
||||||
|
const groundedMovementCandidatePriority = hasGroundedMovementCandidatePriorityOverCurrentInventoryItemClarification(input, entryPoint, candidate);
|
||||||
|
const groundedValueFlowRankingCandidatePriority = hasGroundedValueFlowRankingCandidatePriorityOverCurrentExactReply(input, entryPoint, candidate);
|
||||||
const currentClarificationProtectsCurrentReply = currentClarificationRequiredAddressReply &&
|
const currentClarificationProtectsCurrentReply = currentClarificationRequiredAddressReply &&
|
||||||
!discoveryClarificationCandidatePriority &&
|
!discoveryClarificationCandidatePriority &&
|
||||||
!groundedValueFlowCandidatePriority;
|
!groundedValueFlowCandidatePriority &&
|
||||||
|
!groundedMovementCandidatePriority &&
|
||||||
|
!groundedValueFlowRankingCandidatePriority;
|
||||||
|
const alignedFactualAddressReplyProtectsCurrent = alignedFactualAddressReply && !groundedValueFlowRankingCandidatePriority;
|
||||||
|
const matchedFactualAddressContinuationTargetProtectsCurrent = matchedFactualAddressContinuationTarget && !groundedValueFlowRankingCandidatePriority;
|
||||||
|
const fullConfirmedFactualAddressReplyProtectsCurrent = fullConfirmedFactualAddressReply && !groundedValueFlowRankingCandidatePriority;
|
||||||
|
const exactMatchedFactualAddressReplyProtectsCurrent = exactMatchedFactualAddressReply && !groundedValueFlowRankingCandidatePriority;
|
||||||
const exactBankOperationsProtectsCurrent = exactBankOperationsAddressReply &&
|
const exactBankOperationsProtectsCurrent = exactBankOperationsAddressReply &&
|
||||||
!semanticConflictWithDiscoveryTurnMeaning &&
|
!semanticConflictWithDiscoveryTurnMeaning &&
|
||||||
!valueFlowActionConflictWithDiscoveryTurnMeaning;
|
!valueFlowActionConflictWithDiscoveryTurnMeaning;
|
||||||
|
|
@ -849,6 +986,12 @@ function applyAssistantMcpDiscoveryResponsePolicy(input) {
|
||||||
if (groundedValueFlowCandidatePriority) {
|
if (groundedValueFlowCandidatePriority) {
|
||||||
pushReason(reasonCodes, "mcp_discovery_response_policy_grounded_value_flow_candidate_priority_over_current_clarification");
|
pushReason(reasonCodes, "mcp_discovery_response_policy_grounded_value_flow_candidate_priority_over_current_clarification");
|
||||||
}
|
}
|
||||||
|
if (groundedMovementCandidatePriority) {
|
||||||
|
pushReason(reasonCodes, "mcp_discovery_response_policy_grounded_movement_candidate_priority_over_inventory_item_clarification");
|
||||||
|
}
|
||||||
|
if (groundedValueFlowRankingCandidatePriority) {
|
||||||
|
pushReason(reasonCodes, "mcp_discovery_response_policy_grounded_value_flow_ranking_candidate_priority_over_current_exact_reply");
|
||||||
|
}
|
||||||
if (openScopeValueFlowDiscoveryPriority) {
|
if (openScopeValueFlowDiscoveryPriority) {
|
||||||
pushReason(reasonCodes, "mcp_discovery_response_policy_open_scope_value_flow_candidate_priority");
|
pushReason(reasonCodes, "mcp_discovery_response_policy_open_scope_value_flow_candidate_priority");
|
||||||
}
|
}
|
||||||
|
|
@ -911,11 +1054,11 @@ function applyAssistantMcpDiscoveryResponsePolicy(input) {
|
||||||
}
|
}
|
||||||
const canApply = Boolean(entryPoint) &&
|
const canApply = Boolean(entryPoint) &&
|
||||||
(unsupportedBoundary || discoveryReadyChatCandidate || discoveryReadyDeepCandidate || discoveryReadyAddressCandidate) &&
|
(unsupportedBoundary || discoveryReadyChatCandidate || discoveryReadyDeepCandidate || discoveryReadyAddressCandidate) &&
|
||||||
!alignedFactualAddressReply &&
|
!alignedFactualAddressReplyProtectsCurrent &&
|
||||||
!matchedFactualAddressContinuationTarget &&
|
!matchedFactualAddressContinuationTargetProtectsCurrent &&
|
||||||
!matchedFactualSuggestedIntentPivotTarget &&
|
!matchedFactualSuggestedIntentPivotTarget &&
|
||||||
!fullConfirmedFactualAddressReply &&
|
!fullConfirmedFactualAddressReplyProtectsCurrent &&
|
||||||
!exactMatchedFactualAddressReply &&
|
!exactMatchedFactualAddressReplyProtectsCurrent &&
|
||||||
!runtimeAdjustedExactReply &&
|
!runtimeAdjustedExactReply &&
|
||||||
!runtimeMatchedExactReply &&
|
!runtimeMatchedExactReply &&
|
||||||
!staleMetadataDiscoveryFallbackAgainstExactAddressReply &&
|
!staleMetadataDiscoveryFallbackAgainstExactAddressReply &&
|
||||||
|
|
|
||||||
|
|
@ -541,6 +541,7 @@ function collectFollowupDiscoverySeed(followupContext) {
|
||||||
const loopAskedDomainFamily = toNonEmptyString(followupContext?.previous_discovery_loop_asked_domain_family);
|
const loopAskedDomainFamily = toNonEmptyString(followupContext?.previous_discovery_loop_asked_domain_family);
|
||||||
const loopAskedActionFamily = toNonEmptyString(followupContext?.previous_discovery_loop_asked_action_family);
|
const loopAskedActionFamily = toNonEmptyString(followupContext?.previous_discovery_loop_asked_action_family);
|
||||||
const loopUnsupportedFamily = toNonEmptyString(followupContext?.previous_discovery_loop_unsupported_family);
|
const loopUnsupportedFamily = toNonEmptyString(followupContext?.previous_discovery_loop_unsupported_family);
|
||||||
|
const loopExplicitDateScope = toNonEmptyString(followupContext?.previous_discovery_loop_explicit_date_scope);
|
||||||
const loopMetadataScopeHint = toNonEmptyString(followupContext?.previous_discovery_loop_metadata_scope_hint);
|
const loopMetadataScopeHint = toNonEmptyString(followupContext?.previous_discovery_loop_metadata_scope_hint);
|
||||||
const loopSubjectResolutionOptional = followupContext?.previous_discovery_loop_subject_resolution_optional === true;
|
const loopSubjectResolutionOptional = followupContext?.previous_discovery_loop_subject_resolution_optional === true;
|
||||||
const previousIntent = toNonEmptyString(followupContext?.target_intent) ?? toNonEmptyString(followupContext?.previous_intent);
|
const previousIntent = toNonEmptyString(followupContext?.target_intent) ?? toNonEmptyString(followupContext?.previous_intent);
|
||||||
|
|
@ -592,7 +593,7 @@ function collectFollowupDiscoverySeed(followupContext) {
|
||||||
const loopProvidedAllTimeScope = loopProvidedAxes.includes("all_time_scope");
|
const loopProvidedAllTimeScope = loopProvidedAxes.includes("all_time_scope");
|
||||||
const dateScope = loopProvidedAllTimeScope
|
const dateScope = loopProvidedAllTimeScope
|
||||||
? "all_time_scope"
|
? "all_time_scope"
|
||||||
: collectDateScopeFromFilters(previousFilters) ?? collectDateScopeFromFilters(rootFilters);
|
: loopExplicitDateScope ?? collectDateScopeFromFilters(previousFilters) ?? collectDateScopeFromFilters(rootFilters);
|
||||||
return {
|
return {
|
||||||
pilotScope: effectivePilotScope,
|
pilotScope: effectivePilotScope,
|
||||||
domain: mapped.domain,
|
domain: mapped.domain,
|
||||||
|
|
|
||||||
|
|
@ -1011,6 +1011,7 @@ function createAssistantTransitionPolicy(deps) {
|
||||||
const sourceDiscoveryLoopAskedDomainFamily = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryLoopAskedDomainFamily)(carryoverSourceDebug, deps.toNonEmptyString);
|
const sourceDiscoveryLoopAskedDomainFamily = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryLoopAskedDomainFamily)(carryoverSourceDebug, deps.toNonEmptyString);
|
||||||
const sourceDiscoveryLoopAskedActionFamily = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryLoopAskedActionFamily)(carryoverSourceDebug, deps.toNonEmptyString);
|
const sourceDiscoveryLoopAskedActionFamily = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryLoopAskedActionFamily)(carryoverSourceDebug, deps.toNonEmptyString);
|
||||||
const sourceDiscoveryLoopUnsupportedFamily = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryLoopUnsupportedFamily)(carryoverSourceDebug, deps.toNonEmptyString);
|
const sourceDiscoveryLoopUnsupportedFamily = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryLoopUnsupportedFamily)(carryoverSourceDebug, deps.toNonEmptyString);
|
||||||
|
const sourceDiscoveryLoopExplicitDateScope = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryLoopExplicitDateScope)(carryoverSourceDebug, deps.toNonEmptyString);
|
||||||
const sourceDiscoveryLoopMetadataScopeHint = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryLoopMetadataScopeHint)(carryoverSourceDebug, deps.toNonEmptyString);
|
const sourceDiscoveryLoopMetadataScopeHint = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryLoopMetadataScopeHint)(carryoverSourceDebug, deps.toNonEmptyString);
|
||||||
const sourceDiscoveryLoopSubjectResolutionOptional = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryLoopSubjectResolutionOptional)(carryoverSourceDebug);
|
const sourceDiscoveryLoopSubjectResolutionOptional = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryLoopSubjectResolutionOptional)(carryoverSourceDebug);
|
||||||
const sourceDiscoveryRankingNeed = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryRankingNeed)(carryoverSourceDebug, deps.toNonEmptyString);
|
const sourceDiscoveryRankingNeed = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryRankingNeed)(carryoverSourceDebug, deps.toNonEmptyString);
|
||||||
|
|
@ -1346,6 +1347,7 @@ function createAssistantTransitionPolicy(deps) {
|
||||||
previous_discovery_loop_asked_domain_family: sourceDiscoveryLoopAskedDomainFamily ?? undefined,
|
previous_discovery_loop_asked_domain_family: sourceDiscoveryLoopAskedDomainFamily ?? undefined,
|
||||||
previous_discovery_loop_asked_action_family: sourceDiscoveryLoopAskedActionFamily ?? undefined,
|
previous_discovery_loop_asked_action_family: sourceDiscoveryLoopAskedActionFamily ?? undefined,
|
||||||
previous_discovery_loop_unsupported_family: sourceDiscoveryLoopUnsupportedFamily ?? undefined,
|
previous_discovery_loop_unsupported_family: sourceDiscoveryLoopUnsupportedFamily ?? undefined,
|
||||||
|
previous_discovery_loop_explicit_date_scope: sourceDiscoveryLoopExplicitDateScope ?? undefined,
|
||||||
previous_discovery_loop_metadata_scope_hint: sourceDiscoveryLoopMetadataScopeHint ?? undefined,
|
previous_discovery_loop_metadata_scope_hint: sourceDiscoveryLoopMetadataScopeHint ?? undefined,
|
||||||
previous_discovery_loop_subject_resolution_optional: sourceDiscoveryLoopSubjectResolutionOptional || undefined,
|
previous_discovery_loop_subject_resolution_optional: sourceDiscoveryLoopSubjectResolutionOptional || undefined,
|
||||||
previous_discovery_ranking_need: sourceDiscoveryRankingNeed ?? undefined,
|
previous_discovery_ranking_need: sourceDiscoveryRankingNeed ?? undefined,
|
||||||
|
|
|
||||||
|
|
@ -330,6 +330,17 @@ export function readAssistantMcpDiscoveryLoopUnsupportedFamily(
|
||||||
return toNonEmptyString(readAssistantMcpDiscoveryLoopState(debug)?.unsupported_but_understood_family);
|
return toNonEmptyString(readAssistantMcpDiscoveryLoopState(debug)?.unsupported_but_understood_family);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function readAssistantMcpDiscoveryLoopExplicitDateScope(
|
||||||
|
debug: Record<string, unknown> | null,
|
||||||
|
toNonEmptyString: (value: unknown) => string | null = fallbackToNonEmptyString
|
||||||
|
): string | null {
|
||||||
|
return (
|
||||||
|
toNonEmptyString(readAssistantMcpDiscoveryLoopState(debug)?.explicit_date_scope) ??
|
||||||
|
toNonEmptyString(readAssistantMcpDiscoveryTurnMeaning(debug)?.explicit_date_scope) ??
|
||||||
|
toNonEmptyString(readAssistantMcpDiscoveryDataNeedGraph(debug)?.explicit_date_scope)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function readAssistantMcpDiscoveryLoopMetadataScopeHint(
|
export function readAssistantMcpDiscoveryLoopMetadataScopeHint(
|
||||||
debug: Record<string, unknown> | null,
|
debug: Record<string, unknown> | null,
|
||||||
toNonEmptyString: (value: unknown) => string | null = fallbackToNonEmptyString
|
toNonEmptyString: (value: unknown) => string | null = fallbackToNonEmptyString
|
||||||
|
|
|
||||||
|
|
@ -358,6 +358,36 @@ function isOpenScopeValueFlowRanking(
|
||||||
return businessFactFamily === "value_flow" && subjectCandidates.length === 0 && Boolean(toNonEmptyString(graph?.ranking_need));
|
return businessFactFamily === "value_flow" && subjectCandidates.length === 0 && Boolean(toNonEmptyString(graph?.ranking_need));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readDiscoverySelectedChainId(
|
||||||
|
entryPoint: AssistantMcpDiscoveryRuntimeEntryPointContract | null
|
||||||
|
): string | null {
|
||||||
|
const bridge = toRecordObject(entryPoint?.bridge);
|
||||||
|
const pilot = toRecordObject(bridge?.pilot);
|
||||||
|
const routeCandidate = toRecordObject(bridge?.route_candidate);
|
||||||
|
return (
|
||||||
|
toNonEmptyString(routeCandidate?.selected_chain_id) ??
|
||||||
|
toNonEmptyString(bridge?.selected_chain_id) ??
|
||||||
|
toNonEmptyString(pilot?.selected_chain_id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValueFlowRankingDiscoveryTurn(
|
||||||
|
entryPoint: AssistantMcpDiscoveryRuntimeEntryPointContract | null
|
||||||
|
): boolean {
|
||||||
|
const graph = readDiscoveryDataNeedGraph(entryPoint);
|
||||||
|
const bridge = toRecordObject(entryPoint?.bridge);
|
||||||
|
const routeCandidate = toRecordObject(bridge?.route_candidate);
|
||||||
|
const selectedChainId = readDiscoverySelectedChainId(entryPoint);
|
||||||
|
const graphReasonCodes = readStringArray(graph?.reason_codes);
|
||||||
|
return Boolean(
|
||||||
|
toNonEmptyString(graph?.business_fact_family) === "value_flow" &&
|
||||||
|
(selectedChainId === "value_flow_ranking" ||
|
||||||
|
toNonEmptyString(routeCandidate?.nearest_catalog_chain_template) === "value_flow_ranking" ||
|
||||||
|
toNonEmptyString(graph?.ranking_need) === "top_desc" ||
|
||||||
|
graphReasonCodes.includes("data_need_graph_ranking_top_desc"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function readTruthAnswerShape(input: ApplyAssistantMcpDiscoveryResponsePolicyInput): Record<string, unknown> | null {
|
function readTruthAnswerShape(input: ApplyAssistantMcpDiscoveryResponsePolicyInput): Record<string, unknown> | null {
|
||||||
const directShape = toRecordObject(input.addressRuntimeMeta?.answer_shape_contract);
|
const directShape = toRecordObject(input.addressRuntimeMeta?.answer_shape_contract);
|
||||||
if (directShape) {
|
if (directShape) {
|
||||||
|
|
@ -433,6 +463,69 @@ function hasDiscoveryClarificationCandidatePriorityOverCurrentClarification(
|
||||||
return asksForMovements && staleInventoryItemClarification;
|
return asksForMovements && staleInventoryItemClarification;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasGroundedMovementCandidatePriorityOverCurrentInventoryItemClarification(
|
||||||
|
input: ApplyAssistantMcpDiscoveryResponsePolicyInput,
|
||||||
|
entryPoint: AssistantMcpDiscoveryRuntimeEntryPointContract | null,
|
||||||
|
candidate: AssistantMcpDiscoveryResponseCandidateContract
|
||||||
|
): boolean {
|
||||||
|
if (!hasCurrentClarificationRequiredAddressReply(input)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (candidate.candidate_status !== "ready_for_guarded_use") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!candidate.eligible_for_future_hot_runtime || !toNonEmptyString(candidate.reply_text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (candidate.reply_text && hasInternalMechanics(candidate.reply_text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const graph = readDiscoveryDataNeedGraph(entryPoint);
|
||||||
|
const turnMeaning = readDiscoveryTurnMeaning(entryPoint);
|
||||||
|
const bridge = toRecordObject(entryPoint?.bridge);
|
||||||
|
const pilot = toRecordObject(bridge?.pilot);
|
||||||
|
const handoff = toRecordObject(bridge?.execution_handoff);
|
||||||
|
const selectedChainId = readDiscoverySelectedChainId(entryPoint);
|
||||||
|
const discoveryFamily = toNonEmptyString(graph?.business_fact_family);
|
||||||
|
const askedDomain = toNonEmptyString(turnMeaning?.asked_domain_family);
|
||||||
|
const askedAction =
|
||||||
|
toNonEmptyString(turnMeaning?.asked_action_family) ?? toNonEmptyString(graph?.action_family);
|
||||||
|
const asksForMovements = Boolean(
|
||||||
|
selectedChainId === "movement_evidence" ||
|
||||||
|
discoveryFamily === "movement_evidence" ||
|
||||||
|
askedDomain === "movements" ||
|
||||||
|
askedAction === "list_movements"
|
||||||
|
);
|
||||||
|
|
||||||
|
const truthAnswerShape = readTruthAnswerShape(input);
|
||||||
|
const detectedIntent = toNonEmptyString(input.addressRuntimeMeta?.detected_intent);
|
||||||
|
const selectedRecipe = toNonEmptyString(input.addressRuntimeMeta?.selected_recipe);
|
||||||
|
const capabilityId =
|
||||||
|
toNonEmptyString(input.addressRuntimeMeta?.capability_id) ??
|
||||||
|
toNonEmptyString(input.addressRuntimeMeta?.capability_contract_id) ??
|
||||||
|
toNonEmptyString(truthAnswerShape?.capability_contract_id);
|
||||||
|
const missingFilters = [
|
||||||
|
...readStringArray(input.addressRuntimeMeta?.missing_required_filters),
|
||||||
|
...readStringArray(input.addressRuntimeMeta?.missing_anchors),
|
||||||
|
...readStringArray(toRecordObject(input.addressRuntimeMeta?.capability_binding_contract)?.missing_anchors)
|
||||||
|
];
|
||||||
|
const staleInventoryItemClarification = Boolean(
|
||||||
|
missingFilters.includes("item") &&
|
||||||
|
(detectedIntent?.startsWith("inventory_") ||
|
||||||
|
selectedRecipe?.includes("inventory_") ||
|
||||||
|
capabilityId?.includes("inventory_"))
|
||||||
|
);
|
||||||
|
const mcpExecutionPerformed = handoff?.mcp_execution_performed === true || pilot?.mcp_execution_performed === true;
|
||||||
|
const boundedMovementAnswerAllowed =
|
||||||
|
bridge?.user_facing_response_allowed === true && bridge?.business_fact_answer_allowed === true;
|
||||||
|
|
||||||
|
return asksForMovements && staleInventoryItemClarification && mcpExecutionPerformed && boundedMovementAnswerAllowed;
|
||||||
|
}
|
||||||
|
|
||||||
function hasGroundedValueFlowCandidatePriorityOverCurrentClarification(
|
function hasGroundedValueFlowCandidatePriorityOverCurrentClarification(
|
||||||
input: ApplyAssistantMcpDiscoveryResponsePolicyInput,
|
input: ApplyAssistantMcpDiscoveryResponsePolicyInput,
|
||||||
entryPoint: AssistantMcpDiscoveryRuntimeEntryPointContract | null,
|
entryPoint: AssistantMcpDiscoveryRuntimeEntryPointContract | null,
|
||||||
|
|
@ -455,11 +548,7 @@ function hasGroundedValueFlowCandidatePriorityOverCurrentClarification(
|
||||||
const bridge = toRecordObject(entryPoint?.bridge);
|
const bridge = toRecordObject(entryPoint?.bridge);
|
||||||
const pilot = toRecordObject(bridge?.pilot);
|
const pilot = toRecordObject(bridge?.pilot);
|
||||||
const handoff = toRecordObject(bridge?.execution_handoff);
|
const handoff = toRecordObject(bridge?.execution_handoff);
|
||||||
const routeCandidate = toRecordObject(bridge?.route_candidate);
|
const selectedChainId = readDiscoverySelectedChainId(entryPoint);
|
||||||
const selectedChainId =
|
|
||||||
toNonEmptyString(routeCandidate?.selected_chain_id) ??
|
|
||||||
toNonEmptyString(bridge?.selected_chain_id) ??
|
|
||||||
toNonEmptyString(pilot?.selected_chain_id);
|
|
||||||
const pilotScope = toNonEmptyString(pilot?.pilot_scope);
|
const pilotScope = toNonEmptyString(pilot?.pilot_scope);
|
||||||
const actionFamily = toNonEmptyString(graph?.action_family);
|
const actionFamily = toNonEmptyString(graph?.action_family);
|
||||||
const comparisonNeed = toNonEmptyString(graph?.comparison_need);
|
const comparisonNeed = toNonEmptyString(graph?.comparison_need);
|
||||||
|
|
@ -477,6 +566,91 @@ function hasGroundedValueFlowCandidatePriorityOverCurrentClarification(
|
||||||
return valueFlowComparison && mcpExecutionPerformed && guardedBusinessAnswerAllowed;
|
return valueFlowComparison && mcpExecutionPerformed && guardedBusinessAnswerAllowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasCounterpartyRankingQuestionSignal(
|
||||||
|
entryPoint: AssistantMcpDiscoveryRuntimeEntryPointContract | null
|
||||||
|
): boolean {
|
||||||
|
const graph = readDiscoveryDataNeedGraph(entryPoint);
|
||||||
|
const turnMeaning = readDiscoveryTurnMeaning(entryPoint);
|
||||||
|
const rawMessage = toNonEmptyString(turnMeaning?.raw_message) ?? "";
|
||||||
|
const effectiveMessage = toNonEmptyString(turnMeaning?.effective_message) ?? "";
|
||||||
|
const askedDomain = toNonEmptyString(turnMeaning?.asked_domain_family);
|
||||||
|
const askedAction = toNonEmptyString(turnMeaning?.asked_action_family);
|
||||||
|
const text = `${rawMessage}\n${effectiveMessage}`.toLowerCase();
|
||||||
|
return Boolean(
|
||||||
|
isValueFlowRankingDiscoveryTurn(entryPoint) &&
|
||||||
|
(askedDomain === "counterparty_value" || askedDomain === "counterparty") &&
|
||||||
|
(askedAction === "turnover" || askedAction === "counterparty_value_or_turnover") &&
|
||||||
|
(/\bкто\b/u.test(text) ||
|
||||||
|
text.includes("контрагент") ||
|
||||||
|
text.includes("клиент") ||
|
||||||
|
text.includes("покупател") ||
|
||||||
|
text.includes("заказчик") ||
|
||||||
|
text.includes("принес") ||
|
||||||
|
text.includes("принёс") ||
|
||||||
|
text.includes("больше всего") ||
|
||||||
|
toNonEmptyString(graph?.ranking_need) === "top_desc")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasWrongPeriodRankingCurrentReply(input: ApplyAssistantMcpDiscoveryResponsePolicyInput): boolean {
|
||||||
|
const currentReply = String(input.currentReply ?? "").toLowerCase();
|
||||||
|
return (
|
||||||
|
currentReply.includes("самый доходный год") ||
|
||||||
|
currentReply.includes("топ-1 лет") ||
|
||||||
|
currentReply.includes("топ-5 лет") ||
|
||||||
|
currentReply.includes("топ-10 лет") ||
|
||||||
|
/топ-\d+\s+лет/u.test(currentReply) ||
|
||||||
|
currentReply.includes("лет по сумме поступлений") ||
|
||||||
|
currentReply.includes("год по подтвержденным поступлениям")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasConfirmedValueFlowRankingEvidence(
|
||||||
|
entryPoint: AssistantMcpDiscoveryRuntimeEntryPointContract | null
|
||||||
|
): boolean {
|
||||||
|
const bridge = toRecordObject(entryPoint?.bridge);
|
||||||
|
const pilot = toRecordObject(bridge?.pilot);
|
||||||
|
const handoff = toRecordObject(bridge?.execution_handoff);
|
||||||
|
const evidence = toRecordObject(pilot?.evidence);
|
||||||
|
const confirmedFacts = readStringArray(evidence?.confirmed_facts);
|
||||||
|
const mcpExecutionPerformed = handoff?.mcp_execution_performed === true || pilot?.mcp_execution_performed === true;
|
||||||
|
const businessAnswerAllowed =
|
||||||
|
bridge?.user_facing_response_allowed === true && bridge?.business_fact_answer_allowed === true;
|
||||||
|
const confirmedRankingEvidence = Boolean(
|
||||||
|
toNonEmptyString(evidence?.evidence_status) === "confirmed" ||
|
||||||
|
toNonEmptyString(evidence?.answer_permission) === "confirmed_answer" ||
|
||||||
|
confirmedFacts.some((fact) => /ranked by counterparty|рейтинг.*контрагент/iu.test(fact))
|
||||||
|
);
|
||||||
|
return mcpExecutionPerformed && businessAnswerAllowed && confirmedRankingEvidence;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasGroundedValueFlowRankingCandidatePriorityOverCurrentExactReply(
|
||||||
|
input: ApplyAssistantMcpDiscoveryResponsePolicyInput,
|
||||||
|
entryPoint: AssistantMcpDiscoveryRuntimeEntryPointContract | null,
|
||||||
|
candidate: AssistantMcpDiscoveryResponseCandidateContract
|
||||||
|
): boolean {
|
||||||
|
if (candidate.candidate_status !== "ready_for_guarded_use") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!candidate.eligible_for_future_hot_runtime || !toNonEmptyString(candidate.reply_text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (candidate.reply_text && hasInternalMechanics(candidate.reply_text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!hasEffectivelyFactualAddressReply(input)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Boolean(
|
||||||
|
hasCounterpartyRankingQuestionSignal(entryPoint) &&
|
||||||
|
hasWrongPeriodRankingCurrentReply(input) &&
|
||||||
|
hasConfirmedValueFlowRankingEvidence(entryPoint)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function hasEffectivelyFactualAddressReply(input: ApplyAssistantMcpDiscoveryResponsePolicyInput): boolean {
|
function hasEffectivelyFactualAddressReply(input: ApplyAssistantMcpDiscoveryResponsePolicyInput): boolean {
|
||||||
if (toNonEmptyString(input.currentReplyType) === "factual") {
|
if (toNonEmptyString(input.currentReplyType) === "factual") {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -1092,10 +1266,24 @@ export function applyAssistantMcpDiscoveryResponsePolicy(
|
||||||
entryPoint,
|
entryPoint,
|
||||||
candidate
|
candidate
|
||||||
);
|
);
|
||||||
|
const groundedMovementCandidatePriority =
|
||||||
|
hasGroundedMovementCandidatePriorityOverCurrentInventoryItemClarification(input, entryPoint, candidate);
|
||||||
|
const groundedValueFlowRankingCandidatePriority =
|
||||||
|
hasGroundedValueFlowRankingCandidatePriorityOverCurrentExactReply(input, entryPoint, candidate);
|
||||||
const currentClarificationProtectsCurrentReply =
|
const currentClarificationProtectsCurrentReply =
|
||||||
currentClarificationRequiredAddressReply &&
|
currentClarificationRequiredAddressReply &&
|
||||||
!discoveryClarificationCandidatePriority &&
|
!discoveryClarificationCandidatePriority &&
|
||||||
!groundedValueFlowCandidatePriority;
|
!groundedValueFlowCandidatePriority &&
|
||||||
|
!groundedMovementCandidatePriority &&
|
||||||
|
!groundedValueFlowRankingCandidatePriority;
|
||||||
|
const alignedFactualAddressReplyProtectsCurrent =
|
||||||
|
alignedFactualAddressReply && !groundedValueFlowRankingCandidatePriority;
|
||||||
|
const matchedFactualAddressContinuationTargetProtectsCurrent =
|
||||||
|
matchedFactualAddressContinuationTarget && !groundedValueFlowRankingCandidatePriority;
|
||||||
|
const fullConfirmedFactualAddressReplyProtectsCurrent =
|
||||||
|
fullConfirmedFactualAddressReply && !groundedValueFlowRankingCandidatePriority;
|
||||||
|
const exactMatchedFactualAddressReplyProtectsCurrent =
|
||||||
|
exactMatchedFactualAddressReply && !groundedValueFlowRankingCandidatePriority;
|
||||||
const exactBankOperationsProtectsCurrent =
|
const exactBankOperationsProtectsCurrent =
|
||||||
exactBankOperationsAddressReply &&
|
exactBankOperationsAddressReply &&
|
||||||
!semanticConflictWithDiscoveryTurnMeaning &&
|
!semanticConflictWithDiscoveryTurnMeaning &&
|
||||||
|
|
@ -1147,6 +1335,18 @@ export function applyAssistantMcpDiscoveryResponsePolicy(
|
||||||
"mcp_discovery_response_policy_grounded_value_flow_candidate_priority_over_current_clarification"
|
"mcp_discovery_response_policy_grounded_value_flow_candidate_priority_over_current_clarification"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (groundedMovementCandidatePriority) {
|
||||||
|
pushReason(
|
||||||
|
reasonCodes,
|
||||||
|
"mcp_discovery_response_policy_grounded_movement_candidate_priority_over_inventory_item_clarification"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (groundedValueFlowRankingCandidatePriority) {
|
||||||
|
pushReason(
|
||||||
|
reasonCodes,
|
||||||
|
"mcp_discovery_response_policy_grounded_value_flow_ranking_candidate_priority_over_current_exact_reply"
|
||||||
|
);
|
||||||
|
}
|
||||||
if (openScopeValueFlowDiscoveryPriority) {
|
if (openScopeValueFlowDiscoveryPriority) {
|
||||||
pushReason(reasonCodes, "mcp_discovery_response_policy_open_scope_value_flow_candidate_priority");
|
pushReason(reasonCodes, "mcp_discovery_response_policy_open_scope_value_flow_candidate_priority");
|
||||||
}
|
}
|
||||||
|
|
@ -1229,11 +1429,11 @@ export function applyAssistantMcpDiscoveryResponsePolicy(
|
||||||
const canApply =
|
const canApply =
|
||||||
Boolean(entryPoint) &&
|
Boolean(entryPoint) &&
|
||||||
(unsupportedBoundary || discoveryReadyChatCandidate || discoveryReadyDeepCandidate || discoveryReadyAddressCandidate) &&
|
(unsupportedBoundary || discoveryReadyChatCandidate || discoveryReadyDeepCandidate || discoveryReadyAddressCandidate) &&
|
||||||
!alignedFactualAddressReply &&
|
!alignedFactualAddressReplyProtectsCurrent &&
|
||||||
!matchedFactualAddressContinuationTarget &&
|
!matchedFactualAddressContinuationTargetProtectsCurrent &&
|
||||||
!matchedFactualSuggestedIntentPivotTarget &&
|
!matchedFactualSuggestedIntentPivotTarget &&
|
||||||
!fullConfirmedFactualAddressReply &&
|
!fullConfirmedFactualAddressReplyProtectsCurrent &&
|
||||||
!exactMatchedFactualAddressReply &&
|
!exactMatchedFactualAddressReplyProtectsCurrent &&
|
||||||
!runtimeAdjustedExactReply &&
|
!runtimeAdjustedExactReply &&
|
||||||
!runtimeMatchedExactReply &&
|
!runtimeMatchedExactReply &&
|
||||||
!staleMetadataDiscoveryFallbackAgainstExactAddressReply &&
|
!staleMetadataDiscoveryFallbackAgainstExactAddressReply &&
|
||||||
|
|
|
||||||
|
|
@ -699,6 +699,7 @@ function collectFollowupDiscoverySeed(followupContext: Record<string, unknown> |
|
||||||
const loopAskedDomainFamily = toNonEmptyString(followupContext?.previous_discovery_loop_asked_domain_family);
|
const loopAskedDomainFamily = toNonEmptyString(followupContext?.previous_discovery_loop_asked_domain_family);
|
||||||
const loopAskedActionFamily = toNonEmptyString(followupContext?.previous_discovery_loop_asked_action_family);
|
const loopAskedActionFamily = toNonEmptyString(followupContext?.previous_discovery_loop_asked_action_family);
|
||||||
const loopUnsupportedFamily = toNonEmptyString(followupContext?.previous_discovery_loop_unsupported_family);
|
const loopUnsupportedFamily = toNonEmptyString(followupContext?.previous_discovery_loop_unsupported_family);
|
||||||
|
const loopExplicitDateScope = toNonEmptyString(followupContext?.previous_discovery_loop_explicit_date_scope);
|
||||||
const loopMetadataScopeHint = toNonEmptyString(followupContext?.previous_discovery_loop_metadata_scope_hint);
|
const loopMetadataScopeHint = toNonEmptyString(followupContext?.previous_discovery_loop_metadata_scope_hint);
|
||||||
const loopSubjectResolutionOptional =
|
const loopSubjectResolutionOptional =
|
||||||
followupContext?.previous_discovery_loop_subject_resolution_optional === true;
|
followupContext?.previous_discovery_loop_subject_resolution_optional === true;
|
||||||
|
|
@ -763,7 +764,7 @@ function collectFollowupDiscoverySeed(followupContext: Record<string, unknown> |
|
||||||
const loopProvidedAllTimeScope = loopProvidedAxes.includes("all_time_scope");
|
const loopProvidedAllTimeScope = loopProvidedAxes.includes("all_time_scope");
|
||||||
const dateScope = loopProvidedAllTimeScope
|
const dateScope = loopProvidedAllTimeScope
|
||||||
? "all_time_scope"
|
? "all_time_scope"
|
||||||
: collectDateScopeFromFilters(previousFilters) ?? collectDateScopeFromFilters(rootFilters);
|
: loopExplicitDateScope ?? collectDateScopeFromFilters(previousFilters) ?? collectDateScopeFromFilters(rootFilters);
|
||||||
return {
|
return {
|
||||||
pilotScope: effectivePilotScope,
|
pilotScope: effectivePilotScope,
|
||||||
domain: mapped.domain,
|
domain: mapped.domain,
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import {
|
||||||
readAssistantMcpDiscoveryLoopAskedDomainFamily,
|
readAssistantMcpDiscoveryLoopAskedDomainFamily,
|
||||||
readAssistantMcpDiscoveryLoopAskedActionFamily,
|
readAssistantMcpDiscoveryLoopAskedActionFamily,
|
||||||
readAssistantMcpDiscoveryLoopUnsupportedFamily,
|
readAssistantMcpDiscoveryLoopUnsupportedFamily,
|
||||||
|
readAssistantMcpDiscoveryLoopExplicitDateScope,
|
||||||
readAssistantMcpDiscoveryLoopMetadataScopeHint,
|
readAssistantMcpDiscoveryLoopMetadataScopeHint,
|
||||||
readAssistantMcpDiscoveryLoopSubjectResolutionOptional,
|
readAssistantMcpDiscoveryLoopSubjectResolutionOptional,
|
||||||
readAddressDebugTemporalScope,
|
readAddressDebugTemporalScope,
|
||||||
|
|
@ -1388,6 +1389,10 @@ export function createAssistantTransitionPolicy(deps) {
|
||||||
carryoverSourceDebug,
|
carryoverSourceDebug,
|
||||||
deps.toNonEmptyString
|
deps.toNonEmptyString
|
||||||
);
|
);
|
||||||
|
const sourceDiscoveryLoopExplicitDateScope = readAssistantMcpDiscoveryLoopExplicitDateScope(
|
||||||
|
carryoverSourceDebug,
|
||||||
|
deps.toNonEmptyString
|
||||||
|
);
|
||||||
const sourceDiscoveryLoopMetadataScopeHint = readAssistantMcpDiscoveryLoopMetadataScopeHint(
|
const sourceDiscoveryLoopMetadataScopeHint = readAssistantMcpDiscoveryLoopMetadataScopeHint(
|
||||||
carryoverSourceDebug,
|
carryoverSourceDebug,
|
||||||
deps.toNonEmptyString
|
deps.toNonEmptyString
|
||||||
|
|
@ -1859,6 +1864,7 @@ export function createAssistantTransitionPolicy(deps) {
|
||||||
previous_discovery_loop_asked_domain_family: sourceDiscoveryLoopAskedDomainFamily ?? undefined,
|
previous_discovery_loop_asked_domain_family: sourceDiscoveryLoopAskedDomainFamily ?? undefined,
|
||||||
previous_discovery_loop_asked_action_family: sourceDiscoveryLoopAskedActionFamily ?? undefined,
|
previous_discovery_loop_asked_action_family: sourceDiscoveryLoopAskedActionFamily ?? undefined,
|
||||||
previous_discovery_loop_unsupported_family: sourceDiscoveryLoopUnsupportedFamily ?? undefined,
|
previous_discovery_loop_unsupported_family: sourceDiscoveryLoopUnsupportedFamily ?? undefined,
|
||||||
|
previous_discovery_loop_explicit_date_scope: sourceDiscoveryLoopExplicitDateScope ?? undefined,
|
||||||
previous_discovery_loop_metadata_scope_hint: sourceDiscoveryLoopMetadataScopeHint ?? undefined,
|
previous_discovery_loop_metadata_scope_hint: sourceDiscoveryLoopMetadataScopeHint ?? undefined,
|
||||||
previous_discovery_loop_subject_resolution_optional:
|
previous_discovery_loop_subject_resolution_optional:
|
||||||
sourceDiscoveryLoopSubjectResolutionOptional || undefined,
|
sourceDiscoveryLoopSubjectResolutionOptional || undefined,
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,76 @@ describe("assistant MCP discovery response policy", () => {
|
||||||
expect(result.reason_codes).toContain("mcp_discovery_response_policy_keep_current_clarification_required_reply");
|
expect(result.reason_codes).toContain("mcp_discovery_response_policy_keep_current_clarification_required_reply");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("applies a grounded movement candidate over a stale inventory item clarification", () => {
|
||||||
|
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
||||||
|
currentReply: "Need an item before I can continue.",
|
||||||
|
currentReplySource: "address_query_runtime_v1",
|
||||||
|
currentReplyType: "partial_coverage",
|
||||||
|
addressRuntimeMeta: {
|
||||||
|
truth_mode: "clarification_required",
|
||||||
|
answer_shape: "clarification_required",
|
||||||
|
answer_shape_contract: {
|
||||||
|
answer_shape: "clarification_required",
|
||||||
|
reply_type: "partial_coverage",
|
||||||
|
capability_contract_id: "inventory_inventory_purchase_documents_for_item"
|
||||||
|
},
|
||||||
|
detected_intent: "inventory_purchase_documents_for_item",
|
||||||
|
selected_recipe: "address_inventory_purchase_documents_for_item_v1",
|
||||||
|
capability_id: "inventory_inventory_purchase_documents_for_item",
|
||||||
|
missing_required_filters: ["item"],
|
||||||
|
assistant_mcp_discovery_entry_point_v1: entryPoint({
|
||||||
|
turn_input: {
|
||||||
|
adapter_status: "ready",
|
||||||
|
should_run_discovery: true,
|
||||||
|
turn_meaning_ref: {
|
||||||
|
asked_domain_family: "movements",
|
||||||
|
asked_action_family: "list_movements",
|
||||||
|
explicit_date_scope: "2020"
|
||||||
|
},
|
||||||
|
data_need_graph: {
|
||||||
|
business_fact_family: "movement_evidence",
|
||||||
|
action_family: "list_movements"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bridge: {
|
||||||
|
bridge_status: "answer_draft_ready",
|
||||||
|
user_facing_response_allowed: true,
|
||||||
|
business_fact_answer_allowed: true,
|
||||||
|
requires_user_clarification: false,
|
||||||
|
route_candidate: {
|
||||||
|
selected_chain_id: "movement_evidence"
|
||||||
|
},
|
||||||
|
pilot: {
|
||||||
|
selected_chain_id: "movement_evidence",
|
||||||
|
mcp_execution_performed: true
|
||||||
|
},
|
||||||
|
execution_handoff: {
|
||||||
|
mcp_execution_performed: true,
|
||||||
|
can_use_guarded_response: false
|
||||||
|
},
|
||||||
|
answer_draft: {
|
||||||
|
answer_mode: "bounded_inference_only",
|
||||||
|
headline: "Movement evidence for Group SVK in 2020 is bounded.",
|
||||||
|
confirmed_lines: ["No confirmed movement rows were found in the checked 2020 window."],
|
||||||
|
inference_lines: [],
|
||||||
|
unknown_lines: [],
|
||||||
|
limitation_lines: ["Full movement coverage outside the checked window is not confirmed."],
|
||||||
|
next_step_line: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.applied).toBe(true);
|
||||||
|
expect(result.reply_text).toContain("Movement evidence for Group SVK in 2020 is bounded.");
|
||||||
|
expect(result.reply_text).not.toContain("Need an item");
|
||||||
|
expect(result.reason_codes).toContain(
|
||||||
|
"mcp_discovery_response_policy_grounded_movement_candidate_priority_over_inventory_item_clarification"
|
||||||
|
);
|
||||||
|
expect(result.reason_codes).toContain("mcp_discovery_response_policy_candidate_applied");
|
||||||
|
});
|
||||||
|
|
||||||
it("keeps the current reply when the turn is not an unsupported boundary", () => {
|
it("keeps the current reply when the turn is not an unsupported boundary", () => {
|
||||||
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
||||||
currentReply: "regular chat",
|
currentReply: "regular chat",
|
||||||
|
|
@ -1252,6 +1322,107 @@ describe("assistant MCP discovery response policy", () => {
|
||||||
expect(result.reason_codes).toContain("mcp_discovery_response_policy_open_scope_value_flow_candidate_priority");
|
expect(result.reason_codes).toContain("mcp_discovery_response_policy_open_scope_value_flow_candidate_priority");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("overrides a year-ranking exact reply when the user asked who brought the most money", () => {
|
||||||
|
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
||||||
|
currentReply:
|
||||||
|
"Самый доходный год по подтвержденным поступлениям: 2020 (47.628.853,03 ₽ по 44 операциям).\nТоп-1 лет по сумме поступлений:\n1. 2020 | сумма: 47.628.853,03 ₽ | операций: 44 | контрагентов: 13",
|
||||||
|
currentReplySource: "address_query_runtime_v1",
|
||||||
|
currentReplyType: "factual",
|
||||||
|
addressRuntimeMeta: {
|
||||||
|
detected_intent: "customer_revenue_and_payments",
|
||||||
|
selected_recipe: "address_customer_revenue_and_payments_v1",
|
||||||
|
mcp_call_status: "matched_non_empty",
|
||||||
|
truth_mode: "confirmed",
|
||||||
|
capability_binding_status: "bound",
|
||||||
|
capability_binding_violations: [],
|
||||||
|
dialogContinuationContract: {
|
||||||
|
target_intent: "customer_revenue_and_payments"
|
||||||
|
},
|
||||||
|
truth_gate_contract_status: "full_confirmed",
|
||||||
|
assistant_truth_answer_policy_v1: {
|
||||||
|
truth_gate: {
|
||||||
|
coverage_status: "full",
|
||||||
|
grounding_status: "grounded",
|
||||||
|
source_truth_gate_status: "full_confirmed"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
assistant_mcp_discovery_entry_point_v1: entryPoint({
|
||||||
|
turn_input: {
|
||||||
|
adapter_status: "ready",
|
||||||
|
should_run_discovery: true,
|
||||||
|
data_need_graph: {
|
||||||
|
business_fact_family: "value_flow",
|
||||||
|
subject_candidates: ["Группа СВК"],
|
||||||
|
ranking_need: "top_desc",
|
||||||
|
reason_codes: ["data_need_graph_built", "data_need_graph_ranking_top_desc"]
|
||||||
|
},
|
||||||
|
turn_meaning_ref: {
|
||||||
|
raw_message: "И кто больше всего принес денег этой организации в 2020 году?",
|
||||||
|
effective_message: "Определить, какой контрагент принёс наибольшую выручку организации в 2020 году",
|
||||||
|
asked_domain_family: "counterparty_value",
|
||||||
|
asked_action_family: "turnover",
|
||||||
|
explicit_entity_candidates: ["Группа СВК"],
|
||||||
|
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||||||
|
explicit_date_scope: "2020"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bridge: {
|
||||||
|
bridge_status: "answer_draft_ready",
|
||||||
|
user_facing_response_allowed: true,
|
||||||
|
business_fact_answer_allowed: true,
|
||||||
|
requires_user_clarification: false,
|
||||||
|
route_candidate: {
|
||||||
|
selected_chain_id: "value_flow_ranking",
|
||||||
|
nearest_catalog_chain_template: "value_flow_ranking"
|
||||||
|
},
|
||||||
|
execution_handoff: {
|
||||||
|
selected_chain_id: "value_flow_ranking",
|
||||||
|
mcp_execution_performed: true,
|
||||||
|
can_use_guarded_response: false,
|
||||||
|
must_keep_internal_mechanics_hidden: true
|
||||||
|
},
|
||||||
|
pilot: {
|
||||||
|
pilot_scope: "counterparty_value_flow_query_movements_v1",
|
||||||
|
mcp_execution_performed: true,
|
||||||
|
evidence: {
|
||||||
|
evidence_status: "confirmed",
|
||||||
|
coverage_status: "full",
|
||||||
|
answer_permission: "confirmed_answer",
|
||||||
|
confirmed_facts: [
|
||||||
|
"1C incoming value-flow rows were ranked by counterparty for the checked scope; leader=Группа СВК, rows_with_amount=13"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
answer_draft: {
|
||||||
|
answer_mode: "confirmed_with_bounded_inference",
|
||||||
|
headline: "По данным 1С можно построить ограниченный рейтинг по контрагентам.",
|
||||||
|
confirmed_lines: [
|
||||||
|
"В проверенных входящих поступлениях найден один контрагент Группа СВК по организации ООО Альтернатива Плюс за период 2020: 12 093 465 руб. по 13 строкам с суммой."
|
||||||
|
],
|
||||||
|
inference_lines: [
|
||||||
|
"Рейтинг по контрагентам по организации ООО Альтернатива Плюс за период 2020 рассчитан только по подтвержденным строкам 1С."
|
||||||
|
],
|
||||||
|
unknown_lines: ["Полный рейтинг контрагентов вне периода 2020 этим поиском не подтвержден."],
|
||||||
|
limitation_lines: [],
|
||||||
|
next_step_line: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.applied).toBe(true);
|
||||||
|
expect(result.decision).toBe("apply_candidate");
|
||||||
|
expect(result.reply_text).toContain("Группа СВК");
|
||||||
|
expect(result.reply_text).toContain("12 093 465 руб.");
|
||||||
|
expect(result.reply_text).not.toContain("Самый доходный год");
|
||||||
|
expect(result.reply_text).not.toContain("Топ-1 лет");
|
||||||
|
expect(result.reason_codes).toContain(
|
||||||
|
"mcp_discovery_response_policy_grounded_value_flow_ranking_candidate_priority_over_current_exact_reply"
|
||||||
|
);
|
||||||
|
expect(result.reason_codes).toContain("mcp_discovery_response_policy_candidate_applied");
|
||||||
|
});
|
||||||
|
|
||||||
it("keeps full-confirmed factual address replies even when discovery has a guarded candidate", () => {
|
it("keeps full-confirmed factual address replies even when discovery has a guarded candidate", () => {
|
||||||
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
||||||
currentReply: "ООО Ромашка | сумма: 128000 | операций: 3",
|
currentReply: "ООО Ромашка | сумма: 128000 | операций: 3",
|
||||||
|
|
|
||||||
|
|
@ -838,6 +838,54 @@ describe("assistant MCP discovery turn input adapter", () => {
|
||||||
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("prefers semantic loop date scope over technical current-date filters for net value-flow follow-ups", () => {
|
||||||
|
const counterpartyName = "\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a";
|
||||||
|
const organizationName =
|
||||||
|
"\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441";
|
||||||
|
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||||
|
userMessage: "\u0430 \u043a\u0430\u043a\u043e\u0435 \u043d\u0435\u0442\u0442\u043e?",
|
||||||
|
assistantTurnMeaning: {
|
||||||
|
asked_domain_family: "counterparty",
|
||||||
|
asked_action_family: "turnover",
|
||||||
|
explicit_intent_candidate: "customer_revenue_and_payments"
|
||||||
|
},
|
||||||
|
followupContext: {
|
||||||
|
previous_discovery_pilot_scope: "counterparty_supplier_payout_query_movements_v1",
|
||||||
|
previous_discovery_loop_status: "ready_for_next_hop",
|
||||||
|
previous_discovery_loop_selected_chain_id: "value_flow",
|
||||||
|
previous_discovery_loop_asked_domain_family: "counterparty_value",
|
||||||
|
previous_discovery_loop_asked_action_family: "payout",
|
||||||
|
previous_discovery_loop_explicit_date_scope: "2020",
|
||||||
|
previous_discovery_loop_metadata_scope_hint: counterpartyName,
|
||||||
|
previous_filters: {
|
||||||
|
counterparty: counterpartyName,
|
||||||
|
organization: organizationName,
|
||||||
|
period_from: "2020-01-01",
|
||||||
|
period_to: "2026-06-03"
|
||||||
|
},
|
||||||
|
previous_anchor_type: "counterparty",
|
||||||
|
previous_anchor_value: counterpartyName
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.adapter_status).toBe("ready");
|
||||||
|
expect(result.should_run_discovery).toBe(true);
|
||||||
|
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||||||
|
expect(result.turn_meaning_ref).toMatchObject({
|
||||||
|
asked_domain_family: "counterparty_value",
|
||||||
|
asked_action_family: "net_value_flow",
|
||||||
|
explicit_entity_candidates: [counterpartyName],
|
||||||
|
explicit_organization_scope: organizationName,
|
||||||
|
explicit_date_scope: "2020",
|
||||||
|
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting",
|
||||||
|
stale_replay_forbidden: true
|
||||||
|
});
|
||||||
|
expect(result.data_need_graph?.comparison_need).toBe("incoming_vs_outgoing");
|
||||||
|
expect(result.reason_codes).toContain("mcp_discovery_grounded_value_flow_followup");
|
||||||
|
expect(result.reason_codes).toContain("mcp_discovery_date_scope_from_followup_context");
|
||||||
|
expect(result.reason_codes).toContain("mcp_discovery_bidirectional_value_flow_signal_detected");
|
||||||
|
});
|
||||||
|
|
||||||
it("prefers grounded discovery metadata scope over stale conflicting counterparty in a short net follow-up", () => {
|
it("prefers grounded discovery metadata scope over stale conflicting counterparty in a short net follow-up", () => {
|
||||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||||
userMessage: "а какое нетто?",
|
userMessage: "а какое нетто?",
|
||||||
|
|
|
||||||
|
|
@ -1903,6 +1903,7 @@ describe("assistantTransitionPolicy", () => {
|
||||||
expect(carryover?.followupContext?.previous_discovery_loop_unsupported_family).toBe(
|
expect(carryover?.followupContext?.previous_discovery_loop_unsupported_family).toBe(
|
||||||
"counterparty_value_or_turnover"
|
"counterparty_value_or_turnover"
|
||||||
);
|
);
|
||||||
|
expect(carryover?.followupContext?.previous_discovery_loop_explicit_date_scope).toBe("2020");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("treats a plain organization reply as continuation of a pending route-candidate organization scope", () => {
|
it("treats a plain organization reply as continuation of a pending route-candidate organization scope", () => {
|
||||||
|
|
@ -1963,6 +1964,7 @@ describe("assistantTransitionPolicy", () => {
|
||||||
expect(carryover?.followupContext?.previous_discovery_loop_status).toBe("awaiting_clarification");
|
expect(carryover?.followupContext?.previous_discovery_loop_status).toBe("awaiting_clarification");
|
||||||
expect(carryover?.followupContext?.previous_discovery_loop_selected_chain_id).toBe("value_flow_ranking");
|
expect(carryover?.followupContext?.previous_discovery_loop_selected_chain_id).toBe("value_flow_ranking");
|
||||||
expect(carryover?.followupContext?.previous_discovery_loop_pending_axes).toEqual(["organization"]);
|
expect(carryover?.followupContext?.previous_discovery_loop_pending_axes).toEqual(["organization"]);
|
||||||
|
expect(carryover?.followupContext?.previous_discovery_loop_explicit_date_scope).toBe("2020");
|
||||||
expect(carryover?.followupContext?.target_intent).toBe("customer_revenue_and_payments");
|
expect(carryover?.followupContext?.target_intent).toBe("customer_revenue_and_payments");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"generation_id": "gen-ag06031252-8c5319",
|
"generation_id": "gen-ag06031328-3e1dd1",
|
||||||
"created_at": "2026-06-03T12:52:51+00:00",
|
"created_at": "2026-06-03T13:28:09+00:00",
|
||||||
"mode": "saved_user_sessions",
|
"mode": "saved_user_sessions",
|
||||||
"title": "AGENT | Phase 83 mixed planner-brain replay for catalog alignment, pivots, and legacy continuity",
|
"title": "AGENT | Phase 83 mixed planner-brain replay for catalog alignment, pivots, and legacy continuity",
|
||||||
"count": 20,
|
"count": 20,
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
"а чем капибара отличается от утки?"
|
"а чем капибара отличается от утки?"
|
||||||
],
|
],
|
||||||
"generated_by": "codex_agent",
|
"generated_by": "codex_agent",
|
||||||
"saved_case_set_file": "assistant_autogen_saved_user_sessions_20260603125251_gen-ag06031252-8c5319.json",
|
"saved_case_set_file": "assistant_autogen_saved_user_sessions_20260603132809_gen-ag06031328-3e1dd1.json",
|
||||||
"context": {
|
"context": {
|
||||||
"llm_provider": null,
|
"llm_provider": null,
|
||||||
"model": null,
|
"model": null,
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
"autogen_personality_id": null,
|
"autogen_personality_id": null,
|
||||||
"autogen_personality_prompt": null,
|
"autogen_personality_prompt": null,
|
||||||
"source_session_id": null,
|
"source_session_id": null,
|
||||||
"saved_session_file": "assistant_saved_session_20260603125251_gen-ag06031252-8c5319.json",
|
"saved_session_file": "assistant_saved_session_20260603132809_gen-ag06031328-3e1dd1.json",
|
||||||
"saved_case_set_kind": "agent_semantic_scenario",
|
"saved_case_set_kind": "agent_semantic_scenario",
|
||||||
"agent_run": true,
|
"agent_run": true,
|
||||||
"agent_focus": "Mixed AGENT replay for Planner Autonomy Consolidation. The pack interleaves selected-counterparty catalog-alignment probes, open-organization money flow, ranking, broad-evaluation continuity, metadata drilldown, and off-domain living-chat safety.",
|
"agent_focus": "Mixed AGENT replay for Planner Autonomy Consolidation. The pack interleaves selected-counterparty catalog-alignment probes, open-organization money flow, ranking, broad-evaluation continuity, metadata drilldown, and off-domain living-chat safety.",
|
||||||
|
|
@ -85,7 +85,7 @@
|
||||||
"year_switch"
|
"year_switch"
|
||||||
],
|
],
|
||||||
"validation_status": "accepted_live_replay",
|
"validation_status": "accepted_live_replay",
|
||||||
"validated_run_dir": "artifacts\\domain_runs\\phase83_planner_brain_alignment_mix_20260603_live6",
|
"validated_run_dir": "artifacts\\domain_runs\\phase83_planner_brain_alignment_mix_20260603_live9",
|
||||||
"saved_after_validated_replay": true
|
"saved_after_validated_replay": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"saved_at": "2026-06-03T12:52:51+00:00",
|
"saved_at": "2026-06-03T13:28:09+00:00",
|
||||||
"generation_id": "gen-ag06031252-8c5319",
|
"generation_id": "gen-ag06031328-3e1dd1",
|
||||||
"mode": "saved_user_sessions",
|
"mode": "saved_user_sessions",
|
||||||
"title": "AGENT | Phase 83 mixed planner-brain replay for catalog alignment, pivots, and legacy continuity",
|
"title": "AGENT | Phase 83 mixed planner-brain replay for catalog alignment, pivots, and legacy continuity",
|
||||||
"agent_run": true,
|
"agent_run": true,
|
||||||
|
|
@ -73,12 +73,12 @@
|
||||||
"year_switch"
|
"year_switch"
|
||||||
],
|
],
|
||||||
"validation_status": "accepted_live_replay",
|
"validation_status": "accepted_live_replay",
|
||||||
"validated_run_dir": "artifacts\\domain_runs\\phase83_planner_brain_alignment_mix_20260603_live6",
|
"validated_run_dir": "artifacts\\domain_runs\\phase83_planner_brain_alignment_mix_20260603_live9",
|
||||||
"saved_after_validated_replay": true,
|
"saved_after_validated_replay": true,
|
||||||
"save_gate": {
|
"save_gate": {
|
||||||
"schema_version": "agent_semantic_save_gate_v1",
|
"schema_version": "agent_semantic_save_gate_v1",
|
||||||
"validation_status": "accepted_live_replay",
|
"validation_status": "accepted_live_replay",
|
||||||
"validated_run_dir": "artifacts\\domain_runs\\phase83_planner_brain_alignment_mix_20260603_live6",
|
"validated_run_dir": "artifacts\\domain_runs\\phase83_planner_brain_alignment_mix_20260603_live9",
|
||||||
"final_status": "accepted",
|
"final_status": "accepted",
|
||||||
"review_overall_status": "pass",
|
"review_overall_status": "pass",
|
||||||
"business_overall_status": "pass",
|
"business_overall_status": "pass",
|
||||||
|
|
@ -89,9 +89,9 @@
|
||||||
"steps_with_business_warnings": 0,
|
"steps_with_business_warnings": 0,
|
||||||
"acceptance_gate_passed": true,
|
"acceptance_gate_passed": true,
|
||||||
"effective_runtime": {
|
"effective_runtime": {
|
||||||
"manifest_path": "artifacts\\domain_runs\\phase83_planner_brain_alignment_mix_20260603_live6\\effective_runtime.json",
|
"manifest_path": "artifacts\\domain_runs\\phase83_planner_brain_alignment_mix_20260603_live9\\effective_runtime.json",
|
||||||
"runner": "domain_truth_harness.run-live",
|
"runner": "domain_truth_harness.run-live",
|
||||||
"git_sha": "f24cddc492138745aa896f52d1a6ebee8bab08ab",
|
"git_sha": "5ed25bad8115842e835b08b35a0edf623a757d9b",
|
||||||
"backend_url": "http://127.0.0.1:8787",
|
"backend_url": "http://127.0.0.1:8787",
|
||||||
"mcp_proxy_url": "http://127.0.0.1:6003",
|
"mcp_proxy_url": "http://127.0.0.1:6003",
|
||||||
"llm_provider": "local",
|
"llm_provider": "local",
|
||||||
|
|
@ -115,7 +115,7 @@
|
||||||
"message_id": "agent-user-001",
|
"message_id": "agent-user-001",
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"text": "привет, ты на связи?",
|
"text": "привет, ты на связи?",
|
||||||
"created_at": "2026-06-03T12:52:51+00:00",
|
"created_at": "2026-06-03T13:28:09+00:00",
|
||||||
"reply_type": null,
|
"reply_type": null,
|
||||||
"trace_id": null,
|
"trace_id": null,
|
||||||
"debug": null
|
"debug": null
|
||||||
|
|
@ -124,7 +124,7 @@
|
||||||
"message_id": "agent-user-002",
|
"message_id": "agent-user-002",
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"text": "найди в 1С контрагента СВК",
|
"text": "найди в 1С контрагента СВК",
|
||||||
"created_at": "2026-06-03T12:52:51+00:00",
|
"created_at": "2026-06-03T13:28:09+00:00",
|
||||||
"reply_type": null,
|
"reply_type": null,
|
||||||
"trace_id": null,
|
"trace_id": null,
|
||||||
"debug": null
|
"debug": null
|
||||||
|
|
@ -133,7 +133,7 @@
|
||||||
"message_id": "agent-user-003",
|
"message_id": "agent-user-003",
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"text": "сколько получили по нему за 2020 год",
|
"text": "сколько получили по нему за 2020 год",
|
||||||
"created_at": "2026-06-03T12:52:51+00:00",
|
"created_at": "2026-06-03T13:28:09+00:00",
|
||||||
"reply_type": null,
|
"reply_type": null,
|
||||||
"trace_id": null,
|
"trace_id": null,
|
||||||
"debug": null
|
"debug": null
|
||||||
|
|
@ -142,7 +142,7 @@
|
||||||
"message_id": "agent-user-004",
|
"message_id": "agent-user-004",
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"text": "а теперь сколько заплатили?",
|
"text": "а теперь сколько заплатили?",
|
||||||
"created_at": "2026-06-03T12:52:51+00:00",
|
"created_at": "2026-06-03T13:28:09+00:00",
|
||||||
"reply_type": null,
|
"reply_type": null,
|
||||||
"trace_id": null,
|
"trace_id": null,
|
||||||
"debug": null
|
"debug": null
|
||||||
|
|
@ -151,7 +151,7 @@
|
||||||
"message_id": "agent-user-005",
|
"message_id": "agent-user-005",
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"text": "а какое нетто?",
|
"text": "а какое нетто?",
|
||||||
"created_at": "2026-06-03T12:52:51+00:00",
|
"created_at": "2026-06-03T13:28:09+00:00",
|
||||||
"reply_type": null,
|
"reply_type": null,
|
||||||
"trace_id": null,
|
"trace_id": null,
|
||||||
"debug": null
|
"debug": null
|
||||||
|
|
@ -160,7 +160,7 @@
|
||||||
"message_id": "agent-user-006",
|
"message_id": "agent-user-006",
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"text": "а по документам?",
|
"text": "а по документам?",
|
||||||
"created_at": "2026-06-03T12:52:51+00:00",
|
"created_at": "2026-06-03T13:28:09+00:00",
|
||||||
"reply_type": null,
|
"reply_type": null,
|
||||||
"trace_id": null,
|
"trace_id": null,
|
||||||
"debug": null
|
"debug": null
|
||||||
|
|
@ -169,7 +169,7 @@
|
||||||
"message_id": "agent-user-007",
|
"message_id": "agent-user-007",
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"text": "а по движениям?",
|
"text": "а по движениям?",
|
||||||
"created_at": "2026-06-03T12:52:51+00:00",
|
"created_at": "2026-06-03T13:28:09+00:00",
|
||||||
"reply_type": null,
|
"reply_type": null,
|
||||||
"trace_id": null,
|
"trace_id": null,
|
||||||
"debug": null
|
"debug": null
|
||||||
|
|
@ -178,7 +178,7 @@
|
||||||
"message_id": "agent-user-008",
|
"message_id": "agent-user-008",
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"text": "Хочу быстрый денежный срез по одной организации без привязки к контрагенту. Сколько вообще входящих денег было за 2020 год?",
|
"text": "Хочу быстрый денежный срез по одной организации без привязки к контрагенту. Сколько вообще входящих денег было за 2020 год?",
|
||||||
"created_at": "2026-06-03T12:52:51+00:00",
|
"created_at": "2026-06-03T13:28:09+00:00",
|
||||||
"reply_type": null,
|
"reply_type": null,
|
||||||
"trace_id": null,
|
"trace_id": null,
|
||||||
"debug": null
|
"debug": null
|
||||||
|
|
@ -187,7 +187,7 @@
|
||||||
"message_id": "agent-user-009",
|
"message_id": "agent-user-009",
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"text": "По ООО Альтернатива Плюс.",
|
"text": "По ООО Альтернатива Плюс.",
|
||||||
"created_at": "2026-06-03T12:52:51+00:00",
|
"created_at": "2026-06-03T13:28:09+00:00",
|
||||||
"reply_type": null,
|
"reply_type": null,
|
||||||
"trace_id": null,
|
"trace_id": null,
|
||||||
"debug": null
|
"debug": null
|
||||||
|
|
@ -196,7 +196,7 @@
|
||||||
"message_id": "agent-user-010",
|
"message_id": "agent-user-010",
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"text": "Понял, тогда за все время.",
|
"text": "Понял, тогда за все время.",
|
||||||
"created_at": "2026-06-03T12:52:51+00:00",
|
"created_at": "2026-06-03T13:28:09+00:00",
|
||||||
"reply_type": null,
|
"reply_type": null,
|
||||||
"trace_id": null,
|
"trace_id": null,
|
||||||
"debug": null
|
"debug": null
|
||||||
|
|
@ -205,7 +205,7 @@
|
||||||
"message_id": "agent-user-011",
|
"message_id": "agent-user-011",
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"text": "Хорошо. А что по ООО Альтернатива Плюс больше в 2020 году: входящие или исходящие деньги?",
|
"text": "Хорошо. А что по ООО Альтернатива Плюс больше в 2020 году: входящие или исходящие деньги?",
|
||||||
"created_at": "2026-06-03T12:52:51+00:00",
|
"created_at": "2026-06-03T13:28:09+00:00",
|
||||||
"reply_type": null,
|
"reply_type": null,
|
||||||
"trace_id": null,
|
"trace_id": null,
|
||||||
"debug": null
|
"debug": null
|
||||||
|
|
@ -214,7 +214,7 @@
|
||||||
"message_id": "agent-user-012",
|
"message_id": "agent-user-012",
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"text": "А что по ООО Альтернатива Плюс больше уже за 2021 год: входящие или исходящие деньги?",
|
"text": "А что по ООО Альтернатива Плюс больше уже за 2021 год: входящие или исходящие деньги?",
|
||||||
"created_at": "2026-06-03T12:52:51+00:00",
|
"created_at": "2026-06-03T13:28:09+00:00",
|
||||||
"reply_type": null,
|
"reply_type": null,
|
||||||
"trace_id": null,
|
"trace_id": null,
|
||||||
"debug": null
|
"debug": null
|
||||||
|
|
@ -223,7 +223,7 @@
|
||||||
"message_id": "agent-user-013",
|
"message_id": "agent-user-013",
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"text": "И кто больше всего принес денег этой организации в 2020 году?",
|
"text": "И кто больше всего принес денег этой организации в 2020 году?",
|
||||||
"created_at": "2026-06-03T12:52:51+00:00",
|
"created_at": "2026-06-03T13:28:09+00:00",
|
||||||
"reply_type": null,
|
"reply_type": null,
|
||||||
"trace_id": null,
|
"trace_id": null,
|
||||||
"debug": null
|
"debug": null
|
||||||
|
|
@ -232,7 +232,7 @@
|
||||||
"message_id": "agent-user-014",
|
"message_id": "agent-user-014",
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"text": "А в 2021 году?",
|
"text": "А в 2021 году?",
|
||||||
"created_at": "2026-06-03T12:52:51+00:00",
|
"created_at": "2026-06-03T13:28:09+00:00",
|
||||||
"reply_type": null,
|
"reply_type": null,
|
||||||
"trace_id": null,
|
"trace_id": null,
|
||||||
"debug": null
|
"debug": null
|
||||||
|
|
@ -241,7 +241,7 @@
|
||||||
"message_id": "agent-user-015",
|
"message_id": "agent-user-015",
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"text": "а по Альтернативе Плюс сколько лет активности в базе 1С?",
|
"text": "а по Альтернативе Плюс сколько лет активности в базе 1С?",
|
||||||
"created_at": "2026-06-03T12:52:51+00:00",
|
"created_at": "2026-06-03T13:28:09+00:00",
|
||||||
"reply_type": null,
|
"reply_type": null,
|
||||||
"trace_id": null,
|
"trace_id": null,
|
||||||
"debug": null
|
"debug": null
|
||||||
|
|
@ -250,7 +250,7 @@
|
||||||
"message_id": "agent-user-016",
|
"message_id": "agent-user-016",
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"text": "Как ты оценишь деятельность компании?",
|
"text": "Как ты оценишь деятельность компании?",
|
||||||
"created_at": "2026-06-03T12:52:51+00:00",
|
"created_at": "2026-06-03T13:28:09+00:00",
|
||||||
"reply_type": null,
|
"reply_type": null,
|
||||||
"trace_id": null,
|
"trace_id": null,
|
||||||
"debug": null
|
"debug": null
|
||||||
|
|
@ -259,7 +259,7 @@
|
||||||
"message_id": "agent-user-017",
|
"message_id": "agent-user-017",
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"text": "какое нетто по деньгам с Группа СВК за 2020 год: сколько получили и сколько заплатили?",
|
"text": "какое нетто по деньгам с Группа СВК за 2020 год: сколько получили и сколько заплатили?",
|
||||||
"created_at": "2026-06-03T12:52:51+00:00",
|
"created_at": "2026-06-03T13:28:09+00:00",
|
||||||
"reply_type": null,
|
"reply_type": null,
|
||||||
"trace_id": null,
|
"trace_id": null,
|
||||||
"debug": null
|
"debug": null
|
||||||
|
|
@ -268,7 +268,7 @@
|
||||||
"message_id": "agent-user-018",
|
"message_id": "agent-user-018",
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"text": "какие справочники 1С есть по контрагентам?",
|
"text": "какие справочники 1С есть по контрагентам?",
|
||||||
"created_at": "2026-06-03T12:52:51+00:00",
|
"created_at": "2026-06-03T13:28:09+00:00",
|
||||||
"reply_type": null,
|
"reply_type": null,
|
||||||
"trace_id": null,
|
"trace_id": null,
|
||||||
"debug": null
|
"debug": null
|
||||||
|
|
@ -277,7 +277,7 @@
|
||||||
"message_id": "agent-user-019",
|
"message_id": "agent-user-019",
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"text": "давай дальше",
|
"text": "давай дальше",
|
||||||
"created_at": "2026-06-03T12:52:51+00:00",
|
"created_at": "2026-06-03T13:28:09+00:00",
|
||||||
"reply_type": null,
|
"reply_type": null,
|
||||||
"trace_id": null,
|
"trace_id": null,
|
||||||
"debug": null
|
"debug": null
|
||||||
|
|
@ -286,7 +286,7 @@
|
||||||
"message_id": "agent-user-020",
|
"message_id": "agent-user-020",
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"text": "а чем капибара отличается от утки?",
|
"text": "а чем капибара отличается от утки?",
|
||||||
"created_at": "2026-06-03T12:52:51+00:00",
|
"created_at": "2026-06-03T13:28:09+00:00",
|
||||||
"reply_type": null,
|
"reply_type": null,
|
||||||
"trace_id": null,
|
"trace_id": null,
|
||||||
"debug": null
|
"debug": null
|
||||||
|
|
@ -340,12 +340,12 @@
|
||||||
"year_switch"
|
"year_switch"
|
||||||
],
|
],
|
||||||
"validation_status": "accepted_live_replay",
|
"validation_status": "accepted_live_replay",
|
||||||
"validated_run_dir": "artifacts\\domain_runs\\phase83_planner_brain_alignment_mix_20260603_live6",
|
"validated_run_dir": "artifacts\\domain_runs\\phase83_planner_brain_alignment_mix_20260603_live9",
|
||||||
"saved_after_validated_replay": true,
|
"saved_after_validated_replay": true,
|
||||||
"save_gate": {
|
"save_gate": {
|
||||||
"schema_version": "agent_semantic_save_gate_v1",
|
"schema_version": "agent_semantic_save_gate_v1",
|
||||||
"validation_status": "accepted_live_replay",
|
"validation_status": "accepted_live_replay",
|
||||||
"validated_run_dir": "artifacts\\domain_runs\\phase83_planner_brain_alignment_mix_20260603_live6",
|
"validated_run_dir": "artifacts\\domain_runs\\phase83_planner_brain_alignment_mix_20260603_live9",
|
||||||
"final_status": "accepted",
|
"final_status": "accepted",
|
||||||
"review_overall_status": "pass",
|
"review_overall_status": "pass",
|
||||||
"business_overall_status": "pass",
|
"business_overall_status": "pass",
|
||||||
|
|
@ -356,9 +356,9 @@
|
||||||
"steps_with_business_warnings": 0,
|
"steps_with_business_warnings": 0,
|
||||||
"acceptance_gate_passed": true,
|
"acceptance_gate_passed": true,
|
||||||
"effective_runtime": {
|
"effective_runtime": {
|
||||||
"manifest_path": "artifacts\\domain_runs\\phase83_planner_brain_alignment_mix_20260603_live6\\effective_runtime.json",
|
"manifest_path": "artifacts\\domain_runs\\phase83_planner_brain_alignment_mix_20260603_live9\\effective_runtime.json",
|
||||||
"runner": "domain_truth_harness.run-live",
|
"runner": "domain_truth_harness.run-live",
|
||||||
"git_sha": "f24cddc492138745aa896f52d1a6ebee8bab08ab",
|
"git_sha": "5ed25bad8115842e835b08b35a0edf623a757d9b",
|
||||||
"backend_url": "http://127.0.0.1:8787",
|
"backend_url": "http://127.0.0.1:8787",
|
||||||
"mcp_proxy_url": "http://127.0.0.1:6003",
|
"mcp_proxy_url": "http://127.0.0.1:6003",
|
||||||
"llm_provider": "local",
|
"llm_provider": "local",
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
{
|
{
|
||||||
"suite_id": "assistant_saved_session_gen-ag06031252-8c5319",
|
"suite_id": "assistant_saved_session_gen-ag06031328-3e1dd1",
|
||||||
"suite_version": "0.1.0",
|
"suite_version": "0.1.0",
|
||||||
"schema_version": "assistant_saved_session_suite_v0_1",
|
"schema_version": "assistant_saved_session_suite_v0_1",
|
||||||
"generated_at": "2026-06-03T12:52:51+00:00",
|
"generated_at": "2026-06-03T13:28:09+00:00",
|
||||||
"generation_id": "gen-ag06031252-8c5319",
|
"generation_id": "gen-ag06031328-3e1dd1",
|
||||||
"mode": "saved_user_sessions",
|
"mode": "saved_user_sessions",
|
||||||
"title": "AGENT | Phase 83 mixed planner-brain replay for catalog alignment, pivots, and legacy continuity",
|
"title": "AGENT | Phase 83 mixed planner-brain replay for catalog alignment, pivots, and legacy continuity",
|
||||||
"domain": "planner_autonomy_consolidation",
|
"domain": "planner_autonomy_consolidation",
|
||||||
Loading…
Reference in New Issue