ARCH: ввести broad business evaluation bridge
This commit is contained in:
@@ -3141,5 +3141,113 @@ describe("assistant address follow-up carryover", () => {
|
||||
expect(calls[0].options?.followupContext?.root_filters?.counterparty).toBeUndefined();
|
||||
expect(normalizerService.normalize).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.skip("passes grounded MCP discovery payout context into a short year-switch follow-up", async () => {
|
||||
const followupMessage = "а теперь за 2021?";
|
||||
const calls: Array<{ message: string; options?: any }> = [];
|
||||
|
||||
const addressQueryService = {
|
||||
tryHandle: vi.fn(async (message: string, options?: any) => {
|
||||
calls.push({ message, options });
|
||||
if (message === followupMessage && options?.followupContext) {
|
||||
return buildAddressLaneResult({
|
||||
reply_text: "Подтверждены исходящие платежи по Группа СВК за 2021 год.",
|
||||
debug: {
|
||||
...buildAddressLaneResult().debug,
|
||||
detected_intent: "supplier_payouts_profile",
|
||||
selected_recipe: "address_supplier_payouts_profile_v1",
|
||||
extracted_filters: {
|
||||
counterparty: "Группа СВК",
|
||||
organization: "ООО Альтернатива Плюс",
|
||||
period_from: "2021-01-01",
|
||||
period_to: "2021-12-31"
|
||||
},
|
||||
reasons: ["address_action_detected", "address_entity_detected", "address_followup_context_applied"]
|
||||
}
|
||||
});
|
||||
}
|
||||
return null;
|
||||
})
|
||||
} as any;
|
||||
|
||||
const normalizerService = {
|
||||
normalize: vi.fn(async () => ({
|
||||
assistant_reply: "normalizer_fallback_should_not_be_used",
|
||||
reply_type: "partial_coverage",
|
||||
debug: {}
|
||||
}))
|
||||
} as any;
|
||||
|
||||
const sessions = new AssistantSessionStore();
|
||||
const service = new AssistantService(
|
||||
normalizerService,
|
||||
sessions as any,
|
||||
{} as any,
|
||||
{ persistSession: vi.fn() } as any,
|
||||
addressQueryService
|
||||
);
|
||||
|
||||
const sessionId = `asst-discovery-followup-year-switch-${Date.now()}`;
|
||||
sessions.appendItem(sessionId, {
|
||||
message_id: "msg-discovery-payout-seed",
|
||||
session_id: sessionId,
|
||||
role: "assistant",
|
||||
text: "Подтверждены исходящие платежи по Группа СВК за 2020 год.",
|
||||
reply_type: "partial_coverage",
|
||||
created_at: "2026-04-20T10:00:00.000Z",
|
||||
trace_id: "living-discovery-seed",
|
||||
debug: {
|
||||
execution_lane: "living_chat",
|
||||
mcp_discovery_response_applied: true,
|
||||
assistant_active_organization: "ООО Альтернатива Плюс",
|
||||
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_action_family: "payout",
|
||||
explicit_entity_candidates: ["Группа СВК"],
|
||||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} as any);
|
||||
|
||||
const response = await service.handleMessage({
|
||||
session_id: sessionId,
|
||||
user_message: followupMessage,
|
||||
useMock: true
|
||||
} as any);
|
||||
|
||||
expect(response.ok).toBe(true);
|
||||
expect(response.reply_type).toBe("factual");
|
||||
expect(calls).toHaveLength(1);
|
||||
expect(calls[0].message).toBe(followupMessage);
|
||||
expect(calls[0].options?.followupContext?.previous_intent).toBe("supplier_payouts_profile");
|
||||
expect(calls[0].options?.followupContext?.previous_discovery_pilot_scope).toBe(
|
||||
"counterparty_supplier_payout_query_movements_v1"
|
||||
);
|
||||
expect(calls[0].options?.followupContext?.previous_anchor_type).toBe("counterparty");
|
||||
expect(calls[0].options?.followupContext?.previous_anchor_value).toBe("Группа СВК");
|
||||
expect(calls[0].options?.followupContext?.previous_filters).toMatchObject({
|
||||
counterparty: "Группа СВК",
|
||||
organization: "ООО Альтернатива Плюс",
|
||||
period_from: "2020-01-01",
|
||||
period_to: "2020-12-31"
|
||||
});
|
||||
expect(normalizerService.normalize).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -154,6 +154,97 @@ describe("assistant address orchestration runtime adapter", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("passes grounded discovery follow-up carryover into MCP discovery entry point for a short year switch", async () => {
|
||||
const runMcpDiscoveryRuntimeEntryPoint = vi.fn(async () => ({
|
||||
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
|
||||
policy_owner: "assistantMcpDiscoveryRuntimeEntryPoint",
|
||||
entry_status: "bridge_executed",
|
||||
hot_runtime_wired: false,
|
||||
discovery_attempted: true
|
||||
}));
|
||||
const input = buildInput({
|
||||
userMessage: "а теперь за 2021?",
|
||||
runAddressLlmPreDecompose: vi.fn(async () => ({
|
||||
attempted: true,
|
||||
applied: false,
|
||||
effectiveMessage: "а теперь за 2021?",
|
||||
reason: "raw_kept",
|
||||
predecomposeContract: {
|
||||
mode: "unsupported",
|
||||
intent: "unknown",
|
||||
period: {
|
||||
scope: "year",
|
||||
period_from: "2021-01-01",
|
||||
period_to: "2021-12-31",
|
||||
has_explicit_period: true
|
||||
}
|
||||
}
|
||||
})),
|
||||
resolveAddressFollowupCarryoverContext: vi.fn(() => ({
|
||||
followupContext: {
|
||||
previous_intent: "supplier_payouts_profile",
|
||||
target_intent: "supplier_payouts_profile",
|
||||
previous_discovery_pilot_scope: "counterparty_supplier_payout_query_movements_v1",
|
||||
previous_anchor_type: "counterparty",
|
||||
previous_anchor_value: "Группа СВК",
|
||||
previous_filters: {
|
||||
counterparty: "Группа СВК",
|
||||
organization: "ООО Альтернатива Плюс",
|
||||
period_from: "2020-01-01",
|
||||
period_to: "2020-12-31"
|
||||
}
|
||||
}
|
||||
})),
|
||||
resolveAssistantOrchestrationDecision: vi.fn(() => ({
|
||||
runAddressLane: true,
|
||||
livingMode: "address_data",
|
||||
livingReason: "address_lane_triggered",
|
||||
toolGateDecision: "run_address_lane",
|
||||
toolGateReason: "followup_context_detected",
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
assistant_turn_meaning: {
|
||||
schema_version: "assistant_turn_meaning_v1",
|
||||
raw_message: "а теперь за 2021?",
|
||||
effective_message: "а теперь за 2021?",
|
||||
explicit_entity_candidates: []
|
||||
}
|
||||
}
|
||||
})),
|
||||
runMcpDiscoveryRuntimeEntryPoint
|
||||
});
|
||||
|
||||
const output = await buildAssistantAddressOrchestrationRuntime(input);
|
||||
|
||||
expect(output.orchestrationDecision.runAddressLane).toBe(true);
|
||||
expect(runMcpDiscoveryRuntimeEntryPoint).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
userMessage: "а теперь за 2021?",
|
||||
effectiveMessage: "а теперь за 2021?",
|
||||
followupContext: expect.objectContaining({
|
||||
previous_intent: "supplier_payouts_profile",
|
||||
target_intent: "supplier_payouts_profile",
|
||||
previous_discovery_pilot_scope: "counterparty_supplier_payout_query_movements_v1",
|
||||
previous_anchor_type: "counterparty",
|
||||
previous_anchor_value: "Группа СВК",
|
||||
previous_filters: expect.objectContaining({
|
||||
counterparty: "Группа СВК",
|
||||
organization: "ООО Альтернатива Плюс",
|
||||
period_from: "2020-01-01",
|
||||
period_to: "2020-12-31"
|
||||
})
|
||||
})
|
||||
})
|
||||
);
|
||||
expect(output.addressRuntimeMeta.mcpDiscoveryRuntimeEntryPoint).toEqual(
|
||||
expect.objectContaining({
|
||||
entry_status: "bridge_executed",
|
||||
discovery_attempted: true,
|
||||
hot_runtime_wired: false
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps address orchestration alive when MCP discovery entry point fails", async () => {
|
||||
const input = buildInput({
|
||||
runMcpDiscoveryRuntimeEntryPoint: vi.fn(async () => {
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
applyTemporalCarryoverFilters,
|
||||
buildRootScopedCarryoverFilters,
|
||||
hydrateInventoryRootFrameState,
|
||||
readAddressDebugIntent,
|
||||
readAddressDebugTemporalScope,
|
||||
resolveNavigationSessionContextState,
|
||||
resolveAddressDebugCarryoverFilters,
|
||||
@@ -147,6 +148,49 @@ describe("assistantContinuityPolicy organization authority", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("hydrates intent and carryover filters from grounded MCP discovery payout scope", () => {
|
||||
const debug = {
|
||||
execution_lane: "living_chat",
|
||||
mcp_discovery_response_applied: true,
|
||||
assistant_active_organization: "ООО Альтернатива Плюс",
|
||||
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_action_family: "payout",
|
||||
explicit_entity_candidates: ["Группа СВК"],
|
||||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||||
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(readAddressDebugIntent(debug)).toBe("supplier_payouts_profile");
|
||||
expect(readAddressDebugTemporalScope(debug)).toEqual({
|
||||
asOfDate: null,
|
||||
periodFrom: "2020-01-01",
|
||||
periodTo: "2020-12-31"
|
||||
});
|
||||
expect(resolveAddressDebugCarryoverFilters(debug)).toEqual({
|
||||
counterparty: "Группа СВК",
|
||||
organization: "ООО Альтернатива Плюс",
|
||||
period_from: "2020-01-01",
|
||||
period_to: "2020-12-31"
|
||||
});
|
||||
});
|
||||
|
||||
it("resolves navigation session context through one shared helper", () => {
|
||||
const state = resolveNavigationSessionContextState({
|
||||
session_context: {
|
||||
|
||||
@@ -133,6 +133,91 @@ describe("assistant living chat runtime adapter", () => {
|
||||
expect(executeLlmChat).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("builds deterministic broad business evaluation summary from grounded continuity instead of replaying lifecycle noise", async () => {
|
||||
const executeLlmChat = vi.fn(async () => "raw-llm");
|
||||
const input = buildRuntimeInput({
|
||||
userMessage: "Как ты оценишь деятельность компании?",
|
||||
modeDecision: { mode: "chat", reason: "unsupported_current_turn_meaning_boundary" },
|
||||
sessionScope: {
|
||||
knownOrganizations: ["ООО Альтернатива Плюс"],
|
||||
selectedOrganization: null,
|
||||
activeOrganization: "ООО Альтернатива Плюс"
|
||||
},
|
||||
sessionItems: [
|
||||
{
|
||||
role: "assistant",
|
||||
debug: {
|
||||
execution_lane: "address_query",
|
||||
answer_grounding_check: {
|
||||
status: "grounded"
|
||||
},
|
||||
detected_intent: "counterparty_activity_lifecycle",
|
||||
extracted_filters: {
|
||||
organization: "ООО Альтернатива Плюс"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
debug: {
|
||||
execution_lane: "living_chat",
|
||||
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: {
|
||||
explicit_entity_candidates: ["Группа СВК"],
|
||||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||||
explicit_date_scope: "2020"
|
||||
}
|
||||
},
|
||||
bridge: {
|
||||
bridge_status: "answer_draft_ready",
|
||||
business_fact_answer_allowed: true,
|
||||
answer_draft: {
|
||||
answer_mode: "confirmed_with_bounded_inference"
|
||||
},
|
||||
pilot: {
|
||||
pilot_scope: "counterparty_bidirectional_value_flow_query_movements_v1",
|
||||
derived_bidirectional_value_flow: {
|
||||
net_amount_human_ru: "3 865 501,50 руб.",
|
||||
incoming_customer_revenue: {
|
||||
total_amount_human_ru: "47 628 853,03 руб."
|
||||
},
|
||||
outgoing_supplier_payout: {
|
||||
total_amount_human_ru: "43 763 351,53 руб."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
addressRuntimeMeta: {
|
||||
toolGateReason: "unsupported_current_turn_meaning_boundary",
|
||||
orchestrationContract: {
|
||||
unsupported_current_turn_meaning_boundary: true,
|
||||
assistant_turn_meaning: {
|
||||
unsupported_but_understood_family: "broad_business_evaluation"
|
||||
}
|
||||
}
|
||||
},
|
||||
executeLlmChat
|
||||
});
|
||||
|
||||
const output = await runAssistantLivingChatRuntime(input);
|
||||
|
||||
expect(output.handled).toBe(true);
|
||||
expect(output.chatText.toLowerCase()).toContain("оценка бизнеса");
|
||||
expect(output.chatText).toContain("ООО Альтернатива Плюс");
|
||||
expect(output.chatText).toContain("Группа СВК");
|
||||
expect(output.chatText).toContain("нетто");
|
||||
expect(output.debug?.living_chat_response_source).toBe("deterministic_broad_business_evaluation_contract");
|
||||
expect(executeLlmChat).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("builds deterministic boundary for unsupported current-turn business meaning", async () => {
|
||||
const executeLlmChat = vi.fn(async () => "raw-llm");
|
||||
const input = buildRuntimeInput({
|
||||
|
||||
@@ -323,4 +323,50 @@ describe("assistant MCP discovery response policy", () => {
|
||||
expect(result.reason_codes).toContain("mcp_discovery_response_policy_candidate_not_eligible");
|
||||
expect(result.reason_codes).toContain("mcp_discovery_response_policy_kept_current_reply");
|
||||
});
|
||||
|
||||
it("keeps deterministic broad business evaluation summary instead of replacing it with a clarification candidate", () => {
|
||||
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
||||
currentReply: "Коротко: по уже подтвержденным данным в 1С компания выглядит живой операционно.",
|
||||
currentReplySource: "deterministic_broad_business_evaluation_contract",
|
||||
livingChatSource: "deterministic_broad_business_evaluation_contract",
|
||||
modeDecisionReason: "unsupported_current_turn_meaning_boundary",
|
||||
addressRuntimeMeta: {
|
||||
assistant_mcp_discovery_entry_point_v1: entryPoint({
|
||||
turn_input: {
|
||||
adapter_status: "ready",
|
||||
should_run_discovery: true,
|
||||
turn_meaning_ref: {
|
||||
asked_domain_family: "business_summary",
|
||||
asked_action_family: "broad_evaluation",
|
||||
unsupported_but_understood_family: "broad_business_evaluation",
|
||||
stale_replay_forbidden: true
|
||||
}
|
||||
},
|
||||
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: "Нужно уточнить контекст перед поиском в 1С.",
|
||||
confirmed_lines: [],
|
||||
inference_lines: [],
|
||||
unknown_lines: ["MCP discovery pilot needs more scope before execution"],
|
||||
limitation_lines: ["MCP discovery pilot needs more scope before execution"],
|
||||
next_step_line: "Уточните контрагента, период или организацию."
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.applied).toBe(false);
|
||||
expect(result.decision).toBe("keep_current_reply");
|
||||
expect(result.reply_source).toBe("deterministic_broad_business_evaluation_contract");
|
||||
expect(result.reply_text).toContain("компания выглядит живой операционно");
|
||||
expect(result.reason_codes).toContain(
|
||||
"mcp_discovery_response_policy_keep_broad_business_summary_over_clarification_candidate"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -151,6 +151,69 @@ describe("assistant MCP discovery turn input adapter", () => {
|
||||
expect(result.reason_codes).toContain("mcp_discovery_monthly_aggregation_signal_detected");
|
||||
});
|
||||
|
||||
it("seeds short monthly follow-up from prior bidirectional discovery context", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage: "а по месяцам?",
|
||||
followupContext: {
|
||||
previous_discovery_pilot_scope: "counterparty_bidirectional_value_flow_query_movements_v1",
|
||||
previous_filters: {
|
||||
counterparty: "Группа СВК",
|
||||
organization: "ООО Альтернатива Плюс",
|
||||
period_from: "2020-01-01",
|
||||
period_to: "2020-12-31"
|
||||
},
|
||||
previous_anchor_type: "counterparty",
|
||||
previous_anchor_value: "Группа СВК"
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.adapter_status).toBe("ready");
|
||||
expect(result.should_run_discovery).toBe(true);
|
||||
expect(result.source_signal).toBe("followup_context");
|
||||
expect(result.turn_meaning_ref).toMatchObject({
|
||||
asked_domain_family: "counterparty_value",
|
||||
asked_action_family: "net_value_flow",
|
||||
asked_aggregation_axis: "month",
|
||||
explicit_entity_candidates: ["Группа СВК"],
|
||||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||||
explicit_date_scope: "2020",
|
||||
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting",
|
||||
stale_replay_forbidden: true
|
||||
});
|
||||
expect(result.reason_codes).toContain("mcp_discovery_seeded_from_followup_context");
|
||||
expect(result.reason_codes).toContain("mcp_discovery_counterparty_from_followup_context");
|
||||
expect(result.reason_codes).toContain("mcp_discovery_date_scope_from_followup_context");
|
||||
});
|
||||
|
||||
it("switches the checked year on a short payout follow-up while keeping prior discovery counterparty", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage: "а теперь за 2021?",
|
||||
followupContext: {
|
||||
previous_discovery_pilot_scope: "counterparty_supplier_payout_query_movements_v1",
|
||||
previous_filters: {
|
||||
counterparty: "Группа СВК",
|
||||
organization: "ООО Альтернатива Плюс",
|
||||
period_from: "2020-01-01",
|
||||
period_to: "2020-12-31"
|
||||
},
|
||||
previous_anchor_type: "counterparty",
|
||||
previous_anchor_value: "Группа СВК"
|
||||
}
|
||||
});
|
||||
|
||||
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: "payout",
|
||||
explicit_entity_candidates: ["Группа СВК"],
|
||||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||||
explicit_date_scope: "2021",
|
||||
unsupported_but_understood_family: "counterparty_payouts_or_outflow",
|
||||
stale_replay_forbidden: true
|
||||
});
|
||||
});
|
||||
|
||||
it("does not activate discovery for supported exact current-turn intent", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
assistantTurnMeaning: {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildAddressMemoryRecapReply,
|
||||
buildBroadBusinessEvaluationReply,
|
||||
buildSelectedObjectAnswerInspectionReply,
|
||||
createAssistantMemoryRecapPolicy,
|
||||
resolveAssistantLivingChatMemoryContext
|
||||
@@ -385,6 +386,75 @@ describe("assistantMemoryRecapPolicy", () => {
|
||||
expect(reply).toContain("43 763 351,53 руб.");
|
||||
});
|
||||
|
||||
it("builds deterministic broad business evaluation summary from recent grounded organization facts", () => {
|
||||
const sessionItems = [
|
||||
{
|
||||
role: "assistant",
|
||||
debug: {
|
||||
execution_lane: "address_query",
|
||||
answer_grounding_check: {
|
||||
status: "grounded"
|
||||
},
|
||||
detected_intent: "counterparty_activity_lifecycle",
|
||||
extracted_filters: {
|
||||
organization: "ООО Альтернатива Плюс"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
debug: {
|
||||
execution_lane: "living_chat",
|
||||
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: {
|
||||
explicit_entity_candidates: ["Группа СВК"],
|
||||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||||
explicit_date_scope: "2020"
|
||||
}
|
||||
},
|
||||
bridge: {
|
||||
bridge_status: "answer_draft_ready",
|
||||
business_fact_answer_allowed: true,
|
||||
answer_draft: {
|
||||
answer_mode: "confirmed_with_bounded_inference"
|
||||
},
|
||||
pilot: {
|
||||
pilot_scope: "counterparty_bidirectional_value_flow_query_movements_v1",
|
||||
derived_bidirectional_value_flow: {
|
||||
net_amount_human_ru: "3 865 501,50 руб.",
|
||||
incoming_customer_revenue: {
|
||||
total_amount_human_ru: "47 628 853,03 руб."
|
||||
},
|
||||
outgoing_supplier_payout: {
|
||||
total_amount_human_ru: "43 763 351,53 руб."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const reply = buildBroadBusinessEvaluationReply({
|
||||
organization: "ООО Альтернатива Плюс",
|
||||
addressDebug: sessionItems[1].debug as any,
|
||||
sessionItems,
|
||||
toNonEmptyString: (value: unknown) => {
|
||||
const text = String(value ?? "").trim();
|
||||
return text.length > 0 ? text : null;
|
||||
}
|
||||
});
|
||||
|
||||
expect(reply.toLowerCase()).toContain("оценка бизнеса");
|
||||
expect(reply).toContain("ООО Альтернатива Плюс");
|
||||
expect(reply).toContain("47 628 853,03");
|
||||
});
|
||||
|
||||
it("builds grounded answer inspection reply for MCP discovery net answer", () => {
|
||||
const context = resolveAssistantLivingChatMemoryContext({
|
||||
modeDecisionReason: "answer_inspection_followup_detected",
|
||||
|
||||
@@ -606,7 +606,7 @@ describe("assistantRoutePolicy", () => {
|
||||
expect(decision.orchestrationContract?.organization_scope_switch_detected).not.toBe(true);
|
||||
});
|
||||
|
||||
it("keeps company activity assessment follow-up in address lane when lifecycle intent is resolved from grounded continuity", () => {
|
||||
it("routes broad business evaluation follow-up to chat instead of replaying lifecycle address intent", () => {
|
||||
const policy = buildPolicy({
|
||||
resolveAddressIntent: () => ({ intent: "counterparty_activity_lifecycle", confidence: "high" }),
|
||||
findLastGroundedAddressAnswerDebug: () => ({
|
||||
@@ -618,6 +618,15 @@ describe("assistantRoutePolicy", () => {
|
||||
period_to: "2026-04-18"
|
||||
}
|
||||
}),
|
||||
resolveAssistantTurnMeaning: () => ({
|
||||
schema_version: "assistant_turn_meaning_v1",
|
||||
asked_domain_family: "business_summary",
|
||||
asked_action_family: "broad_evaluation",
|
||||
explicit_intent_candidate: null,
|
||||
unsupported_but_understood_family: "broad_business_evaluation",
|
||||
stale_replay_forbidden: true,
|
||||
reason_codes: ["broad_business_evaluation_current_turn_signal"]
|
||||
}),
|
||||
resolveAddressToolGateDecision: () => ({
|
||||
runAddressLane: false,
|
||||
decision: "skip_address_lane",
|
||||
@@ -666,9 +675,12 @@ describe("assistantRoutePolicy", () => {
|
||||
useMock: false
|
||||
});
|
||||
|
||||
expect(decision.runAddressLane).toBe(true);
|
||||
expect(decision.toolGateDecision).toBe("run_address_lane");
|
||||
expect(decision.livingMode).toBe("address_data");
|
||||
expect(decision.runAddressLane).toBe(false);
|
||||
expect(decision.toolGateDecision).toBe("skip_address_lane");
|
||||
expect(decision.toolGateReason).toBe("unsupported_current_turn_meaning_boundary");
|
||||
expect(decision.livingMode).toBe("chat");
|
||||
expect(decision.livingReason).toBe("unsupported_current_turn_meaning_boundary");
|
||||
expect(decision.orchestrationContract?.unsupported_current_turn_family).toBe("broad_business_evaluation");
|
||||
});
|
||||
|
||||
it("recovers an address route from current-turn meaning when L0 resolver is noisy", () => {
|
||||
|
||||
@@ -1014,6 +1014,45 @@ describe("assistantTransitionPolicy", () => {
|
||||
expect(carryover).toBeNull();
|
||||
});
|
||||
|
||||
it("drops carryover for broad business evaluation so lifecycle context does not stick to the new question", () => {
|
||||
const policy = buildPolicy({
|
||||
findLastAddressAssistantItem: () => ({
|
||||
text: "Lifecycle answer",
|
||||
debug: {
|
||||
execution_lane: "address_query",
|
||||
answer_grounding_check: { status: "grounded" },
|
||||
detected_intent: "counterparty_activity_lifecycle",
|
||||
extracted_filters: {
|
||||
organization: 'ООО "Альтернатива Плюс"',
|
||||
period_to: "2020-12-31"
|
||||
},
|
||||
anchor_type: "organization",
|
||||
anchor_value_resolved: 'ООО "Альтернатива Плюс"'
|
||||
}
|
||||
}),
|
||||
hasAddressFollowupContextSignal: () => true,
|
||||
resolveAssistantTurnMeaning: () => ({
|
||||
schema_version: "assistant_turn_meaning_v1",
|
||||
asked_domain_family: "business_summary",
|
||||
asked_action_family: "broad_evaluation",
|
||||
explicit_intent_candidate: null,
|
||||
unsupported_but_understood_family: "broad_business_evaluation",
|
||||
explicit_entity_candidates: [],
|
||||
stale_replay_forbidden: true
|
||||
})
|
||||
});
|
||||
|
||||
const carryover = policy.resolveAddressFollowupCarryoverContext(
|
||||
"Как ты оценишь деятельность компании?",
|
||||
[],
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
expect(carryover).toBeNull();
|
||||
});
|
||||
|
||||
it("reuses grounded MCP discovery payout context for a short year-switch follow-up", () => {
|
||||
const policy = buildPolicy({
|
||||
findLastAddressAssistantItem: () => null,
|
||||
|
||||
@@ -93,4 +93,21 @@ describe("assistantTurnMeaningPolicy", () => {
|
||||
expect(meaning.asked_action_family).toBe("confirmed_tax_period");
|
||||
expect(meaning.stale_replay_forbidden).toBe(false);
|
||||
});
|
||||
|
||||
it("marks broad business evaluation as unsupported-but-understood instead of stale lifecycle replay", () => {
|
||||
const policy = buildPolicy({
|
||||
resolveAddressIntent: () => ({ intent: "counterparty_activity_lifecycle", confidence: "high" })
|
||||
});
|
||||
|
||||
const meaning = policy.resolveAssistantTurnMeaning({
|
||||
rawUserMessage: "Как ты оценишь деятельность компании?"
|
||||
});
|
||||
|
||||
expect(meaning.explicit_intent_candidate).toBeNull();
|
||||
expect(meaning.asked_domain_family).toBe("business_summary");
|
||||
expect(meaning.asked_action_family).toBe("broad_evaluation");
|
||||
expect(meaning.unsupported_but_understood_family).toBe("broad_business_evaluation");
|
||||
expect(meaning.stale_replay_forbidden).toBe(true);
|
||||
expect(meaning.reason_codes).toContain("broad_business_evaluation_current_turn_signal");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user