明说要校正时,第一步就能打开(BUG-1317)

本命和申报时段的第 0 步,校正工具挂上时可以排盘,也可以直接打开校正。还没看盘就顺口提议不算已经提议过。规则第一句写明 explicitRequest=true。定向测试 149 通过,类型检查和 lint 通过,隐私标记通过。名人生平回测未跑。

Co-Authored-By: Grok (agent loop)
This commit is contained in:
jesse-ux
2026-10-13 22:02:41 +08:00
co-authored by Grok (agent loop)
parent 0c04755560
commit bccceff91f
12 changed files with 251 additions and 33 deletions
+8 -2
View File
@@ -1215,11 +1215,17 @@ export async function POST(request: Request) {
};
const natalStreamOptions = {
...streamOptions,
prepareStep: consultationNatalPrepareStep,
prepareStep: (input: { stepNumber: number }) => consultationNatalPrepareStep({
stepNumber: input.stepNumber,
rectificationOffer: true,
}),
};
const windowStreamOptions = {
...streamOptions,
prepareStep: consultationWindowPrepareStep,
prepareStep: (input: { stepNumber: number }) => consultationWindowPrepareStep({
stepNumber: input.stepNumber,
rectificationOffer: true,
}),
};
const answerPhaseSignal = runClock.answerSignal;
const startAnswerPhase = () => {
@@ -459,7 +459,7 @@ export function natalAnswerShapeInstruction(): string {
* Never decided from keywords. Defined here once; the offer tool names the
* same parameters and does not restate this rule.
*/
export const RECTIFICATION_OFFER_RULE = "出生时间核对:用户说你推得不准时,先分清是读法问题还是时间问题。只是不认同某个解释,按纠正规则认下、改读,不提校正。不承诺能收窄出生时间,不在对话里收集经历做校正。用户这一轮明确要做校正,就在写回答之前调用 offer-birth-time-rectification 并传 explicitRequest=true;工具通过后只写那一句过渡,不写解读。用户说出过去某件具体的事和盘对不上、说自己的出生时间是估的或记不准、但没有明确要做校正,才在写回答之前调用 offer-birth-time-rectification,再按工具结果在正文里用一句话提一下(不写价格和题数,卡片上有)。同一次对话提议过、用户没接,就不再主动提;用户自己再要求时调用并传 userAsked=true。工具说不能提供时(比如问的是家人),照工具说明用一句话说清楚。";
export const RECTIFICATION_OFFER_RULE = "校正就在这个对话里能开:用户这一轮明确要做出生时间校正(说法可能是校正、纠正、核对出生时间、看看出生时间准不准等),第一步就调用 offer-birth-time-rectification 并传 explicitRequest=true,不用先排盘,工具通过后只写那一句过渡,不写解读。用户说你推得不准时,先分清是读法问题还是时间问题。只是不认同某个解释,按改读规则认下、改读,不提校正。不承诺能收窄出生时间,不在对话里收集经历做校正。用户说出过去某件具体的事和盘对不上、说自己的出生时间是估的或记不准、但没有明确要做校正,才在看过盘之后调用 offer-birth-time-rectification,再按工具结果在正文里用一句话提一下(不写价格和题数,卡片上有)。同一次对话提议过、用户没接,就不再主动提;用户自己再要求时调用并传 userAsked=true。工具说不能提供时(比如问的是家人),照工具说明用一句话说清楚。";
/** D4 / BUG-1166: the user corrects an earlier reading; acknowledge it, never defend the reading. */
export const FOLLOW_UP_CORRECTION_RULE = "用户纠正了前面的判断(比如说父亲其实不常在身边):先认下这件事,再只说盘上真正对得上的那几条、原来哪句读偏了;盘本身分不出的,直说「盘本身分不出这一点」,不把普通配置说成必然;不为原判断辩护。";
+31 -18
View File
@@ -176,34 +176,41 @@ export const MAX_EVIDENCE_LOOKUPS_PER_TURN = 3;
export const CONSULTATION_WINDOW_CALC_TOOL_ID = "run-jyotish-window-consultation";
/**
* Step 0 exposes only the natal chart tool. Some thinking-mode providers
* reject named and required toolChoice (BUG-282, BUG-937); the first step
* therefore narrows with activeTools and leaves the choice as auto. Later
* steps stay auto so the model can write after the request-scoped
* calculation is in hand.
* Step 0 narrows with activeTools and leaves toolChoice auto. Thinking-mode
* providers reject named and required toolChoice (BUG-282, BUG-937).
* When this turn mounted the rectification offer, that tool is available
* beside the chart tool so an explicit request can open before a reading
* (BUG-1317). Without it, step 0 is still only the chart tool (BUG-923).
* Later steps stay auto.
*
* Window and general agents must not share this hook: they do not own this tool.
* Window and general agents must not share the natal hook.
*/
export function consultationNatalPrepareStep(input: { stepNumber: number }) {
return input.stepNumber === 0
? {
export function consultationNatalPrepareStep(input: { stepNumber: number; rectificationOffer?: boolean }) {
if (input.stepNumber !== 0) return { toolChoice: "auto" as const };
if (!input.rectificationOffer) {
return {
activeTools: [CONSULTATION_NATAL_CALC_TOOL_ID],
toolChoice: "auto" as const,
}
: {
toolChoice: "auto" as const,
};
}
return {
activeTools: [CONSULTATION_NATAL_CALC_TOOL_ID, RECTIFICATION_OFFER_TOOL_ID],
toolChoice: "auto" as const,
};
}
export function consultationWindowPrepareStep(input: { stepNumber: number }) {
return input.stepNumber === 0
? {
export function consultationWindowPrepareStep(input: { stepNumber: number; rectificationOffer?: boolean }) {
if (input.stepNumber !== 0) return { toolChoice: "auto" as const };
if (!input.rectificationOffer) {
return {
activeTools: [CONSULTATION_WINDOW_CALC_TOOL_ID],
toolChoice: "auto" as const,
}
: {
toolChoice: "auto" as const,
};
}
return {
activeTools: [CONSULTATION_WINDOW_CALC_TOOL_ID, RECTIFICATION_OFFER_TOOL_ID],
toolChoice: "auto" as const,
};
}
const CONSULTATION_DOMAIN_DURATION_MS = 31_000;
@@ -1174,6 +1181,12 @@ function createRectificationOfferTool(ctx: Readonly<{ state: ConsultationRuntime
}
return { offered: false as const, note: "已经在打开。不要再写解读。" };
}
if (input.explicitRequest !== true && !ctx.state.consultationToolCompleted) {
return {
offered: false as const,
note: "先排盘,看过盘再决定要不要提。这次不算已经提议过。",
};
}
if (hook.alreadyOffered && input.userAsked !== true && input.explicitRequest !== true) {
return { offered: false as const, reason: "already_offered_in_session", note: "这次对话里已经提议过一次,用户没接。不再主动提校正,正常回答。" };
}