АРЧ АП11 - Подключить shadow-контракты переходов и truth gate в debug payload

This commit is contained in:
2026-04-15 23:03:11 +03:00
parent 93ad18daa3
commit 1d95ed9b60
10 changed files with 645 additions and 9 deletions
@@ -7,6 +7,7 @@ import {
type AddressLlmPreDecomposeMetaLogInput,
type FinalizeAssistantAddressTurnInput
} from "./assistantAddressTurnFinalizeRuntimeAdapter";
import { attachAssistantRuntimeContractShadow } from "./assistantRuntimeContractResolver";
export interface RunAssistantAddressLaneResponseRuntimeInput<ResponseType = AssistantMessageResponsePayload> {
sessionId: string;
@@ -246,6 +247,10 @@ export function runAssistantAddressLaneResponseRuntime<ResponseType = AssistantM
period_to: input.toNonEmptyString(rootFilters?.period_to)
};
}
const debugWithRuntimeContracts = attachAssistantRuntimeContractShadow(debug, {
userMessage: input.userMessage,
addressRuntimeMeta: input.llmPreDecomposeMeta
});
const finalization = finalizeAddressTurnSafe({
sessionId: input.sessionId,
userMessage: input.userMessage,
@@ -253,7 +258,7 @@ export function runAssistantAddressLaneResponseRuntime<ResponseType = AssistantM
assistantReply: safeAddressReply,
replyType: normalizeAddressReplyType(input.addressLane.reply_type),
addressLaneDebug: normalizeAddressLaneDebug(input.addressLane.debug),
debug,
debug: debugWithRuntimeContracts,
carryoverMeta: normalizeCarryoverMeta(input.carryoverMeta),
llmPreDecomposeMeta: normalizeLlmPreDecomposeMeta(input.llmPreDecomposeMeta),
appendItem: input.appendItem,
@@ -266,6 +271,6 @@ export function runAssistantAddressLaneResponseRuntime<ResponseType = AssistantM
return {
response: finalization.response as ResponseType,
debug
debug: debugWithRuntimeContracts
};
}
@@ -23,6 +23,7 @@ import type {
GroundedAnswerEligibilityAudit,
TemporalGuardAudit
} from "./assistantRuntimeGuards";
import { attachAssistantRuntimeContractShadow } from "./assistantRuntimeContractResolver";
import { buildStage4AnswerContractAuditV1 } from "./assistantStage4AnswerContractAudit";
type RetrievalStatusItem = AssistantDebugPayload["retrieval_status"][number];
@@ -97,7 +98,7 @@ function toAnalysisContext(input: DeepAnalysisDebugPayloadInput["runtimeAnalysis
export function buildDeepAnalysisDebugPayload(input: DeepAnalysisDebugPayloadInput): AssistantDebugPayload {
const analysisContext = toAnalysisContext(input.runtimeAnalysisContext);
const answerContractStage4Audit = buildStage4AnswerContractAuditV1(input.assistantReply);
return {
const debugPayload = {
trace_id: input.traceId,
prompt_version: input.promptVersion,
schema_version: input.schemaVersion,
@@ -173,4 +174,8 @@ export function buildDeepAnalysisDebugPayload(input: DeepAnalysisDebugPayloadInp
investigation_state_snapshot: input.investigationStateSnapshot,
normalized: input.normalizedPayload
} as unknown as AssistantDebugPayload;
return attachAssistantRuntimeContractShadow(debugPayload as unknown as Record<string, unknown>, {
addressRuntimeMeta: input.addressRuntimeMetaForDeep as unknown as Record<string, unknown> | null | undefined,
groundingStatus: input.groundingCheck.status
}) as unknown as AssistantDebugPayload;
}
@@ -0,0 +1,294 @@
import {
ASSISTANT_RUNTIME_CONTRACTS_SCHEMA_VERSION,
type AssistantCarryoverDepth,
type AssistantRuntimeContractShadowDecision,
type AssistantTransitionClassId
} from "../types/assistantRuntimeContracts";
import type { AddressIntent } from "../types/addressQuery";
import {
getAssistantCapabilityContract,
getAssistantCapabilityContractByIntent,
getAssistantTransitionContract
} from "./assistantRuntimeContractRegistry";
export interface ResolveAssistantRuntimeContractShadowInput {
userMessage?: unknown;
addressDebug?: Record<string, unknown> | null;
addressRuntimeMeta?: Record<string, unknown> | null;
groundingStatus?: unknown;
}
export interface AssistantRuntimeContractShadowFields {
assistant_runtime_contract_v1: AssistantRuntimeContractShadowDecision;
transition_contract_id: AssistantTransitionClassId | null;
capability_contract_id: string | null;
truth_gate_contract_status: AssistantRuntimeContractShadowDecision["truth_gate_contract_status"];
}
function toRecordObject(value: unknown): Record<string, unknown> | null {
if (!value || typeof value !== "object" || Array.isArray(value)) {
return null;
}
return value as Record<string, unknown>;
}
function toNonEmptyString(value: unknown): string | null {
if (value === null || value === undefined) {
return null;
}
const text = String(value).trim();
return text.length > 0 ? text : null;
}
function isAddressIntent(value: string | null): value is AddressIntent {
return Boolean(value) && value !== "unknown";
}
function asNumber(value: unknown): number | null {
if (typeof value === "number" && Number.isFinite(value)) {
return value;
}
if (typeof value === "string" && value.trim().length > 0) {
const parsed = Number(value);
return Number.isFinite(parsed) ? parsed : null;
}
return null;
}
function runtimeMetaFrom(input: ResolveAssistantRuntimeContractShadowInput): Record<string, unknown> | null {
const direct = toRecordObject(input.addressRuntimeMeta);
if (direct) {
return direct;
}
const debug = toRecordObject(input.addressDebug);
if (!debug) {
return null;
}
const dialogContinuationContract = toRecordObject(debug.dialog_continuation_contract_v2);
const orchestrationContract = toRecordObject(debug.orchestration_contract_v1);
if (!dialogContinuationContract && !orchestrationContract) {
return null;
}
return {
dialogContinuationContract,
orchestrationContract,
toolGateDecision: debug.tool_gate_decision,
toolGateReason: debug.tool_gate_reason
};
}
function resolveCapabilityContractId(debug: Record<string, unknown>, meta: Record<string, unknown> | null): {
capabilityId: string | null;
reasons: string[];
} {
const explicitCapabilityId = toNonEmptyString(debug.capability_id);
if (explicitCapabilityId && getAssistantCapabilityContract(explicitCapabilityId)) {
return {
capabilityId: explicitCapabilityId,
reasons: ["debug_capability_id_matched_contract"]
};
}
const orchestrationContract = toRecordObject(meta?.orchestrationContract) ?? toRecordObject(debug.orchestration_contract_v1);
const intent =
toNonEmptyString(debug.detected_intent) ??
toNonEmptyString(orchestrationContract?.address_intent);
if (isAddressIntent(intent)) {
const contract = getAssistantCapabilityContractByIntent(intent);
if (contract) {
return {
capabilityId: contract.capability_id,
reasons: ["intent_matched_capability_contract"]
};
}
}
return {
capabilityId: null,
reasons: explicitCapabilityId ? ["debug_capability_id_has_no_contract"] : ["capability_contract_not_resolved"]
};
}
function isSelectedObjectCapability(capabilityId: string | null): boolean {
if (!capabilityId) {
return false;
}
const contract = getAssistantCapabilityContract(capabilityId);
return Boolean(contract?.requires_focus_object);
}
function resolveTransitionId(input: {
debug: Record<string, unknown>;
meta: Record<string, unknown> | null;
capabilityId: string | null;
groundingStatus: string | null;
}): { transitionId: AssistantTransitionClassId | null; reasons: string[] } {
const dialogContinuationContract =
toRecordObject(input.meta?.dialogContinuationContract) ??
toRecordObject(input.debug.dialog_continuation_contract_v2);
const orchestrationContract =
toRecordObject(input.meta?.orchestrationContract) ??
toRecordObject(input.debug.orchestration_contract_v1);
const finalDecision = toRecordObject(orchestrationContract?.final_decision);
const hardMetaMode = toNonEmptyString(orchestrationContract?.hard_meta_mode);
const toolGateReason =
toNonEmptyString(input.meta?.toolGateReason) ??
toNonEmptyString(input.debug.tool_gate_reason) ??
toNonEmptyString(finalDecision?.tool_gate_reason);
const continuationDecision = toNonEmptyString(dialogContinuationContract?.decision);
const targetIntent =
toNonEmptyString(dialogContinuationContract?.target_intent) ??
toNonEmptyString(input.debug.detected_intent);
const currentFrameKind = toNonEmptyString(toRecordObject(input.debug.address_root_frame_context)?.current_frame_kind);
const missingFilters = Array.isArray(input.debug.missing_required_filters) ? input.debug.missing_required_filters.length : 0;
if (
input.groundingStatus === "route_mismatch_blocked" ||
input.debug.route_expectation_status === "mismatch" ||
input.debug.limited_reason_category === "execution_error"
) {
return { transitionId: "T10", reasons: ["blocked_or_route_mismatch_debug_status"] };
}
if (missingFilters > 0 || input.debug.limited_reason_category === "missing_anchor") {
return { transitionId: "T7", reasons: ["missing_anchor_or_filter_requires_clarification"] };
}
if (toolGateReason === "memory_recap_followup_detected") {
return { transitionId: "T9", reasons: ["memory_recap_tool_gate_reason"] };
}
if (hardMetaMode === "capability" || toolGateReason === "assistant_capability_query_detected") {
return { transitionId: "T8", reasons: ["capability_meta_followup_tool_gate_reason"] };
}
if (isSelectedObjectCapability(input.capabilityId)) {
if (continuationDecision === "continue_previous") {
return { transitionId: "T4", reasons: ["selected_object_capability_continue_previous"] };
}
if (continuationDecision === "new_topic") {
return { transitionId: "T3", reasons: ["selected_object_capability_explicit_entry"] };
}
return { transitionId: "T5", reasons: ["selected_object_capability_without_explicit_continuation_decision"] };
}
if (currentFrameKind === "inventory_drilldown" && targetIntent && targetIntent !== "inventory_on_hand_as_of_date") {
return { transitionId: "T6", reasons: ["root_context_reused_after_drilldown_context"] };
}
if (continuationDecision === "continue_previous") {
return { transitionId: "T2", reasons: ["root_followup_continue_previous"] };
}
if (input.capabilityId) {
return { transitionId: "T1", reasons: ["capability_contract_root_or_new_entry"] };
}
return { transitionId: null, reasons: ["transition_contract_not_resolved"] };
}
function resolveTruthGateStatus(input: {
debug: Record<string, unknown>;
groundingStatus: string | null;
}): AssistantRuntimeContractShadowDecision["truth_gate_contract_status"] {
if (input.groundingStatus === "route_mismatch_blocked" || input.debug.route_expectation_status === "mismatch") {
return "blocked_route_expectation_failure";
}
if (input.debug.limited_reason_category === "execution_error") {
return "blocked_execution_error";
}
if (input.debug.limited_reason_category === "missing_anchor") {
return "blocked_missing_anchor";
}
if (
input.debug.temporal_guard_outcome === "ambiguous_limited" ||
input.debug.temporal_alignment_status === "conflicting"
) {
return "limited_temporal_or_contextual";
}
if (input.debug.limited_reason_category || input.groundingStatus === "partial") {
return "partial_supported";
}
const rowsMatched = asNumber(input.debug.rows_matched);
const routeExpectationStatus = toNonEmptyString(input.debug.route_expectation_status);
if ((input.groundingStatus === "grounded" || (rowsMatched !== null && rowsMatched > 0)) && (!routeExpectationStatus || routeExpectationStatus === "matched")) {
return "full_confirmed";
}
return "unknown";
}
function carryoverEligibilityFor(
transitionId: AssistantTransitionClassId | null,
truthGateStatus: AssistantRuntimeContractShadowDecision["truth_gate_contract_status"]
): AssistantCarryoverDepth {
if (truthGateStatus.startsWith("blocked")) {
return "none";
}
if (transitionId === "T3" || transitionId === "T4" || transitionId === "T5") {
return "object_only";
}
if (transitionId === "T8" || transitionId === "T9") {
return "meta_only";
}
if (transitionId === "T1" || transitionId === "T2" || transitionId === "T6") {
return "root_only";
}
if (transitionId === "T7") {
return "full";
}
return "none";
}
export function resolveAssistantRuntimeContractShadow(
input: ResolveAssistantRuntimeContractShadowInput
): AssistantRuntimeContractShadowDecision {
const debug = toRecordObject(input.addressDebug) ?? {};
const meta = runtimeMetaFrom(input);
const groundingStatus =
toNonEmptyString(input.groundingStatus) ??
toNonEmptyString(toRecordObject(debug.answer_grounding_check)?.status);
const capability = resolveCapabilityContractId(debug, meta);
const transition = resolveTransitionId({
debug,
meta,
capabilityId: capability.capabilityId,
groundingStatus
});
const transitionContract = transition.transitionId ? getAssistantTransitionContract(transition.transitionId) : null;
const truthGateStatus = resolveTruthGateStatus({ debug, groundingStatus });
return {
schema_version: ASSISTANT_RUNTIME_CONTRACTS_SCHEMA_VERSION,
transition_contract_id: transition.transitionId,
transition_contract_title: transitionContract?.title ?? null,
transition_contract_reason: transition.reasons,
capability_contract_id: capability.capabilityId,
capability_contract_reason: capability.reasons,
truth_gate_contract_status: truthGateStatus,
carryover_eligibility: carryoverEligibilityFor(transition.transitionId, truthGateStatus)
};
}
export function buildAssistantRuntimeContractShadowFields(
input: ResolveAssistantRuntimeContractShadowInput
): AssistantRuntimeContractShadowFields {
const decision = resolveAssistantRuntimeContractShadow(input);
return {
assistant_runtime_contract_v1: decision,
transition_contract_id: decision.transition_contract_id,
capability_contract_id: decision.capability_contract_id,
truth_gate_contract_status: decision.truth_gate_contract_status
};
}
export function attachAssistantRuntimeContractShadow<T extends Record<string, unknown>>(
debugPayload: T,
input: Omit<ResolveAssistantRuntimeContractShadowInput, "addressDebug">
): T & AssistantRuntimeContractShadowFields {
return {
...debugPayload,
...buildAssistantRuntimeContractShadowFields({
...input,
addressDebug: debugPayload
})
};
}
@@ -17,6 +17,10 @@ import type {
} from "./stage2ProblemUnits";
import type { AccountingGraphBuildResult } from "./stage4Graph";
import type { AddressNavigationState } from "./addressNavigation";
import type {
AssistantRuntimeContractShadowDecision,
AssistantTransitionClassId
} from "./assistantRuntimeContracts";
export type AssistantFallbackType = "none" | "out_of_scope" | "clarification" | "partial" | "unknown";
export type AssistantReplyType =
@@ -446,6 +450,10 @@ export interface AssistantDebugPayload {
route_expectation_expected_selected_recipes?: string[];
route_expectation_expected_requested_result_modes?: Array<"heuristic_candidates" | "confirmed_balance">;
route_expectation_expected_result_modes?: Array<"heuristic_candidates" | "confirmed_balance">;
assistant_runtime_contract_v1?: AssistantRuntimeContractShadowDecision;
transition_contract_id?: AssistantTransitionClassId | null;
capability_contract_id?: string | null;
truth_gate_contract_status?: AssistantRuntimeContractShadowDecision["truth_gate_contract_status"];
execution_lane?: "address_query" | "deep_analysis";
llm_decomposition_applied?: boolean;
llm_decomposition_attempted?: boolean;
@@ -152,3 +152,21 @@ export interface AssistantCapabilityContract {
required_transition_tests: string[];
required_scenario_families: string[];
}
export interface AssistantRuntimeContractShadowDecision {
schema_version: typeof ASSISTANT_RUNTIME_CONTRACTS_SCHEMA_VERSION;
transition_contract_id: AssistantTransitionClassId | null;
transition_contract_title: string | null;
transition_contract_reason: string[];
capability_contract_id: string | null;
capability_contract_reason: string[];
truth_gate_contract_status:
| "full_confirmed"
| "partial_supported"
| "blocked_missing_anchor"
| "blocked_route_expectation_failure"
| "blocked_execution_error"
| "limited_temporal_or_contextual"
| "unknown";
carryover_eligibility: AssistantCarryoverDepth;
}