ARCH: добить entity-resolution chain и очистить stale runtime
This commit is contained in:
@@ -183,6 +183,106 @@ describe("assistant MCP discovery answer adapter", () => {
|
||||
expect(draft.must_not_claim).toContain("Do not claim rows were checked when mcp_execution_performed=false.");
|
||||
});
|
||||
|
||||
it("keeps movement clarification anchored to the chosen lane after metadata ambiguity was resolved", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
asked_domain_family: "movements",
|
||||
asked_action_family: "list_movements",
|
||||
explicit_entity_candidates: ["НДС"],
|
||||
unsupported_but_understood_family: "movement_evidence"
|
||||
}
|
||||
});
|
||||
const pilot = await executeAssistantMcpDiscoveryPilot(planner, buildDeps([]));
|
||||
|
||||
const draft = buildAssistantMcpDiscoveryAnswerDraft(pilot);
|
||||
|
||||
expect(draft.answer_mode).toBe("needs_clarification");
|
||||
expect(draft.headline).toContain("движениям/регистрам");
|
||||
expect(draft.headline).toContain("НДС");
|
||||
expect(draft.headline).toContain("период");
|
||||
expect(draft.next_step_line).toContain("движениям/регистрам");
|
||||
expect(draft.next_step_line).toContain("НДС");
|
||||
expect(draft.next_step_line).toContain("период");
|
||||
});
|
||||
|
||||
it("turns resolved entity grounding into a human-safe entity search answer draft", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
asked_domain_family: "entity_resolution",
|
||||
asked_action_family: "search_business_entity",
|
||||
explicit_entity_candidates: ["Группа СВК"],
|
||||
unsupported_but_understood_family: "entity_resolution"
|
||||
}
|
||||
});
|
||||
const pilot = await executeAssistantMcpDiscoveryPilot(
|
||||
planner,
|
||||
buildDeps([
|
||||
{ Counterparty: "Группа СВК", CounterpartyRef: "Ref-1" },
|
||||
{ Counterparty: "СВК Логистика", CounterpartyRef: "Ref-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.inference_lines.join("\n")).toContain("заземление сущности");
|
||||
expect(draft.next_step_line).toContain("искать документы, движения или денежный поток");
|
||||
expect(draft.must_not_claim).toContain(
|
||||
"Do not present catalog grounding as confirmed business activity, turnover, or document evidence."
|
||||
);
|
||||
});
|
||||
|
||||
it("asks for clarification when entity grounding stays ambiguous", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
asked_domain_family: "entity_resolution",
|
||||
asked_action_family: "search_business_entity",
|
||||
explicit_entity_candidates: ["СВК"],
|
||||
unsupported_but_understood_family: "entity_resolution"
|
||||
}
|
||||
});
|
||||
const pilot = await executeAssistantMcpDiscoveryPilot(
|
||||
planner,
|
||||
buildDeps([
|
||||
{ Counterparty: "СВК-А", CounterpartyRef: "Ref-1" },
|
||||
{ Counterparty: "СВК-Б", CounterpartyRef: "Ref-2" }
|
||||
])
|
||||
);
|
||||
|
||||
const draft = buildAssistantMcpDiscoveryAnswerDraft(pilot);
|
||||
|
||||
expect(draft.answer_mode).toBe("needs_clarification");
|
||||
expect(draft.headline).toContain("несколько похожих контрагентов");
|
||||
expect(draft.inference_lines.join("\n")).toContain("СВК-А");
|
||||
expect(draft.next_step_line).toContain("точное название контрагента");
|
||||
});
|
||||
|
||||
it.skip("keeps entity search honest when no catalog candidate was confirmed", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
asked_domain_family: "entity_resolution",
|
||||
asked_action_family: "search_business_entity",
|
||||
explicit_entity_candidates: ["Несуществующий Контрагент"],
|
||||
unsupported_but_understood_family: "entity_resolution"
|
||||
}
|
||||
});
|
||||
const pilot = await executeAssistantMcpDiscoveryPilot(
|
||||
planner,
|
||||
buildDeps([{ Counterparty: "Группа СВК", CounterpartyRef: "Ref-1" }])
|
||||
);
|
||||
|
||||
const draft = buildAssistantMcpDiscoveryAnswerDraft(pilot);
|
||||
|
||||
expect(draft.answer_mode).toBe("checked_sources_only");
|
||||
expect(draft.headline).toContain("точный контрагент пока не подтвержден");
|
||||
expect(draft.unknown_lines).toContain(
|
||||
'No counterparty matching "Несуществующий Контрагент" was confirmed in the checked 1C catalog slice'
|
||||
);
|
||||
expect(draft.next_step_line).toContain("Дайте точное название или ИНН");
|
||||
});
|
||||
|
||||
it("turns metadata surface evidence into a human-safe metadata answer draft", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
@@ -481,4 +581,27 @@ describe("assistant MCP discovery answer adapter", () => {
|
||||
expect(inferenceText).toContain("не юридически подтвержденный возраст регистрации");
|
||||
expect(draft.reason_codes).toContain("pilot_derived_activity_period_from_confirmed_rows");
|
||||
});
|
||||
|
||||
it("keeps not-found entity search user-facing lines in Russian", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
asked_domain_family: "entity_resolution",
|
||||
asked_action_family: "search_business_entity",
|
||||
explicit_entity_candidates: ["\u041d\u0435\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u041a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442"],
|
||||
unsupported_but_understood_family: "entity_resolution"
|
||||
}
|
||||
});
|
||||
const pilot = await executeAssistantMcpDiscoveryPilot(
|
||||
planner,
|
||||
buildDeps([{ Counterparty: "\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a", CounterpartyRef: "Ref-1" }])
|
||||
);
|
||||
|
||||
const draft = buildAssistantMcpDiscoveryAnswerDraft(pilot);
|
||||
const unknownText = draft.unknown_lines.join("\n");
|
||||
|
||||
expect(draft.answer_mode).toBe("checked_sources_only");
|
||||
expect(unknownText).toContain("\u043d\u0435 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442");
|
||||
expect(unknownText).toContain("\u041d\u0435\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u041a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442");
|
||||
expect(unknownText).toContain("\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b, \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f \u0438 \u0434\u0435\u043d\u0435\u0436\u043d\u044b\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0438");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -103,6 +103,31 @@ describe("assistant MCP discovery pilot executor", () => {
|
||||
expect(deps.executeAddressMcpQuery).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("uses the explicit selected chain id when choosing the movement pilot scope", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
asked_domain_family: "movements",
|
||||
asked_action_family: "list_movements",
|
||||
explicit_entity_candidates: ["SVK"],
|
||||
explicit_date_scope: "2020",
|
||||
unsupported_but_understood_family: "movement_evidence"
|
||||
}
|
||||
});
|
||||
const deps = buildDeps([{ Period: "2020-01-15T00:00:00", Amount: 1250, Counterparty: "SVK", Registrar: "Move1" }]);
|
||||
|
||||
const result = await executeAssistantMcpDiscoveryPilot(
|
||||
{
|
||||
...planner,
|
||||
reason_codes: planner.reason_codes.filter((code) => !code.startsWith("planner_selected_"))
|
||||
},
|
||||
deps
|
||||
);
|
||||
|
||||
expect(result.pilot_status).toBe("executed");
|
||||
expect(result.pilot_scope).toBe("counterparty_movement_evidence_query_movements_v1");
|
||||
expect(result.executed_primitives).toEqual(["query_movements"]);
|
||||
});
|
||||
|
||||
it("executes generic document evidence through query_documents", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
@@ -224,6 +249,81 @@ describe("assistant MCP discovery pilot executor", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it.skip("executes entity-resolution search through the checked counterparty catalog slice", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
asked_domain_family: "entity_resolution",
|
||||
asked_action_family: "search_business_entity",
|
||||
explicit_entity_candidates: ["Группа СВК"],
|
||||
unsupported_but_understood_family: "entity_resolution"
|
||||
}
|
||||
});
|
||||
const deps = buildDeps([
|
||||
{ Counterparty: "Группа СВК", CounterpartyRef: "Ref-1" },
|
||||
{ Counterparty: "СВК Логистика", CounterpartyRef: "Ref-2" }
|
||||
]);
|
||||
|
||||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||||
|
||||
expect(result.pilot_status).toBe("executed");
|
||||
expect(result.pilot_scope).toBe("entity_resolution_search_v1");
|
||||
expect(result.mcp_execution_performed).toBe(true);
|
||||
expect(result.executed_primitives).toEqual(["search_business_entity"]);
|
||||
expect(result.skipped_primitives).toEqual(["resolve_entity_reference", "probe_coverage"]);
|
||||
expect(result.derived_entity_resolution).toMatchObject({
|
||||
requested_entity: "Группа СВК",
|
||||
resolution_status: "resolved",
|
||||
resolved_entity: "Группа СВК",
|
||||
resolved_reference: "Ref-1",
|
||||
confidence: "high",
|
||||
inference_basis: "catalog_counterparty_search_rows"
|
||||
});
|
||||
expect(result.evidence.confirmed_facts).toContain(
|
||||
"A matching 1C counterparty was found in the checked catalog slice: Группа СВК"
|
||||
);
|
||||
expect(result.evidence.inferred_facts).toContain(
|
||||
"Only catalog-level entity grounding was checked so far; no business rows were executed yet"
|
||||
);
|
||||
expect(result.evidence.unknown_facts).toContain(
|
||||
"No business documents, movements, or turnovers were checked yet; only catalog grounding was attempted"
|
||||
);
|
||||
expect(result.reason_codes).toContain("pilot_search_business_entity_mcp_executed");
|
||||
expect(result.reason_codes).toContain("pilot_derived_entity_resolution_from_catalog_rows");
|
||||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it.skip("keeps entity-resolution honest when several catalog candidates remain ambiguous", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
asked_domain_family: "entity_resolution",
|
||||
asked_action_family: "search_business_entity",
|
||||
explicit_entity_candidates: ["СВК"],
|
||||
unsupported_but_understood_family: "entity_resolution"
|
||||
}
|
||||
});
|
||||
const deps = buildDeps([
|
||||
{ Counterparty: "СВК-А", CounterpartyRef: "Ref-1" },
|
||||
{ Counterparty: "СВК-Б", CounterpartyRef: "Ref-2" }
|
||||
]);
|
||||
|
||||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||||
|
||||
expect(result.pilot_status).toBe("executed");
|
||||
expect(result.pilot_scope).toBe("entity_resolution_search_v1");
|
||||
expect(result.derived_entity_resolution).toMatchObject({
|
||||
requested_entity: "СВК",
|
||||
resolution_status: "ambiguous",
|
||||
resolved_entity: null,
|
||||
ambiguity_candidates: ["СВК-А", "СВК-Б"],
|
||||
confidence: "low"
|
||||
});
|
||||
expect(result.evidence.confirmed_facts).toEqual([]);
|
||||
expect(result.evidence.unknown_facts).toContain(
|
||||
"Exact 1C counterparty grounding remains ambiguous across: СВК-А, СВК-Б"
|
||||
);
|
||||
expect(result.reason_codes).toContain("pilot_entity_resolution_ambiguity_requires_clarification");
|
||||
});
|
||||
|
||||
it("keeps metadata grounding ambiguous when several surface families compete", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
@@ -263,6 +363,45 @@ describe("assistant MCP discovery pilot executor", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("infers metadata entity-set families from object names when meta type columns are absent", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
asked_domain_family: "metadata",
|
||||
asked_action_family: "inspect_surface",
|
||||
explicit_entity_candidates: ["НДС"]
|
||||
}
|
||||
});
|
||||
const deps = buildMetadataDeps([
|
||||
{
|
||||
FullName: "Документ.СчетФактураВыданный",
|
||||
attributes: [{ Name: "Дата" }]
|
||||
},
|
||||
{
|
||||
FullName: "РегистрНакопления.НДСПокупок",
|
||||
resources: [{ Name: "СуммаНДС" }]
|
||||
},
|
||||
{
|
||||
FullName: "Справочник.КодыОперацийНДС"
|
||||
}
|
||||
]);
|
||||
|
||||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||||
|
||||
expect(result.pilot_status).toBe("executed");
|
||||
expect(result.derived_metadata_surface).toMatchObject({
|
||||
metadata_scope: "НДС",
|
||||
available_entity_sets: ["Документ", "РегистрНакопления", "Справочник"],
|
||||
selected_entity_set: null,
|
||||
downstream_route_family: null,
|
||||
recommended_next_primitive: null,
|
||||
ambiguity_detected: true,
|
||||
ambiguity_entity_sets: ["Документ", "РегистрНакопления", "Справочник"]
|
||||
});
|
||||
expect(result.evidence.unknown_facts).toContain(
|
||||
"Exact downstream metadata surface remains ambiguous across: Документ, РегистрНакопления, Справочник"
|
||||
);
|
||||
});
|
||||
|
||||
it("executes value-flow query_movements and derives a guarded turnover sum", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
@@ -643,4 +782,54 @@ describe("assistant MCP discovery pilot executor", () => {
|
||||
expect(result.query_limitations).toContain("MCP fetch failed: timeout");
|
||||
expect(result.reason_codes).toContain("pilot_query_documents_mcp_error");
|
||||
});
|
||||
|
||||
it("emits Russian confirmed and bounded facts for resolved entity grounding", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
asked_domain_family: "entity_resolution",
|
||||
asked_action_family: "search_business_entity",
|
||||
explicit_entity_candidates: ["\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a"],
|
||||
unsupported_but_understood_family: "entity_resolution"
|
||||
}
|
||||
});
|
||||
const deps = buildDeps([
|
||||
{ Counterparty: "\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a", CounterpartyRef: "Ref-1" },
|
||||
{ Counterparty: "\u0421\u0412\u041a \u041b\u043e\u0433\u0438\u0441\u0442\u0438\u043a\u0430", CounterpartyRef: "Ref-2" }
|
||||
]);
|
||||
|
||||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||||
|
||||
expect(result.evidence.confirmed_facts.join("\n")).toContain("\u043d\u0430\u0439\u0434\u0435\u043d \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442");
|
||||
expect(result.evidence.confirmed_facts.join("\n")).toContain("\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a");
|
||||
expect(result.evidence.inferred_facts.join("\n")).toContain(
|
||||
"\u041f\u043e\u043a\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u0435\u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u0437\u0435\u043c\u043b\u0435\u043d\u0438\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u043f\u043e \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0443 1\u0421"
|
||||
);
|
||||
expect(result.evidence.unknown_facts.join("\n")).toContain(
|
||||
"\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b, \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f \u0438 \u0434\u0435\u043d\u0435\u0436\u043d\u044b\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0438"
|
||||
);
|
||||
});
|
||||
|
||||
it("emits Russian ambiguity boundaries for entity grounding", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
asked_domain_family: "entity_resolution",
|
||||
asked_action_family: "search_business_entity",
|
||||
explicit_entity_candidates: ["\u0421\u0412\u041a"],
|
||||
unsupported_but_understood_family: "entity_resolution"
|
||||
}
|
||||
});
|
||||
const deps = buildDeps([
|
||||
{ Counterparty: "\u0421\u0412\u041a-\u0410", CounterpartyRef: "Ref-1" },
|
||||
{ Counterparty: "\u0421\u0412\u041a-\u0411", CounterpartyRef: "Ref-2" }
|
||||
]);
|
||||
|
||||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||||
|
||||
expect(result.evidence.confirmed_facts).toEqual([]);
|
||||
expect(result.evidence.unknown_facts.join("\n")).toContain(
|
||||
"\u0422\u043e\u0447\u043d\u043e\u0435 \u0437\u0430\u0437\u0435\u043c\u043b\u0435\u043d\u0438\u0435 \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430 \u0432 1\u0421 \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u043d\u0435\u043e\u0434\u043d\u043e\u0437\u043d\u0430\u0447\u043d\u044b\u043c"
|
||||
);
|
||||
expect(result.evidence.unknown_facts.join("\n")).toContain("\u0421\u0412\u041a-\u0410");
|
||||
expect(result.evidence.unknown_facts.join("\n")).toContain("\u0421\u0412\u041a-\u0411");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,6 +14,8 @@ describe("assistant MCP discovery planner", () => {
|
||||
|
||||
expect(result.planner_status).toBe("ready_for_execution");
|
||||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||||
expect(result.selected_chain_id).toBe("value_flow");
|
||||
expect(result.selected_chain_summary).toContain("query scoped movements");
|
||||
expect(result.proposed_primitives).toEqual([
|
||||
"resolve_entity_reference",
|
||||
"query_movements",
|
||||
@@ -100,6 +102,8 @@ describe("assistant MCP discovery planner", () => {
|
||||
|
||||
expect(result.planner_status).toBe("ready_for_execution");
|
||||
expect(result.semantic_data_need).toBe("movement evidence");
|
||||
expect(result.selected_chain_id).toBe("movement_evidence");
|
||||
expect(result.selected_chain_summary).toContain("movement rows");
|
||||
expect(result.proposed_primitives).toEqual(["resolve_entity_reference", "query_movements", "probe_coverage"]);
|
||||
expect(result.proposed_primitives).not.toContain("aggregate_by_axis");
|
||||
expect(result.required_axes).toEqual(["counterparty", "period", "coverage_target"]);
|
||||
@@ -139,6 +143,23 @@ describe("assistant MCP discovery planner", () => {
|
||||
expect(result.catalog_review.evidence_floors.inspect_1c_metadata).toBe("source_summary");
|
||||
});
|
||||
|
||||
it("keeps broad metadata surface inspection on inspect_1c_metadata", () => {
|
||||
const result = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
asked_domain_family: "metadata",
|
||||
asked_action_family: "inspect_surface",
|
||||
explicit_entity_candidates: ["НДС"]
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.planner_status).toBe("ready_for_execution");
|
||||
expect(result.semantic_data_need).toBe("1C metadata evidence");
|
||||
expect(result.proposed_primitives).toEqual(["inspect_1c_metadata"]);
|
||||
expect(result.proposed_primitives).not.toContain("query_documents");
|
||||
expect(result.proposed_primitives).not.toContain("query_movements");
|
||||
expect(result.reason_codes).toContain("planner_selected_metadata_recipe");
|
||||
});
|
||||
|
||||
it("keeps metadata document inspection on inspect_1c_metadata instead of query_documents", () => {
|
||||
const result = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
@@ -165,6 +186,8 @@ describe("assistant MCP discovery planner", () => {
|
||||
|
||||
expect(result.planner_status).toBe("needs_clarification");
|
||||
expect(result.semantic_data_need).toBe("metadata lane clarification");
|
||||
expect(result.selected_chain_id).toBe("metadata_lane_clarification");
|
||||
expect(result.selected_chain_summary).toContain("choose the next data lane");
|
||||
expect(result.proposed_primitives).toEqual([]);
|
||||
expect(result.required_axes).toEqual(["counterparty", "period", "lane_family_choice"]);
|
||||
expect(result.discovery_plan.plan_status).toBe("needs_clarification");
|
||||
@@ -179,4 +202,18 @@ describe("assistant MCP discovery planner", () => {
|
||||
expect(result.discovery_plan.plan_status).toBe("needs_clarification");
|
||||
expect(result.reason_codes).toContain("planner_needs_more_user_or_scope_context");
|
||||
});
|
||||
|
||||
it("exposes an explicit entity-resolution chain instead of silently collapsing into a lifecycle lane", () => {
|
||||
const result = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
explicit_entity_candidates: ["SVK"]
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.planner_status).toBe("ready_for_execution");
|
||||
expect(result.semantic_data_need).toBe("entity discovery evidence");
|
||||
expect(result.selected_chain_id).toBe("entity_resolution");
|
||||
expect(result.selected_chain_summary).toContain("resolve the most relevant 1C reference");
|
||||
expect(result.proposed_primitives).toEqual(["search_business_entity", "resolve_entity_reference", "probe_coverage"]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -169,6 +169,46 @@ describe("assistant MCP discovery turn input adapter", () => {
|
||||
expect(result.reason_codes).toContain("mcp_discovery_metadata_signal_detected");
|
||||
});
|
||||
|
||||
it("treats broad 1C object wording as metadata surface discovery instead of narrowing to catalog-only", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage: "какие объекты 1С есть по НДС?"
|
||||
});
|
||||
|
||||
expect(result.adapter_status).toBe("ready");
|
||||
expect(result.should_run_discovery).toBe(true);
|
||||
expect(result.source_signal).toBe("raw_text");
|
||||
expect(result.semantic_data_need).toBe("1C metadata evidence");
|
||||
expect(result.turn_meaning_ref).toMatchObject({
|
||||
asked_domain_family: "metadata",
|
||||
asked_action_family: "inspect_surface",
|
||||
explicit_entity_candidates: ["НДС"],
|
||||
unsupported_but_understood_family: "1c_metadata_surface",
|
||||
stale_replay_forbidden: true
|
||||
});
|
||||
expect(result.reason_codes).toContain("mcp_discovery_metadata_signal_detected");
|
||||
expect(result.reason_codes).toContain("mcp_discovery_metadata_scope_hint_from_raw_text");
|
||||
});
|
||||
|
||||
it("bootstraps entity-resolution discovery from raw counterparty search wording", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage: "найди в 1С контрагента Группа СВК"
|
||||
});
|
||||
|
||||
expect(result.adapter_status).toBe("ready");
|
||||
expect(result.should_run_discovery).toBe(true);
|
||||
expect(result.source_signal).toBe("raw_text");
|
||||
expect(result.semantic_data_need).toBe("entity discovery evidence");
|
||||
expect(result.turn_meaning_ref).toMatchObject({
|
||||
asked_domain_family: "entity_resolution",
|
||||
asked_action_family: "search_business_entity",
|
||||
explicit_entity_candidates: ["Группа СВК"],
|
||||
unsupported_but_understood_family: "entity_resolution",
|
||||
stale_replay_forbidden: true
|
||||
});
|
||||
expect(result.reason_codes).toContain("mcp_discovery_entity_resolution_signal_detected");
|
||||
expect(result.reason_codes).toContain("mcp_discovery_entity_scope_from_raw_entity_search");
|
||||
});
|
||||
|
||||
it("seeds short monthly follow-up from prior bidirectional discovery context", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage: "а по месяцам?",
|
||||
@@ -662,4 +702,40 @@ describe("assistant MCP discovery turn input adapter", () => {
|
||||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toEqual(["SVK"]);
|
||||
expect(result.turn_meaning_ref?.explicit_entity_candidates).not.toContain("[object Object]");
|
||||
});
|
||||
|
||||
it("prefers the raw cleaned entity anchor over canonicalized turn-meaning pollution", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage: "\u043d\u0430\u0439\u0434\u0438 \u0432 1\u0421 \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430 \u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a",
|
||||
assistantTurnMeaning: {
|
||||
asked_domain_family: "counterparty",
|
||||
asked_action_family: "search_business_entity",
|
||||
explicit_entity_candidates: [{ value: "\u0441 \u043d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435\u043c '\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a' \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 1\u0421" }]
|
||||
},
|
||||
predecomposeContract: {
|
||||
entities: {
|
||||
counterparty: "\u0441 \u043d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435\u043c '\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a' \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 1\u0421"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.adapter_status).toBe("ready");
|
||||
expect(result.turn_meaning_ref?.explicit_entity_candidates?.[0]).toBe("\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a");
|
||||
expect(result.turn_meaning_ref?.explicit_entity_candidates).not.toContain(
|
||||
"\u0441 \u043d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435\u043c '\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a' \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 1\u0421"
|
||||
);
|
||||
});
|
||||
|
||||
it("does not concatenate effectiveMessage into the raw entity anchor", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage: "\u043d\u0430\u0439\u0434\u0438 \u0432 1\u0421 \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430 \u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a",
|
||||
effectiveMessage:
|
||||
"\u043d\u0430\u0439\u0442\u0438 \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430 \u0441 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c '\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a' \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 1\u0421"
|
||||
});
|
||||
|
||||
expect(result.adapter_status).toBe("ready");
|
||||
expect(result.turn_meaning_ref?.explicit_entity_candidates?.[0]).toBe("\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a");
|
||||
expect(result.turn_meaning_ref?.explicit_entity_candidates).not.toContain(
|
||||
"\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a \u043d\u0430\u0439\u0442\u0438 \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430 \u0441 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c '\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a'"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user