feat: add VedAstro rectification post-validation

This commit is contained in:
Jesse_Chen
2026-07-30 11:48:02 +08:00
parent 2384faf367
commit 31e5709f33
13 changed files with 856 additions and 24 deletions
@@ -17,18 +17,51 @@ import { recordRectificationAgentTelemetry } from "./telemetry.ts";
import {
candidateFeatureSnapshotSchema,
diagnosticsSummarySchema,
vedAstroPostValidationSchema,
validateRectificationDecision,
type AgentRun,
type CandidateFeatureSnapshot,
type DiagnosticsSummary,
type StoredPublicMessage,
type ValidatedDecision,
type VedAstroPostValidation,
} from "./contracts.ts";
function hash(value: unknown): string {
return createHash("sha256").update(JSON.stringify(value)).digest("hex");
}
function vedAstroCandidateTimes(snapshot: CandidateSnapshot): readonly [string, string] | null {
const primary = snapshot.clusters[0]?.representativeTime;
if (!primary) return null;
const runnerUp = snapshot.clusters[1]?.representativeTime
?? [...snapshot.candidates].sort((left, right) => right.score - left.score).find((candidate) => candidate.time !== primary)?.time;
return runnerUp && runnerUp !== primary ? [primary, runnerUp] : null;
}
function blockedVedAstroValidation(now: Date, blocker: string, candidateTimes: readonly [string, string] | null): VedAstroPostValidation {
const safe = {
contractVersion: "vedastro-post-validation-v1" as const,
provider: "vedastro_official" as const,
status: "blocked" as const,
providerStatus: "unavailable",
blockers: [blocker],
primaryCandidateTime: candidateTimes?.[0] ?? null,
runnerUpCandidateTime: candidateTimes?.[1] ?? null,
eligibleEventCount: 0,
selectedEventCount: 0,
unsupportedEventCount: 0,
candidateMetrics: [],
minuteSensitiveValidation: { comparisonReady: false, discriminated: false, discriminatedLayers: [] },
canConfirmExactMinute: false as const,
};
return vedAstroPostValidationSchema.parse({
...safe,
validationHash: hash(safe),
validatedAt: now.toISOString(),
});
}
const analysisPhaseLabels = {
extracting_evidence: "整理用户经历",
scoring_candidates: "扫描候选分钟",
@@ -240,6 +273,50 @@ export async function processRectificationAgentTurn(input: Readonly<{
});
}
if (snapshot?.canAcceptRange && diagnostics && claimed.case.deploymentMode === "v5_agent") {
const candidateTimes = vedAstroCandidateTimes(snapshot);
const validationStarted = Date.now();
let externalValidation: VedAstroPostValidation;
let outcome: "succeeded" | "failed" | "rejected";
if (!candidateTimes) {
externalValidation = blockedVedAstroValidation(now, "vedastro_runner_up_candidate_missing", null);
outcome = "rejected";
} else if (!input.engine.validateWithVedAstro) {
externalValidation = blockedVedAstroValidation(now, "vedastro_validator_unavailable", candidateTimes);
outcome = "failed";
} else {
try {
externalValidation = await input.engine.validateWithVedAstro({
calculationSpec: claimed.case.calculationSpec,
events: scoreable,
candidateTimes,
});
outcome = externalValidation.status === "pass" ? "succeeded" : "rejected";
} catch {
externalValidation = blockedVedAstroValidation(now, "vedastro_validation_failed", candidateTimes);
outcome = "failed";
}
}
diagnostics = diagnosticsSummarySchema.parse({ ...diagnostics, externalValidation });
analysisToolCalls.push({
category: "diagnostic",
label: "VedAstro 事后校验",
outcome,
durationMs: Date.now() - validationStarted,
});
if (externalValidation.status !== "pass") {
snapshot = {
...snapshot,
canAcceptRange: false,
gateReasons: [...new Set([
...snapshot.gateReasons,
"vedastro_validation_not_passed",
...externalValidation.blockers,
])].slice(0, 20),
};
}
}
const safeDiagnostics = diagnostics ?? diagnosticsSummarySchema.parse({
id: randomUUID(),
caseId: claimed.case.id,