fix(rectification): hang spoken year follow-ups and drop engine representative from model tables (BUG-678/676)
Spoken collect without askedTurnId now hangs on the last assistant message, remaining-candidate copy uses credible_range, and engine representativeTime no longer keeps eliminated minutes in model house tables.
This commit is contained in:
@@ -332,6 +332,8 @@ function adoptHostNarration(input: {
|
||||
input.dossier.evidence,
|
||||
input.dossier.conversationSummary.declinedSkippedTopics,
|
||||
catalog.remainingSplitTimes,
|
||||
undefined,
|
||||
catalog.remainingCredibleRange,
|
||||
);
|
||||
if (!hint || delivered.includes(hint)) return delivered;
|
||||
return `${delivered} ${hint}`.replace(/\s+/g, " ").trim();
|
||||
@@ -1781,6 +1783,7 @@ async function persistExhaustionCollect(input: {
|
||||
dossier.conversationSummary.declinedSkippedTopics,
|
||||
catalog.remainingSplitTimes,
|
||||
catalog.remainingCandidateCount,
|
||||
catalog.remainingCredibleRange,
|
||||
);
|
||||
const trainingGate = trainingScoreableGate(dossier.evidence);
|
||||
const ceiling = engineCapabilityCeilingFromReceipt(receipt);
|
||||
@@ -1889,6 +1892,7 @@ async function persistExhaustionCollect(input: {
|
||||
dossier.conversationSummary.declinedSkippedTopics,
|
||||
catalog.remainingSplitTimes,
|
||||
catalog.remainingCandidateCount,
|
||||
catalog.remainingCredibleRange,
|
||||
);
|
||||
targetedSkipCount += 1;
|
||||
continue;
|
||||
@@ -1972,6 +1976,7 @@ async function persistExhaustionCollect(input: {
|
||||
dossier.conversationSummary.declinedSkippedTopics,
|
||||
catalog.remainingSplitTimes,
|
||||
catalog.remainingCandidateCount,
|
||||
catalog.remainingCredibleRange,
|
||||
);
|
||||
const range = nonConvergingRangeNarration({
|
||||
credibleRange: decision.credibleRange ?? input.decision.credibleRange,
|
||||
|
||||
@@ -13,7 +13,7 @@ export function slimDecisionReceipt(
|
||||
: dossier.latestResult?.candidates ?? []) {
|
||||
activeTimes.add(candidate.time);
|
||||
}
|
||||
for (const value of [inference?.representative_time, dossier.latestResult?.representativeTime, dossier.latestResult?.selectedTime]) {
|
||||
for (const value of [inference?.representative_time, dossier.latestResult?.selectedTime]) {
|
||||
if (typeof value === "string" && value.trim()) activeTimes.add(value.slice(0, 5));
|
||||
}
|
||||
const focusSchema = dossier.conversationSummary.activeFocus?.expectedAnswerSchema ?? {};
|
||||
|
||||
@@ -751,13 +751,23 @@ function remainingTargetedDomains(
|
||||
return domains;
|
||||
}
|
||||
|
||||
function clockRangePair(
|
||||
value: readonly [string, string] | null | undefined,
|
||||
): readonly [string, string] | null {
|
||||
const start = clockValue(value?.[0]);
|
||||
const end = clockValue(value?.[1]);
|
||||
return start && end ? [start, end] : null;
|
||||
}
|
||||
|
||||
export function remainingCandidatesLine(
|
||||
splitTimes: readonly [string, string] | null | undefined,
|
||||
candidateCount: number,
|
||||
examples: readonly string[],
|
||||
credibleRange?: readonly [string, string] | null,
|
||||
): string | null {
|
||||
if (!splitTimes?.[0] || !splitTimes[1] || candidateCount < 1 || examples.length === 0) return null;
|
||||
return `现在还剩 ${splitTimes[0]}–${splitTimes[1]} 里 ${candidateCount} 个候选,能把它们分开的是这几条线:${examples.join("、")}`;
|
||||
const range = clockRangePair(credibleRange) ?? clockRangePair(splitTimes);
|
||||
if (!range || candidateCount < 1 || examples.length === 0) return null;
|
||||
return `现在还剩 ${range[0]}–${range[1]} 里 ${candidateCount} 个候选,能把它们分开的是这几条线:${examples.join("、")}`;
|
||||
}
|
||||
|
||||
export function targetedCollectPool(
|
||||
@@ -766,6 +776,7 @@ export function targetedCollectPool(
|
||||
declinedTopics: readonly CollectionTopic[] = [],
|
||||
splitTimes?: readonly [string, string] | null,
|
||||
candidateCount?: number,
|
||||
credibleRange?: readonly [string, string] | null,
|
||||
): CollectionPoolItem[] {
|
||||
if (pendingTargetedYearDomain(declinedTopics, evidence)) return [];
|
||||
const declined = collectDeclinedKinds(declinedTopics);
|
||||
@@ -773,7 +784,7 @@ export function targetedCollectPool(
|
||||
if (domains.length === 0) return [];
|
||||
const count = candidateCount ?? 0;
|
||||
const examples = domains.map((domain) => TARGETED_EXAMPLES[domain][0]);
|
||||
const remainingLine = remainingCandidatesLine(splitTimes, count, examples);
|
||||
const remainingLine = remainingCandidatesLine(splitTimes, count, examples, credibleRange);
|
||||
return domains.map((domain) => {
|
||||
const existencePrompt = TARGETED_EXISTENCE_PROMPT[domain];
|
||||
return {
|
||||
@@ -821,15 +832,24 @@ export function rangeNarrowHint(
|
||||
declinedTopics: readonly CollectionTopic[] = [],
|
||||
splitTimes?: readonly [string, string] | null,
|
||||
candidateCount?: number,
|
||||
credibleRange?: readonly [string, string] | null,
|
||||
): string {
|
||||
const open = targetedCollectPool(remainingLayers, evidence, [], splitTimes, candidateCount);
|
||||
const open = targetedCollectPool(remainingLayers, evidence, [], splitTimes, candidateCount, credibleRange);
|
||||
const remaining = remainingCandidatesLine(
|
||||
splitTimes,
|
||||
candidateCount ?? 0,
|
||||
open.map((item) => TARGETED_EXAMPLES[item.domain as CollectKind]?.[0] ?? item.examples?.[0] ?? "").filter(Boolean),
|
||||
credibleRange,
|
||||
);
|
||||
const hint = targetedCollectHintFromPool(open)
|
||||
?? targetedCollectHint(targetedCollectPool(remainingLayers, evidence, declinedTopics, splitTimes, candidateCount)[0])
|
||||
?? targetedCollectHint(targetedCollectPool(
|
||||
remainingLayers,
|
||||
evidence,
|
||||
declinedTopics,
|
||||
splitTimes,
|
||||
candidateCount,
|
||||
credibleRange,
|
||||
)[0])
|
||||
?? `还能再收窄:${moreCollectHint(evidence, declinedTopics)}`;
|
||||
if (remaining && !hint.startsWith(remaining)) {
|
||||
return `${remaining}。${hint}`;
|
||||
|
||||
@@ -393,6 +393,7 @@ export function rectificationFollowupCatalog(
|
||||
remainingLayers,
|
||||
remainingSplitTimes: remainingSplitTimes(activeTimes.length ? activeTimes : topCandidateTimes),
|
||||
remainingCandidateCount: remainingCandidateCount(activeTimes.length ? activeTimes : topCandidateTimes),
|
||||
remainingCredibleRange: inference?.credible_range ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -779,6 +780,7 @@ export function decideFromDossier(
|
||||
remainingLayers: catalog.remainingLayers,
|
||||
remainingSplitTimes: catalog.remainingSplitTimes,
|
||||
remainingCandidateCount: catalog.remainingCandidateCount,
|
||||
remainingCredibleRange: catalog.remainingCredibleRange,
|
||||
...followupCaseArgs({
|
||||
stage: dossier.case.stage,
|
||||
blockScan: dossier.case.blockScan,
|
||||
@@ -898,6 +900,7 @@ export function decideAfterInferenceChange(input: {
|
||||
remainingLayers: catalog.remainingLayers,
|
||||
remainingSplitTimes: catalog.remainingSplitTimes,
|
||||
remainingCandidateCount: catalog.remainingCandidateCount,
|
||||
remainingCredibleRange: catalog.remainingCredibleRange,
|
||||
...followupCaseArgs({
|
||||
stage: input.dossier.case.stage,
|
||||
blockScan: input.dossier.case.blockScan,
|
||||
|
||||
@@ -416,6 +416,7 @@ export function buildRangeDelivery(input: {
|
||||
input.declinedTopics ?? [],
|
||||
remainingSplitTimes(activeTimes),
|
||||
remainingCandidateCount(activeTimes),
|
||||
inference?.credible_range,
|
||||
),
|
||||
tie_break_available: input.tieBreakAvailable === true,
|
||||
};
|
||||
|
||||
@@ -1448,6 +1448,7 @@ export function targetedCollectFollowup(
|
||||
declinedTopics: readonly Readonly<Record<string, unknown>>[] = [],
|
||||
splitTimes?: readonly [string, string] | null,
|
||||
candidateCount?: number,
|
||||
credibleRange?: readonly [string, string] | null,
|
||||
): MethodFollowup | null {
|
||||
const pendingYear = pendingTargetedYearDomain(declinedTopics, evidence);
|
||||
if (pendingYear) {
|
||||
@@ -1459,9 +1460,10 @@ export function targetedCollectFollowup(
|
||||
declinedTopics,
|
||||
splitTimes,
|
||||
candidateCount,
|
||||
credibleRange,
|
||||
)[0];
|
||||
if (!top) return null;
|
||||
return targetedCollectExistenceFollowup(top, splitTimes, candidateCount);
|
||||
return targetedCollectExistenceFollowup(top, splitTimes, candidateCount, credibleRange);
|
||||
}
|
||||
|
||||
export function targetedCollectYearFollowup(domain: CollectKind): MethodFollowup {
|
||||
@@ -1490,6 +1492,7 @@ function targetedCollectExistenceFollowup(
|
||||
item: CollectionPoolItem,
|
||||
splitTimes?: readonly [string, string] | null,
|
||||
candidateCount?: number,
|
||||
credibleRange?: readonly [string, string] | null,
|
||||
): MethodFollowup {
|
||||
const base = followupFromPoolItem(item);
|
||||
const frame = buildTargetedCollectExistenceFrame({
|
||||
@@ -1502,6 +1505,7 @@ function targetedCollectExistenceFollowup(
|
||||
splitTimes,
|
||||
candidateCount ?? remainingCandidateCount(),
|
||||
item.examples?.length ? [item.examples[0]!] : [],
|
||||
credibleRange,
|
||||
);
|
||||
return {
|
||||
...base,
|
||||
@@ -2098,6 +2102,7 @@ export function buildMethodFollowupPlan(input: {
|
||||
remainingLayers?: readonly string[];
|
||||
remainingSplitTimes?: readonly [string, string] | null;
|
||||
remainingCandidateCount?: number;
|
||||
remainingCredibleRange?: readonly [string, string] | null;
|
||||
tieBreakRequested?: boolean;
|
||||
}): MethodFollowupPlan {
|
||||
const makeFollowup = (
|
||||
@@ -2762,6 +2767,7 @@ export function buildMethodFollowupPlan(input: {
|
||||
],
|
||||
input.remainingSplitTimes,
|
||||
input.remainingCandidateCount,
|
||||
input.remainingCredibleRange,
|
||||
))
|
||||
) {
|
||||
// BUG-651: no yearless personality as the next discriminator.
|
||||
@@ -2963,6 +2969,7 @@ export function buildMethodFollowupPlan(input: {
|
||||
],
|
||||
input.remainingSplitTimes,
|
||||
input.remainingCandidateCount,
|
||||
input.remainingCredibleRange,
|
||||
);
|
||||
if (targeted) next = targeted;
|
||||
else if (pendingHoldout) next = makeFollowup(pendingHoldout, false);
|
||||
|
||||
@@ -25,7 +25,8 @@ const ANSWER_OPTIONS = new Set(["A", "B", "C", "D", "stop", "skip_probe"]);
|
||||
export function focusSpokenPrompt(schema: Readonly<Record<string, unknown>> | null | undefined): string | null {
|
||||
if (!schema) return null;
|
||||
const top = typeof schema.prompt === "string" ? schema.prompt.trim() : "";
|
||||
if (top.length >= 8 && top.length <= 120) return top;
|
||||
// Collect schemas keep short stems such as "大概哪年几月?" (7 chars).
|
||||
if (top && top.length <= 120 && (schema.collect === true || top.length >= 8)) return top;
|
||||
const copy = parseAgentChoiceCopy(schema);
|
||||
const fromChoice = copy?.prompt?.trim() ?? "";
|
||||
return fromChoice || null;
|
||||
@@ -107,9 +108,7 @@ export function attachQuestionsToTurns<T extends { id: string; role: string; tex
|
||||
const hanging: ConversationFocus[] = [];
|
||||
for (const focus of focuses) {
|
||||
if (!focus.askedTurnId) {
|
||||
if (focus.status === "active" && turnQuestionKind(focus) === "choice") {
|
||||
hanging.push(focus);
|
||||
}
|
||||
if (focus.status === "active") hanging.push(focus);
|
||||
continue;
|
||||
}
|
||||
byTurn.set(focus.askedTurnId, focus);
|
||||
|
||||
Reference in New Issue
Block a user