Open-World: расширить бизнес-обзор факт-семействами
This commit is contained in:
@@ -180,6 +180,283 @@ describe("assistant MCP discovery answer adapter", () => {
|
||||
expect(draft.must_not_claim).toContain("Do not present the confirmed movement rows as a complete movement universe.");
|
||||
});
|
||||
|
||||
it("turns business overview multi-probe evidence into an analyst-safe draft", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
dataNeedGraph: {
|
||||
schema_version: "assistant_data_need_graph_v1",
|
||||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||||
subject_candidates: [],
|
||||
business_fact_family: "business_overview",
|
||||
action_family: "broad_evaluation",
|
||||
aggregation_need: null,
|
||||
time_scope_need: "all_time_scope",
|
||||
comparison_need: null,
|
||||
ranking_need: null,
|
||||
proof_expectation: "bounded_inference",
|
||||
clarification_gaps: [],
|
||||
decomposition_candidates: [
|
||||
"collect_scoped_movements",
|
||||
"aggregate_checked_amounts",
|
||||
"aggregate_ranked_axis_values",
|
||||
"fetch_supporting_documents",
|
||||
"probe_coverage",
|
||||
"explain_evidence_basis"
|
||||
],
|
||||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_profit_or_margin_claim_without_evidence"],
|
||||
reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"]
|
||||
},
|
||||
turnMeaning: {
|
||||
asked_domain_family: "business_summary",
|
||||
asked_action_family: "broad_evaluation",
|
||||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||||
unsupported_but_understood_family: "broad_business_evaluation"
|
||||
}
|
||||
});
|
||||
const pilot = await executeAssistantMcpDiscoveryPilot(
|
||||
planner,
|
||||
buildSequentialDeps([
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-01-15T00:00:00", Amount: 120000, Counterparty: "Клиент А" },
|
||||
{ Period: "2020-02-15T00:00:00", Amount: 80000, Counterparty: "Клиент Б" }
|
||||
]
|
||||
},
|
||||
{
|
||||
rows: [{ Period: "2020-01-20T00:00:00", Amount: 150000, Counterparty: "Поставщик А" }]
|
||||
},
|
||||
{
|
||||
rows: [
|
||||
{ Период: "2020-01-15T00:00:00", Регистратор: "Поступление 1" },
|
||||
{ Период: "2020-12-15T00:00:00", Регистратор: "Поступление 2" }
|
||||
]
|
||||
}
|
||||
])
|
||||
);
|
||||
|
||||
const draft = buildAssistantMcpDiscoveryAnswerDraft(pilot);
|
||||
|
||||
expect(draft.answer_mode).toBe("confirmed_with_bounded_inference");
|
||||
expect(draft.headline).toContain("бизнес-обзор");
|
||||
expect(draft.confirmed_lines.join("\n")).toContain("Входящие поступления");
|
||||
expect(draft.confirmed_lines.join("\n")).toContain("Самый крупный подтвержденный клиент");
|
||||
expect(draft.inference_lines.join("\n")).toContain("не прибыль и не маржа");
|
||||
expect(draft.unknown_lines.join("\n")).toContain("Прибыль и маржа");
|
||||
expect(draft.unknown_lines.join("\n")).toContain("Налоговая/VAT-позиция");
|
||||
expect(draft.must_not_claim).toContain("Do not present business overview cash-flow spread as profit or margin.");
|
||||
expect(draft.reason_codes).toContain("answer_contains_business_overview");
|
||||
});
|
||||
|
||||
it("surfaces checked VAT/tax position in business overview without treating it as profit", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
dataNeedGraph: {
|
||||
schema_version: "assistant_data_need_graph_v1",
|
||||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||||
subject_candidates: [],
|
||||
business_fact_family: "business_overview",
|
||||
action_family: "broad_evaluation",
|
||||
aggregation_need: null,
|
||||
time_scope_need: "explicit_period",
|
||||
comparison_need: null,
|
||||
ranking_need: null,
|
||||
proof_expectation: "bounded_inference",
|
||||
clarification_gaps: [],
|
||||
decomposition_candidates: [
|
||||
"collect_scoped_movements",
|
||||
"aggregate_checked_amounts",
|
||||
"aggregate_ranked_axis_values",
|
||||
"fetch_supporting_documents",
|
||||
"probe_coverage",
|
||||
"explain_evidence_basis"
|
||||
],
|
||||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_profit_or_margin_claim_without_evidence"],
|
||||
reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"]
|
||||
},
|
||||
turnMeaning: {
|
||||
asked_domain_family: "business_summary",
|
||||
asked_action_family: "broad_evaluation",
|
||||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||||
explicit_date_scope: "2020",
|
||||
unsupported_but_understood_family: "broad_business_evaluation"
|
||||
}
|
||||
});
|
||||
const pilot = await executeAssistantMcpDiscoveryPilot(
|
||||
planner,
|
||||
buildSequentialDeps([
|
||||
{ rows: [{ Period: "2020-01-15T00:00:00", Amount: 120000, Counterparty: "Клиент А" }] },
|
||||
{ rows: [{ Period: "2020-01-20T00:00:00", Amount: 50000, Counterparty: "Поставщик А" }] },
|
||||
{
|
||||
rows: [
|
||||
{ Регистратор: "VAT_BOOK_SALES", СчетДт: "68.02", Сумма: 40000 },
|
||||
{ Регистратор: "VAT_BOOK_PURCHASES", СчетДт: "19", Сумма: 12000 }
|
||||
]
|
||||
},
|
||||
{
|
||||
rows: [
|
||||
{ Период: "2020-01-15T00:00:00", Регистратор: "Поступление 1" },
|
||||
{ Период: "2020-12-15T00:00:00", Регистратор: "Поступление 2" }
|
||||
]
|
||||
}
|
||||
])
|
||||
);
|
||||
|
||||
const draft = buildAssistantMcpDiscoveryAnswerDraft(pilot);
|
||||
|
||||
expect(draft.headline).toContain("НДС-позиция");
|
||||
expect(draft.confirmed_lines.join("\n")).toContain("НДС-позиция за 2020");
|
||||
expect(draft.confirmed_lines.join("\n")).toContain("нетто к уплате 28 000 руб.");
|
||||
expect(draft.inference_lines.join("\n")).toContain("не прибыль и не маржа");
|
||||
expect(draft.unknown_lines.join("\n")).not.toContain("Налоговая/VAT-позиция");
|
||||
expect(draft.reason_codes).toContain("answer_contains_business_overview_tax_position");
|
||||
expect(draft.must_not_claim).toContain("Do not present business overview cash-flow spread as profit or margin.");
|
||||
});
|
||||
|
||||
it("surfaces checked debt-position and open-settlement quality without treating them as overdue debt", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
dataNeedGraph: {
|
||||
schema_version: "assistant_data_need_graph_v1",
|
||||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||||
subject_candidates: [],
|
||||
business_fact_family: "business_overview",
|
||||
action_family: "broad_evaluation",
|
||||
aggregation_need: null,
|
||||
time_scope_need: "explicit_period",
|
||||
comparison_need: null,
|
||||
ranking_need: null,
|
||||
proof_expectation: "bounded_inference",
|
||||
clarification_gaps: [],
|
||||
decomposition_candidates: [
|
||||
"collect_scoped_movements",
|
||||
"aggregate_checked_amounts",
|
||||
"aggregate_ranked_axis_values",
|
||||
"fetch_supporting_documents",
|
||||
"probe_coverage",
|
||||
"explain_evidence_basis"
|
||||
],
|
||||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_profit_or_margin_claim_without_evidence"],
|
||||
reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"]
|
||||
},
|
||||
turnMeaning: {
|
||||
asked_domain_family: "business_summary",
|
||||
asked_action_family: "broad_evaluation",
|
||||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||||
explicit_date_scope: "2020",
|
||||
unsupported_but_understood_family: "broad_business_evaluation"
|
||||
}
|
||||
});
|
||||
const pilot = await executeAssistantMcpDiscoveryPilot(
|
||||
planner,
|
||||
buildSequentialDeps([
|
||||
{ rows: [{ Period: "2020-01-15T00:00:00", Amount: 120000, Counterparty: "Клиент А" }] },
|
||||
{ rows: [{ Period: "2020-01-20T00:00:00", Amount: 50000, Counterparty: "Поставщик А" }] },
|
||||
{ rows: [] },
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-12-31T00:00:00", Amount: 100000, Counterparty: "Клиент А" }
|
||||
]
|
||||
},
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-12-31T00:00:00", Amount: 40000, Counterparty: "Поставщик А" }
|
||||
]
|
||||
},
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-12-31T00:00:00", Amount: 100000, Counterparty: "Клиент А", Contract: "Договор А" },
|
||||
{ Period: "2020-12-31T00:00:00", Amount: 50000, Counterparty: "Поставщик А", Contract: "Договор Б" }
|
||||
]
|
||||
},
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-01-15T00:00:00", Registrator: "Поступление 1" },
|
||||
{ Period: "2020-12-15T00:00:00", Registrator: "Поступление 2" }
|
||||
]
|
||||
}
|
||||
])
|
||||
);
|
||||
|
||||
const draft = buildAssistantMcpDiscoveryAnswerDraft(pilot);
|
||||
|
||||
expect(draft.headline).toContain("долговой срез");
|
||||
expect(draft.headline).toContain("качество открытых расчетов");
|
||||
expect(draft.confirmed_lines.join("\n")).toContain("Долговой срез на 2020-12-31");
|
||||
expect(draft.confirmed_lines.join("\n")).toContain("Качество открытых расчетов на 2020-12-31");
|
||||
expect(draft.confirmed_lines.join("\n")).toContain("нетто");
|
||||
expect(draft.unknown_lines.join("\n")).toContain("due-date");
|
||||
expect(draft.reason_codes).toContain("answer_contains_business_overview_debt_position");
|
||||
expect(draft.reason_codes).toContain("answer_contains_business_overview_open_settlement_quality");
|
||||
expect(draft.must_not_claim).toContain("Do not present a debt-position snapshot as debt aging, overdue debt, or credit-quality analysis.");
|
||||
expect(draft.must_not_claim).toContain("Do not present open-settlement concentration as contractual due-date aging or confirmed overdue debt.");
|
||||
});
|
||||
|
||||
it("surfaces checked inventory-position snapshot in business overview without treating it as warehouse liquidity", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
dataNeedGraph: {
|
||||
schema_version: "assistant_data_need_graph_v1",
|
||||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||||
subject_candidates: [],
|
||||
business_fact_family: "business_overview",
|
||||
action_family: "broad_evaluation",
|
||||
aggregation_need: null,
|
||||
time_scope_need: "explicit_period",
|
||||
comparison_need: null,
|
||||
ranking_need: null,
|
||||
proof_expectation: "bounded_inference",
|
||||
clarification_gaps: [],
|
||||
decomposition_candidates: [
|
||||
"collect_scoped_movements",
|
||||
"aggregate_checked_amounts",
|
||||
"aggregate_ranked_axis_values",
|
||||
"fetch_supporting_documents",
|
||||
"probe_coverage",
|
||||
"explain_evidence_basis"
|
||||
],
|
||||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_profit_or_margin_claim_without_evidence"],
|
||||
reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"]
|
||||
},
|
||||
turnMeaning: {
|
||||
asked_domain_family: "business_summary",
|
||||
asked_action_family: "broad_evaluation",
|
||||
explicit_organization_scope: "ООО Тест",
|
||||
explicit_date_scope: "2020",
|
||||
unsupported_but_understood_family: "broad_business_evaluation"
|
||||
}
|
||||
});
|
||||
const pilot = await executeAssistantMcpDiscoveryPilot(
|
||||
planner,
|
||||
buildSequentialDeps([
|
||||
{ rows: [{ Period: "2020-01-15T00:00:00", Amount: 120000, Counterparty: "Клиент А" }] },
|
||||
{ rows: [{ Period: "2020-01-20T00:00:00", Amount: 50000, Counterparty: "Поставщик А" }] },
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-12-31T00:00:00", Amount: 250000, Quantity: 10, Item: "Товар А" },
|
||||
{ Period: "2020-12-31T00:00:00", Amount: 50000, Quantity: 5, Item: "Товар Б" }
|
||||
]
|
||||
},
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-01-10T00:00:00", Amount: 200000, Quantity: 8, Item: "Товар А" }
|
||||
]
|
||||
},
|
||||
{ rows: [{ Period: "2020-01-15T00:00:00", Registrator: "Поступление 1" }] }
|
||||
])
|
||||
);
|
||||
|
||||
const draft = buildAssistantMcpDiscoveryAnswerDraft(pilot);
|
||||
|
||||
expect(draft.headline).toContain("складской срез");
|
||||
expect(draft.confirmed_lines.join("\n")).toContain("Складской срез на 2020-12-31");
|
||||
expect(draft.confirmed_lines.join("\n")).toContain("Товар А");
|
||||
expect(draft.unknown_lines.join("\n")).toContain("оборачиваемость");
|
||||
expect(draft.reason_codes).toContain("answer_contains_business_overview_inventory_position");
|
||||
expect(draft.must_not_claim).toContain("Do not present an inventory snapshot or purchase-date aging signal as turnover, obsolescence, liquidation value, or full inventory health.");
|
||||
});
|
||||
|
||||
it("renders metadata-scoped movement all-time follow-up as an all-time bounded answer", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
dataNeedGraph: {
|
||||
|
||||
@@ -103,7 +103,7 @@ describe("assistant MCP discovery pilot executor", () => {
|
||||
expect(deps.executeAddressMcpQuery).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("keeps business overview as an explicit unsupported runtime scope until the fresh multi-probe bridge exists", async () => {
|
||||
it("executes business overview as a bounded multi-probe bridge over money flow and activity evidence", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
dataNeedGraph: {
|
||||
schema_version: "assistant_data_need_graph_v1",
|
||||
@@ -134,24 +134,350 @@ describe("assistant MCP discovery pilot executor", () => {
|
||||
explicit_organization_scope: "ООО Альтернатива Плюс"
|
||||
}
|
||||
});
|
||||
const deps = buildDeps([]);
|
||||
const deps = buildSequentialDeps([
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-01-15T00:00:00", Amount: 120000, Counterparty: "Клиент А" },
|
||||
{ Period: "2020-02-15T00:00:00", Amount: 80000, Counterparty: "Клиент Б" }
|
||||
]
|
||||
},
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-01-20T00:00:00", Amount: 150000, Counterparty: "Поставщик А" }
|
||||
]
|
||||
},
|
||||
{
|
||||
rows: [
|
||||
{ Период: "2020-01-15T00:00:00", Регистратор: "Поступление 1" },
|
||||
{ Период: "2020-12-15T00:00:00", Регистратор: "Поступление 2" }
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||||
|
||||
expect(planner.planner_status).toBe("ready_for_execution");
|
||||
expect(result.pilot_status).toBe("unsupported");
|
||||
expect(result.pilot_status).toBe("executed");
|
||||
expect(result.pilot_scope).toBe("business_overview_route_template_v1");
|
||||
expect(result.mcp_execution_performed).toBe(false);
|
||||
expect(result.executed_primitives).toEqual([]);
|
||||
expect(result.mcp_execution_performed).toBe(true);
|
||||
expect(result.executed_primitives).toEqual(["query_movements", "query_documents"]);
|
||||
expect(result.skipped_primitives).toEqual([
|
||||
"query_movements",
|
||||
"aggregate_by_axis",
|
||||
"query_documents",
|
||||
"probe_coverage",
|
||||
"explain_evidence_basis"
|
||||
]);
|
||||
expect(result.reason_codes).toContain("pilot_scope_unsupported_for_live_execution");
|
||||
expect(deps.executeAddressMcpQuery).not.toHaveBeenCalled();
|
||||
expect(result.derived_business_overview).toMatchObject({
|
||||
organization_scope: "ООО Альтернатива Плюс",
|
||||
incoming_customer_revenue: {
|
||||
total_amount: 200000,
|
||||
rows_with_amount: 2
|
||||
},
|
||||
outgoing_supplier_payout: {
|
||||
total_amount: 150000,
|
||||
rows_with_amount: 1
|
||||
},
|
||||
net_amount: 50000,
|
||||
net_direction: "net_incoming"
|
||||
});
|
||||
expect(result.derived_business_overview?.top_customers[0]).toMatchObject({
|
||||
axis_value: "Клиент А",
|
||||
total_amount: 120000
|
||||
});
|
||||
expect(result.derived_business_overview?.activity_period?.duration_total_months).toBe(11);
|
||||
expect(result.evidence.confirmed_facts.join("\n")).toContain("В 1С подтверждены входящие поступления");
|
||||
expect(result.evidence.unknown_facts).toContain(
|
||||
"Прибыль и маржа этим бизнес-обзором не подтверждены: нужны себестоимость, расходы и закрывающие документы."
|
||||
);
|
||||
expect(result.reason_codes).toContain("pilot_derived_business_overview_from_confirmed_rows");
|
||||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it("adds a checked VAT/tax family to business overview only when an explicit period is available", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
dataNeedGraph: {
|
||||
schema_version: "assistant_data_need_graph_v1",
|
||||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||||
subject_candidates: [],
|
||||
business_fact_family: "business_overview",
|
||||
action_family: "broad_evaluation",
|
||||
aggregation_need: null,
|
||||
time_scope_need: "explicit_period",
|
||||
comparison_need: null,
|
||||
ranking_need: null,
|
||||
proof_expectation: "bounded_inference",
|
||||
clarification_gaps: [],
|
||||
decomposition_candidates: [
|
||||
"collect_scoped_movements",
|
||||
"aggregate_checked_amounts",
|
||||
"aggregate_ranked_axis_values",
|
||||
"fetch_supporting_documents",
|
||||
"probe_coverage",
|
||||
"explain_evidence_basis"
|
||||
],
|
||||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_profit_or_margin_claim_without_evidence"],
|
||||
reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"]
|
||||
},
|
||||
turnMeaning: {
|
||||
asked_domain_family: "business_overview",
|
||||
asked_action_family: "broad_evaluation",
|
||||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||||
explicit_date_scope: "2020"
|
||||
}
|
||||
});
|
||||
const deps = buildSequentialDeps([
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-01-15T00:00:00", Amount: 120000, Counterparty: "Клиент А" }
|
||||
]
|
||||
},
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-01-20T00:00:00", Amount: 50000, Counterparty: "Поставщик А" }
|
||||
]
|
||||
},
|
||||
{
|
||||
rows: [
|
||||
{ Регистратор: "VAT_BOOK_SALES", СчетДт: "68.02", Сумма: 40000 },
|
||||
{ Регистратор: "VAT_BOOK_PURCHASES", СчетДт: "19", Сумма: 12000 }
|
||||
]
|
||||
},
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{
|
||||
rows: [
|
||||
{ Период: "2020-01-15T00:00:00", Регистратор: "Поступление 1" },
|
||||
{ Период: "2020-12-15T00:00:00", Регистратор: "Поступление 2" }
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||||
|
||||
expect(result.pilot_status).toBe("executed");
|
||||
expect(result.derived_business_overview?.tax_position).toMatchObject({
|
||||
period_scope: "2020",
|
||||
rows_matched: 2,
|
||||
rows_with_amount: 2,
|
||||
sales_vat_amount: 40000,
|
||||
purchase_vat_amount: 12000,
|
||||
net_vat_amount: 28000,
|
||||
net_vat_direction: "vat_to_pay"
|
||||
});
|
||||
expect(result.derived_business_overview?.missing_signal_families).not.toContain("tax_position");
|
||||
expect(result.evidence.confirmed_facts.join("\n")).toContain("НДС-позиция за 2020 подтверждена");
|
||||
expect(result.evidence.unknown_facts.join("\n")).not.toContain("Налоговая/VAT-позиция этим бизнес-обзором не подтверждена");
|
||||
expect(result.reason_codes).toContain("pilot_business_overview_tax_query_mcp_executed");
|
||||
expect(result.reason_codes).toContain("pilot_derived_business_overview_tax_position_from_confirmed_rows");
|
||||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(9);
|
||||
const taxCall = deps.executeAddressMcpQuery.mock.calls[2]?.[0];
|
||||
expect(String(taxCall?.query ?? "")).toContain("НДСЗаписиКнигиПродаж");
|
||||
expect(String(taxCall?.query ?? "")).toContain("НДСЗаписиКнигиПокупок");
|
||||
});
|
||||
|
||||
it("adds a checked debt-position family to business overview only as an as-of-date snapshot", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
dataNeedGraph: {
|
||||
schema_version: "assistant_data_need_graph_v1",
|
||||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||||
subject_candidates: [],
|
||||
business_fact_family: "business_overview",
|
||||
action_family: "broad_evaluation",
|
||||
aggregation_need: null,
|
||||
time_scope_need: "explicit_period",
|
||||
comparison_need: null,
|
||||
ranking_need: null,
|
||||
proof_expectation: "bounded_inference",
|
||||
clarification_gaps: [],
|
||||
decomposition_candidates: [
|
||||
"collect_scoped_movements",
|
||||
"aggregate_checked_amounts",
|
||||
"aggregate_ranked_axis_values",
|
||||
"fetch_supporting_documents",
|
||||
"probe_coverage",
|
||||
"explain_evidence_basis"
|
||||
],
|
||||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_profit_or_margin_claim_without_evidence"],
|
||||
reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"]
|
||||
},
|
||||
turnMeaning: {
|
||||
asked_domain_family: "business_overview",
|
||||
asked_action_family: "broad_evaluation",
|
||||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||||
explicit_date_scope: "2020"
|
||||
}
|
||||
});
|
||||
const deps = buildSequentialDeps([
|
||||
{ rows: [{ Period: "2020-01-15T00:00:00", Amount: 120000, Counterparty: "Клиент А" }] },
|
||||
{ rows: [{ Period: "2020-01-20T00:00:00", Amount: 50000, Counterparty: "Поставщик А" }] },
|
||||
{ rows: [] },
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-12-31T00:00:00", Amount: 70000, Counterparty: "Клиент А" },
|
||||
{ Period: "2020-12-31T00:00:00", Amount: 30000, Counterparty: "Клиент Б" }
|
||||
]
|
||||
},
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-12-31T00:00:00", Amount: 40000, Counterparty: "Поставщик А" }
|
||||
]
|
||||
},
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-12-31T00:00:00", Amount: 90000, Counterparty: "Клиент А", Contract: "Договор А" },
|
||||
{ Period: "2020-12-31T00:00:00", Amount: 30000, Counterparty: "Клиент Б", Contract: "Договор Б" }
|
||||
]
|
||||
},
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-01-15T00:00:00", Registrator: "Поступление 1" },
|
||||
{ Period: "2020-12-15T00:00:00", Registrator: "Поступление 2" }
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||||
|
||||
expect(result.pilot_status).toBe("executed");
|
||||
expect(result.derived_business_overview?.debt_position).toMatchObject({
|
||||
as_of_date: "2020-12-31",
|
||||
receivables: {
|
||||
total_amount: 100000,
|
||||
rows_with_amount: 2
|
||||
},
|
||||
payables: {
|
||||
total_amount: 40000,
|
||||
rows_with_amount: 1
|
||||
},
|
||||
net_debt_position_amount: 60000,
|
||||
net_debt_position_direction: "net_receivable"
|
||||
});
|
||||
expect(result.derived_business_overview?.debt_position?.receivables.top_counterparties[0]).toMatchObject({
|
||||
axis_value: "Клиент А",
|
||||
total_amount: 70000
|
||||
});
|
||||
expect(result.derived_business_overview?.debt_open_settlement_quality).toMatchObject({
|
||||
as_of_date: "2020-12-31",
|
||||
rows_with_amount: 2,
|
||||
gross_open_amount: 120000,
|
||||
unique_counterparties: 2,
|
||||
unique_contracts: 2,
|
||||
concentration_top_contract_pct: 75
|
||||
});
|
||||
expect(result.derived_business_overview?.missing_signal_families).not.toContain("debt_position");
|
||||
expect(result.derived_business_overview?.missing_signal_families).not.toContain("debt_open_settlement_quality");
|
||||
expect(result.derived_business_overview?.missing_signal_families).toContain("debt_due_date_aging_quality");
|
||||
expect(result.evidence.confirmed_facts.join("\n")).toContain("Долговая позиция на 2020-12-31");
|
||||
expect(result.evidence.confirmed_facts.join("\n")).toContain("Качество открытых расчетов на 2020-12-31");
|
||||
expect(result.evidence.unknown_facts.join("\n")).toContain("due-date");
|
||||
expect(result.reason_codes).toContain("pilot_business_overview_debt_query_mcp_executed");
|
||||
expect(result.reason_codes).toContain("pilot_derived_business_overview_debt_position_from_confirmed_rows");
|
||||
expect(result.reason_codes).toContain("pilot_business_overview_open_contracts_query_mcp_executed");
|
||||
expect(result.reason_codes).toContain("pilot_derived_business_overview_open_settlement_quality_from_confirmed_rows");
|
||||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(9);
|
||||
const receivablesCall = deps.executeAddressMcpQuery.mock.calls[3]?.[0];
|
||||
const payablesCall = deps.executeAddressMcpQuery.mock.calls[4]?.[0];
|
||||
const openContractsCall = deps.executeAddressMcpQuery.mock.calls[5]?.[0];
|
||||
expect(String(receivablesCall?.query ?? "")).toContain("62");
|
||||
expect(String(payablesCall?.query ?? "")).toContain("60");
|
||||
expect(String(openContractsCall?.query ?? "")).toContain("СуммаРазвернутыйОстатокКт");
|
||||
});
|
||||
|
||||
it("adds a checked inventory-position family to business overview only as an as-of-date snapshot", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
dataNeedGraph: {
|
||||
schema_version: "assistant_data_need_graph_v1",
|
||||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||||
subject_candidates: [],
|
||||
business_fact_family: "business_overview",
|
||||
action_family: "broad_evaluation",
|
||||
aggregation_need: null,
|
||||
time_scope_need: "explicit_period",
|
||||
comparison_need: null,
|
||||
ranking_need: null,
|
||||
proof_expectation: "bounded_inference",
|
||||
clarification_gaps: [],
|
||||
decomposition_candidates: [
|
||||
"collect_scoped_movements",
|
||||
"aggregate_checked_amounts",
|
||||
"aggregate_ranked_axis_values",
|
||||
"fetch_supporting_documents",
|
||||
"probe_coverage",
|
||||
"explain_evidence_basis"
|
||||
],
|
||||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_profit_or_margin_claim_without_evidence"],
|
||||
reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"]
|
||||
},
|
||||
turnMeaning: {
|
||||
asked_domain_family: "business_overview",
|
||||
asked_action_family: "broad_evaluation",
|
||||
explicit_organization_scope: "ООО Тест",
|
||||
explicit_date_scope: "2020"
|
||||
}
|
||||
});
|
||||
const deps = buildSequentialDeps([
|
||||
{ rows: [{ Period: "2020-01-15T00:00:00", Amount: 120000, Counterparty: "Клиент А" }] },
|
||||
{ rows: [{ Period: "2020-01-20T00:00:00", Amount: 50000, Counterparty: "Поставщик А" }] },
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-12-31T00:00:00", Amount: 250000, Quantity: 10, Item: "Товар А" },
|
||||
{ Period: "2020-12-31T00:00:00", Amount: 50000, Quantity: 5, Item: "Товар Б" }
|
||||
]
|
||||
},
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-01-10T00:00:00", Amount: 200000, Quantity: 8, Item: "Товар А" },
|
||||
{ Period: "2020-11-01T00:00:00", Amount: 50000, Quantity: 2, Item: "Товар Б" }
|
||||
]
|
||||
},
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-01-15T00:00:00", Registrator: "Поступление 1" },
|
||||
{ Period: "2020-12-15T00:00:00", Registrator: "Поступление 2" }
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||||
|
||||
expect(result.pilot_status).toBe("executed");
|
||||
expect(result.derived_business_overview?.inventory_position).toMatchObject({
|
||||
as_of_date: "2020-12-31",
|
||||
rows_matched: 2,
|
||||
rows_with_amount: 2,
|
||||
rows_with_quantity: 2,
|
||||
total_amount: 300000,
|
||||
total_quantity: 15,
|
||||
aging_signal: {
|
||||
rows_matched: 2,
|
||||
rows_with_purchase_date: 2,
|
||||
oldest_purchase_date: "2020-01-10",
|
||||
latest_purchase_date: "2020-11-01",
|
||||
max_age_days: 356
|
||||
}
|
||||
});
|
||||
expect(result.derived_business_overview?.inventory_position?.top_items[0]).toMatchObject({
|
||||
item: "Товар А",
|
||||
total_amount: 250000,
|
||||
total_quantity: 10
|
||||
});
|
||||
expect(result.derived_business_overview?.missing_signal_families).not.toContain("inventory_position");
|
||||
expect(result.derived_business_overview?.missing_signal_families).toContain("inventory_turnover_quality");
|
||||
expect(result.evidence.confirmed_facts.join("\n")).toContain("Складской срез на 2020-12-31");
|
||||
expect(result.evidence.unknown_facts.join("\n")).toContain("оборачиваемость");
|
||||
expect(result.reason_codes).toContain("pilot_business_overview_inventory_query_mcp_executed");
|
||||
expect(result.reason_codes).toContain("pilot_derived_business_overview_inventory_position_from_confirmed_rows");
|
||||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(9);
|
||||
const inventoryCall = deps.executeAddressMcpQuery.mock.calls[6]?.[0];
|
||||
expect(inventoryCall?.account_scope).toContain("41.01");
|
||||
});
|
||||
|
||||
it("uses the explicit selected chain id when choosing the movement pilot scope", async () => {
|
||||
|
||||
@@ -221,7 +221,7 @@ describe("assistant MCP discovery planner", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("builds a catalog-compatible business overview plan without pretending the fresh runtime probe exists yet", () => {
|
||||
it("builds a catalog-compatible business overview plan for the fresh multi-probe runtime bridge", () => {
|
||||
const result = planAssistantMcpDiscovery({
|
||||
dataNeedGraph: {
|
||||
schema_version: "assistant_data_need_graph_v1",
|
||||
|
||||
@@ -737,4 +737,67 @@ describe("assistant MCP discovery response policy", () => {
|
||||
"mcp_discovery_response_policy_keep_broad_business_summary_over_clarification_candidate"
|
||||
);
|
||||
});
|
||||
|
||||
it("replaces deterministic broad business evaluation summary with a grounded business overview candidate", () => {
|
||||
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
||||
currentReply: "legacy broad summary",
|
||||
currentReplySource: "deterministic_broad_business_evaluation_contract",
|
||||
livingChatSource: "deterministic_broad_business_evaluation_contract",
|
||||
modeDecisionReason: "unsupported_current_turn_meaning_boundary",
|
||||
addressRuntimeMeta: {
|
||||
assistant_mcp_discovery_entry_point_v1: entryPoint({
|
||||
turn_input: {
|
||||
adapter_status: "ready",
|
||||
should_run_discovery: true,
|
||||
turn_meaning_ref: {
|
||||
asked_domain_family: "business_summary",
|
||||
asked_action_family: "broad_evaluation",
|
||||
unsupported_but_understood_family: "broad_business_evaluation",
|
||||
stale_replay_forbidden: true
|
||||
},
|
||||
data_need_graph: {
|
||||
business_fact_family: "business_overview",
|
||||
clarification_gaps: []
|
||||
}
|
||||
},
|
||||
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: "Business overview was assembled from confirmed 1C money flow and activity rows.",
|
||||
confirmed_lines: [
|
||||
"Incoming customer money flow: 200000.00 RUB.",
|
||||
"Outgoing supplier payouts: 150000.00 RUB.",
|
||||
"Top confirmed customer by incoming money flow: Client A - 120000.00 RUB."
|
||||
],
|
||||
inference_lines: [
|
||||
"Net confirmed cash-flow spread is +50000.00 RUB; this is not profit or margin."
|
||||
],
|
||||
unknown_lines: [
|
||||
"Profit and margin are not confirmed by this overview.",
|
||||
"VAT/tax position is not confirmed by this overview."
|
||||
],
|
||||
limitation_lines: ["Business overview is limited to checked 1C rows."],
|
||||
next_step_line: "Check profit/margin, debt quality, VAT/tax position, and inventory liquidity."
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.applied).toBe(true);
|
||||
expect(result.decision).toBe("apply_candidate");
|
||||
expect(result.reply_source).toBe("mcp_discovery_response_candidate_guarded");
|
||||
expect(result.reply_text).toContain("Incoming customer money flow");
|
||||
expect(result.reply_text).toContain("this is not profit or margin");
|
||||
expect(result.reply_text).toContain("VAT/tax position is not confirmed");
|
||||
expect(result.reply_text).not.toContain("query_movements");
|
||||
expect(result.reason_codes).toContain("mcp_discovery_response_policy_candidate_applied");
|
||||
expect(result.reason_codes).not.toContain(
|
||||
"mcp_discovery_response_policy_keep_broad_business_summary_over_clarification_candidate"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -37,6 +37,22 @@ function buildBidirectionalDeps(
|
||||
};
|
||||
}
|
||||
|
||||
function buildSequentialDeps(results: Array<{ rows: Array<Record<string, unknown>>; error?: string | null }>) {
|
||||
const executeAddressMcpQuery = vi.fn(async () => {
|
||||
const next = results.shift() ?? { rows: [] };
|
||||
const rows = next.rows;
|
||||
const error = next.error ?? null;
|
||||
return {
|
||||
fetched_rows: rows.length,
|
||||
matched_rows: error ? 0 : rows.length,
|
||||
raw_rows: rows,
|
||||
rows: error ? [] : rows,
|
||||
error
|
||||
};
|
||||
});
|
||||
return { executeAddressMcpQuery };
|
||||
}
|
||||
|
||||
function buildMetadataDeps(rows: Array<Record<string, unknown>>, error: string | null = null) {
|
||||
return {
|
||||
executeAddressMcpMetadata: vi.fn(async () => ({
|
||||
@@ -114,6 +130,56 @@ describe("assistant MCP discovery runtime entry point", () => {
|
||||
expect(result.reason_codes).toContain("mcp_discovery_unsupported_but_understood_turn");
|
||||
});
|
||||
|
||||
it("runs the business overview bridge from broad evaluation turn meaning through multi-probe evidence", async () => {
|
||||
const deps = buildSequentialDeps([
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-01-15T00:00:00", Amount: 120000, Counterparty: "Client A" },
|
||||
{ Period: "2020-02-15T00:00:00", Amount: 80000, Counterparty: "Client B" }
|
||||
]
|
||||
},
|
||||
{
|
||||
rows: [{ Period: "2020-01-20T00:00:00", Amount: 150000, Counterparty: "Supplier A" }]
|
||||
},
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-01-15T00:00:00", Registrar: "Customer payment 1" },
|
||||
{ Period: "2020-12-15T00:00:00", Registrar: "Customer payment 2" }
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
const result = await runAssistantMcpDiscoveryRuntimeEntryPoint({
|
||||
assistantTurnMeaning: {
|
||||
asked_domain_family: "business_summary",
|
||||
asked_action_family: "broad_evaluation",
|
||||
explicit_organization_scope: "Alternative Plus",
|
||||
unsupported_but_understood_family: "broad_business_evaluation",
|
||||
stale_replay_forbidden: true
|
||||
},
|
||||
deps
|
||||
});
|
||||
|
||||
expect(result.entry_status).toBe("bridge_executed");
|
||||
expect(result.discovery_attempted).toBe(true);
|
||||
expect(result.turn_input.semantic_data_need).toBe("business overview evidence with bounded analyst interpretation");
|
||||
expect(result.turn_input.data_need_graph?.business_fact_family).toBe("business_overview");
|
||||
expect(result.turn_input.data_need_graph?.clarification_gaps).toEqual([]);
|
||||
expect(result.bridge?.bridge_status).toBe("answer_draft_ready");
|
||||
expect(result.bridge?.pilot.pilot_scope).toBe("business_overview_route_template_v1");
|
||||
expect(result.bridge?.pilot.derived_business_overview).toMatchObject({
|
||||
organization_scope: "Alternative Plus",
|
||||
net_amount: 50000,
|
||||
net_direction: "net_incoming"
|
||||
});
|
||||
expect(result.bridge?.answer_draft.answer_mode).toBe("confirmed_with_bounded_inference");
|
||||
expect(result.bridge?.answer_draft.confirmed_lines.join("\n")).toContain("Client A");
|
||||
expect(result.bridge?.answer_draft.inference_lines.join("\n")).toContain("\u043d\u0435 \u043f\u0440\u0438\u0431\u044b\u043b\u044c");
|
||||
expect(result.bridge?.answer_draft.unknown_lines.join("\n")).toContain("VAT");
|
||||
expect(result.reason_codes).toContain("pilot_derived_business_overview_from_confirmed_rows");
|
||||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it("runs the bridge for raw metadata wording without an exact route owner", async () => {
|
||||
const result = await runAssistantMcpDiscoveryRuntimeEntryPoint({
|
||||
userMessage: "какие документы и поля есть в 1С по НДС?",
|
||||
|
||||
@@ -1409,7 +1409,7 @@ 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", () => {
|
||||
it("routes broad business evaluation into business overview discovery without metadata drift", () => {
|
||||
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?",
|
||||
@@ -1422,11 +1422,117 @@ describe("assistant MCP discovery turn input adapter", () => {
|
||||
}
|
||||
});
|
||||
|
||||
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");
|
||||
expect(result.adapter_status).toBe("ready");
|
||||
expect(result.should_run_discovery).toBe(true);
|
||||
expect(result.semantic_data_need).toBe("business overview evidence with bounded analyst interpretation");
|
||||
expect(result.data_need_graph?.business_fact_family).toBe("business_overview");
|
||||
expect(result.data_need_graph?.clarification_gaps).toContain("organization");
|
||||
expect(result.turn_meaning_ref).toMatchObject({
|
||||
asked_domain_family: "business_overview",
|
||||
asked_action_family: "broad_evaluation",
|
||||
explicit_date_scope: "2026-05-01",
|
||||
unsupported_but_understood_family: "broad_business_evaluation",
|
||||
stale_replay_forbidden: true
|
||||
});
|
||||
expect(result.reason_codes).toContain("mcp_discovery_broad_business_evaluation_route_candidate");
|
||||
expect(result.reason_codes).toContain("mcp_discovery_data_need_graph_built");
|
||||
expect(result.reason_codes).not.toContain("mcp_discovery_metadata_signal_detected");
|
||||
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||||
});
|
||||
|
||||
it("lets raw business-overview wording override stale exact turnover meaning", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage:
|
||||
"\u0414\u0430\u0439 \u0431\u0438\u0437\u043d\u0435\u0441-\u043e\u0431\u0437\u043e\u0440 \u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441 \u043f\u043e \u0434\u0430\u043d\u043d\u044b\u043c 1\u0421: \u043e\u0431\u043e\u0440\u043e\u0442\u044b, \u043d\u0435\u0442\u0442\u043e, \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u044c.",
|
||||
assistantTurnMeaning: {
|
||||
asked_domain_family: "counterparty",
|
||||
asked_action_family: "counterparty_value_or_turnover",
|
||||
explicit_intent_candidate: "customer_revenue_and_payments",
|
||||
explicit_entity_candidates: [{ value: "\u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u043e." }],
|
||||
stale_replay_forbidden: false
|
||||
},
|
||||
predecomposeContract: {
|
||||
entities: { organization: "\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441" },
|
||||
period: { period_to: "2026-05-03" }
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.adapter_status).toBe("ready");
|
||||
expect(result.should_run_discovery).toBe(true);
|
||||
expect(result.semantic_data_need).toBe("business overview evidence with bounded analyst interpretation");
|
||||
expect(result.data_need_graph?.business_fact_family).toBe("business_overview");
|
||||
expect(result.data_need_graph?.time_scope_need).toBe("all_time_scope");
|
||||
expect(result.data_need_graph?.clarification_gaps).toEqual([]);
|
||||
expect(result.turn_meaning_ref).toMatchObject({
|
||||
asked_domain_family: "business_overview",
|
||||
asked_action_family: "broad_evaluation",
|
||||
explicit_organization_scope: "\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441",
|
||||
unsupported_but_understood_family: "broad_business_evaluation",
|
||||
stale_replay_forbidden: true
|
||||
});
|
||||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||||
expect(result.reason_codes).toContain("mcp_discovery_broad_business_evaluation_route_candidate");
|
||||
expect(result.reason_codes).not.toContain("mcp_discovery_value_flow_signal_detected");
|
||||
});
|
||||
|
||||
it("keeps explicit year out of the organization scope for raw business overview wording", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage:
|
||||
"Дай бизнес-обзор ООО Альтернатива Плюс за 2020 год по данным 1С: деньги, нетто, активность, дебиторка и кредиторка на дату.",
|
||||
assistantTurnMeaning: {
|
||||
asked_domain_family: "business_summary",
|
||||
asked_action_family: "broad_evaluation",
|
||||
unsupported_but_understood_family: "broad_business_evaluation",
|
||||
stale_replay_forbidden: true
|
||||
},
|
||||
predecomposeContract: {
|
||||
entities: { organization: "ООО Альтернатива Плюс" },
|
||||
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.data_need_graph?.business_fact_family).toBe("business_overview");
|
||||
expect(result.data_need_graph?.time_scope_need).toBe("explicit_period");
|
||||
expect(result.turn_meaning_ref).toMatchObject({
|
||||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||||
explicit_date_scope: "2020"
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps all-time business overview from reusing a negated VAT period as active scope", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage:
|
||||
"\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u043e \u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441 \u0437\u0430 \u0432\u0441\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0434\u0430\u0439 \u0431\u0438\u0437\u043d\u0435\u0441-\u043e\u0431\u0437\u043e\u0440 \u0432 \u0446\u0435\u043b\u043e\u043c, \u043d\u043e \u043d\u0435 \u0442\u0430\u0449\u0438 \u041d\u0414\u0421 \u0437\u0430 2020 \u043a\u0430\u043a \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u0443\u044e \u043e\u0431\u0449\u0443\u044e \u043d\u0430\u043b\u043e\u0433\u043e\u0432\u0443\u044e \u043f\u043e\u0437\u0438\u0446\u0438\u044e.",
|
||||
assistantTurnMeaning: {
|
||||
asked_domain_family: "business_summary",
|
||||
asked_action_family: "broad_evaluation",
|
||||
explicit_date_scope: "2020-12-31",
|
||||
unsupported_but_understood_family: "broad_business_evaluation",
|
||||
stale_replay_forbidden: true
|
||||
},
|
||||
predecomposeContract: {
|
||||
entities: { organization: "\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441" },
|
||||
period: { period_to: "2020-12-31" }
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.adapter_status).toBe("ready");
|
||||
expect(result.should_run_discovery).toBe(true);
|
||||
expect(result.data_need_graph?.business_fact_family).toBe("business_overview");
|
||||
expect(result.data_need_graph?.time_scope_need).toBe("all_time_scope");
|
||||
expect(result.turn_meaning_ref).toMatchObject({
|
||||
asked_domain_family: "business_overview",
|
||||
asked_action_family: "broad_evaluation",
|
||||
explicit_organization_scope: "\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441",
|
||||
unsupported_but_understood_family: "broad_business_evaluation",
|
||||
stale_replay_forbidden: true
|
||||
});
|
||||
expect(result.turn_meaning_ref?.explicit_date_scope).toBeUndefined();
|
||||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||||
expect(result.reason_codes).toContain("mcp_discovery_all_time_scope_signal_detected");
|
||||
expect(result.reason_codes).toContain("mcp_discovery_negated_tax_period_scope_suppressed");
|
||||
});
|
||||
|
||||
it("does not bootstrap metadata discovery from a referential document exclusion follow-up over exact document context", () => {
|
||||
|
||||
@@ -126,4 +126,23 @@ describe("assistantTurnMeaningPolicy", () => {
|
||||
expect(meaning.unsupported_but_understood_family).toBe("broad_business_evaluation");
|
||||
expect(meaning.reason_codes).toContain("broad_business_evaluation_current_turn_signal");
|
||||
});
|
||||
|
||||
it("lets explicit business overview wording beat turnover and net-flow cues", () => {
|
||||
const policy = buildPolicy({
|
||||
resolveAddressIntent: () => ({ intent: "customer_revenue_and_payments", confidence: "high" })
|
||||
});
|
||||
|
||||
const meaning = policy.resolveAssistantTurnMeaning({
|
||||
rawUserMessage:
|
||||
"\u0414\u0430\u0439 \u0431\u0438\u0437\u043d\u0435\u0441-\u043e\u0431\u0437\u043e\u0440 \u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441: \u043e\u0431\u043e\u0440\u043e\u0442\u044b, \u043d\u0435\u0442\u0442\u043e, \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u044c."
|
||||
});
|
||||
|
||||
expect(meaning.explicit_intent_candidate).toBeNull();
|
||||
expect(meaning.asked_domain_family).toBe("business_summary");
|
||||
expect(meaning.asked_action_family).toBe("broad_evaluation");
|
||||
expect(meaning.explicit_entity_candidates).toEqual([]);
|
||||
expect(meaning.unsupported_but_understood_family).toBe("broad_business_evaluation");
|
||||
expect(meaning.stale_replay_forbidden).toBe(true);
|
||||
expect(meaning.reason_codes).toContain("broad_business_evaluation_current_turn_signal");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user