Закрепить phase83 carryover и движения ассистента
This commit is contained in:
@@ -130,6 +130,76 @@ describe("assistant MCP discovery response policy", () => {
|
||||
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", () => {
|
||||
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
||||
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");
|
||||
});
|
||||
|
||||
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", () => {
|
||||
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
||||
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");
|
||||
});
|
||||
|
||||
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", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage: "а какое нетто?",
|
||||
|
||||
@@ -1903,6 +1903,7 @@ describe("assistantTransitionPolicy", () => {
|
||||
expect(carryover?.followupContext?.previous_discovery_loop_unsupported_family).toBe(
|
||||
"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", () => {
|
||||
@@ -1963,6 +1964,7 @@ describe("assistantTransitionPolicy", () => {
|
||||
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_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");
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user