Semantic Gate: закрепить бизнес-аудит и executive summary
This commit is contained in:
@@ -426,6 +426,20 @@ describe("assistant living chat runtime adapter", () => {
|
||||
userMessage: "а ты помнишь мы зеркало обсуждали?",
|
||||
modeDecision: { mode: "chat", reason: "memory_recap_followup_detected" },
|
||||
sessionItems: [
|
||||
{
|
||||
role: "assistant",
|
||||
debug: {
|
||||
execution_lane: "address_query",
|
||||
answer_grounding_check: {
|
||||
status: "grounded"
|
||||
},
|
||||
detected_intent: "list_documents_by_counterparty",
|
||||
extracted_filters: {
|
||||
organization: "ООО Альтернатива Плюс",
|
||||
counterparty: "для"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
debug: {
|
||||
@@ -504,6 +518,81 @@ describe("assistant living chat runtime adapter", () => {
|
||||
expect(executeLlmChat).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("builds executive summary from memory instead of running generic address lookup", async () => {
|
||||
const executeLlmChat = vi.fn(async () => "raw-llm");
|
||||
const input = buildRuntimeInput({
|
||||
userMessage:
|
||||
"Финально собери executive summary по всему диалогу: где подтверждено, где proxy и что смотреть руками.",
|
||||
modeDecision: { mode: "chat", reason: "memory_recap_followup_detected" },
|
||||
sessionItems: [
|
||||
{
|
||||
role: "assistant",
|
||||
debug: {
|
||||
execution_lane: "address_query",
|
||||
answer_grounding_check: {
|
||||
status: "grounded"
|
||||
},
|
||||
detected_intent: "list_documents_by_counterparty",
|
||||
extracted_filters: {
|
||||
organization: "ООО Альтернатива Плюс",
|
||||
counterparty: "для"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
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_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: {
|
||||
period_scope: "2020",
|
||||
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 руб."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
executeLlmChat
|
||||
});
|
||||
|
||||
const output = await runAssistantLivingChatRuntime(input);
|
||||
|
||||
expect(output.handled).toBe(true);
|
||||
expect(output.chatText).toContain("Executive summary");
|
||||
expect(output.chatText).toContain("Подтверждено");
|
||||
expect(output.chatText).toContain("Proxy");
|
||||
expect(output.chatText).toContain("3 865 501,50");
|
||||
expect(output.chatText).toContain("директору смотреть руками");
|
||||
expect(output.chatText).not.toContain("«для»");
|
||||
expect(output.debug?.living_chat_response_source).toBe("deterministic_conversation_executive_summary_contract");
|
||||
expect(executeLlmChat).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("uses continuity-backed active organization for organization-fact boundary even when session scope is empty", async () => {
|
||||
const executeLlmChat = vi.fn(async () => "raw-llm");
|
||||
const input = buildRuntimeInput({
|
||||
|
||||
@@ -50,6 +50,16 @@ describe("assistantLivingModePolicy", () => {
|
||||
expect(policy.hasConversationMemoryRecallFollowupSignal("а что мы уже выяснили по этой позиции?")).toBe(true);
|
||||
});
|
||||
|
||||
it("detects final executive summary wording as memory signal", () => {
|
||||
const policy = buildPolicy();
|
||||
|
||||
expect(
|
||||
policy.hasConversationMemoryRecallFollowupSignal(
|
||||
"Финально собери executive summary по всему диалогу: где подтверждено, где proxy и что смотреть руками"
|
||||
)
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("detects answer inspection wording for previous answer correction", () => {
|
||||
const policy = buildPolicy();
|
||||
|
||||
|
||||
@@ -104,6 +104,42 @@ describe("assistantMemoryRecapPolicy", () => {
|
||||
expect(signals.contextualMemoryRecapFollowupDetected).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps final executive summary in memory lane even with strong data wording", () => {
|
||||
const executivePolicy = createAssistantMemoryRecapPolicy({
|
||||
hasHistoricalCapabilityFollowupSignal: () => false,
|
||||
hasConversationMemoryRecallFollowupSignal: (text: unknown) => /executive summary/i.test(String(text ?? "")),
|
||||
isGroundedInventoryContextDebug: () => false
|
||||
});
|
||||
|
||||
const signals = executivePolicy.resolveRouteMemorySignals({
|
||||
rawUserMessage:
|
||||
"Финально собери executive summary по всему диалогу: где подтверждено, где proxy и где не хватило доказательств.",
|
||||
repairedRawUserMessage: "",
|
||||
effectiveAddressUserMessage: "",
|
||||
repairedEffectiveAddressUserMessage: "",
|
||||
dataScopeMetaQuery: false,
|
||||
capabilityMetaQuery: false,
|
||||
dataRetrievalSignal: false,
|
||||
strongDataSignal: true,
|
||||
aggregateBusinessAnalyticsSignal: false,
|
||||
sessionItems: [
|
||||
{
|
||||
role: "assistant",
|
||||
debug: {
|
||||
execution_lane: "address_query",
|
||||
answer_grounding_check: { status: "grounded" },
|
||||
detected_intent: "receivables_confirmed_as_of_date",
|
||||
extracted_filters: {
|
||||
organization: "ООО Альтернатива Плюс"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
expect(signals.contextualMemoryRecapFollowupDetected).toBe(true);
|
||||
});
|
||||
|
||||
it("does not trigger recap from ungrounded address history", () => {
|
||||
const signals = policy.resolveRouteMemorySignals({
|
||||
rawUserMessage: "а ты помнишь что мы обсуждали?",
|
||||
|
||||
@@ -683,6 +683,45 @@ describe("assistantRoutePolicy", () => {
|
||||
expect(decision.orchestrationContract?.unsupported_current_turn_family).toBe("broad_business_evaluation");
|
||||
});
|
||||
|
||||
it("lets broad business-audit meaning override noisy capability wording", () => {
|
||||
const policy = buildPolicy({
|
||||
resolveMetaSignalSet: () => ({
|
||||
dataScopeMetaQuery: false,
|
||||
capabilityMetaQuery: true,
|
||||
metaAnswerFollowupSignal: false,
|
||||
answerInspectionFollowupSignal: false
|
||||
}),
|
||||
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"]
|
||||
})
|
||||
});
|
||||
|
||||
const decision = policy.resolveAssistantOrchestrationDecision({
|
||||
rawUserMessage:
|
||||
"Собери это как нормальный бизнес-аудит: что уже можно сказать уверенно, что proxy и что директору проверить руками.",
|
||||
effectiveAddressUserMessage:
|
||||
"Собери это как нормальный бизнес-аудит: что уже можно сказать уверенно, что proxy и что директору проверить руками.",
|
||||
followupContext: null,
|
||||
llmPreDecomposeMeta: null,
|
||||
useMock: false
|
||||
});
|
||||
|
||||
expect(decision.runAddressLane).toBe(false);
|
||||
expect(decision.toolGateReason).toBe("unsupported_current_turn_meaning_boundary");
|
||||
expect(decision.livingReason).toBe("unsupported_current_turn_meaning_boundary");
|
||||
expect(decision.orchestrationContract?.unsupported_current_turn_family).toBe("broad_business_evaluation");
|
||||
expect(decision.orchestrationContract?.hard_meta_mode).toBeNull();
|
||||
expect((decision.orchestrationContract as Record<string, unknown>)?.reason_codes).toContain(
|
||||
"business_overview_meaning_overrides_capability_meta_noise"
|
||||
);
|
||||
});
|
||||
|
||||
it("recovers an address route from current-turn meaning when L0 resolver is noisy", () => {
|
||||
const policy = buildPolicy({
|
||||
resolveAddressToolGateDecision: undefined,
|
||||
|
||||
Reference in New Issue
Block a user