ARCH: ввести resumable discovery loop state для clarification follow-up
This commit is contained in:
@@ -110,6 +110,46 @@ describe("assistant MCP discovery runtime bridge", () => {
|
||||
expect(result.answer_draft.next_step_line).not.toContain("Уточните контрагента");
|
||||
});
|
||||
|
||||
it("emits a resumable loop state for clarification-driven ranking chains", async () => {
|
||||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||||
dataNeedGraph: {
|
||||
schema_version: "assistant_data_need_graph_v1",
|
||||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||||
subject_candidates: [],
|
||||
business_fact_family: "value_flow",
|
||||
action_family: "turnover",
|
||||
aggregation_need: null,
|
||||
time_scope_need: "explicit_period",
|
||||
comparison_need: null,
|
||||
ranking_need: "top_desc",
|
||||
proof_expectation: "coverage_checked_fact",
|
||||
clarification_gaps: [],
|
||||
decomposition_candidates: ["collect_scoped_movements", "aggregate_ranked_axis_values", "probe_coverage"],
|
||||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_unchecked_fact_totals"],
|
||||
reason_codes: ["data_need_graph_built", "data_need_graph_ranking_top_desc"]
|
||||
},
|
||||
turnMeaning: {
|
||||
asked_domain_family: "counterparty_value",
|
||||
asked_action_family: "turnover",
|
||||
explicit_date_scope: "2020"
|
||||
},
|
||||
deps: buildDeps([])
|
||||
});
|
||||
|
||||
expect(result.bridge_status).toBe("needs_clarification");
|
||||
expect(result.loop_state).toMatchObject({
|
||||
loop_status: "awaiting_clarification",
|
||||
selected_chain_id: "value_flow_ranking",
|
||||
asked_domain_family: "counterparty_value",
|
||||
asked_action_family: "turnover",
|
||||
ranking_need: "top_desc",
|
||||
explicit_date_scope: "2020"
|
||||
});
|
||||
expect(result.loop_state.pending_axes).toContain("organization");
|
||||
expect(result.loop_state.provided_axes).toContain("aggregate_axis");
|
||||
expect(result.reason_codes).toContain("runtime_bridge_loop_state_awaiting_clarification");
|
||||
});
|
||||
|
||||
it("produces a bounded ranked value-flow answer when period and organization are known", async () => {
|
||||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||||
dataNeedGraph: {
|
||||
|
||||
@@ -1598,6 +1598,81 @@ describe("assistant MCP discovery turn input adapter", () => {
|
||||
expect(result.data_need_graph?.clarification_gaps).toEqual([]);
|
||||
});
|
||||
|
||||
it("resumes an open-scope ranking from saved loop state even without a previous pilot scope", () => {
|
||||
const orgName = "ООО Альтернатива Плюс";
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage: "по ООО Альтернатива Плюс",
|
||||
predecomposeContract: {
|
||||
entities: { organization: orgName }
|
||||
},
|
||||
followupContext: {
|
||||
previous_discovery_loop_status: "awaiting_clarification",
|
||||
previous_discovery_loop_selected_chain_id: "value_flow_ranking",
|
||||
previous_discovery_loop_pending_axes: ["organization"],
|
||||
previous_discovery_loop_provided_axes: ["aggregate_axis", "amount", "coverage_target"],
|
||||
previous_discovery_loop_asked_domain_family: "counterparty_value",
|
||||
previous_discovery_loop_asked_action_family: "turnover",
|
||||
previous_discovery_loop_unsupported_family: "counterparty_value_or_turnover",
|
||||
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.data_need_graph?.ranking_need).toBe("top_desc");
|
||||
expect(result.reason_codes).toContain("mcp_discovery_resumed_from_saved_loop_state");
|
||||
});
|
||||
|
||||
it("resolves metadata lane choice from saved loop state even without a previous pilot scope", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage: "по движениям",
|
||||
followupContext: {
|
||||
previous_discovery_loop_status: "awaiting_clarification",
|
||||
previous_discovery_loop_selected_chain_id: "metadata_lane_clarification",
|
||||
previous_discovery_loop_pending_axes: ["lane_family_choice"],
|
||||
previous_discovery_loop_asked_domain_family: "metadata",
|
||||
previous_discovery_loop_asked_action_family: "resolve_next_lane",
|
||||
previous_discovery_loop_unsupported_family: "metadata_lane_choice_clarification",
|
||||
previous_filters: {
|
||||
counterparty: "Группа СВК",
|
||||
period_from: "2020-01-01",
|
||||
period_to: "2020-12-31"
|
||||
},
|
||||
previous_discovery_metadata_ambiguity_detected: true,
|
||||
previous_discovery_metadata_ambiguity_entity_sets: ["Документ", "РегистрНакопления"]
|
||||
}
|
||||
});
|
||||
|
||||
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("movement evidence");
|
||||
expect(result.turn_meaning_ref).toMatchObject({
|
||||
asked_domain_family: "movements",
|
||||
asked_action_family: "list_movements",
|
||||
explicit_entity_candidates: ["Группа СВК"],
|
||||
explicit_date_scope: "2020",
|
||||
unsupported_but_understood_family: "movement_evidence",
|
||||
stale_replay_forbidden: true
|
||||
});
|
||||
expect(result.reason_codes).toContain("mcp_discovery_resumed_from_saved_loop_state");
|
||||
});
|
||||
|
||||
it("keeps seeded ranking through a year-switch follow-up after organization clarification", () => {
|
||||
const orgName = "ООО Альтернатива Плюс";
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
|
||||
@@ -1332,6 +1332,88 @@ describe("assistantTransitionPolicy", () => {
|
||||
expect(carryover?.followupContext?.target_intent).toBe("customer_revenue_and_payments");
|
||||
});
|
||||
|
||||
it("carries resumable discovery loop state 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: "needs_clarification",
|
||||
business_fact_answer_allowed: false,
|
||||
pilot: {
|
||||
pilot_scope: "counterparty_value_flow_query_movements_v1"
|
||||
},
|
||||
loop_state: {
|
||||
schema_version: "assistant_mcp_discovery_loop_state_v1",
|
||||
policy_owner: "assistantMcpDiscoveryRuntimeBridge",
|
||||
loop_status: "awaiting_clarification",
|
||||
selected_chain_id: "value_flow_ranking",
|
||||
pilot_scope: "counterparty_value_flow_query_movements_v1",
|
||||
asked_domain_family: "counterparty_value",
|
||||
asked_action_family: "turnover",
|
||||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||||
ranking_need: "top_desc",
|
||||
pending_axes: ["organization"],
|
||||
provided_axes: ["aggregate_axis", "amount", "coverage_target"],
|
||||
explicit_entity_candidates: [],
|
||||
explicit_organization_scope: null,
|
||||
explicit_date_scope: "2020"
|
||||
},
|
||||
answer_draft: {
|
||||
answer_mode: "needs_clarification"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
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_provided_axes).toEqual([
|
||||
"aggregate_axis",
|
||||
"amount",
|
||||
"coverage_target"
|
||||
]);
|
||||
expect(carryover?.followupContext?.previous_discovery_loop_asked_domain_family).toBe("counterparty_value");
|
||||
expect(carryover?.followupContext?.previous_discovery_loop_asked_action_family).toBe("turnover");
|
||||
expect(carryover?.followupContext?.previous_discovery_loop_unsupported_family).toBe(
|
||||
"counterparty_value_or_turnover"
|
||||
);
|
||||
});
|
||||
|
||||
it("carries grounded metadata downstream route hints into followup context", () => {
|
||||
const policy = buildPolicy({
|
||||
findLastAddressAssistantItem: () => null,
|
||||
|
||||
Reference in New Issue
Block a user