183 lines
6.5 KiB
TypeScript
183 lines
6.5 KiB
TypeScript
import "server-only";
|
||
|
||
import type { AgentThreadState } from "@/lib/ai/runtime/agent-thread-state";
|
||
import type { AgentExecutionPlan, AgentPlannedToolCall } from "@/lib/ai/auto-agent-graph";
|
||
import type { AgentSpecialistRoute } from "@/lib/ai/specialists";
|
||
import type { AgentProjectToolDefinition } from "@/lib/ai/tools-registry";
|
||
import {
|
||
getImageWorkflowPlanningHints,
|
||
} from "@/lib/ai/image-workflow-memory";
|
||
import {
|
||
getPromptMemoryPlanningHints,
|
||
isPromptCarryForwardRequest,
|
||
} from "@/lib/ai/prompt-memory";
|
||
|
||
function normalizeCompactText(text: string) {
|
||
return String(text || "").toLowerCase().replace(/\s+/g, "").trim();
|
||
}
|
||
|
||
export function mentionsImageGenerationFollowup(text: string): boolean {
|
||
const normalized = normalizeCompactText(text);
|
||
if (!normalized) return false;
|
||
|
||
return /继续生成|继续出图|继续做图|再来(一|两|三|四|五|六|七|八|九|\d+)?张|再出(一|两|三|四|五|六|七|八|九|\d+)?张|再做(一|两|三|四|五|六|七|八|九|\d+)?组|再给我(一|两|三|四|五|六|七|八|九|\d+)?张|补(一|两|三|四|五|六|七|八|九|\d+)?张|多来(一|两|三|四|五|六|七|八|九|\d+)?张|换一版|再换一版|同风格再来|同样风格再来|保持.*(不变|一致).*再来|同提示词再来|沿用上一个效果再来/.test(
|
||
normalized,
|
||
);
|
||
}
|
||
|
||
export function buildImageGenerationSpecialistRoute(params: {
|
||
text: string;
|
||
threadState?: AgentThreadState | null;
|
||
hasImage: boolean;
|
||
}): AgentSpecialistRoute | null {
|
||
const normalized = normalizeCompactText(params.text);
|
||
const promptHints = getPromptMemoryPlanningHints(params.threadState);
|
||
const workflowHints = getImageWorkflowPlanningHints(params.threadState);
|
||
const hasPromptMemory = promptHints.length > 0;
|
||
const hasImageWorkflowMemory = workflowHints.length > 0;
|
||
const carryForward = isPromptCarryForwardRequest({
|
||
text: params.text,
|
||
hasImage: params.hasImage,
|
||
});
|
||
const mentionsGenerationFollowup = mentionsImageGenerationFollowup(params.text);
|
||
const mentionsPromptExecution =
|
||
/提示词|prompt|关键词|咒语/.test(normalized) &&
|
||
/生成|生图|出图|继续做|继续生成|执行/.test(normalized);
|
||
|
||
if (
|
||
!hasPromptMemory &&
|
||
!hasImageWorkflowMemory &&
|
||
!carryForward &&
|
||
!mentionsPromptExecution &&
|
||
!mentionsGenerationFollowup
|
||
) {
|
||
return null;
|
||
}
|
||
|
||
const planningHints = [
|
||
carryForward
|
||
? "用户当前要求沿用上一轮优化提示词继续执行,应优先理解为连续生图执行。"
|
||
: "",
|
||
mentionsGenerationFollowup
|
||
? "如果用户说“再来几张 / 换一版 / 同风格再来”,优先理解为沿用上一轮生图上下文继续执行,而不是开启全新任务。"
|
||
: "",
|
||
hasPromptMemory
|
||
? "当前会话里存在上一轮可复用提示词,如果用户在说“上一个提示词/刚才那个 prompt”,优先复用这段提示词。"
|
||
: "",
|
||
hasImageWorkflowMemory
|
||
? "当前会话里存在图片工作流记忆,优先参考上一轮参考图、结果图、数量和画幅偏好。"
|
||
: "",
|
||
params.hasImage
|
||
? "如果当前回合上传了新图片,并要求沿用上一轮提示词,应理解为“上一轮提示词 + 当前图片条件”的联合执行。"
|
||
: "",
|
||
...promptHints,
|
||
...workflowHints,
|
||
].filter(Boolean);
|
||
|
||
return {
|
||
domain: "image_generation_memory",
|
||
specialistId: "image-generation-subagent",
|
||
reason: carryForward
|
||
? "当前回合命中提示词接力生图"
|
||
: mentionsGenerationFollowup
|
||
? "当前回合命中连续生图跟进语义"
|
||
: "当前回合命中提示词相关的连续生图任务",
|
||
planningHints,
|
||
toolAllowlist: [
|
||
"respondDirect",
|
||
"resolveTargetImage",
|
||
"generateNewImage",
|
||
"generateImage",
|
||
"useSelectedCanvasImage",
|
||
"useLatestResultImage",
|
||
"useOriginalUploadImage",
|
||
"publishResultToCanvas",
|
||
"publishToCanvas",
|
||
],
|
||
};
|
||
}
|
||
|
||
function hasTool(
|
||
availableTools: AgentProjectToolDefinition[],
|
||
name: AgentPlannedToolCall["name"],
|
||
) {
|
||
return availableTools.some((tool) => tool.name === name);
|
||
}
|
||
|
||
function upsertTool(
|
||
toolCalls: AgentPlannedToolCall[],
|
||
name: AgentPlannedToolCall["name"],
|
||
reason: string,
|
||
options?: { prepend?: boolean },
|
||
) {
|
||
const existing = toolCalls.find((tool) => tool.name === name);
|
||
if (existing) return toolCalls;
|
||
const nextTool = { name, reason } satisfies AgentPlannedToolCall;
|
||
return options?.prepend ? [nextTool, ...toolCalls] : [...toolCalls, nextTool];
|
||
}
|
||
|
||
export function refinePlanWithImageGenerationSubagent(params: {
|
||
text: string;
|
||
plan: AgentExecutionPlan;
|
||
availableTools: AgentProjectToolDefinition[];
|
||
threadState?: AgentThreadState | null;
|
||
hasImage: boolean;
|
||
}): AgentExecutionPlan {
|
||
const carryForward = isPromptCarryForwardRequest({
|
||
text: params.text,
|
||
hasImage: params.hasImage,
|
||
});
|
||
const mentionsGenerationFollowup = mentionsImageGenerationFollowup(params.text);
|
||
const hasPromptMemory = getPromptMemoryPlanningHints(params.threadState).length > 0;
|
||
const hasImageWorkflowMemory = getImageWorkflowPlanningHints(params.threadState).length > 0;
|
||
if (
|
||
!carryForward &&
|
||
!mentionsGenerationFollowup &&
|
||
!hasPromptMemory &&
|
||
!hasImageWorkflowMemory
|
||
) {
|
||
return params.plan;
|
||
}
|
||
|
||
let toolCalls = [...params.plan.toolCalls];
|
||
const targetResolverTools = toolCalls.filter((tool) =>
|
||
["resolveTargetImage", "useSelectedCanvasImage", "useLatestResultImage", "useOriginalUploadImage"].includes(
|
||
tool.name,
|
||
),
|
||
);
|
||
if (hasTool(params.availableTools, "generateNewImage")) {
|
||
toolCalls = [
|
||
...targetResolverTools,
|
||
{ name: "generateNewImage", reason: "image-generation 子代理要求优先走连续生图执行链路" },
|
||
];
|
||
} else if (hasTool(params.availableTools, "generateImage")) {
|
||
toolCalls = [
|
||
...targetResolverTools,
|
||
{ name: "generateImage", reason: "image-generation 子代理要求优先走连续生图执行链路" },
|
||
];
|
||
}
|
||
if (hasTool(params.availableTools, "publishResultToCanvas")) {
|
||
toolCalls = upsertTool(
|
||
toolCalls,
|
||
"publishResultToCanvas",
|
||
"image-generation 子代理建议把连续生图结果同步到画布"
|
||
);
|
||
} else if (hasTool(params.availableTools, "publishToCanvas")) {
|
||
toolCalls = upsertTool(
|
||
toolCalls,
|
||
"publishToCanvas",
|
||
"image-generation 子代理建议把连续生图结果同步到画布"
|
||
);
|
||
}
|
||
|
||
return {
|
||
...params.plan,
|
||
nextAction: "generate_image",
|
||
intent: "image_gen",
|
||
responseMode: "tool_then_execute",
|
||
preferredCapability: "generate_image",
|
||
shouldUseStructuredReply: true,
|
||
toolCalls,
|
||
};
|
||
}
|