Закрепить бизнес-обзор контрагентских потоков
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
import iconv from "iconv-lite";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildAssistantMcpDiscoveryDataNeedGraph } from "../src/services/assistantMcpDiscoveryDataNeedGraph";
|
||||
|
||||
function toWin1251Mojibake(value: string): string {
|
||||
return iconv.decode(Buffer.from(value, "utf8"), "win1251");
|
||||
}
|
||||
|
||||
describe("assistant MCP discovery data need graph", () => {
|
||||
it("builds a monthly bidirectional value-flow graph from grounded turn meaning", () => {
|
||||
const result = buildAssistantMcpDiscoveryDataNeedGraph({
|
||||
@@ -403,6 +408,59 @@ describe("assistant MCP discovery data need graph", () => {
|
||||
expect(result.reason_codes).toContain("data_need_graph_comparison_incoming_vs_outgoing");
|
||||
});
|
||||
|
||||
it("keeps a wide formal business overview on the business-overview graph even when money net is requested", () => {
|
||||
const result = buildAssistantMcpDiscoveryDataNeedGraph({
|
||||
semanticDataNeed: "business overview evidence with bounded analyst interpretation",
|
||||
rawUtterance:
|
||||
"\u0414\u0430\u0439 \u0432\u0437\u0440\u043e\u0441\u043b\u044b\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 \u0437\u0430 2020 \u0433\u043e\u0434 \u043f\u043e \u0434\u0430\u043d\u043d\u044b\u043c 1\u0421: \u043e\u0431\u043e\u0440\u043e\u0442\u044b, \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u0438 \u0438\u0441\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u0434\u0435\u043d\u044c\u0433\u0438, \u043d\u0435\u0442\u0442\u043e, \u041d\u0414\u0421, \u0434\u043e\u043b\u0433\u0438, \u0441\u043a\u043b\u0430\u0434, \u043a\u043b\u0438\u0435\u043d\u0442\u044b, \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u0438 \u0438 \u0447\u0442\u043e \u043f\u043e\u043a\u0430 \u043d\u0435\u043b\u044c\u0437\u044f \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u0442\u044c.",
|
||||
turnMeaning: {
|
||||
asked_domain_family: "business_overview",
|
||||
asked_action_family: "broad_evaluation",
|
||||
unsupported_but_understood_family: "broad_business_evaluation",
|
||||
explicit_date_scope: "2020",
|
||||
explicit_organization_scope:
|
||||
"\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441"
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.business_fact_family).toBe("business_overview");
|
||||
expect(result.action_family).toBe("broad_evaluation");
|
||||
expect(result.comparison_need).toBeNull();
|
||||
expect(result.subject_candidates).toEqual([]);
|
||||
expect(result.reason_codes).toContain("data_need_graph_business_overview_wide_surface_preserved");
|
||||
expect(result.reason_codes).not.toContain(
|
||||
"data_need_graph_business_overview_bidirectional_money_comparison_routed_to_value_flow"
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps a wide formal business overview when the runtime signal is win1251 mojibake", () => {
|
||||
const wideQuestion =
|
||||
"\u0414\u0430\u0439 \u0432\u0437\u0440\u043e\u0441\u043b\u044b\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 \u0437\u0430 2020 \u0433\u043e\u0434 \u043f\u043e \u0434\u0430\u043d\u043d\u044b\u043c 1\u0421: \u043e\u0431\u043e\u0440\u043e\u0442\u044b, \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u0438 \u0438\u0441\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u0434\u0435\u043d\u044c\u0433\u0438, \u043d\u0435\u0442\u0442\u043e, \u041d\u0414\u0421, \u0434\u043e\u043b\u0433\u0438, \u0441\u043a\u043b\u0430\u0434, \u043a\u043b\u0438\u0435\u043d\u0442\u044b, \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u0438 \u0438 \u0447\u0442\u043e \u043f\u043e\u043a\u0430 \u043d\u0435\u043b\u044c\u0437\u044f \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u0442\u044c.";
|
||||
const legacyQuestion = toWin1251Mojibake(wideQuestion);
|
||||
const result = buildAssistantMcpDiscoveryDataNeedGraph({
|
||||
semanticDataNeed: "business overview evidence with bounded analyst interpretation",
|
||||
rawUtterance: legacyQuestion,
|
||||
turnMeaning: {
|
||||
raw_message: legacyQuestion,
|
||||
effective_message: legacyQuestion,
|
||||
asked_domain_family: "business_overview",
|
||||
asked_action_family: "broad_evaluation",
|
||||
unsupported_but_understood_family: "broad_business_evaluation",
|
||||
explicit_date_scope: "2020",
|
||||
explicit_organization_scope:
|
||||
"\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441"
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.business_fact_family).toBe("business_overview");
|
||||
expect(result.action_family).toBe("broad_evaluation");
|
||||
expect(result.comparison_need).toBeNull();
|
||||
expect(result.reason_codes).toContain("data_need_graph_business_overview_wide_surface_preserved");
|
||||
expect(result.reason_codes).not.toContain(
|
||||
"data_need_graph_business_overview_bidirectional_money_comparison_routed_to_value_flow"
|
||||
);
|
||||
});
|
||||
|
||||
it("treats a generic incoming total as an understood open-scope ask that still needs organization", () => {
|
||||
const result = buildAssistantMcpDiscoveryDataNeedGraph({
|
||||
semanticDataNeed: "counterparty value-flow evidence",
|
||||
|
||||
@@ -525,6 +525,177 @@ describe("assistant MCP discovery response policy", () => {
|
||||
expect(result.reason_codes).not.toContain("mcp_discovery_response_policy_keep_current_clarification_required_reply");
|
||||
});
|
||||
|
||||
it("uses a business-overview boundary clarification over a stale inventory-item clarification", () => {
|
||||
const valueFlowBundle = {
|
||||
counterparty: "Group SVK",
|
||||
incoming_customer_revenue: { total_amount_human_ru: "20 653 490 rub." },
|
||||
outgoing_supplier_payout: { total_amount_human_ru: "2 129 651 rub." },
|
||||
net_amount_human_ru: "18 523 839 rub.",
|
||||
net_direction: "net_incoming"
|
||||
};
|
||||
const documentBundle = { counterparty: "Group SVK", document_count: 19 };
|
||||
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
||||
currentReply: "Need item before I can continue.",
|
||||
currentReplySource: "address_query_runtime_v1",
|
||||
currentReplyType: "partial_coverage",
|
||||
addressRuntimeMeta: {
|
||||
detected_intent: "inventory_purchase_documents_for_item",
|
||||
selected_recipe: "address_inventory_purchase_documents_for_item_v1",
|
||||
capability_id: "inventory_inventory_purchase_documents_for_item",
|
||||
truth_mode: "clarification_required",
|
||||
answer_shape: "clarification_required",
|
||||
missing_required_filters: ["item"],
|
||||
answer_shape_contract: {
|
||||
answer_shape: "clarification_required",
|
||||
reply_type: "partial_coverage",
|
||||
capability_contract_id: "inventory_inventory_purchase_documents_for_item"
|
||||
},
|
||||
assistant_mcp_discovery_entry_point_v1: entryPoint({
|
||||
turn_input: {
|
||||
adapter_status: "ready",
|
||||
should_run_discovery: true,
|
||||
turn_meaning_ref: {
|
||||
asked_domain_family: "business_overview",
|
||||
asked_action_family: "broad_evaluation",
|
||||
business_overview_separate_entity_candidates: ["Group SVK"],
|
||||
previous_counterparty_value_flow_bundle: valueFlowBundle,
|
||||
previous_counterparty_document_bundle: documentBundle,
|
||||
unsupported_but_understood_family: "broad_business_evaluation"
|
||||
},
|
||||
data_need_graph: {
|
||||
business_fact_family: "business_overview",
|
||||
action_family: "broad_evaluation",
|
||||
clarification_gaps: ["organization"],
|
||||
reason_codes: ["data_need_graph_family_business_overview"]
|
||||
}
|
||||
},
|
||||
bridge: {
|
||||
bridge_status: "needs_clarification",
|
||||
user_facing_response_allowed: true,
|
||||
business_fact_answer_allowed: false,
|
||||
requires_user_clarification: true,
|
||||
answer_draft: {
|
||||
answer_mode: "needs_clarification",
|
||||
headline: "Specify organization.",
|
||||
confirmed_lines: [],
|
||||
inference_lines: [],
|
||||
unknown_lines: [],
|
||||
limitation_lines: [],
|
||||
next_step_line: "Specify organization."
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.applied).toBe(true);
|
||||
expect(result.decision).toBe("apply_candidate");
|
||||
expect(result.reply_text).toContain("Group SVK");
|
||||
expect(result.reply_text).toContain("20 653 490 rub.");
|
||||
expect(result.reply_text).toContain("19");
|
||||
expect(result.reply_text).not.toContain("Need item");
|
||||
expect(result.reason_codes).toContain(
|
||||
"mcp_discovery_response_policy_business_overview_boundary_candidate_priority_over_inventory_item_clarification"
|
||||
);
|
||||
expect(result.reason_codes).not.toContain("mcp_discovery_response_policy_keep_current_clarification_required_reply");
|
||||
});
|
||||
|
||||
it("uses a ready business-overview boundary answer over a stale inventory-item clarification", () => {
|
||||
const valueFlowBundle = {
|
||||
counterparty: "Group SVK",
|
||||
incoming_customer_revenue: { total_amount_human_ru: "20 653 490 rub." },
|
||||
outgoing_supplier_payout: { total_amount_human_ru: "2 129 651 rub." },
|
||||
net_amount_human_ru: "18 523 839 rub.",
|
||||
net_direction: "net_incoming"
|
||||
};
|
||||
const documentBundle = { counterparty: "Group SVK", document_count: 19 };
|
||||
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
||||
currentReply: "Need item before I can continue.",
|
||||
currentReplySource: "address_query_runtime_v1",
|
||||
currentReplyType: "partial_coverage",
|
||||
addressRuntimeMeta: {
|
||||
detected_intent: "inventory_purchase_documents_for_item",
|
||||
selected_recipe: "address_inventory_purchase_documents_for_item_v1",
|
||||
capability_id: "inventory_inventory_purchase_documents_for_item",
|
||||
truth_mode: "clarification_required",
|
||||
answer_shape: "clarification_required",
|
||||
missing_required_filters: ["item"],
|
||||
answer_shape_contract: {
|
||||
answer_shape: "clarification_required",
|
||||
reply_type: "partial_coverage",
|
||||
capability_contract_id: "inventory_inventory_purchase_documents_for_item"
|
||||
},
|
||||
assistant_mcp_discovery_entry_point_v1: entryPoint({
|
||||
hot_runtime_wired: true,
|
||||
turn_input: {
|
||||
adapter_status: "ready",
|
||||
should_run_discovery: true,
|
||||
turn_meaning_ref: {
|
||||
asked_domain_family: "business_overview",
|
||||
asked_action_family: "broad_evaluation",
|
||||
explicit_organization_scope: "Alternative Plus",
|
||||
business_overview_separate_entity_candidates: ["Group SVK"],
|
||||
previous_counterparty_value_flow_bundle: valueFlowBundle,
|
||||
previous_counterparty_document_bundle: documentBundle,
|
||||
unsupported_but_understood_family: "broad_business_evaluation"
|
||||
},
|
||||
data_need_graph: {
|
||||
business_fact_family: "business_overview",
|
||||
action_family: "broad_evaluation",
|
||||
clarification_gaps: [],
|
||||
reason_codes: ["data_need_graph_family_business_overview"]
|
||||
}
|
||||
},
|
||||
bridge: {
|
||||
bridge_status: "answer_draft_ready",
|
||||
hot_runtime_wired: true,
|
||||
user_facing_response_allowed: true,
|
||||
business_fact_answer_allowed: true,
|
||||
requires_user_clarification: false,
|
||||
execution_handoff: {
|
||||
mcp_execution_performed: true,
|
||||
can_use_guarded_response: true,
|
||||
must_keep_internal_mechanics_hidden: true
|
||||
},
|
||||
pilot: {
|
||||
pilot_scope: "business_overview_route_template_v1",
|
||||
mcp_execution_performed: true,
|
||||
derived_business_overview: {
|
||||
period_scope: "all_time",
|
||||
incoming_customer_revenue: { total_amount_human_ru: "285 819 547 rub." },
|
||||
outgoing_supplier_payout: { total_amount_human_ru: "137 963 720 rub." },
|
||||
net_amount_human_ru: "147 855 827 rub.",
|
||||
net_direction: "net_incoming",
|
||||
top_customers: [],
|
||||
top_suppliers: []
|
||||
}
|
||||
},
|
||||
answer_draft: {
|
||||
answer_mode: "confirmed_with_bounded_inference",
|
||||
headline: "Company summary.",
|
||||
confirmed_lines: [],
|
||||
inference_lines: [],
|
||||
unknown_lines: [],
|
||||
limitation_lines: [],
|
||||
next_step_line: null
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.applied).toBe(true);
|
||||
expect(result.decision).toBe("apply_candidate");
|
||||
expect(result.reply_text).toContain("Alternative Plus");
|
||||
expect(result.reply_text).toContain("Group SVK");
|
||||
expect(result.reply_text).toContain("20 653 490 rub.");
|
||||
expect(result.reply_text).not.toContain("Need item");
|
||||
expect(result.reason_codes).toContain(
|
||||
"mcp_discovery_response_policy_business_overview_boundary_candidate_priority_over_inventory_item_clarification"
|
||||
);
|
||||
expect(result.reason_codes).not.toContain("mcp_discovery_response_policy_keep_current_clarification_required_reply");
|
||||
});
|
||||
|
||||
it("overrides exact document-list replies for explicit counterparty received-paid-net questions", () => {
|
||||
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
||||
currentReply: "Контрагент: Группа СВК. Найдено документов: 19.",
|
||||
|
||||
Reference in New Issue
Block a user