ЮИ - редактура вопросов в АВТОПРОГОНАХ

This commit is contained in:
2026-04-18 10:13:16 +03:00
parent 0431595542
commit 9872ef5446
35 changed files with 1088 additions and 44 deletions
@@ -56,6 +56,23 @@ describe("assistantBoundaryPolicy", () => {
expect(reply).not.toContain("\\");
});
it("builds proactive organization offer without technical labels", () => {
const policy = createPolicy();
const reply = policy.buildAssistantProactiveOrganizationOfferReply({
status: "resolved",
channel: "default",
organizations: ["ООО Альтернатива Плюс", "ООО Лайсвуд", "РАЙМ"]
});
expect(reply).toContain("Если дальше пойдём в данные 1С");
expect(reply).toContain("ООО Альтернатива Плюс");
expect(reply).toContain("ООО Лайсвуд");
expect(reply).toContain("РАЙМ");
expect(reply).not.toContain("MCP");
expect(reply.toLowerCase()).not.toContain("snapshot");
});
it("strips unexpected CJK fragments from live chat reply", () => {
const policy = createPolicy();
@@ -23,6 +23,7 @@ function buildInput(overrides: Record<string, unknown> = {}) {
hasOperationalAdminActionRequestSignal: () => false,
hasOrganizationFactLookupSignal: () => false,
hasOrganizationFactFollowupSignal: () => false,
hasLivingChatSignal: () => true,
shouldEmitOrganizationSelectionReply: () => false,
hasAssistantCapabilityQuestionSignal: () => false,
resolveDataScopeProbe: async () => null,
@@ -37,6 +37,7 @@ function buildRuntimeInput(overrides: Record<string, unknown> = {}) {
hasOperationalAdminActionRequestSignal: () => false,
hasOrganizationFactLookupSignal: () => false,
hasOrganizationFactFollowupSignal: () => false,
hasLivingChatSignal: () => true,
shouldEmitOrganizationSelectionReply: () => false,
hasAssistantCapabilityQuestionSignal: () => false,
resolveDataScopeProbe,
@@ -53,6 +54,7 @@ function buildRuntimeInput(overrides: Record<string, unknown> = {}) {
}),
buildAssistantSafetyRefusalReply: () => "safety",
buildAssistantDataScopeContractReply: () => "scope",
buildAssistantProactiveOrganizationOfferReply: () => "",
buildAssistantOrganizationFactBoundaryReply: () => "org-boundary",
buildAssistantDataScopeSelectionReply: () => "org-selection",
buildAssistantOperationalBoundaryReply: () => "ops",
@@ -134,6 +136,56 @@ describe("assistant living chat runtime adapter", () => {
expect(executeLlmChat).toHaveBeenCalledTimes(1);
});
it("adds proactive organization offer on first smalltalk turn when multiple organizations are available", async () => {
const resolveDataScopeProbe = vi.fn(async () => ({
status: "resolved",
channel: "default",
organizations: ["ООО Альтернатива Плюс", "ООО Лайсвуд", "РАЙМ"],
error: null
}));
const input = buildRuntimeInput({
userMessage: "привет, как дела?",
resolveDataScopeProbe,
buildAssistantProactiveOrganizationOfferReply: (scopeProbe: Record<string, unknown> | null) => {
const organizations = Array.isArray(scopeProbe?.organizations) ? scopeProbe.organizations.join(", ") : "";
return `offer:${organizations}`;
}
});
const output = await runAssistantLivingChatRuntime(input);
expect(output.handled).toBe(true);
expect(output.chatText).toContain("llm-text");
expect(output.chatText).toContain("offer:ООО Альтернатива Плюс, ООО Лайсвуд, РАЙМ");
expect(output.debug?.living_chat_response_source).toBe("llm_chat_with_proactive_scope_offer");
expect(output.debug?.living_chat_proactive_scope_offer_applied).toBe(true);
expect(output.debug?.living_chat_data_scope_probe_org_count).toBe(3);
expect(output.debug?.assistant_known_organizations).toEqual(["ООО Альтернатива Плюс", "ООО Лайсвуд", "РАЙМ"]);
});
it("does not add proactive organization offer after the session already has assistant context", async () => {
const resolveDataScopeProbe = vi.fn(async () => ({
status: "resolved",
channel: "default",
organizations: ["ООО Альтернатива Плюс", "ООО Лайсвуд"],
error: null
}));
const input = buildRuntimeInput({
userMessage: "привет еще раз",
sessionItems: [{ role: "assistant", text: "Ранее уже отвечал." }],
resolveDataScopeProbe,
buildAssistantProactiveOrganizationOfferReply: () => "offer"
});
const output = await runAssistantLivingChatRuntime(input);
expect(output.handled).toBe(true);
expect(output.chatText).toBe("llm-text");
expect(output.debug?.living_chat_response_source).toBe("llm_chat");
expect(output.debug?.living_chat_proactive_scope_offer_applied).toBe(false);
expect(resolveDataScopeProbe).not.toHaveBeenCalled();
});
it("builds deterministic memory recap for prior selected-object address context", async () => {
const executeLlmChat = vi.fn(async () => "raw-llm");
const input = buildRuntimeInput({
@@ -144,6 +196,9 @@ describe("assistant living chat runtime adapter", () => {
role: "assistant",
debug: {
execution_lane: "address_query",
answer_grounding_check: {
status: "grounded"
},
detected_intent: "inventory_purchase_provenance_for_item",
extracted_filters: {
item: "Зеркало для инвалидов поворотное травмобезопасное",
@@ -160,7 +215,7 @@ describe("assistant living chat runtime adapter", () => {
expect(output.handled).toBe(true);
expect(output.chatText).toContain("Зеркало для инвалидов поворотное травмобезопасное");
expect(output.chatText).toContain("кто поставил");
expect(output.chatText).toContain("кто поставлял");
expect(output.debug?.living_chat_response_source).toBe("deterministic_memory_recap_contract");
expect(executeLlmChat).not.toHaveBeenCalled();
});
@@ -212,6 +212,48 @@ describe("assistantTransitionPolicy", () => {
expect(contract.decision).toBe("continue_previous");
});
it("retargets same-date inventory follow-up away from receivables intent", () => {
const policy = buildPolicy({
findLastAddressAssistantItem: () => ({
text: "Подтвержденная дебиторская задолженность на 31.03.2020 собрана.",
debug: {
detected_intent: "receivables_confirmed_as_of_date",
extracted_filters: {
organization: 'ООО "Альтернатива Плюс"',
as_of_date: "2020-03-31",
period_from: "2020-03-01",
period_to: "2020-03-31"
},
anchor_type: "organization",
anchor_value_resolved: 'ООО "Альтернатива Плюс"'
}
}),
hasAddressFollowupContextSignal: () => true,
hasReferentialPointer: () => true,
findRecentInventoryRootFrame: () => null,
resolveAddressIntent: () => ({ intent: "unknown" })
});
const carryover = policy.resolveAddressFollowupCarryoverContext(
"остатки по складу на эту же дату",
[],
null,
null,
null
);
expect(carryover?.followupSelectionMode).toBe("carry_previous_intent");
expect(carryover?.followupContext?.previous_intent).toBe("receivables_confirmed_as_of_date");
expect(carryover?.followupContext?.target_intent).toBe("inventory_on_hand_as_of_date");
expect(carryover?.followupContext?.previous_filters).toMatchObject({
organization: 'ООО "Альтернатива Плюс"',
as_of_date: "2020-03-31",
period_from: "2020-03-01",
period_to: "2020-03-31"
});
expect(carryover?.followupContext?.root_context_only).toBeUndefined();
});
it("drops stale carryover for a fresh standalone topic from another intent family", () => {
const policy = buildPolicy({
findLastAddressAssistantItem: () => ({