ГЛОБАЛЬНЫЙ РЕФАКТОРИНГ АРХИТЕКТУРЫ - Рефакторинг этапов Stage 3.4: расширен soft-refusal в deep/living, чтобы при слабом/непокрытом домене не падать в шаблонный Коротко/Что сломано.

This commit is contained in:
2026-04-11 17:41:07 +03:00
parent a0d0f95dde
commit 88d5561fad
5 changed files with 386 additions and 15 deletions
@@ -54,6 +54,7 @@ describe("assistant boundary fallback reply", () => {
expect(output.reply_type).toBe("clarification_required");
expect(output.assistant_reply).toMatch(/мягкий отказ/i);
expect(output.assistant_reply).toContain("Что могу сделать рядом по смыслу:");
expect(output.assistant_reply).toContain("Что могу сделать сейчас:");
expect(output.assistant_reply).not.toContain("Что сломано:");
});
@@ -76,9 +77,10 @@ describe("assistant boundary fallback reply", () => {
});
expect(output.reply_type).toBe("clarification_required");
expect(output.assistant_reply).toMatch(/не могу надежно ответить по сценарию/i);
expect(output.assistant_reply).toMatch(/не могу надежно ответить по сценарию|не хватает подтвержденной опоры/i);
expect(output.assistant_reply).toContain("Чтобы сразу перейти к проверке, уточни:");
expect(output.assistant_reply).toContain("Если удобнее, могу начать с близкого сценария:");
expect(output.assistant_reply).toContain("Что могу сделать сейчас:");
expect(output.assistant_reply).not.toContain("Что сломано:");
});
@@ -101,7 +103,66 @@ describe("assistant boundary fallback reply", () => {
});
expect(output.reply_type).toBe("out_of_scope");
expect(output.assistant_reply).toMatch(/мягкий отказ|не могу надежно/i);
expect(output.assistant_reply).toMatch(/мягкий отказ|не могу надежно|не хватает подтвержденной опоры/i);
expect(output.assistant_reply).toContain("Что могу сделать сейчас:");
expect(output.assistant_reply).not.toContain("Что сломано:");
});
});
it("uses soft refusal in broad_partial mode when domain is not covered and evidence is weak", () => {
const output = composeAssistantAnswer({
userMessage: "What is the expected inflation next quarter and FX trend?",
routeSummary: buildRouteSummary("none"),
retrievalResults: [
{
fragment_id: "F1",
requirement_ids: ["R1"],
route: "store_canonical",
status: "partial",
result_type: "summary",
items: [{ note: "weak candidate" }],
summary: { note: "weak" },
evidence: [],
why_included: [],
selection_reason: [],
risk_factors: [],
business_interpretation: [],
confidence: "low",
limitations: ["weak_source_mapping"],
errors: []
} as any
],
requirements: [
{
requirement_id: "R1",
source_fragment_id: "F1",
requirement_text: "forecast and trend",
subject_tokens: [],
status: "clarification_needed",
route: null
}
],
coverageReport: {
requirements_total: 1,
requirements_covered: 0,
requirements_uncovered: [],
requirements_partially_covered: [],
clarification_needed_for: ["R1"],
out_of_scope_requirements: []
},
groundingCheck: {
status: "partial",
route_subject_match: false,
missing_requirements: ["R1"],
reasons: ["weak_source_mapping"],
why_included_summary: [],
selection_reason_summary: []
},
enableAnswerPolicyV11: true
});
expect(output.reply_type).toBe("partial_coverage");
expect(output.assistant_reply).toContain("Что могу сделать рядом по смыслу:");
expect(output.assistant_reply).toContain("Что могу сделать сейчас:");
expect(output.assistant_reply).not.toContain("Что сломано:");
});
});