fix: dedupe agent workflow scene prompts
This commit is contained in:
parent
23933cfb41
commit
fb2f3461c2
@ -2863,7 +2863,108 @@ function normalizeAgentSupplementPrompt(text: string, presetName?: string | null
|
||||
}
|
||||
|
||||
function normalizeAgentWorkflowPrompt(text: string) {
|
||||
return String(text || "").trim();
|
||||
return String(text || "")
|
||||
.split(/\n+/)
|
||||
.map((line) => line.replace(/\s+/g, " ").trim())
|
||||
.filter(Boolean)
|
||||
.join("\n")
|
||||
.trim();
|
||||
}
|
||||
|
||||
const AGENT_PROMPT_STRUCTURAL_LABELS = new Set([
|
||||
"角色",
|
||||
"技能",
|
||||
"目标",
|
||||
"约束条件",
|
||||
"流程",
|
||||
"输出",
|
||||
]);
|
||||
|
||||
function normalizePromptLineForCompare(line: string) {
|
||||
return String(line || "")
|
||||
.replace(/\s+/g, " ")
|
||||
.replace(/[::]/g, ":")
|
||||
.replace(/[。;;,,、!!??]+$/g, "")
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
function parsePromptLineLabel(line: string) {
|
||||
const normalized = String(line || "").replace(/\s+/g, " ").trim();
|
||||
if (!normalized) return { label: "", value: "" };
|
||||
|
||||
const colonMatch = normalized.match(/^([^::]{1,20})[::]\s*(.+)$/);
|
||||
if (colonMatch) {
|
||||
return {
|
||||
label: colonMatch[1].trim(),
|
||||
value: colonMatch[2].trim(),
|
||||
};
|
||||
}
|
||||
|
||||
const whitespaceMatch = normalized.match(/^([^\s]{1,20})\s+(.+)$/);
|
||||
if (whitespaceMatch) {
|
||||
return {
|
||||
label: whitespaceMatch[1].trim(),
|
||||
value: whitespaceMatch[2].trim(),
|
||||
};
|
||||
}
|
||||
|
||||
return { label: "", value: normalized };
|
||||
}
|
||||
|
||||
function dedupeAgentSlotPromptAgainstWorkflow(params: {
|
||||
workflowPrompt?: string | null;
|
||||
slotPrompt?: string | null;
|
||||
}) {
|
||||
const dedupedSlotLines: string[] = [];
|
||||
const seenExactLines = new Set<string>();
|
||||
const workflowStructuralValues = new Map<string, string>();
|
||||
|
||||
const rememberLine = (line: string) => {
|
||||
const normalizedLine = normalizePromptLineForCompare(line);
|
||||
if (!normalizedLine) {
|
||||
return;
|
||||
}
|
||||
seenExactLines.add(normalizedLine);
|
||||
};
|
||||
|
||||
const normalizedWorkflowLines = normalizeAgentWorkflowPrompt(params.workflowPrompt || "")
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean);
|
||||
normalizedWorkflowLines.forEach((line) => {
|
||||
const parsed = parsePromptLineLabel(line);
|
||||
if (parsed.label && AGENT_PROMPT_STRUCTURAL_LABELS.has(parsed.label)) {
|
||||
workflowStructuralValues.set(
|
||||
parsed.label,
|
||||
normalizePromptLineForCompare(parsed.value || "")
|
||||
);
|
||||
}
|
||||
rememberLine(line);
|
||||
});
|
||||
|
||||
const normalizedSlotLines = String(params.slotPrompt || "")
|
||||
.split(/\n+/)
|
||||
.map((line) => line.replace(/\s+/g, " ").trim())
|
||||
.filter(Boolean);
|
||||
normalizedSlotLines.forEach((line) => {
|
||||
const normalizedLine = normalizePromptLineForCompare(line);
|
||||
if (!normalizedLine || seenExactLines.has(normalizedLine)) {
|
||||
return;
|
||||
}
|
||||
const parsed = parsePromptLineLabel(line);
|
||||
if (parsed.label && AGENT_PROMPT_STRUCTURAL_LABELS.has(parsed.label)) {
|
||||
const normalizedValue = normalizePromptLineForCompare(parsed.value || "");
|
||||
const workflowValue = workflowStructuralValues.get(parsed.label);
|
||||
if (workflowValue && workflowValue === normalizedValue) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
seenExactLines.add(normalizedLine);
|
||||
dedupedSlotLines.push(line);
|
||||
});
|
||||
|
||||
return dedupedSlotLines.join("\n").trim();
|
||||
}
|
||||
|
||||
function buildAgentSlotPrompt(params: {
|
||||
@ -2881,9 +2982,12 @@ function buildAgentSlotPrompt(params: {
|
||||
if (slotTitle) {
|
||||
lines.push(`【槽位】${slotTitle}`);
|
||||
}
|
||||
const slotPrompt = String(params.slotPrompt || "").trim();
|
||||
if (slotPrompt) {
|
||||
lines.push(slotPrompt);
|
||||
const dedupedSlotPrompt = dedupeAgentSlotPromptAgainstWorkflow({
|
||||
workflowPrompt,
|
||||
slotPrompt: params.slotPrompt,
|
||||
});
|
||||
if (dedupedSlotPrompt) {
|
||||
lines.push(dedupedSlotPrompt);
|
||||
}
|
||||
const supplement = String(params.userSupplementPrompt || "").trim();
|
||||
if (supplement) {
|
||||
|
||||
@ -7,6 +7,7 @@ import {
|
||||
} from "@/lib/agents/workflow-ui-config";
|
||||
import type {
|
||||
AgentWorkflowComposerContext,
|
||||
AgentWorkflowComposerSelection,
|
||||
AgentWorkflowMaterialSelections,
|
||||
AgentWorkflowReferenceImage,
|
||||
AgentWorkflowSelectedBrandModel,
|
||||
@ -22,6 +23,57 @@ export interface AgentWorkflowInputsValue {
|
||||
priceBand: string;
|
||||
}
|
||||
|
||||
function normalizeWorkflowText(value: string) {
|
||||
return String(value || "")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
}
|
||||
|
||||
function buildGroupedSelectionLines(params: {
|
||||
selections: AgentWorkflowComposerSelection[];
|
||||
valueKey: "optionName" | "promptFragment";
|
||||
separator: string;
|
||||
}) {
|
||||
const grouped = new Map<
|
||||
string,
|
||||
{
|
||||
stepTitle: string;
|
||||
values: string[];
|
||||
valueSet: Set<string>;
|
||||
}
|
||||
>();
|
||||
|
||||
params.selections.forEach((item) => {
|
||||
const rawValue =
|
||||
params.valueKey === "promptFragment"
|
||||
? item.promptFragment || item.optionName
|
||||
: item.optionName;
|
||||
const normalizedValue = normalizeWorkflowText(rawValue || "");
|
||||
if (!normalizedValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
const groupKey = item.stepKey;
|
||||
const currentGroup = grouped.get(groupKey) || {
|
||||
stepTitle: item.stepTitle,
|
||||
values: [],
|
||||
valueSet: new Set<string>(),
|
||||
};
|
||||
|
||||
const dedupeKey = normalizedValue.toLowerCase();
|
||||
if (!currentGroup.valueSet.has(dedupeKey)) {
|
||||
currentGroup.valueSet.add(dedupeKey);
|
||||
currentGroup.values.push(normalizedValue);
|
||||
}
|
||||
|
||||
grouped.set(groupKey, currentGroup);
|
||||
});
|
||||
|
||||
return Array.from(grouped.values())
|
||||
.filter((group) => group.values.length > 0)
|
||||
.map((group) => `${group.stepTitle}:${group.values.join(params.separator)}`);
|
||||
}
|
||||
|
||||
export function buildAgentWorkflowComposerContext(params: {
|
||||
config?: AgentWorkflowUiConfig | null;
|
||||
inputs: AgentWorkflowInputsValue;
|
||||
@ -89,9 +141,13 @@ export function buildAgentWorkflowComposerContext(params: {
|
||||
`品牌模特图:${referenceImages.filter((item) => item.source === "brandModel").length}张`
|
||||
);
|
||||
}
|
||||
selections.forEach((item) => {
|
||||
summaryParts.push(`${item.stepTitle}:${item.optionName}`);
|
||||
});
|
||||
summaryParts.push(
|
||||
...buildGroupedSelectionLines({
|
||||
selections,
|
||||
valueKey: "optionName",
|
||||
separator: " / ",
|
||||
})
|
||||
);
|
||||
|
||||
if (summaryParts.length === 0) {
|
||||
return null;
|
||||
@ -149,9 +205,13 @@ export function buildAgentWorkflowComposerContext(params: {
|
||||
}`
|
||||
);
|
||||
}
|
||||
selections.forEach((item) => {
|
||||
promptLines.push(`${item.stepTitle}:${item.promptFragment || item.optionName}`);
|
||||
});
|
||||
promptLines.push(
|
||||
...buildGroupedSelectionLines({
|
||||
selections,
|
||||
valueKey: "promptFragment",
|
||||
separator: ";",
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
primarySku: params.inputs.primarySku.trim() || undefined,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user