fix(rectification): unify discriminator question contract and rank by information gain
Python and TypeScript now share a four-option probe contract, persist Focus before asking, and pick the highest-value renderable probe instead of preferring low-gain career events over D24. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -21,12 +21,16 @@ import { resolveSessionLanguageModel } from "@/lib/model-catalog";
|
||||
import { createAdminSupabaseClient } from "@/lib/supabase/admin";
|
||||
import { createServerSupabaseClient } from "@/lib/supabase/server";
|
||||
import { defaultMessageOrigin, isRectificationMessageOrigin } from "@/lib/rectification-agentic/v9/message-origin";
|
||||
import { previousInferenceFromReceipt } from "@/lib/rectification-agentic/v9/inference-adapter";
|
||||
import { previousInferenceFromReceipt, askedDiscriminatorKeys } from "@/lib/rectification-agentic/v9/inference-adapter";
|
||||
import { parseAgentChoiceCopy } from "@/lib/rectification-agentic/v9/choice-card";
|
||||
import {
|
||||
classifyRectificationTurnIntent,
|
||||
optionIdForAnswerClass,
|
||||
} from "@/lib/rectification-agentic/v9/turn-intent-classifier";
|
||||
import { decideFromDossier, contrastPacketFromDossier } from "@/lib/rectification-agentic/v9/decision-from-dossier";
|
||||
import { persistServerOwnedFocus, openQuestionFromPersistedFocus } from "@/lib/rectification-agentic/v9/server-focus";
|
||||
import { buildMethodFollowupPlan } from "@/lib/rectification-agentic/v9/method-followup";
|
||||
import { refinementFromDecisionReceipt } from "@/lib/rectification-agentic/v9/refinement-packet";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
export const maxDuration = 240;
|
||||
@@ -308,11 +312,8 @@ export async function POST(request: Request) {
|
||||
try {
|
||||
const dossier = await loadV9CaseDossier(accounting, userId, caseId);
|
||||
const focus = dossier.conversationSummary.activeFocus;
|
||||
if (focus) {
|
||||
const choice = parseAgentChoiceCopy(focus.expectedAnswerSchema);
|
||||
if (!choice) {
|
||||
return completedMessageResponse("当前问题已更新,请刷新后重新作答。", requestId, caseId);
|
||||
}
|
||||
const choice = focus ? parseAgentChoiceCopy(focus.expectedAnswerSchema) : null;
|
||||
if (focus && choice) {
|
||||
let classified = null;
|
||||
try {
|
||||
classified = await classifyRectificationTurnIntent(selectedModel, {
|
||||
@@ -379,6 +380,81 @@ export async function POST(request: Request) {
|
||||
await transitionV9CaseStatus(accounting, userId, caseId, "paused");
|
||||
return completedMessageResponse(applied.narration, requestId, caseId);
|
||||
}
|
||||
} else {
|
||||
const decision = decideFromDossier(dossier);
|
||||
if (decision.nextAction === "ask_candidate_discriminator") {
|
||||
const refinement = refinementFromDecisionReceipt(dossier.latestResult?.decisionReceipt ?? null);
|
||||
const plan = buildMethodFollowupPlan({
|
||||
evidence: dossier.evidence,
|
||||
declinedTopics: dossier.conversationSummary.declinedSkippedTopics,
|
||||
sessionOutcome: "discriminate_candidates",
|
||||
eventProbes: refinement.discriminating_event_probes,
|
||||
askedProbeKeys: askedDiscriminatorKeys(
|
||||
dossier.latestResult?.decisionReceipt,
|
||||
dossier.evidence,
|
||||
),
|
||||
contrastPacket: contrastPacketFromDossier(dossier),
|
||||
candidatesSeparated: false,
|
||||
});
|
||||
const persisted = await persistServerOwnedFocus({
|
||||
accounting,
|
||||
userId,
|
||||
caseId,
|
||||
activeFocus: focus,
|
||||
decisionReceipt: dossier.latestResult?.decisionReceipt,
|
||||
followup: plan.next_followup,
|
||||
});
|
||||
const open = openQuestionFromPersistedFocus(persisted);
|
||||
if (open && persisted.focus) {
|
||||
let classified = null;
|
||||
try {
|
||||
classified = await classifyRectificationTurnIntent(selectedModel, {
|
||||
focus: persisted.focus,
|
||||
userMessage: parsed.data.message ?? "",
|
||||
caseStatus,
|
||||
signal: request.signal,
|
||||
});
|
||||
} catch {
|
||||
classified = null;
|
||||
}
|
||||
if (classified?.intent === "answer_current_focus" && classified.answer_class) {
|
||||
const optionId = optionIdForAnswerClass(persisted.focus, classified.answer_class);
|
||||
if (optionId) {
|
||||
const previous = previousInferenceFromReceipt(dossier.latestResult?.decisionReceipt ?? null);
|
||||
const applied = await applyRectificationChoice(accounting, {
|
||||
userId,
|
||||
caseId,
|
||||
sessionId,
|
||||
actionId: requestId,
|
||||
action: CHOICE_ACTION,
|
||||
focusId: persisted.focus.id,
|
||||
questionId: persisted.focus.questionId,
|
||||
probeId: typeof persisted.focus.expectedAnswerSchema.probe_id === "string"
|
||||
? persisted.focus.expectedAnswerSchema.probe_id
|
||||
: null,
|
||||
optionId,
|
||||
expectedRevision: previous?.revision ?? 0,
|
||||
userDisplay: parsed.data.message ?? null,
|
||||
});
|
||||
return completedMessageResponse(applied.narration, requestId, caseId);
|
||||
}
|
||||
}
|
||||
const narration = "请点选下面的选项作答。点选和一句话回答会按同一规则计入当前区分题。";
|
||||
await persistV9DeterministicTurn(accounting, userId, caseId, {
|
||||
requestId,
|
||||
userMessage: parsed.data.message ?? null,
|
||||
assistantMessage: narration,
|
||||
});
|
||||
return completedMessageResponse(narration, requestId, caseId);
|
||||
}
|
||||
const narration = "目前没有可继续区分的问题。当前几个候选构成可信区间,不再泛问已经覆盖过的经历。";
|
||||
await persistV9DeterministicTurn(accounting, userId, caseId, {
|
||||
requestId,
|
||||
userMessage: parsed.data.message ?? null,
|
||||
assistantMessage: narration,
|
||||
});
|
||||
return completedMessageResponse(narration, requestId, caseId);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof RectificationToolServiceError) {
|
||||
|
||||
Reference in New Issue
Block a user