АРЧ АП11 - Включить response guard для capability binding ассистента
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
type AddressLlmPreDecomposeMetaLogInput,
|
||||
type FinalizeAssistantAddressTurnInput
|
||||
} from "./assistantAddressTurnFinalizeRuntimeAdapter";
|
||||
import { applyAssistantCapabilityBindingResponseGuard } from "./assistantCapabilityBindingResponseGuard";
|
||||
import { attachAssistantCapabilityRuntimeBinding } from "./assistantCapabilityRuntimeBindingAdapter";
|
||||
import { attachAssistantRuntimeContractShadow } from "./assistantRuntimeContractResolver";
|
||||
import { attachAssistantStateTransition } from "./assistantStateTransitionRuntimeAdapter";
|
||||
@@ -266,14 +267,23 @@ export function runAssistantAddressLaneResponseRuntime<ResponseType = AssistantM
|
||||
addressRuntimeMeta: input.llmPreDecomposeMeta,
|
||||
replyType: normalizeAddressReplyType(input.addressLane.reply_type)
|
||||
});
|
||||
const guardedResponse = applyAssistantCapabilityBindingResponseGuard({
|
||||
assistantReply: safeAddressReply,
|
||||
replyType: normalizeAddressReplyType(input.addressLane.reply_type),
|
||||
capabilityBinding: debugWithCapabilityBinding.assistant_capability_binding_v1
|
||||
});
|
||||
const debugWithResponseGuard = {
|
||||
...debugWithCapabilityBinding,
|
||||
capability_binding_response_guard: guardedResponse.audit
|
||||
};
|
||||
const finalization = finalizeAddressTurnSafe({
|
||||
sessionId: input.sessionId,
|
||||
userMessage: input.userMessage,
|
||||
effectiveAddressUserMessage: input.effectiveAddressUserMessage,
|
||||
assistantReply: safeAddressReply,
|
||||
replyType: normalizeAddressReplyType(input.addressLane.reply_type),
|
||||
assistantReply: guardedResponse.assistantReply,
|
||||
replyType: guardedResponse.replyType,
|
||||
addressLaneDebug: normalizeAddressLaneDebug(input.addressLane.debug),
|
||||
debug: debugWithCapabilityBinding,
|
||||
debug: debugWithResponseGuard,
|
||||
carryoverMeta: normalizeCarryoverMeta(input.carryoverMeta),
|
||||
llmPreDecomposeMeta: normalizeLlmPreDecomposeMeta(input.llmPreDecomposeMeta),
|
||||
appendItem: input.appendItem,
|
||||
@@ -286,6 +296,6 @@ export function runAssistantAddressLaneResponseRuntime<ResponseType = AssistantM
|
||||
|
||||
return {
|
||||
response: finalization.response as ResponseType,
|
||||
debug: debugWithCapabilityBinding
|
||||
debug: debugWithResponseGuard
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
import type { AssistantReplyType } from "../types/assistant";
|
||||
import type {
|
||||
AssistantCapabilityBindingAction,
|
||||
AssistantCapabilityRuntimeBindingContract
|
||||
} from "../types/assistantRuntimeContracts";
|
||||
|
||||
export interface ApplyAssistantCapabilityBindingResponseGuardInput {
|
||||
assistantReply: string;
|
||||
replyType: AssistantReplyType;
|
||||
capabilityBinding: AssistantCapabilityRuntimeBindingContract | null | undefined;
|
||||
}
|
||||
|
||||
export interface AssistantCapabilityBindingResponseGuardAudit {
|
||||
schema_version: "assistant_capability_binding_response_guard_v1";
|
||||
guard_owner: "assistantCapabilityBindingResponseGuard";
|
||||
applied: boolean;
|
||||
action: AssistantCapabilityBindingAction | "none";
|
||||
original_reply_type: AssistantReplyType;
|
||||
guarded_reply_type: AssistantReplyType;
|
||||
reason_codes: string[];
|
||||
}
|
||||
|
||||
export interface ApplyAssistantCapabilityBindingResponseGuardOutput {
|
||||
assistantReply: string;
|
||||
replyType: AssistantReplyType;
|
||||
audit: AssistantCapabilityBindingResponseGuardAudit;
|
||||
}
|
||||
|
||||
function formatMissingAnchors(anchors: string[]): string {
|
||||
if (anchors.length === 0) {
|
||||
return "нужный объект, период или организацию";
|
||||
}
|
||||
return anchors.join(", ");
|
||||
}
|
||||
|
||||
function buildClarificationReply(binding: AssistantCapabilityRuntimeBindingContract): string {
|
||||
return [
|
||||
"Нужно уточнение, чтобы не подставить неподтвержденный объект в расчет.",
|
||||
`Не хватает: ${formatMissingAnchors(binding.missing_anchors)}.`,
|
||||
"Уточните это, и я продолжу тот же сценарий."
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function buildBlockedReply(binding: AssistantCapabilityRuntimeBindingContract): string {
|
||||
const reasons = binding.violations.length > 0 ? binding.violations.join(", ") : "runtime_binding_blocked";
|
||||
return [
|
||||
"Не могу надежно подтвердить ответ в текущем контексте.",
|
||||
`Проверка сценария остановила ответ: ${reasons}.`,
|
||||
"Лучше уточнить объект или перезапустить вопрос от корневого запроса, чем выдавать это как подтвержденный факт."
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
export function applyAssistantCapabilityBindingResponseGuard(
|
||||
input: ApplyAssistantCapabilityBindingResponseGuardInput
|
||||
): ApplyAssistantCapabilityBindingResponseGuardOutput {
|
||||
const binding = input.capabilityBinding;
|
||||
const baseAudit: AssistantCapabilityBindingResponseGuardAudit = {
|
||||
schema_version: "assistant_capability_binding_response_guard_v1",
|
||||
guard_owner: "assistantCapabilityBindingResponseGuard",
|
||||
applied: false,
|
||||
action: binding?.binding_action ?? "none",
|
||||
original_reply_type: input.replyType,
|
||||
guarded_reply_type: input.replyType,
|
||||
reason_codes: binding?.reason_codes ?? []
|
||||
};
|
||||
|
||||
if (!binding || binding.binding_action === "allow" || binding.binding_action === "observe_only") {
|
||||
return {
|
||||
assistantReply: input.assistantReply,
|
||||
replyType: input.replyType,
|
||||
audit: baseAudit
|
||||
};
|
||||
}
|
||||
|
||||
if (binding.binding_action === "clarify") {
|
||||
return {
|
||||
assistantReply: buildClarificationReply(binding),
|
||||
replyType: "partial_coverage",
|
||||
audit: {
|
||||
...baseAudit,
|
||||
applied: true,
|
||||
guarded_reply_type: "partial_coverage",
|
||||
reason_codes: [...baseAudit.reason_codes, "capability_binding_guard_clarification_reply"]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (binding.binding_action === "block") {
|
||||
return {
|
||||
assistantReply: buildBlockedReply(binding),
|
||||
replyType: "partial_coverage",
|
||||
audit: {
|
||||
...baseAudit,
|
||||
applied: true,
|
||||
guarded_reply_type: "partial_coverage",
|
||||
reason_codes: [...baseAudit.reason_codes, "capability_binding_guard_blocked_reply"]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
assistantReply: input.assistantReply,
|
||||
replyType: input.replyType === "factual" ? "partial_coverage" : input.replyType,
|
||||
audit: {
|
||||
...baseAudit,
|
||||
applied: input.replyType === "factual",
|
||||
guarded_reply_type: input.replyType === "factual" ? "partial_coverage" : input.replyType,
|
||||
reason_codes: [...baseAudit.reason_codes, "capability_binding_guard_limited_reply_type"]
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -479,6 +479,7 @@ export interface AssistantDebugPayload {
|
||||
capability_binding_status?: AssistantCapabilityBindingStatus;
|
||||
capability_binding_action?: AssistantCapabilityBindingAction;
|
||||
capability_binding_violations?: AssistantCapabilityBindingViolation[];
|
||||
capability_binding_response_guard?: Record<string, unknown>;
|
||||
execution_lane?: "address_query" | "deep_analysis";
|
||||
llm_decomposition_applied?: boolean;
|
||||
llm_decomposition_attempted?: boolean;
|
||||
|
||||
Reference in New Issue
Block a user