ARCH: удержать open-scope ranking через org clarification и year-switch
This commit is contained in:
@@ -84,13 +84,34 @@ describe("assistant MCP discovery data need graph", () => {
|
||||
|
||||
expect(result.business_fact_family).toBe("value_flow");
|
||||
expect(result.ranking_need).toBe("top_desc");
|
||||
expect(result.clarification_gaps).toEqual([]);
|
||||
expect(result.clarification_gaps).toEqual(["organization"]);
|
||||
expect(result.proof_expectation).toBe("clarification_required");
|
||||
expect(result.decomposition_candidates).toEqual([
|
||||
"collect_scoped_movements",
|
||||
"aggregate_ranked_axis_values",
|
||||
"probe_coverage"
|
||||
]);
|
||||
expect(result.reason_codes).toContain("data_need_graph_ranking_top_desc");
|
||||
expect(result.reason_codes).toContain("data_need_graph_open_scope_total_needs_organization");
|
||||
});
|
||||
|
||||
it("keeps organization-scoped ranking executable when the ranking axis comes from follow-up context", () => {
|
||||
const result = buildAssistantMcpDiscoveryDataNeedGraph({
|
||||
semanticDataNeed: "counterparty value-flow evidence",
|
||||
rawUtterance: "по ООО Альтернатива Плюс",
|
||||
turnMeaning: {
|
||||
asked_domain_family: "counterparty_value",
|
||||
asked_action_family: "turnover",
|
||||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||||
explicit_date_scope: "2020",
|
||||
seeded_ranking_need: "top_desc"
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.business_fact_family).toBe("value_flow");
|
||||
expect(result.ranking_need).toBe("top_desc");
|
||||
expect(result.clarification_gaps).toEqual([]);
|
||||
expect(result.proof_expectation).toBe("coverage_checked_fact");
|
||||
});
|
||||
|
||||
it("treats incoming-vs-outgoing comparison as an open-scope value need rather than a missing-subject fact ask", () => {
|
||||
@@ -106,7 +127,7 @@ describe("assistant MCP discovery data need graph", () => {
|
||||
|
||||
expect(result.business_fact_family).toBe("value_flow");
|
||||
expect(result.comparison_need).toBe("incoming_vs_outgoing");
|
||||
expect(result.clarification_gaps).toEqual([]);
|
||||
expect(result.clarification_gaps).toEqual(["organization"]);
|
||||
expect(result.decomposition_candidates).toEqual([
|
||||
"collect_incoming_movements",
|
||||
"collect_outgoing_movements",
|
||||
|
||||
@@ -235,6 +235,121 @@ describe("assistant MCP discovery response policy", () => {
|
||||
expect(result.reason_codes).toContain("mcp_discovery_response_policy_keep_factual_address_continuation_target");
|
||||
});
|
||||
|
||||
it("overrides an exact ranking-shaped address reply when open-scope ranking still needs organization", () => {
|
||||
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
||||
currentReply:
|
||||
"Самый доходный год по подтвержденным поступлениям: 2020 (15 744 052,48 ₽ по 20 операциям).",
|
||||
currentReplySource: "address_query_runtime_v1",
|
||||
currentReplyType: "factual",
|
||||
addressRuntimeMeta: {
|
||||
detected_intent: "customer_revenue_and_payments",
|
||||
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",
|
||||
clarification_gaps: ["organization"],
|
||||
reason_codes: ["data_need_graph_built", "data_need_graph_ranking_top_desc"]
|
||||
},
|
||||
turn_meaning_ref: {
|
||||
asked_domain_family: "counterparty_value",
|
||||
asked_action_family: "turnover",
|
||||
explicit_date_scope: "2020",
|
||||
seeded_ranking_need: "top_desc"
|
||||
}
|
||||
},
|
||||
bridge: {
|
||||
bridge_status: "answer_draft_ready",
|
||||
user_facing_response_allowed: true,
|
||||
business_fact_answer_allowed: false,
|
||||
requires_user_clarification: true,
|
||||
answer_draft: {
|
||||
answer_mode: "needs_clarification",
|
||||
headline: "Нужно уточнить организацию.",
|
||||
confirmed_lines: [],
|
||||
inference_lines: [],
|
||||
unknown_lines: ["Без организации поиск по контрагентам не запустить."],
|
||||
limitation_lines: [],
|
||||
next_step_line: "Уточните организацию, и я продолжу поиск по контрагентам."
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.applied).toBe(true);
|
||||
expect(result.decision).toBe("apply_candidate");
|
||||
expect(result.reason_codes).toContain("mcp_discovery_response_policy_semantic_conflict_allows_candidate_override");
|
||||
expect(result.reason_codes).not.toContain("mcp_discovery_response_policy_keep_aligned_factual_address_reply");
|
||||
expect(result.reason_codes).not.toContain("mcp_discovery_response_policy_keep_factual_address_continuation_target");
|
||||
expect(result.reason_codes).not.toContain("mcp_discovery_response_policy_keep_full_confirmed_factual_address_reply");
|
||||
});
|
||||
|
||||
it("overrides an exact ranking-shaped address reply when bounded open-scope ranking already has organization and period", () => {
|
||||
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
||||
currentReply:
|
||||
"Самый доходный клиент за доступное время по подтвержденным поступлениям: Группа СВК (12 224 925,00 ₽ по 16 операциям).",
|
||||
currentReplySource: "address_query_runtime_v1",
|
||||
currentReplyType: "factual",
|
||||
addressRuntimeMeta: {
|
||||
detected_intent: "customer_revenue_and_payments",
|
||||
dialogContinuationContract: {
|
||||
target_intent: "customer_revenue_and_payments"
|
||||
},
|
||||
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",
|
||||
clarification_gaps: [],
|
||||
reason_codes: ["data_need_graph_built", "data_need_graph_ranking_top_desc"]
|
||||
},
|
||||
turn_meaning_ref: {
|
||||
asked_domain_family: "counterparty_value",
|
||||
asked_action_family: "turnover",
|
||||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||||
explicit_date_scope: "2020",
|
||||
seeded_ranking_need: "top_desc"
|
||||
}
|
||||
},
|
||||
bridge: {
|
||||
bridge_status: "answer_draft_ready",
|
||||
user_facing_response_allowed: true,
|
||||
business_fact_answer_allowed: true,
|
||||
requires_user_clarification: false,
|
||||
answer_draft: {
|
||||
answer_mode: "confirmed_with_bounded_inference",
|
||||
headline: "Рейтинг по контрагентам построен по подтвержденным строкам 1С.",
|
||||
confirmed_lines: [
|
||||
"Больше всего денег принёс контрагент СБЕРБАНК, ПАО по организации ООО Альтернатива Плюс за период 2020: 12 792 194,31 руб. по 9 строкам с суммой."
|
||||
],
|
||||
inference_lines: [
|
||||
"Рейтинг по контрагентам по организации ООО Альтернатива Плюс за период 2020 рассчитан только по подтвержденным строкам 1С."
|
||||
],
|
||||
unknown_lines: ["Полный исторический рейтинг вне проверенного окна не доказан."],
|
||||
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("2020");
|
||||
expect(result.reason_codes).toContain("mcp_discovery_response_policy_semantic_conflict_allows_candidate_override");
|
||||
expect(result.reason_codes).not.toContain("mcp_discovery_response_policy_keep_aligned_factual_address_reply");
|
||||
expect(result.reason_codes).not.toContain("mcp_discovery_response_policy_keep_factual_address_continuation_target");
|
||||
});
|
||||
|
||||
it("keeps full-confirmed factual address replies even when discovery has a guarded candidate", () => {
|
||||
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
||||
currentReply: "ООО Ромашка | сумма: 128000 | операций: 3",
|
||||
|
||||
@@ -1273,7 +1273,7 @@ describe("assistant MCP discovery turn input adapter", () => {
|
||||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||||
expect(result.data_need_graph?.business_fact_family).toBe("value_flow");
|
||||
expect(result.data_need_graph?.ranking_need).toBe("top_desc");
|
||||
expect(result.data_need_graph?.clarification_gaps).toEqual([]);
|
||||
expect(result.data_need_graph?.clarification_gaps).toEqual(["organization"]);
|
||||
expect(result.data_need_graph?.decomposition_candidates).toEqual([
|
||||
"collect_scoped_movements",
|
||||
"aggregate_ranked_axis_values",
|
||||
@@ -1476,6 +1476,73 @@ describe("assistant MCP discovery turn input adapter", () => {
|
||||
expect(result.reason_codes).toContain("mcp_discovery_seeded_from_followup_context");
|
||||
expect(result.data_need_graph?.clarification_gaps).toEqual([]);
|
||||
});
|
||||
|
||||
it("resumes an open-scope ranking from follow-up context when the user clarifies only the organization", () => {
|
||||
const orgName = "ООО Альтернатива Плюс";
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage: "по ООО Альтернатива Плюс",
|
||||
predecomposeContract: {
|
||||
entities: { organization: orgName }
|
||||
},
|
||||
followupContext: {
|
||||
previous_discovery_pilot_scope: "counterparty_value_flow_query_movements_v1",
|
||||
previous_discovery_ranking_need: "top_desc",
|
||||
previous_filters: {
|
||||
period_from: "2020-01-01",
|
||||
period_to: "2020-12-31"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.adapter_status).toBe("ready");
|
||||
expect(result.should_run_discovery).toBe(true);
|
||||
expect(result.source_signal).toBe("followup_context");
|
||||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||||
expect(result.turn_meaning_ref).toMatchObject({
|
||||
asked_domain_family: "counterparty_value",
|
||||
asked_action_family: "turnover",
|
||||
seeded_ranking_need: "top_desc",
|
||||
explicit_organization_scope: orgName,
|
||||
explicit_date_scope: "2020",
|
||||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||||
stale_replay_forbidden: true
|
||||
});
|
||||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||||
expect(result.data_need_graph?.ranking_need).toBe("top_desc");
|
||||
expect(result.data_need_graph?.clarification_gaps).toEqual([]);
|
||||
});
|
||||
|
||||
it("keeps seeded ranking through a year-switch follow-up after organization clarification", () => {
|
||||
const orgName = "ООО Альтернатива Плюс";
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage: "Р° Р·Р° 2021?",
|
||||
followupContext: {
|
||||
previous_discovery_pilot_scope: "counterparty_value_flow_query_movements_v1",
|
||||
previous_discovery_ranking_need: "top_desc",
|
||||
previous_filters: {
|
||||
organization: orgName,
|
||||
period_from: "2020-01-01",
|
||||
period_to: "2020-12-31"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.adapter_status).toBe("ready");
|
||||
expect(result.should_run_discovery).toBe(true);
|
||||
expect(result.source_signal).toBe("followup_context");
|
||||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||||
expect(result.turn_meaning_ref).toMatchObject({
|
||||
asked_domain_family: "counterparty_value",
|
||||
asked_action_family: "turnover",
|
||||
seeded_ranking_need: "top_desc",
|
||||
explicit_organization_scope: orgName,
|
||||
explicit_date_scope: "2021",
|
||||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||||
stale_replay_forbidden: true
|
||||
});
|
||||
expect(result.data_need_graph?.ranking_need).toBe("top_desc");
|
||||
expect(result.data_need_graph?.clarification_gaps).toEqual([]);
|
||||
});
|
||||
it("forces discovery over a supported exact intent when organization-only follow-up resolves an open-scope total", () => {
|
||||
const orgName = "\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441";
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
|
||||
@@ -1274,6 +1274,64 @@ describe("assistantTransitionPolicy", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("carries ranking need from grounded discovery into followup context", () => {
|
||||
const policy = buildPolicy({
|
||||
findLastAddressAssistantItem: () => null,
|
||||
hasAddressFollowupContextSignal: () => true
|
||||
});
|
||||
|
||||
const carryover = policy.resolveAddressFollowupCarryoverContext(
|
||||
"по ООО Альтернатива Плюс",
|
||||
[
|
||||
{
|
||||
role: "assistant",
|
||||
text: "Нужно уточнить организацию, чтобы продолжить поиск по контрагентам.",
|
||||
debug: {
|
||||
execution_lane: "living_chat",
|
||||
mcp_discovery_response_applied: true,
|
||||
assistant_mcp_discovery_entry_point_v1: {
|
||||
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
|
||||
entry_status: "bridge_executed",
|
||||
turn_input: {
|
||||
data_need_graph: {
|
||||
business_fact_family: "value_flow",
|
||||
ranking_need: "top_desc",
|
||||
subject_candidates: [],
|
||||
clarification_gaps: ["organization"]
|
||||
},
|
||||
turn_meaning_ref: {
|
||||
asked_domain_family: "counterparty_value",
|
||||
asked_action_family: "turnover",
|
||||
explicit_date_scope: "2020",
|
||||
seeded_ranking_need: "top_desc"
|
||||
}
|
||||
},
|
||||
bridge: {
|
||||
bridge_status: "answer_draft_ready",
|
||||
business_fact_answer_allowed: false,
|
||||
pilot: {
|
||||
pilot_scope: "counterparty_value_flow_query_movements_v1"
|
||||
},
|
||||
answer_draft: {
|
||||
answer_mode: "needs_clarification"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
expect(carryover?.followupContext?.previous_discovery_pilot_scope).toBe(
|
||||
"counterparty_value_flow_query_movements_v1"
|
||||
);
|
||||
expect(carryover?.followupContext?.previous_discovery_ranking_need).toBe("top_desc");
|
||||
expect(carryover?.followupContext?.target_intent).toBe("customer_revenue_and_payments");
|
||||
});
|
||||
|
||||
it("carries grounded metadata downstream route hints into followup context", () => {
|
||||
const policy = buildPolicy({
|
||||
findLastAddressAssistantItem: () => null,
|
||||
|
||||
Reference in New Issue
Block a user