Стабилизировать денежные ответы и прибыль 1С-ассистента

This commit is contained in:
2026-05-23 13:14:22 +03:00
parent b18613961c
commit 473cdc3a9b
17 changed files with 1184 additions and 27 deletions
@@ -218,7 +218,8 @@ describe("assistant address orchestration runtime adapter", () => {
root_filters: expect.objectContaining({
organization: "Org A"
})
})
}),
knownOrganizations: ["Org A"]
})
);
});
@@ -578,6 +578,76 @@ describe("assistant MCP discovery response candidate", () => {
expect(candidate.reply_text).not.toContain("Складской срез");
});
it("lets compact cashflow wording override profit-boundary and overview prose", () => {
const candidate = buildAssistantMcpDiscoveryResponseCandidate(
entryPoint({
turn_input: {
adapter_status: "ready",
turn_meaning_ref: {
raw_message:
"\u0421\u043a\u043e\u043b\u044c\u043a\u043e \u0434\u0435\u043d\u0435\u0433 \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u0437\u0430\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0430 \u0437\u0430 2020 \u0433\u043e\u0434? \u041e\u0442\u0432\u0435\u0442\u044c \u043a\u043e\u0440\u043e\u0442\u043a\u043e: \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438, \u0437\u0430\u043f\u043b\u0430\u0442\u0438\u043b\u0438, \u0434\u0435\u043d\u0435\u0436\u043d\u043e\u0435 \u043d\u0435\u0442\u0442\u043e, \u044d\u0442\u043e \u043f\u0440\u0438\u0431\u044b\u043b\u044c \u0438\u043b\u0438 \u043d\u0435\u0442.",
asked_action_family: "profit_margin_boundary",
unsupported_but_understood_family: "profit_margin_boundary",
explicit_organization_scope:
"\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441",
explicit_date_scope: "2020"
},
data_need_graph: {
business_fact_family: "business_overview",
ranking_need: null,
reason_codes: [
"data_need_graph_family_business_overview",
"data_need_graph_business_overview_direct_money_answer"
]
}
},
bridge: {
bridge_status: "answer_draft_ready",
user_facing_response_allowed: true,
business_fact_answer_allowed: true,
requires_user_clarification: false,
pilot: {
pilot_scope: "business_overview_route_template_v1",
derived_business_overview: {
period_scope: "2020",
incoming_customer_revenue: {
total_amount_human_ru: "47 628 853,03 руб."
},
outgoing_supplier_payout: {
total_amount_human_ru: "43 763 351,53 руб."
},
net_amount_human_ru: "3 865 501,50 руб.",
net_direction: "net_incoming",
top_customers: [{ axis_value: "СБЕРБАНК", total_amount_human_ru: "12 792 194,31 руб." }],
top_suppliers: [],
accounting_financial_result: {
final_result_direction: "loss",
final_result_amount_human_ru: "7 136 815,85 руб.",
period_scope: "2020"
}
}
},
answer_draft: {
answer_mode: "confirmed_with_bounded_inference",
headline: "wide overview should not leak",
confirmed_lines: [],
inference_lines: [],
unknown_lines: [],
limitation_lines: [],
next_step_line: null
}
}
})
);
expect(candidate.reply_text).toContain("47 628 853,03");
expect(candidate.reply_text).toContain("43 763 351,53");
expect(candidate.reply_text).toContain("3 865 501,50");
expect(candidate.reply_text).not.toContain("СБЕРБАНК");
expect(candidate.reply_text).not.toContain("7 136 815,85");
expect(candidate.reply_text).not.toContain("wide overview");
});
it("labels organization-scoped bidirectional value-flow continuations as company scope", () => {
const candidate = buildAssistantMcpDiscoveryResponseCandidate(
entryPoint({
@@ -1789,6 +1789,56 @@ describe("assistant MCP discovery turn input adapter", () => {
expect(result.reason_codes).not.toContain("mcp_discovery_counterparty_from_predecompose");
});
it("grounds organization aliases from known organizations for clean-session earnings questions", () => {
const result = buildAssistantMcpDiscoveryTurnInput({
userMessage: "скока денег альтернатива заработала за 20 год?",
effectiveMessage: "скока денег альтернатива заработала за 20 год?",
knownOrganizations: ["ООО Альтернатива Плюс", "ООО Лайсвуд", "РАЙМ"],
predecomposeContract: {
period: {
period_from: "2020-01-01",
period_to: "2020-12-31",
has_explicit_period: true
}
}
});
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.turn_meaning_ref?.explicit_organization_scope).toBe("ООО Альтернатива Плюс");
expect(result.turn_meaning_ref?.explicit_date_scope).toBe("2020");
expect(result.reason_codes).toContain("mcp_discovery_organization_scope_from_known_organizations");
expect(result.data_need_graph?.clarification_gaps ?? []).not.toContain("organization");
});
it("treats a business-overview company alias misread as counterparty as organization scope", () => {
const result = buildAssistantMcpDiscoveryTurnInput({
userMessage: "Сколько денег Альтернатива заработала за 2020 год?",
effectiveMessage: "Определить финансовый результат компании Альтернатива за 2020 год",
assistantTurnMeaning: {
asked_domain_family: "business_summary",
asked_action_family: "broad_evaluation",
unsupported_but_understood_family: "broad_business_evaluation"
},
predecomposeContract: {
entities: { counterparty: "Альтернатива", organization: null },
period: {
period_from: "2020-01-01",
period_to: "2020-12-31",
has_explicit_period: true
}
}
});
expect(result.adapter_status).toBe("ready");
expect(result.data_need_graph?.business_fact_family).toBe("business_overview");
expect(result.turn_meaning_ref?.explicit_organization_scope).toBe("Альтернатива");
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
expect(result.data_need_graph?.clarification_gaps ?? []).not.toContain("organization");
expect(result.reason_codes).toContain("mcp_discovery_counterparty_reinterpreted_as_organization_scope");
});
it("keeps all-time business overview from reusing a negated VAT period as active scope", () => {
const result = buildAssistantMcpDiscoveryTurnInput({
userMessage:
@@ -2308,6 +2358,28 @@ describe("assistant MCP discovery turn input adapter", () => {
expect(result.data_need_graph?.reason_codes).toContain("data_need_graph_business_overview_direct_money_answer");
});
it("does not inherit ranking when a cashflow follow-up explicitly says no overview or tops", () => {
const orgName = "\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441";
const result = buildAssistantMcpDiscoveryTurnInput({
userMessage:
"\u043d\u0435 \u043e\u0431\u0437\u043e\u0440, \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u0435\u043d\u044c\u0433\u0438: \u043f\u0440\u0438\u0448\u043b\u043e, \u0443\u0448\u043b\u043e, \u043d\u0435\u0442\u0442\u043e \u0437\u0430 2020 \u0431\u0435\u0437 \u0442\u043e\u043f\u043e\u0432",
followupContext: {
previous_discovery_pilot_scope: "business_overview_route_template_v1",
previous_filters: {
organization: orgName
},
previous_seeded_ranking_need: "top_desc"
}
});
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?.ranking_need).toBeNull();
expect(result.turn_meaning_ref?.explicit_date_scope).toBe("2020");
expect(result.data_need_graph?.reason_codes).toContain("data_need_graph_business_overview_direct_money_answer");
});
it("routes organization-level profit and margin wording to business overview instead of exact value recipes", () => {
const orgName = "\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441";
const result = buildAssistantMcpDiscoveryTurnInput({