Починить проверку цепочки поставщик товар покупатель

This commit is contained in:
2026-06-03 13:35:31 +03:00
parent 584edb7e5b
commit 325fcf50d1
8 changed files with 322 additions and 9 deletions
@@ -1147,12 +1147,84 @@ describe("inventory selected-object follow-up", () => {
expect(result?.response_type).toBe("FACTUAL_SUMMARY");
expect(result?.debug.detected_intent).toBe("inventory_purchase_to_sale_chain");
expect(result?.debug.extracted_filters?.organization).toBe("ООО Альтернатива Плюс");
expect(result?.debug.reasons).toContain("organization_restored_from_inventory_chain_context");
expect(result?.debug.reasons).toContain("organization_scope_normalized_for_inventory_chain");
expect(result?.debug.reasons).toContain("inventory_chain_counterparty_anchor_kept_for_verification");
expect(String(result?.reply_text ?? "").split("\n")[0]).toContain("полностью не подтверждена");
expect(String(result?.reply_text ?? "")).toContain("ЭталонМебель");
});
it("restores known organization when live chain semantics misclassify buyer as active organization", async () => {
executeAddressMcpQueryMock.mockResolvedValueOnce({
fetched_rows: 2,
matched_rows: 2,
raw_rows: [
{
Период: "2019-12-10T00:00:00Z",
Регистратор: "Поступление товаров и услуг 00000000111 от 10.12.2019 12:00:01",
СчетДт: "41.01",
СчетКт: "",
Сумма: 855000,
Номенклатура: "Шкаф картотечный 1000*400*2100",
Контрагент: "ЭталонМебель",
Организация: "ООО \\Альтернатива Плюс\\",
Количество: 15
},
{
Период: "2020-04-01T00:00:00Z",
Регистратор: "Реализация товаров и услуг 00000000001 от 01.04.2020 0:00:00",
СчетДт: "",
СчетКт: "41.01",
Сумма: 855000,
Номенклатура: "Шкаф картотечный 1000*400*2100",
Контрагент: "ЭталонМебель",
Организация: "ООО \\Альтернатива Плюс\\",
Количество: 15
}
],
rows: [],
error: null
});
const service = new AddressQueryService();
const result = await service.tryHandle(
"Есть ли документально подтвержденная цепочка: поставщик Гамма-мебель, ООО -> товар Шкаф картотечный 1000*400*2100 -> покупатель Департамент капитального ремонта города Москвы",
{
activeOrganization: "Департамент капитального ремонта города Москвы",
knownOrganizations: ["ООО \"Альтернатива Плюс\""],
analysisDateHint: "2020-03-31",
llmSemanticHints: {
scope_target_kind: "organization",
scope_target_text: "Департамент капитального ремонта города Москвы",
date_scope_kind: "missing",
self_scope_detected: false,
selected_object_scope_detected: false
},
followupContext: {
previous_intent: "inventory_purchase_to_sale_chain",
previous_filters: {
as_of_date: "2020-03-31",
item: "Шкаф картотечный 1000*400*2100",
organization: "ООО \"Альтернатива Плюс\""
},
previous_anchor_type: "item",
previous_anchor_value: "Шкаф картотечный 1000*400*2100"
}
}
);
expect(result?.handled).toBe(true);
expect(result?.response_type).toBe("FACTUAL_SUMMARY");
expect(result?.debug.detected_intent).toBe("inventory_purchase_to_sale_chain");
expect(result?.debug.extracted_filters?.counterparty).toBe("Гамма-мебель, ООО");
expect(result?.debug.extracted_filters?.organization).toBe("ООО Альтернатива Плюс");
expect(result?.debug.reasons).toContain("organization_scope_normalized_for_inventory_chain");
expect(result?.debug.reasons).toContain("inventory_chain_counterparty_anchor_kept_for_verification");
expect(String(result?.reply_text ?? "").split("\n")[0]).toContain("полностью не подтверждена");
expect(String(result?.reply_text ?? "").split("\n")[0]).toContain("Гамма-мебель, ООО");
expect(String(result?.reply_text ?? "").split("\n")[0]).toContain("Департамент капитального ремонта города Москвы");
expect(String(result?.reply_text ?? "")).toContain("ЭталонМебель");
});
it.skip("keeps the full selected item when sale trace is asked in canonical wording after provenance", async () => {
executeAddressMcpQueryMock.mockResolvedValueOnce({
fetched_rows: 1,
@@ -186,6 +186,35 @@ describe("address query shape classifier", () => {
expect(extraction.warnings).toContain("supplier_anchor_derived_for_inventory_documentary_chain");
});
it("extracts supplier anchor from normalized supplier-buyer chain without swallowing buyer and item tail", () => {
const extraction = extractAddressFilters(
"Проверить наличие документально подтвержденной цепочки поставки от поставщика Гамма-мебель, ООО к покупателю Департамент капитального ремонта города Москвы через товар Шкаф картотечный 1000*400*2100",
"inventory_purchase_to_sale_chain"
);
expect(extraction.extracted_filters.counterparty).toBe("Гамма-мебель, ООО");
expect(extraction.extracted_filters.item).toBe("Шкаф картотечный 1000*400*2100");
expect(extraction.warnings).toContain("supplier_anchor_derived_for_inventory_documentary_chain");
});
it("ignores buyer-as-organization llm semantics for supplier-item-buyer inventory chain", () => {
const result = runAddressDecomposeStage(
"Проверить наличие документально подтвержденной цепочки поставки от поставщика Гамма-мебель, ООО к покупателю Департамент капитального ремонта города Москвы через товар Шкаф картотечный 1000*400*2100",
null,
{
scope_target_kind: "organization",
scope_target_text: "Департамент капитального ремонта города Москвы",
date_scope_kind: "missing",
self_scope_detected: false,
selected_object_scope_detected: false
}
);
expect(result?.filters.extracted_filters.counterparty).toBe("Гамма-мебель, ООО");
expect(result?.filters.extracted_filters.organization).toBeUndefined();
expect(result?.filters.warnings).toContain("organization_llm_semantics_ignored_for_inventory_documentary_chain");
});
it("cuts inventory item anchor before purchase-doc residue tail", () => {
const filters = extractAddressFilters(
"По каким документам был куплен товар Диван трехместный для остатка на складе Основной склад",