132 lines
4.5 KiB
TypeScript
132 lines
4.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 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 mentionsPromptExecution =
|
||
/提示词|prompt|关键词|咒语/.test(normalized) &&
|
||
/生成|生图|出图|继续做|继续生成|执行/.test(normalized);
|
||
|
||
if (!hasPromptMemory && !hasImageWorkflowMemory && !carryForward && !mentionsPromptExecution) {
|
||
return null;
|
||
}
|
||
|
||
const planningHints = [
|
||
carryForward
|
||
? "用户当前要求沿用上一轮优化提示词继续执行,应优先理解为连续生图执行。"
|
||
: "",
|
||
hasPromptMemory
|
||
? "当前会话里存在上一轮可复用提示词,如果用户在说“上一个提示词/刚才那个 prompt”,优先复用这段提示词。"
|
||
: "",
|
||
hasImageWorkflowMemory
|
||
? "当前会话里存在图片工作流记忆,优先参考上一轮参考图、结果图、数量和画幅偏好。"
|
||
: "",
|
||
params.hasImage
|
||
? "如果当前回合上传了新图片,并要求沿用上一轮提示词,应理解为“上一轮提示词 + 当前图片条件”的联合执行。"
|
||
: "",
|
||
...promptHints,
|
||
...workflowHints,
|
||
].filter(Boolean);
|
||
|
||
return {
|
||
domain: "image_generation_memory",
|
||
specialistId: "image-generation-subagent",
|
||
reason: carryForward ? "当前回合命中提示词接力生图" : "当前回合命中提示词相关的连续生图任务",
|
||
planningHints,
|
||
toolAllowlist: [
|
||
"respondDirect",
|
||
"generateImage",
|
||
"useSelectedCanvasImage",
|
||
"useLatestResultImage",
|
||
"useOriginalUploadImage",
|
||
"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 hasPromptMemory = getPromptMemoryPlanningHints(params.threadState).length > 0;
|
||
const hasImageWorkflowMemory = getImageWorkflowPlanningHints(params.threadState).length > 0;
|
||
if (!carryForward && !hasPromptMemory && !hasImageWorkflowMemory) {
|
||
return params.plan;
|
||
}
|
||
|
||
let toolCalls = [...params.plan.toolCalls];
|
||
if (hasTool(params.availableTools, "generateImage")) {
|
||
toolCalls = [{ name: "generateImage", reason: "image-generation 子代理要求优先走连续生图执行链路" }];
|
||
}
|
||
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,
|
||
};
|
||
}
|