Planner Autonomy: закрепить bounded inference и semantic bridges

This commit is contained in:
2026-05-01 12:53:21 +03:00
parent 3634404b1c
commit c7c85e9b42
26 changed files with 3496 additions and 55 deletions
@@ -57,6 +57,7 @@ describe("assistant MCP catalog index", () => {
const valueFlowTemplate = getAssistantMcpCatalogChainTemplate("value_flow");
const valueFlowComparisonTemplate = getAssistantMcpCatalogChainTemplate("value_flow_comparison");
const valueFlowRankingTemplate = getAssistantMcpCatalogChainTemplate("value_flow_ranking");
const lifecycleTemplate = getAssistantMcpCatalogChainTemplate("lifecycle");
expect(documentTemplate.fallback_primitives).toEqual([
"resolve_entity_reference",
@@ -75,6 +76,16 @@ describe("assistant MCP catalog index", () => {
"aggregate_by_axis",
"probe_coverage"
]);
expect(lifecycleTemplate.base_required_axes).toEqual([
"document_date",
"coverage_target",
"evidence_basis",
"activity_window",
"legal_fact_boundary"
]);
expect(lifecycleTemplate.planning_tags).toEqual(
expect.arrayContaining(["bounded_inference", "activity_window", "legal_fact_boundary"])
);
});
it("can search reviewed primitives from data-need decomposition candidates", () => {
@@ -80,10 +80,18 @@ describe("assistant MCP discovery answer adapter", () => {
expect(draft.internal_mechanics_allowed).toBe(false);
expect(draft.headline).toContain("подтвержденная активность");
expect(draft.confirmed_lines[0]).toContain("SVK");
expect(draft.confirmed_lines[0]).toContain("matched_rows=1");
expect(draft.inference_lines[0]).toContain("меньше месяца");
expect(draft.inference_lines.join("\n")).toContain("Первая найденная активность: 2020-01-15");
expect(draft.inference_lines.join("\n")).toContain("не юридически подтвержденный возраст регистрации");
expect(pilot.evidence.inferred_facts).toContain(
"Activity window is bounded by first=2020-01-15, latest=2020-01-15, matched_rows=1"
);
expect(pilot.evidence.inferred_facts).toContain("Activity-window inference is not legal registration age");
expect(draft.unknown_lines).toContain("Legal registration date is not proven by this MCP discovery pilot");
expect(draft.unknown_lines).toContain(
"Business activity before the first confirmed 1C activity row is not proven by this MCP discovery pilot"
);
expect(draft.must_not_claim).toContain("Do not present inferred activity duration as a formally confirmed legal fact.");
expect(draft.reason_codes).toContain("answer_contains_unknown_fact_boundary");
});
@@ -912,10 +920,10 @@ describe("assistant MCP discovery answer adapter", () => {
const draft = buildAssistantMcpDiscoveryAnswerDraft(pilot);
expect(draft.inference_lines).toContain(
"Requested period coverage was recovered through monthly 1C value-flow probes after the broad probe hit the row limit"
"Requested period coverage was recovered through monthly 1C value-flow probes"
);
expect(draft.unknown_lines).not.toContain(
"Complete requested-period coverage is not proven because the MCP discovery probe row limit was reached"
"Complete requested-period coverage is not proven by the available checked rows"
);
});
@@ -971,6 +979,9 @@ describe("assistant MCP discovery answer adapter", () => {
expect(inferenceText).toContain("2020-01-15");
expect(inferenceText).toContain("2023-12-20");
expect(inferenceText).toContain("не юридически подтвержденный возраст регистрации");
expect(pilot.evidence.inferred_facts).toContain(
"Activity window is bounded by first=2020-01-15, latest=2023-12-20, matched_rows=2"
);
expect(draft.reason_codes).toContain("pilot_derived_activity_period_from_confirmed_rows");
});
@@ -714,7 +714,7 @@ describe("assistant MCP discovery pilot executor", () => {
expect(result.derived_value_flow?.coverage_limited_by_probe_limit).toBe(true);
expect(result.evidence.unknown_facts).toContain(
"Complete requested-period coverage is not proven because the MCP discovery probe row limit was reached"
"Complete requested-period coverage is not proven by the available checked rows"
);
});
@@ -760,10 +760,10 @@ describe("assistant MCP discovery pilot executor", () => {
latest_movement_date: "2020-12-05"
});
expect(result.evidence.inferred_facts).toContain(
"Requested period coverage was recovered through monthly 1C value-flow probes after the broad probe hit the row limit"
"Requested period coverage was recovered through monthly 1C value-flow probes"
);
expect(result.evidence.unknown_facts).not.toContain(
"Complete requested-period coverage is not proven because the MCP discovery probe row limit was reached"
"Complete requested-period coverage is not proven by the available checked rows"
);
expect(result.reason_codes).toContain("pilot_monthly_period_chunking_recovered_coverage");
});
@@ -942,10 +942,10 @@ describe("assistant MCP discovery pilot executor", () => {
}
});
expect(result.evidence.inferred_facts).toContain(
"Requested period coverage for bidirectional value-flow was recovered through monthly 1C side probes after a broad probe hit the row limit"
"Requested period coverage for bidirectional value-flow was recovered through monthly 1C side probes"
);
expect(result.evidence.unknown_facts).not.toContain(
"Complete requested-period coverage for bidirectional value-flow is not proven because at least one MCP discovery probe row limit was reached"
"Complete requested-period coverage for bidirectional value-flow is not proven by the available checked rows"
);
expect(result.reason_codes).toContain("pilot_bidirectional_outgoing_monthly_period_chunking_recovered_coverage");
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(14);
@@ -671,7 +671,16 @@ describe("assistant MCP discovery planner", () => {
"probe_coverage",
"explain_evidence_basis"
]);
expect(result.required_axes).toEqual(["counterparty", "document_date", "coverage_target", "evidence_basis"]);
expect(result.required_axes).toEqual([
"counterparty",
"document_date",
"coverage_target",
"evidence_basis",
"activity_window",
"legal_fact_boundary"
]);
expect(result.reason_codes).toContain("planner_lifecycle_bounded_activity_window_template");
expect(result.reason_codes).toContain("planner_lifecycle_legal_fact_boundary_required");
});
it("uses metadata-only planning when the user asks about available schema surface", () => {
@@ -133,6 +133,68 @@ describe("assistant MCP discovery turn input adapter", () => {
expect(result.reason_codes).not.toContain("mcp_discovery_payout_signal_detected");
});
it("overrides a supported exact current-turn payout route when the question asks for a payment amount", () => {
const result = buildAssistantMcpDiscoveryTurnInput({
userMessage:
"\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u044b \u0437\u0430\u043f\u043b\u0430\u0442\u0438\u043b\u0438 \u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a \u0437\u0430 2020 \u0433\u043e\u0434?",
effectiveMessage:
"\u0421\u043a\u043e\u043b\u044c\u043a\u043e \u0431\u044b\u043b\u043e \u043e\u043f\u043b\u0430\u0447\u0435\u043d\u043e \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0443 '\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a' \u0432 2020 \u0433\u043e\u0434\u0443?",
assistantTurnMeaning: {
asked_domain_family: "counterparty",
asked_action_family: "counterparty_population_and_roles",
explicit_intent_candidate: "counterparty_population_and_roles",
explicit_entity_candidates: [{ value: "\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a" }]
},
predecomposeContract: {
entities: { counterparty: "\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a" },
period: { 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.turn_meaning_ref).toMatchObject({
asked_domain_family: "counterparty_value",
asked_action_family: "payout",
explicit_entity_candidates: ["\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a"],
explicit_date_scope: "2020",
unsupported_but_understood_family: "counterparty_payouts_or_outflow",
stale_replay_forbidden: true
});
expect(result.reason_codes).toContain("mcp_discovery_current_turn_value_flow_overrides_supported_exact");
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
});
it("overrides a supported exact current-turn bank list when the question asks for net cash flow", () => {
const result = buildAssistantMcpDiscoveryTurnInput({
userMessage:
"\u043a\u0430\u043a\u043e\u0435 \u043d\u0435\u0442\u0442\u043e \u043f\u043e \u0434\u0435\u043d\u044c\u0433\u0430\u043c \u0441 \u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a \u0437\u0430 2020 \u0433\u043e\u0434: \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0438 \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u043f\u043b\u0430\u0442\u0438\u043b\u0438?",
assistantTurnMeaning: {
asked_domain_family: "counterparty",
asked_action_family: "bank_operations_by_counterparty",
explicit_intent_candidate: "bank_operations_by_counterparty",
explicit_entity_candidates: [{ value: "\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a" }]
},
predecomposeContract: {
entities: { counterparty: "\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a" },
period: { 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.turn_meaning_ref).toMatchObject({
asked_domain_family: "counterparty_value",
asked_action_family: "net_value_flow",
explicit_entity_candidates: ["\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a"],
explicit_date_scope: "2020",
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting",
stale_replay_forbidden: true
});
expect(result.reason_codes).toContain("mcp_discovery_current_turn_value_flow_overrides_supported_exact");
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
});
it("prefers the explicit current-turn counterparty over stale organization-scoped carryover in net follow-up discovery", () => {
const result = buildAssistantMcpDiscoveryTurnInput({
userMessage: "какое нетто по деньгам с Группа СВК за 2020 год: сколько получили и сколько заплатили?",
@@ -1347,6 +1409,26 @@ describe("assistant MCP discovery turn input adapter", () => {
expect(result.reason_codes).toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
});
it("does not replace broad business evaluation with metadata discovery", () => {
const result = buildAssistantMcpDiscoveryTurnInput({
userMessage:
"\u041a\u0430\u043a \u0442\u044b \u043e\u0446\u0435\u043d\u0438\u0448\u044c \u0434\u0435\u044f\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438?",
assistantTurnMeaning: {
asked_domain_family: "business_summary",
asked_action_family: "broad_evaluation",
explicit_date_scope: "2026-05-01",
unsupported_but_understood_family: "broad_business_evaluation",
stale_replay_forbidden: true
}
});
expect(result.adapter_status).toBe("not_applicable");
expect(result.should_run_discovery).toBe(false);
expect(result.turn_meaning_ref).toBeNull();
expect(result.reason_codes).toContain("mcp_discovery_broad_business_evaluation_kept_in_living_chat");
expect(result.reason_codes).toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
});
it("does not bootstrap metadata discovery from a referential document exclusion follow-up over exact document context", () => {
const result = buildAssistantMcpDiscoveryTurnInput({
userMessage: "кроме этого документа есть еще что-то?",