fix(rectification): remove static interview fallbacks
Independent Staging Quality Gate / validate (push) Successful in 10m50s
Independent Staging Quality Gate / publish (push) Successful in 13m29s

This commit is contained in:
Jesse_Chen
2026-08-27 16:40:47 +08:00
parent 5f855c610c
commit f3327235ea
43 changed files with 1798 additions and 509 deletions
@@ -5,8 +5,16 @@ import { buildInferenceState } from "../src/lib/rectification-agentic/core/build
import { persistServerOwnedFocus, shouldSkipDiscriminatorFollowup, stableFollowupQuestionId } from "../src/lib/rectification-agentic/v9/server-focus.ts";
import { buildChoiceFrame, serverOwnedChoiceCopy } from "../src/lib/rectification-agentic/v9/choice-card.ts";
import type { MethodFollowup } from "../src/lib/rectification-agentic/v9/method-followup.ts";
import type { EventProbeStyleOption } from "../src/lib/rectification-agentic/v9/refinement-packet.ts";
import { fakeAccounting, CASE_ID, FOCUS_ID, USER_ID, activeFocusFixture } from "./rectification-v9-test-support.ts";
const DYNAMIC_STYLE_OPTIONS = [
{ label: "明确发生且时间吻合", answer_class: "yes" },
{ label: "发生过但程度较弱", answer_class: "weak_yes" },
{ label: "明确没有发生", answer_class: "no" },
{ label: "这段记不清楚", answer_class: "unsure" },
] as const;
function discriminatorFollowup(overrides: Partial<MethodFollowup> = {}): MethodFollowup {
const frame = buildChoiceFrame({
method_id: "dasha_events",
@@ -18,17 +26,19 @@ function discriminatorFollowup(overrides: Partial<MethodFollowup> = {}): MethodF
year: 2016,
year_label: "2016 年前后",
domain: "education",
event_family: "高考或重要考试发挥明显失常",
event_family: "升学结果或学习环境出现明显变化",
source: "dasha_activation",
tracks: ["vimshottari", "narayana"],
tracks_agree: true,
unique_minute_claim: false,
user_meaning: "2016 年前后高考或重要考试发挥明显失常",
user_meaning: "2016 年前后升学结果或学习环境出现明显变化",
role: "distinguish",
information_gain: 0.4,
semantic_key: "education:2016",
style_options: DYNAMIC_STYLE_OPTIONS,
}],
});
assert.ok(frame);
return {
method_id: "dasha_events",
intent: "distinguish_candidates",
@@ -51,6 +61,107 @@ function discriminatorFollowup(overrides: Partial<MethodFollowup> = {}): MethodF
};
}
function invalidStyleFollowup(
styleOptions: readonly EventProbeStyleOption[] | undefined,
eventFamily = "升学结果或学习环境出现明显变化",
): MethodFollowup {
const valid = discriminatorFollowup();
return {
...valid,
choice_frame: buildChoiceFrame({
method_id: "dasha_events",
ask_theme: "dated_event",
domain: "education",
user_prompt_hint: "ask",
}, {
probes: [{
year: 2016,
year_label: "2016 年前后",
domain: "education",
event_family: eventFamily,
source: "dasha_activation",
tracks: ["vimshottari", "narayana"],
tracks_agree: true,
unique_minute_claim: false,
user_meaning: "2016 年前后升学结果或学习环境出现明显变化",
role: "distinguish",
information_gain: 0.4,
semantic_key: "education:2016",
style_options: styleOptions,
}],
}),
};
}
async function assertInvalidChoiceSkipped(followup: MethodFollowup) {
const accounting = fakeAccounting({}, {
fallback: () => {
throw new Error("invalid choice frame must not call the database");
},
});
const result = await persistServerOwnedFocus({
accounting: accounting.client,
userId: USER_ID,
caseId: CASE_ID,
activeFocus: null,
decisionReceipt: null,
followup,
});
assert.equal(result.status, "skipped");
assert.equal(accounting.calls.length, 0);
}
test("complete dynamic event options persist a server-owned focus", async () => {
const followup = discriminatorFollowup({ source: "precision_stage", information_gain: 0.2 });
const accounting = fakeAccounting({
set_agentic_rectification_conversation_focus: (_fn, args) => ({
id: FOCUS_ID,
case_id: CASE_ID,
question_id: args.p_question_id,
intent: args.p_intent,
target_evidence_id: null,
target_domain: args.p_target_domain,
target_kind: args.p_target_kind,
expected_answer_schema: args.p_expected_answer_schema,
status: "active",
asked_at: "2026-08-27T00:00:00.000Z",
resolved_at: null,
idempotent: false,
}),
});
const result = await persistServerOwnedFocus({
accounting: accounting.client,
userId: USER_ID,
caseId: CASE_ID,
activeFocus: null,
decisionReceipt: null,
followup,
});
assert.equal(result.status, "created");
assert.equal(accounting.calls.length, 1);
assert.deepEqual(result.focus?.expectedAnswerSchema.choice, {
prompt: "2016 年前后 · 升学结果或学习环境出现明显变化",
option_a: DYNAMIC_STYLE_OPTIONS[0].label,
option_b: DYNAMIC_STYLE_OPTIONS[1].label,
option_c: DYNAMIC_STYLE_OPTIONS[2].label,
option_d: DYNAMIC_STYLE_OPTIONS[3].label,
});
});
test("missing, duplicate, incomplete, or illegal dynamic options skip focus persistence", async () => {
await assertInvalidChoiceSkipped(invalidStyleFollowup(undefined));
await assertInvalidChoiceSkipped(invalidStyleFollowup(DYNAMIC_STYLE_OPTIONS.slice(0, 3)));
await assertInvalidChoiceSkipped(invalidStyleFollowup([
...DYNAMIC_STYLE_OPTIONS,
{ label: "另一种明确发生", answer_class: "yes" },
]));
await assertInvalidChoiceSkipped(invalidStyleFollowup([
{ ...DYNAMIC_STYLE_OPTIONS[0], label: "08:12 左右发生" },
...DYNAMIC_STYLE_OPTIONS.slice(1),
]));
await assertInvalidChoiceSkipped(invalidStyleFollowup(DYNAMIC_STYLE_OPTIONS, " "));
});
test("does not ask an already-answered discriminator probe again", async () => {
const followup = discriminatorFollowup();
const active = activeFocusFixture({