ARCH: усилить semantic integrity и добавить human AGENT сценарии

This commit is contained in:
2026-04-23 19:10:28 +03:00
parent 62e2b3323f
commit 65d15c156b
29 changed files with 1966 additions and 93 deletions
@@ -112,6 +112,89 @@ describe("address navigation state", () => {
expect(evolved.session_context.date_scope.period_to).toBe("2020-12-31");
});
it("prefers grounded discovery counterparty over stale referential anchor when updating focus", () => {
const initial = normalizeAddressNavigationState(
{
schema_version: "address_navigation_state_v1",
session_id: "asst-3b",
updated_at: "2026-04-12T10:00:00.000Z",
session_context: {
active_result_set_id: "rs-prev",
active_focus_object: {
object_type: "counterparty",
object_id: "counterparty:нортон",
label: "НОРТОН",
provenance_result_set_id: "rs-prev",
selected_at: "2026-04-12T09:59:00.000Z"
},
last_confirmed_route: "address_customer_revenue_and_payments_v1",
date_scope: {
as_of_date: null,
period_from: "2020-01-01",
period_to: "2020-12-31"
},
organization_scope: "ООО Альтернатива Плюс"
},
result_sets: [],
navigation_history: []
} as any,
"asst-3b"
);
const assistantItem = {
message_id: "msg-a2b",
session_id: "asst-3b",
role: "assistant",
text: "По Группа СВК подтверждены исходящие платежи за 2020 год.",
reply_type: "factual",
created_at: "2026-04-12T10:02:30.000Z",
trace_id: "address-456b",
debug: {
detected_mode: "address_query",
detected_intent: "supplier_payouts_profile",
selected_recipe: "address_supplier_payouts_profile_v1",
extracted_filters: {
counterparty: "НОРТОН",
period_from: "2020-01-01",
period_to: "2020-12-31"
},
anchor_type: "counterparty",
anchor_value_raw: "он",
anchor_value_resolved: "он",
mcp_discovery_response_applied: true,
assistant_mcp_discovery_entry_point_v1: {
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
entry_status: "bridge_executed",
turn_input: {
turn_meaning_ref: {
asked_domain_family: "counterparty_value",
asked_action_family: "payout",
explicit_entity_candidates: ["Группа СВК"]
}
},
bridge: {
bridge_status: "answer_draft_ready",
business_fact_answer_allowed: true,
pilot: {
pilot_scope: "counterparty_supplier_payout_query_movements_v1"
},
answer_draft: {
answer_mode: "confirmed_factual"
}
}
},
dialog_continuation_contract_v2: {
decision: "continue_previous"
}
}
} as any;
const evolved = evolveAddressNavigationStateWithAssistantItem(initial, assistantItem, 4);
expect(evolved.session_context.active_focus_object?.object_type).toBe("counterparty");
expect(evolved.session_context.active_focus_object?.label).toBe("Группа СВК");
expect(evolved.session_context.active_focus_object?.object_id).toBe("counterparty:группа свк");
expect(evolved.navigation_history[0]?.target_object_id).toBe("counterparty:группа свк");
});
it("captures item focus from inventory answers when no anchor is materialized", () => {
const base = createEmptyAddressNavigationState("asst-4", "2026-04-12T10:00:00.000Z");
const assistantItem = {
@@ -731,13 +731,14 @@ describe("assistant address follow-up carryover", () => {
expect(second.reply_type).toBe("factual");
expect(calls).toHaveLength(2);
expect(calls[1].message).toBe(followupMessage);
expect(calls[1].options?.followupContext?.previous_anchor_type).toBe("counterparty");
expect(String(calls[1].options?.followupContext?.previous_anchor_value ?? "")).toContain("Жуковка 51");
expect(String(calls[1].options?.followupContext?.previous_filters?.counterparty ?? "")).toContain("жуковке 51");
expect(normalizerService.normalize).not.toHaveBeenCalled();
});
it("retries with raw user message after rewrite degraded anchor and returns factual follow-up result", async () => {
it("prefers the raw referential document follow-up over a degraded rewrite and returns factual follow-up result", async () => {
const calls: Array<{ message: string; options?: any }> = [];
const firstMessage = "покажи документы по жуковке 51";
const followupMessage = "кроме этого документа есть еще чтото?";
@@ -826,12 +827,10 @@ describe("assistant address follow-up carryover", () => {
} as any);
expect(second.ok).toBe(true);
expect(second.reply_type).toBe("factual");
expect(second.debug?.address_retry_audit?.attempted).toBe(true);
expect(second.debug?.address_retry_audit?.initial_limited_category).toBe("missing_anchor");
expect(second.debug?.address_retry_audit?.retry_message).toBe(followupMessage);
expect(second.debug?.address_retry_audit?.attempted).toBe(false);
expect(calls.some((entry) => String(entry.message).toLowerCase().startsWith("документы по контрагенту"))).toBe(true);
expect(calls.some((entry) => String(entry.message).toLowerCase() === followupMessage)).toBe(true);
expect(calls).toHaveLength(2);
expect(calls[1].message).toBe(followupMessage);
expect(normalizerService.normalize).not.toHaveBeenCalled();
});
@@ -230,6 +230,102 @@ describe("assistantContinuityPolicy organization authority", () => {
});
});
it("replaces referential counterparty placeholders with grounded discovery entity during carryover", () => {
const debug = {
execution_lane: "address_query",
extracted_filters: {
counterparty: "РѕРЅ",
period_from: "2020-01-01",
period_to: "2020-12-31"
},
anchor_type: "counterparty",
anchor_value_raw: "РѕРЅ",
anchor_value_resolved: "РѕРЅ",
mcp_discovery_response_applied: true,
assistant_mcp_discovery_entry_point_v1: {
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
entry_status: "bridge_executed",
turn_input: {
turn_meaning_ref: {
asked_domain_family: "counterparty_value",
asked_action_family: "turnover",
explicit_entity_candidates: ["Группа СВК"],
explicit_date_scope: "2020"
}
},
bridge: {
bridge_status: "answer_draft_ready",
business_fact_answer_allowed: true,
pilot: {
pilot_scope: "counterparty_value_flow_query_movements_v1"
},
answer_draft: {
answer_mode: "confirmed_with_bounded_inference"
}
}
}
};
expect(readAddressDebugCounterparty(debug)).toBe("Группа СВК");
expect(resolveAddressDebugCarryoverFilters(debug)).toEqual({
counterparty: "Группа СВК",
period_from: "2020-01-01",
period_to: "2020-12-31"
});
expect(resolveAddressDebugAnchorContext(debug)).toEqual({
anchorType: "counterparty",
anchorValue: "Группа СВК"
});
});
it("prefers grounded discovery metadata scope over conflicting stale exact-route counterparty", () => {
const debug = {
execution_lane: "address_query",
extracted_filters: {
counterparty: "РќРћР РўРћРќ",
period_from: "2020-01-01",
period_to: "2020-12-31"
},
anchor_type: "counterparty",
anchor_value_resolved: "СервисКонсалт, ООО",
mcp_discovery_response_applied: true,
assistant_mcp_discovery_entry_point_v1: {
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
entry_status: "bridge_executed",
turn_input: {
turn_meaning_ref: {
asked_domain_family: "counterparty_value",
asked_action_family: "payout",
explicit_entity_candidates: ["РѕРЅ"],
metadata_scope_hint: "Группа СВК",
explicit_date_scope: "2020"
}
},
bridge: {
bridge_status: "answer_draft_ready",
business_fact_answer_allowed: true,
pilot: {
pilot_scope: "counterparty_supplier_payout_query_movements_v1"
},
answer_draft: {
answer_mode: "confirmed_with_bounded_inference"
}
}
}
};
expect(readAddressDebugCounterparty(debug)).toBe("Группа СВК");
expect(resolveAddressDebugCarryoverFilters(debug)).toEqual({
counterparty: "Группа СВК",
period_from: "2020-01-01",
period_to: "2020-12-31"
});
expect(resolveAddressDebugAnchorContext(debug)).toEqual({
anchorType: "counterparty",
anchorValue: "Группа СВК"
});
});
it("prefers the resolved entity from grounded entity-resolution discovery for counterparty carryover", () => {
const debug = {
execution_lane: "living_chat",
@@ -235,6 +235,40 @@ describe("assistant orchestration contract", () => {
expect(decision.orchestrationContract?.hard_meta_mode).toBe("non_domain");
});
it("keeps address lane for a short net follow-up over grounded value-flow context", () => {
const decision = resolveAssistantOrchestrationDecision({
rawUserMessage: "А какое получилось нетто?",
effectiveAddressUserMessage: "А какое получилось нетто?",
followupContext: {
previous_intent: "customer_revenue_and_payments",
previous_filters: {
counterparty: "Группа СВК",
period_from: "2020-01-01",
period_to: "2020-12-31"
},
previous_anchor_type: "counterparty",
previous_anchor_value: "Группа СВК",
previous_discovery_pilot_scope: "counterparty_supplier_payout_query_movements_v1"
},
llmPreDecomposeMeta: {
applied: false,
reason: "no_usable_fragment",
predecomposeContract: {
mode: "unsupported",
mode_confidence: "low",
intent: "unknown",
intent_confidence: "low"
}
} as any,
useMock: false
});
expect(decision.runAddressLane).toBe(true);
expect(decision.toolGateDecision).toBe("run_address_lane");
expect(decision.toolGateReason).not.toBe("non_domain_query_indexed");
expect(decision.livingMode).toBe("address_data");
});
it("routes historical capability follow-up over grounded inventory answer to contextual chat", () => {
const decision = resolveAssistantOrchestrationDecision({
rawUserMessage: "а исторические данные ты можешь же показать?",
@@ -585,6 +585,41 @@ describe("assistant MCP discovery turn input adapter", () => {
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
});
it("prefers grounded discovery metadata scope over stale conflicting counterparty in a short net follow-up", () => {
const result = buildAssistantMcpDiscoveryTurnInput({
userMessage: "а какое нетто?",
assistantTurnMeaning: {
asked_domain_family: "counterparty",
asked_action_family: "turnover",
explicit_intent_candidate: "customer_revenue_and_payments"
},
followupContext: {
previous_discovery_pilot_scope: "counterparty_supplier_payout_query_movements_v1",
previous_filters: {
counterparty: "РќРћР РўРћРќ",
period_from: "2020-01-01",
period_to: "2020-12-31"
},
previous_anchor_type: "counterparty",
previous_anchor_value: "СервисКонсалт, ООО",
previous_discovery_loop_metadata_scope_hint: "Группа СВК"
}
});
expect(result.adapter_status).toBe("ready");
expect(result.should_run_discovery).toBe(true);
expect(result.turn_meaning_ref).toMatchObject({
asked_domain_family: "counterparty_value",
asked_action_family: "net_value_flow",
explicit_entity_candidates: ["Группа СВК"],
explicit_date_scope: "2020",
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting",
stale_replay_forbidden: true
});
expect(result.turn_meaning_ref?.explicit_entity_candidates).not.toContain("РќРћР РўРћРќ");
expect(result.reason_codes).toContain("mcp_discovery_grounded_value_flow_followup");
});
it.skip("switches from a grounded exact value-flow answer into document evidence without restating the counterparty", () => {
const result = buildAssistantMcpDiscoveryTurnInput({
userMessage: "а по документам?",
@@ -1268,6 +1303,35 @@ describe("assistant MCP discovery turn input adapter", () => {
expect(result.reason_codes).toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
});
it("does not bootstrap metadata discovery from a referential document exclusion follow-up over exact document context", () => {
const result = buildAssistantMcpDiscoveryTurnInput({
userMessage: "кроме этого документа есть еще что-то?",
effectiveMessage: "документы по контрагенту документа",
assistantTurnMeaning: {
asked_domain_family: "counterparty",
asked_action_family: "list_documents",
explicit_intent_candidate: "list_documents_by_counterparty",
explicit_entity_candidates: [{ value: "ТСЖ \"Жуковка 51\"" }]
},
followupContext: {
previous_intent: "list_documents_by_counterparty",
target_intent: "list_documents_by_counterparty",
previous_anchor_type: "counterparty",
previous_anchor_value: "ТСЖ \"Жуковка 51\"",
previous_filters: {
counterparty: "жуковке 51"
}
}
});
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_not_applicable_for_supported_exact_turn");
expect(result.reason_codes).not.toContain("mcp_discovery_metadata_signal_detected");
expect(result.reason_codes).not.toContain("mcp_discovery_metadata_scope_hint_from_raw_text");
});
it("never serializes object candidates as [object Object]", () => {
const result = buildAssistantMcpDiscoveryTurnInput({
assistantTurnMeaning: {