diff --git a/app/(chat)/api/chat/route.ts b/app/(chat)/api/chat/route.ts index e4554bb7d..f25ba5c4e 100644 --- a/app/(chat)/api/chat/route.ts +++ b/app/(chat)/api/chat/route.ts @@ -4320,6 +4320,7 @@ export async function POST(request: Request) { selectedVisibilityType, composerMode, hiddenPrompt, + workflowPromptOptimizationEnabled, autoContext, contextPayload, agentModePayload, @@ -4333,6 +4334,7 @@ export async function POST(request: Request) { selectedVisibilityType: VisibilityType; composerMode?: "agent" | "image" | "video" | "detail"; hiddenPrompt?: string; + workflowPromptOptimizationEnabled?: boolean; autoContext?: string; contextPayload?: PostRequestBody["contextPayload"]; agentModePayload?: PostRequestBody["agentModePayload"]; @@ -7478,7 +7480,9 @@ export async function POST(request: Request) { }) : ""; const optimizedSceneWorkflowResult = - hiddenPromptText && sceneOptimizationImageUrls.primaryImageUrl + workflowPromptOptimizationEnabled !== false && + hiddenPromptText && + sceneOptimizationImageUrls.primaryImageUrl ? await optimizeAgentSceneWorkflowPrompt({ workflowPrompt: hiddenPromptText, slotPrompt: dedupedSceneSlotPrompt || null, @@ -7508,6 +7512,12 @@ export async function POST(request: Request) { negativeConstraints: optimizedSceneWorkflowResult.negativeConstraints, conflicts: optimizedSceneWorkflowResult.conflicts, }); + } else if (hiddenPromptText && workflowPromptOptimizationEnabled === false) { + console.info("[Chat API] Agent scene workflow prompt optimization skipped:", { + chatId: id, + agentPresetId: selectedAgentPreset.id, + reason: "workflow_prompt_optimization_disabled", + }); } const scenePrompt = buildAgentSceneWorkflowPrompt({ diff --git a/app/(chat)/api/chat/schema.ts b/app/(chat)/api/chat/schema.ts index 25b5dca2e..1da50a2db 100644 --- a/app/(chat)/api/chat/schema.ts +++ b/app/(chat)/api/chat/schema.ts @@ -98,6 +98,7 @@ export const postRequestBodySchema = z.object({ parts: z.array(partSchema), }), hiddenPrompt: z.string().max(4000).optional(), + workflowPromptOptimizationEnabled: z.boolean().optional(), autoContext: z.string().max(4000).optional(), agentPresetId: z.string().uuid().optional(), // 支持预定义模型名称或数据库中的模型 UUID diff --git a/app/canvas/[id]/page.tsx b/app/canvas/[id]/page.tsx index e4022481f..02865ec66 100644 --- a/app/canvas/[id]/page.tsx +++ b/app/canvas/[id]/page.tsx @@ -2587,6 +2587,7 @@ export default function CanvasEditorPage({ params }: PageProps) { selectedAgentPresetId={isAgentWorkspaceEnabled ? selectedAgentPresetId : null} selectedBrandModel={selectedBrandModel} workflowContext={workflowContext} + workflowPromptOptimizationEnabled={appearanceRecognitionEnabled} getLatestWorkflowContext={getLatestWorkflowContext} agentModeState={agentModeState} onAgentModeStateChange={setAgentModeState} diff --git a/components/agents/AgentGenerationWorkspaceDarkV2.tsx b/components/agents/AgentGenerationWorkspaceDarkV2.tsx index c35cdca5b..6debec61e 100644 --- a/components/agents/AgentGenerationWorkspaceDarkV2.tsx +++ b/components/agents/AgentGenerationWorkspaceDarkV2.tsx @@ -117,6 +117,7 @@ interface AgentGenerationWorkspaceDarkV2Props { selectedAgentPresetId?: string | null; selectedBrandModel?: AgentWorkflowSelectedBrandModel | null; workflowContext?: AgentWorkflowComposerContext | null; + workflowPromptOptimizationEnabled?: boolean; getLatestWorkflowContext?: () => AgentWorkflowComposerContext | null; onBack?: () => void; presetPanel?: ReactNode; @@ -955,6 +956,7 @@ export function AgentGenerationWorkspaceDarkV2({ selectedAgentPresetId, selectedBrandModel = null, workflowContext = null, + workflowPromptOptimizationEnabled = true, getLatestWorkflowContext, onBack, presetPanel, @@ -1625,6 +1627,7 @@ export function AgentGenerationWorkspaceDarkV2({ "", MAX_CHAT_HIDDEN_PROMPT_LENGTH ) || undefined, + workflowPromptOptimizationEnabled, agentPresetId: isValidChatUuid(resolvedAgentPresetId) ? resolvedAgentPresetId : undefined, diff --git a/lib/agents/workflow-composer.ts b/lib/agents/workflow-composer.ts index 98252b318..3bcdb9ed3 100644 --- a/lib/agents/workflow-composer.ts +++ b/lib/agents/workflow-composer.ts @@ -41,8 +41,65 @@ const WORKFLOW_FRAGMENT_INLINE_LABELS = [ "模特", ]; +const BUSINESS_METADATA_NOISE_PATTERNS = [ + /官方旗舰店/gi, + /旗舰店/gi, + /专卖店/gi, + /专营店/gi, + /企业店/gi, + /品牌馆/gi, + /拼多多/gi, + /天猫/gi, + /淘宝/gi, + /京东/gi, +]; + +function escapeRegExp(value: string) { + return String(value || "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} + +function sanitizePromptBusinessMetadata(params: { + value: string; + inputs: AgentWorkflowInputsValue; +}) { + const metadataTerms = [ + params.inputs.brand, + params.inputs.store, + params.inputs.thirdCategory, + params.inputs.fourthCategory, + params.inputs.fifthCategory, + params.inputs.priceBand, + ] + .map((item) => normalizeWorkflowText(item || "")) + .filter(Boolean) + .sort((left, right) => right.length - left.length); + + let nextValue = normalizeWorkflowText(params.value || ""); + if (!nextValue) { + return ""; + } + + metadataTerms.forEach((term) => { + nextValue = nextValue.replace(new RegExp(escapeRegExp(term), "gi"), " "); + }); + + BUSINESS_METADATA_NOISE_PATTERNS.forEach((pattern) => { + nextValue = nextValue.replace(pattern, " "); + }); + + nextValue = nextValue + .replace(/[()()【】[\]「」『』]/g, " ") + .replace(/[::]/g, " ") + .replace(/\s*[/-]\s*/g, " ") + .replace(/\s+/g, " ") + .trim(); + + return nextValue; +} + function buildGoodsInfoPromptLine(params: { label: string; + inputs: AgentWorkflowInputsValue; result?: AgentWorkflowMaterialSelections["primary"]["result"] | null; }) { const goodsInfo = params.result?.goodsInfo; @@ -50,8 +107,12 @@ function buildGoodsInfoPromptLine(params: { return ""; } + const cleanedProductName = sanitizePromptBusinessMetadata({ + value: goodsInfo.productName || "", + inputs: params.inputs, + }); const parts = [ - goodsInfo.productName ? `商品名称:${goodsInfo.productName}` : "", + cleanedProductName ? `商品名称:${cleanedProductName}` : "", goodsInfo.sizeInfo ? `尺寸信息:${goodsInfo.sizeInfo}` : "", goodsInfo.materialInfo ? `材质信息:${goodsInfo.materialInfo}` : "", goodsInfo.boardType ? `板材类型:${goodsInfo.boardType}` : "", @@ -110,6 +171,7 @@ function buildGoodsInfoAppearanceConstraintLine(params: { function buildGoodsInfoSummaryLine(params: { label: string; + inputs: AgentWorkflowInputsValue; result?: AgentWorkflowMaterialSelections["primary"]["result"] | null; }) { const goodsInfo = params.result?.goodsInfo; @@ -117,8 +179,12 @@ function buildGoodsInfoSummaryLine(params: { return ""; } + const cleanedProductName = sanitizePromptBusinessMetadata({ + value: goodsInfo.productName || "", + inputs: params.inputs, + }); const parts = [ - goodsInfo.productName || "", + cleanedProductName || "", goodsInfo.sizeInfo || "", goodsInfo.materialInfo || "", ].filter(Boolean); @@ -374,6 +440,7 @@ export function buildAgentWorkflowComposerContext(params: { } const primaryGoodsInfoSummary = buildGoodsInfoSummaryLine({ label: "主SKU商品信息", + inputs: params.inputs, result: params.materials?.primary?.result || null, }); if (primaryGoodsInfoSummary) { @@ -381,6 +448,7 @@ export function buildAgentWorkflowComposerContext(params: { } const secondaryGoodsInfoSummary = buildGoodsInfoSummaryLine({ label: "搭配SKU商品信息", + inputs: params.inputs, result: params.materials?.secondary?.result || null, }); if (secondaryGoodsInfoSummary) { @@ -398,7 +466,7 @@ export function buildAgentWorkflowComposerContext(params: { } if (referenceImages.some((item) => item.source === "brandModel")) { summaryParts.push( - `品牌模特图:${referenceImages.filter((item) => item.source === "brandModel").length}张` + `模特参考图:${referenceImages.filter((item) => item.source === "brandModel").length}张` ); } summaryParts.push( @@ -413,35 +481,10 @@ export function buildAgentWorkflowComposerContext(params: { return null; } - const basicContextLines: string[] = []; - if (params.inputs.primarySku.trim()) { - basicContextLines.push(`主SKU:${params.inputs.primarySku.trim()}`); - } - if (params.inputs.secondarySku.trim()) { - basicContextLines.push(`搭配SKU:${params.inputs.secondarySku.trim()}`); - } - if (params.inputs.brand.trim()) { - basicContextLines.push(`品牌:${params.inputs.brand.trim()}`); - } - if (params.inputs.store.trim()) { - basicContextLines.push(`店铺:${params.inputs.store.trim()}`); - } - if (params.inputs.thirdCategory.trim()) { - basicContextLines.push(`三级分类:${params.inputs.thirdCategory.trim()}`); - } - if (params.inputs.fourthCategory.trim()) { - basicContextLines.push(`四级分类:${params.inputs.fourthCategory.trim()}`); - } - if (params.inputs.fifthCategory.trim()) { - basicContextLines.push(`五级分类:${params.inputs.fifthCategory.trim()}`); - } - if (params.inputs.priceBand.trim()) { - basicContextLines.push(`价格带:${params.inputs.priceBand.trim()}`); - } - const primaryLockLines: string[] = []; const primaryGoodsInfoPrompt = buildGoodsInfoPromptLine({ label: "主SKU商品信息", + inputs: params.inputs, result: params.materials?.primary?.result || null, }); if (primaryGoodsInfoPrompt) { @@ -466,6 +509,7 @@ export function buildAgentWorkflowComposerContext(params: { const secondaryLockLines: string[] = []; const secondaryGoodsInfoPrompt = buildGoodsInfoPromptLine({ label: "搭配SKU商品信息", + inputs: params.inputs, result: params.materials?.secondary?.result || null, }); if (secondaryGoodsInfoPrompt) { @@ -519,7 +563,7 @@ export function buildAgentWorkflowComposerContext(params: { .filter(Boolean) .slice(0, 8); referenceLines.push( - `品牌模特图:已选${referenceImages.filter((item) => item.source === "brandModel").length}张${ + `模特参考图:已选${referenceImages.filter((item) => item.source === "brandModel").length}张${ labels.length > 0 ? `(${labels.join("、")})` : "" }` ); @@ -530,7 +574,6 @@ export function buildAgentWorkflowComposerContext(params: { } const promptSections = [ - buildPromptSection("项目背景", basicContextLines), buildPromptSection("主SKU锁定", primaryLockLines), buildPromptSection("搭配SKU锁定", secondaryLockLines), buildPromptSection("参考图", referenceLines),