ai-chatbot/lib/ai/runtime/middlewares/human-confirmation-gate.ts
Your Name 37db7e2d3a
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
fix agent video runtime dependencies
2026-03-13 10:40:45 +08:00

196 lines
5.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {
isAwaitingUserConfirmationWorkflow,
type AgentWorkflowState,
type AgentWorkflowStatus,
type AgentWorkflowType,
} from "@/lib/ai/runtime/agent-workflow-state";
import type { AgentThreadState } from "@/lib/ai/runtime/agent-thread-state";
export type HumanConfirmationIntent =
| "none"
| "confirm"
| "revise"
| "cancel"
| "unclear";
export type HumanConfirmationGateAction =
| "pass"
| "awaiting_confirmation"
| "confirmed"
| "revision_requested"
| "cancel_requested";
export interface HumanConfirmationGateDecision {
action: HumanConfirmationGateAction;
confirmationIntent: HumanConfirmationIntent;
workflowId?: string;
workflowType?: AgentWorkflowType;
nextWorkflowStatus?: AgentWorkflowStatus;
allowWorkflowExecution: boolean;
shouldBypassPlanner: boolean;
friendlyMessage?: string;
}
const CONFIRM_PATTERNS = [
/确认开始生成视频/,
/确认开始生成/,
/按这个分镜生成/,
/开始生成视频/,
/开始生成$/,
/可以开始了/,
/可以执行了/,
/就按这个/,
/没问题开始/,
/确认无误/,
];
const REVISE_PATTERNS = [
/修改第\d+镜头/,
/第\d+镜头/,
/调整第\d+镜头/,
/重生第\d+/,
/重做第\d+/,
/改一下/,
/修改/,
/调整/,
/不要这样/,
/不对/,
/不行/,
/换成/,
];
const CANCEL_PATTERNS = [
/取消/,
/先不要/,
/暂停/,
/停止/,
/先别生成/,
/不用生成了/,
];
function normalizeCompactText(text: string) {
return String(text || "").toLowerCase().replace(/\s+/g, "").trim();
}
export function detectHumanConfirmationIntent(text: string): HumanConfirmationIntent {
const normalized = normalizeCompactText(text);
if (!normalized) return "unclear";
if (CANCEL_PATTERNS.some((pattern) => pattern.test(normalized))) {
return "cancel";
}
if (REVISE_PATTERNS.some((pattern) => pattern.test(normalized))) {
return "revise";
}
if (CONFIRM_PATTERNS.some((pattern) => pattern.test(normalized))) {
return "confirm";
}
return "unclear";
}
function buildAwaitingConfirmationMessage(workflowState: AgentWorkflowState): string {
const title = workflowState.title || "当前工作流";
const summary = workflowState.summary
? `\n当前待确认内容:${workflowState.summary}`
: "";
const pendingItemsPreview = workflowState.pendingItems
.slice(0, 3)
.map((item) => {
const indexText = item.index ? `镜头${item.index}` : item.title || "待确认项";
const summaryText = item.summary ? `:${item.summary}` : "";
return `- ${indexText}${summaryText}`;
})
.join("\n");
const previewBlock = pendingItemsPreview ? `\n${pendingItemsPreview}` : "";
const confirmExample = workflowState.confirmPhrases[0] || "确认开始生成视频";
const reviseExample = workflowState.revisionHints[0] || "修改第3镜头";
return [
`当前有一个待确认流程:${title}。`,
summary ? summary.trim() : "",
previewBlock.trim(),
`如果确认无误,请回复“${confirmExample}”。`,
`如果需要修改,请直接回复类似“${reviseExample}”。`,
]
.filter(Boolean)
.join("\n");
}
function buildConfirmMessage(workflowState: AgentWorkflowState): string {
const title = workflowState.title || "当前流程";
return `已收到确认:${title} 可以进入下一步执行。我会按已确认内容继续处理。`;
}
function buildRevisionMessage(workflowState: AgentWorkflowState): string {
const title = workflowState.title || "当前流程";
return `收到修改意见。我会先更新 ${title} 的草案内容,并在重新确认前暂停执行。`;
}
function buildCancelMessage(workflowState: AgentWorkflowState): string {
const title = workflowState.title || "当前流程";
return `已收到取消指令。${title} 会继续停留在人工确认前状态,不会继续自动执行。`;
}
export function runHumanConfirmationGate(params: {
text: string;
threadState?: AgentThreadState | null;
}): HumanConfirmationGateDecision {
const workflowState = params.threadState?.workflowState;
if (!workflowState || !isAwaitingUserConfirmationWorkflow(workflowState)) {
return {
action: "pass",
confirmationIntent: "none",
allowWorkflowExecution: true,
shouldBypassPlanner: false,
};
}
const confirmationIntent = detectHumanConfirmationIntent(params.text);
const baseDecision = {
workflowId: workflowState.workflowId,
workflowType: workflowState.type,
shouldBypassPlanner: true,
};
switch (confirmationIntent) {
case "confirm":
return {
...baseDecision,
action: "confirmed",
confirmationIntent,
nextWorkflowStatus: "confirmed",
allowWorkflowExecution: true,
friendlyMessage: buildConfirmMessage(workflowState),
};
case "revise":
return {
...baseDecision,
action: "revision_requested",
confirmationIntent,
nextWorkflowStatus: "draft",
allowWorkflowExecution: false,
friendlyMessage: buildRevisionMessage(workflowState),
};
case "cancel":
return {
...baseDecision,
action: "cancel_requested",
confirmationIntent,
nextWorkflowStatus: "cancelled",
allowWorkflowExecution: false,
friendlyMessage: buildCancelMessage(workflowState),
};
case "unclear":
case "none":
default:
return {
...baseDecision,
action: "awaiting_confirmation",
confirmationIntent: "unclear",
nextWorkflowStatus: workflowState.status,
allowWorkflowExecution: false,
friendlyMessage: buildAwaitingConfirmationMessage(workflowState),
};
}
}