feat(consult): adopted segment charts that cannot be determined reach the model as policy and one plain sentence (BUG-1138, T2)
The shared range loader attaches unreliableVargas for segment-v1 adoptions; the consultation tool input carries it, runConsultationWorkflow strips it from the engine payload and writes answer_policy.unreliable_vargas, deterministic_claims_forbidden_for and a user-facing limitation. Legacy and unrectified profiles are byte-identical (request, context, packet). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
35b89bdf21
commit
7f39c9346d
@@ -67,3 +67,58 @@ export function applyBirthTimeModeToWorkflowContext<
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Labels follow VOICE.md (「婚恋(D9)会随分钟变」). */
|
||||
export const UNRELIABLE_VARGA_LABELS: Readonly<Record<"D1" | "D9" | "D10", string>> = {
|
||||
D1: "本命盘(D1)",
|
||||
D9: "婚恋(D9)",
|
||||
D10: "事业(D10)",
|
||||
};
|
||||
|
||||
/**
|
||||
* One plain sentence for charts a segment-v1 adoption could not determine (BUG-1138).
|
||||
* Server-written, so the model repeats it rather than inventing its own caveat.
|
||||
*/
|
||||
export function unreliableVargasNotice(vargas: readonly ("D1" | "D9" | "D10")[]): string {
|
||||
const names = vargas.map((varga) => UNRELIABLE_VARGA_LABELS[varga]).join("、");
|
||||
const these = vargas.length > 1 ? "这几张盘" : "这张盘";
|
||||
const it = vargas.length > 1 ? "它们" : "它";
|
||||
const fallback = vargas.includes("D1") ? `涉及${it}的部分不按上升和宫位下结论` : `涉及${it}的部分只看本命盘和大运,不按${it}下结论`;
|
||||
return `你的出生时间范围里,${names}的上升会变,${these}判断不了;${fallback}。`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Carry the adopted chart tiers into the workflow context the model reads:
|
||||
* `answer_policy.unreliable_vargas` / `deterministic_claims_forbidden_for` and a
|
||||
* user-facing limitation sentence. A context without unreliable charts is returned
|
||||
* as-is, so legacy and unrectified profiles are byte-for-byte unchanged.
|
||||
*/
|
||||
export function applyUnreliableVargasToWorkflowContext<
|
||||
T extends {
|
||||
consumer_context: {
|
||||
answer_policy: Record<string, unknown>;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
[key: string]: unknown;
|
||||
},
|
||||
>(context: T, vargas: readonly ("D1" | "D9" | "D10")[] | undefined): T {
|
||||
if (!vargas?.length) return context;
|
||||
const notice = unreliableVargasNotice(vargas);
|
||||
const policy = context.consumer_context.answer_policy;
|
||||
const forbidden = Array.isArray(policy.deterministic_claims_forbidden_for) ? policy.deterministic_claims_forbidden_for : [];
|
||||
const existing = typeof context.consumer_context.user_facing_limitation === "string"
|
||||
? context.consumer_context.user_facing_limitation.trim()
|
||||
: "";
|
||||
return {
|
||||
...context,
|
||||
consumer_context: {
|
||||
...context.consumer_context,
|
||||
user_facing_limitation: existing ? `${existing} ${notice}` : notice,
|
||||
answer_policy: {
|
||||
...policy,
|
||||
unreliable_vargas: [...vargas],
|
||||
deterministic_claims_forbidden_for: [...new Set([...forbidden.map(String), ...vargas])],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ type ServerChartToolInput = Readonly<{
|
||||
time_source: string;
|
||||
birth_time_accuracy?: "provisional";
|
||||
candidate_range?: Readonly<{ start_time: string; end_time: string }>;
|
||||
unreliable_vargas?: readonly ("D1" | "D9" | "D10")[];
|
||||
}>;
|
||||
|
||||
export type ServerChartConsultation = Readonly<{
|
||||
@@ -594,6 +595,7 @@ export async function prepareConsultationRoute<Reservation, GuardResult>(
|
||||
end_time: range.endTime,
|
||||
...(range.candidate_intervals ? { candidate_intervals: range.candidate_intervals } : {}),
|
||||
}),
|
||||
...(range.unreliableVargas?.length ? { unreliable_vargas: [...range.unreliableVargas] } : {}),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { loadAdoptedChartTiers } from "./rectification-adopted-chart-tiers.ts";
|
||||
import type { TargetChart } from "./rectification-agentic/core/segment-summary.ts";
|
||||
|
||||
export const READ_REPORT_CANDIDATE_RANGE_RPC = "read_report_candidate_range";
|
||||
export const ADOPTED_CREDIBLE_RANGE_SOURCE = "inference_credible_range";
|
||||
|
||||
@@ -5,6 +8,8 @@ export type ReportCandidateClockRange = Readonly<{
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
candidate_intervals?: readonly Readonly<{ start_at: string; end_at: string }>[];
|
||||
/** Charts a segment-v1 adoption could not determine (BUG-1138/1139); absent otherwise. */
|
||||
unreliableVargas?: readonly TargetChart[];
|
||||
}>;
|
||||
|
||||
export type AdoptedCredibleRangePayload = Readonly<{
|
||||
@@ -106,7 +111,10 @@ export async function loadReportCandidateRange(
|
||||
});
|
||||
return null;
|
||||
}
|
||||
return parseReportCandidateRange(result.data);
|
||||
const range = parseReportCandidateRange(result.data);
|
||||
if (!range) return null;
|
||||
const unreliable = await loadUnreliableVargas(client, input.userId);
|
||||
return unreliable.length ? { ...range, unreliableVargas: unreliable } : range;
|
||||
} catch (error) {
|
||||
warn({
|
||||
event: "report_candidate_range_unavailable",
|
||||
@@ -116,6 +124,31 @@ export async function loadReportCandidateRange(
|
||||
}
|
||||
}
|
||||
|
||||
type ProfileProvenanceQuery = Readonly<{
|
||||
select: (columns: string) => Readonly<{
|
||||
eq: (column: string, value: string) => Readonly<{ maybeSingle: () => PromiseLike<{ data: unknown; error: unknown }> }>;
|
||||
}>;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Report routes and the consultation route share this loader (the consultation
|
||||
* route wires it by name), so the adopted chart tiers ride on the range it
|
||||
* already returns. Only the service client has `from`; a failure here never
|
||||
* drops the range itself.
|
||||
*/
|
||||
async function loadUnreliableVargas(client: ReportCandidateRangeRpcClient, userId: string): Promise<readonly TargetChart[]> {
|
||||
const from = (client as { from?: (table: string) => ProfileProvenanceQuery }).from;
|
||||
if (typeof from !== "function") return [];
|
||||
try {
|
||||
const { data, error } = await from.call(client, "profiles").select("active_birth_provenance").eq("id", userId).maybeSingle();
|
||||
if (error || !data || typeof data !== "object") return [];
|
||||
const tiers = await loadAdoptedChartTiers(client, { userId, provenance: (data as Record<string, unknown>).active_birth_provenance });
|
||||
return tiers?.unreliable ?? [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function matchClock(value: unknown): string | null {
|
||||
if (typeof value !== "string") return null;
|
||||
return value.trim().match(candidateClockPattern)?.[1] ?? null;
|
||||
|
||||
@@ -2,6 +2,7 @@ import { z } from "zod";
|
||||
import { consultationEvidenceCategoryValues, createConsultationPlan, type ConsultationPlan } from "../lib/consultation-plan.ts";
|
||||
import { consultationThemeValues, projectConsultationWorkflowRequest } from "../lib/consultation-workflow-request.ts";
|
||||
import { AYANAMSA_VALUES, DEFAULT_AYANAMSA } from "../lib/ayanamsa.ts";
|
||||
import { applyUnreliableVargasToWorkflowContext } from "../lib/consultation-birth-time-mode.ts";
|
||||
|
||||
export const consultationInputSchema = z.object({
|
||||
year: z.number().int().min(1900).max(2100),
|
||||
@@ -26,6 +27,9 @@ export const consultationInputSchema = z.object({
|
||||
declared_window_end: z.string().optional(),
|
||||
uncertainty_before_minutes: z.number().int().min(0).max(720).optional(),
|
||||
uncertainty_after_minutes: z.number().int().min(0).max(720).optional(),
|
||||
// Charts a segment-v1 adoption could not determine (BUG-1138). Server-owned,
|
||||
// never sent to the engine; it only shapes the context the model reads.
|
||||
unreliable_vargas: z.array(z.enum(["D1", "D9", "D10"])).min(1).optional(),
|
||||
});
|
||||
export type ConsultationInput = z.infer<typeof consultationInputSchema>;
|
||||
type JsonRecord = Record<string, unknown>;
|
||||
@@ -133,7 +137,7 @@ export async function runConsultationWorkflow(
|
||||
entrypoint?: string;
|
||||
},
|
||||
) {
|
||||
const { entryMode, question, theme, ...workflowInput } = input;
|
||||
const { entryMode, question, theme, unreliable_vargas: unreliableVargas, ...workflowInput } = input;
|
||||
const plan = options?.plan ?? createConsultationPlan({ userIntent: question, theme });
|
||||
const workflowRequest = projectConsultationWorkflowRequest(question, theme, plan);
|
||||
const timeout = AbortSignal.timeout(90_000);
|
||||
@@ -177,7 +181,7 @@ export async function runConsultationWorkflow(
|
||||
"Jyotish API returned an incomplete consultation contract",
|
||||
);
|
||||
}
|
||||
return attachMinuteSensitiveThemes(parsed.data);
|
||||
return applyUnreliableVargasToWorkflowContext(attachMinuteSensitiveThemes(parsed.data), unreliableVargas);
|
||||
}
|
||||
|
||||
export function minuteSensitiveThemesFromBirthTimeSensitivity(value: unknown): string[] {
|
||||
@@ -723,6 +727,9 @@ function projectEvidenceContract(
|
||||
...(typeof policy.should_lead_with_limitations === "boolean"
|
||||
? { should_lead_with_limitations: policy.should_lead_with_limitations }
|
||||
: {}),
|
||||
...(Array.isArray(policy.unreliable_vargas) && policy.unreliable_vargas.length
|
||||
? { unreliable_vargas: stringList(policy.unreliable_vargas, 3), deterministic_claims_forbidden_for: stringList(policy.unreliable_vargas, 3) }
|
||||
: {}),
|
||||
},
|
||||
...(typeof contract.user_facing_limitation === "string"
|
||||
? { user_facing_limitation: boundedPrimitive(contract.user_facing_limitation) }
|
||||
|
||||
Reference in New Issue
Block a user