fix canvas cutout intent routing
This commit is contained in:
parent
dab44f3825
commit
16f23b3f7c
@ -20,7 +20,7 @@ const DEICTIC_TARGET_REGEX =
|
|||||||
/(这张图|这只|这个|这款|这套|当前这张|当前这个|当前这只|这一张|这一只|这个产品|这个物体|这只猫|这只狗)/i;
|
/(这张图|这只|这个|这款|这套|当前这张|当前这个|当前这只|这一张|这一只|这个产品|这个物体|这只猫|这只狗)/i;
|
||||||
|
|
||||||
const ACTIONABLE_IMAGE_EDIT_REGEX =
|
const ACTIONABLE_IMAGE_EDIT_REGEX =
|
||||||
/(抠图|去背景|透明背景|去背|扣图|移除背景|去水印|水印|去logo|去\s*logo|移除logo|去除logo|换背景|改背景|背景换成|背景改成|白底|纯色背景|替换.*背景|变清晰|高清|锐化|降噪|提升质量|修复|清晰化|扩展|拓展|延伸|扩图|outpainting|拓宽|扩大|多视角|换视角|改视角|视角变体|新角度|换角度|改角度|去除|消除|擦掉|删除|移除|p掉|重绘|风格化|卡通化|动漫风|油画风|调色|调整颜色|亮度|对比度|饱和度|缩放|放大|缩小|调整尺寸|裁剪|resize|crop|修图|p图|改图|调图|编辑.*图|优化.*图|重做|再调整|继续改|继续调|处理)/i;
|
/(抠图|去背景|透明背景|去背|扣图|抠出|扣出|抠出来|扣出来|移除背景|去水印|水印|去logo|去\s*logo|移除logo|去除logo|换背景|改背景|背景换成|背景改成|白底|纯色背景|替换.*背景|变清晰|高清|锐化|降噪|提升质量|修复|清晰化|扩展|拓展|延伸|扩图|outpainting|拓宽|扩大|多视角|换视角|改视角|视角变体|新角度|换角度|改角度|去除|消除|擦掉|删除|移除|p掉|重绘|风格化|卡通化|动漫风|油画风|调色|调整颜色|亮度|对比度|饱和度|缩放|放大|缩小|调整尺寸|裁剪|resize|crop|修图|p图|改图|调图|编辑.*图|优化.*图|重做|再调整|继续改|继续调|处理)/i;
|
||||||
|
|
||||||
export function normalizeCanvasAgentRequestText(text: string) {
|
export function normalizeCanvasAgentRequestText(text: string) {
|
||||||
return String(text || "").toLowerCase().replace(/\s+/g, "").trim();
|
return String(text || "").toLowerCase().replace(/\s+/g, "").trim();
|
||||||
@ -42,7 +42,7 @@ export function inferCanvasAgentImageEditCapability(
|
|||||||
if (/去水印|水印|去logo|移除logo|去除logo/.test(normalizedText)) {
|
if (/去水印|水印|去logo|移除logo|去除logo/.test(normalizedText)) {
|
||||||
return "remove_watermark";
|
return "remove_watermark";
|
||||||
}
|
}
|
||||||
if (/抠图|去背景|透明背景|去背|扣图|移除背景/.test(normalizedText)) {
|
if (/抠图|去背景|透明背景|去背|扣图|抠出|扣出|抠出来|扣出来|移除背景/.test(normalizedText)) {
|
||||||
return "remove_background";
|
return "remove_background";
|
||||||
}
|
}
|
||||||
if (/多视角|换视角|改视角|视角变体|新角度|换角度|改角度|侧面|侧视|背面|背后|俯视|俯拍|仰视|仰拍|低机位|高机位/.test(normalizedText)) {
|
if (/多视角|换视角|改视角|视角变体|新角度|换角度|改角度|侧面|侧视|背面|背后|俯视|俯拍|仰视|仰拍|低机位|高机位/.test(normalizedText)) {
|
||||||
|
|||||||
@ -94,10 +94,10 @@ function isExplicitImageGenerationRequest(text: string): boolean {
|
|||||||
const visualTarget =
|
const visualTarget =
|
||||||
"(?:图|图片|海报|头像|插画|场景|主图|产品图|商品图|电商图|壁纸|封面|logo|图标)";
|
"(?:图|图片|海报|头像|插画|场景|主图|产品图|商品图|电商图|壁纸|封面|logo|图标)";
|
||||||
const createVisualTarget = new RegExp(
|
const createVisualTarget = new RegExp(
|
||||||
`(?:生成|创作|创建|制作|设计|做|出|画|绘制)${countToken}?张?.{0,32}${visualTarget}`,
|
`(?:生成|创作|创建|制作|设计|做|出|画(?!布)|绘制)${countToken}?张?.{0,32}${visualTarget}`,
|
||||||
);
|
);
|
||||||
const drawSubject = new RegExp(
|
const drawSubject = new RegExp(
|
||||||
`(?:画|绘制|生成|创作|创建)${countToken}?(?:只|个|位|幅|张)?.{0,24}(?:小猫|猫咪|猫|狗|人物|角色|ip|头像|logo|图标|插画)`,
|
`(?:画(?!布)|绘制|生成|创作|创建)${countToken}?(?:只|个|位|幅|张)?.{0,24}(?:小猫|猫咪|猫|狗|人物|角色|ip|头像|logo|图标|插画)`,
|
||||||
);
|
);
|
||||||
const continueVariants = new RegExp(
|
const continueVariants = new RegExp(
|
||||||
`(?:再来|再出|再做|再给我|补|多来|继续生成|继续出)${countToken}?张`,
|
`(?:再来|再出|再做|再给我|补|多来|继续生成|继续出)${countToken}?张`,
|
||||||
@ -197,6 +197,9 @@ function buildExecutionFromAnalysis(params: {
|
|||||||
targetSource: ResolvedTargetAssets["resolutionMode"];
|
targetSource: ResolvedTargetAssets["resolutionMode"];
|
||||||
}): IntentArbiterExecution {
|
}): IntentArbiterExecution {
|
||||||
const mode = mapIntentToMode(params.analysis.intent, params.analysis.needsClarification);
|
const mode = mapIntentToMode(params.analysis.intent, params.analysis.needsClarification);
|
||||||
|
const shouldKeepPlannedToolCalls =
|
||||||
|
params.execution?.mode === mode &&
|
||||||
|
params.execution?.routeIntent === params.analysis.intent;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mode,
|
mode,
|
||||||
@ -218,7 +221,7 @@ function buildExecutionFromAnalysis(params: {
|
|||||||
shouldStayConversational:
|
shouldStayConversational:
|
||||||
mode === "respond" || mode === "analyze_image" || mode === "clarify",
|
mode === "respond" || mode === "analyze_image" || mode === "clarify",
|
||||||
shouldPublishToCanvas: params.execution?.shouldPublishToCanvas || false,
|
shouldPublishToCanvas: params.execution?.shouldPublishToCanvas || false,
|
||||||
toolCalls: params.execution?.toolCalls || [],
|
toolCalls: shouldKeepPlannedToolCalls ? params.execution?.toolCalls || [] : [],
|
||||||
availableTools: params.execution?.availableTools || [],
|
availableTools: params.execution?.availableTools || [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -262,6 +265,14 @@ function resolvePolicyIntent(params: {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasUsableImage && likelyEdit) {
|
||||||
|
return {
|
||||||
|
intent: "image_edit",
|
||||||
|
subIntent: params.analysis.subIntent || inferDefaultImageEditSubIntent(params.text),
|
||||||
|
reason: "image_edit_policy_override",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
switch (params.actionResolution.kind) {
|
switch (params.actionResolution.kind) {
|
||||||
case "image_edit_followup":
|
case "image_edit_followup":
|
||||||
return {
|
return {
|
||||||
@ -292,13 +303,6 @@ function resolvePolicyIntent(params: {
|
|||||||
reason: "task_boundary_execution_video",
|
reason: "task_boundary_execution_video",
|
||||||
};
|
};
|
||||||
case "execution_image":
|
case "execution_image":
|
||||||
if (hasUsableImage && likelyEdit && !explicitImageGen) {
|
|
||||||
return {
|
|
||||||
intent: "image_edit",
|
|
||||||
subIntent: params.analysis.subIntent || inferDefaultImageEditSubIntent(params.text),
|
|
||||||
reason: "task_boundary_execution_image_edit",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
intent: "image_gen",
|
intent: "image_gen",
|
||||||
reason: "task_boundary_execution_image_generate",
|
reason: "task_boundary_execution_image_generate",
|
||||||
@ -329,14 +333,6 @@ function resolvePolicyIntent(params: {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasUsableImage && likelyEdit && params.analysis.intent === "chat") {
|
|
||||||
return {
|
|
||||||
intent: "image_edit",
|
|
||||||
subIntent: params.analysis.subIntent || inferDefaultImageEditSubIntent(params.text),
|
|
||||||
reason: "image_edit_policy_override",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
intent: params.analysis.intent,
|
intent: params.analysis.intent,
|
||||||
subIntent:
|
subIntent:
|
||||||
|
|||||||
@ -33,7 +33,7 @@ export interface AnalysisResult {
|
|||||||
function inferDefaultImageEditSubIntent(text: string): AnalysisResult["subIntent"] {
|
function inferDefaultImageEditSubIntent(text: string): AnalysisResult["subIntent"] {
|
||||||
const lowerText = text.toLowerCase();
|
const lowerText = text.toLowerCase();
|
||||||
if (/去水印|水印|去logo|去\s*logo|移除logo|去除logo/.test(lowerText)) return "remove_watermark";
|
if (/去水印|水印|去logo|去\s*logo|移除logo|去除logo/.test(lowerText)) return "remove_watermark";
|
||||||
if (/抠图|去背景|透明背景|去背|扣图|移除背景/.test(lowerText)) return "remove_background";
|
if (/抠图|去背景|透明背景|去背|扣图|抠出|扣出|抠出来|扣出来|移除背景/.test(lowerText)) return "remove_background";
|
||||||
if (/换背景|改背景|背景换成|背景改成|白底|透明底/.test(lowerText)) return "replace_background";
|
if (/换背景|改背景|背景换成|背景改成|白底|透明底/.test(lowerText)) return "replace_background";
|
||||||
if (/多视角|换视角|改视角|视角变体|新角度|换角度|改角度|侧面|侧视|背面|背后|俯视|俯拍|仰视|仰拍|低机位|高机位/.test(lowerText)) return "angle_variant";
|
if (/多视角|换视角|改视角|视角变体|新角度|换角度|改角度|侧面|侧视|背面|背后|俯视|俯拍|仰视|仰拍|低机位|高机位/.test(lowerText)) return "angle_variant";
|
||||||
if (hasDirectAgentLocalEditIntent(text)) return "local_edit";
|
if (hasDirectAgentLocalEditIntent(text)) return "local_edit";
|
||||||
@ -112,10 +112,10 @@ export function isExplicitImageGenerationRequest(text: string): boolean {
|
|||||||
const visualTarget =
|
const visualTarget =
|
||||||
"(?:图|图片|海报|头像|插画|场景|主图|产品图|商品图|电商图|壁纸|封面|logo|图标)";
|
"(?:图|图片|海报|头像|插画|场景|主图|产品图|商品图|电商图|壁纸|封面|logo|图标)";
|
||||||
const createVisualTarget = new RegExp(
|
const createVisualTarget = new RegExp(
|
||||||
`(?:生成|创作|创建|制作|设计|做|出|画|绘制)${countToken}?张?.{0,32}${visualTarget}`,
|
`(?:生成|创作|创建|制作|设计|做|出|画(?!布)|绘制)${countToken}?张?.{0,32}${visualTarget}`,
|
||||||
);
|
);
|
||||||
const drawSubject = new RegExp(
|
const drawSubject = new RegExp(
|
||||||
`(?:画|绘制|生成|创作|创建)${countToken}?(?:只|个|位|幅|张)?.{0,24}(?:小猫|猫咪|猫|狗|人物|角色|ip|头像|logo|图标|插画)`,
|
`(?:画(?!布)|绘制|生成|创作|创建)${countToken}?(?:只|个|位|幅|张)?.{0,24}(?:小猫|猫咪|猫|狗|人物|角色|ip|头像|logo|图标|插画)`,
|
||||||
);
|
);
|
||||||
const continueVariants = new RegExp(
|
const continueVariants = new RegExp(
|
||||||
`(?:再来|再出|再做|再给我|补|多来|继续生成|继续出)${countToken}?张`,
|
`(?:再来|再出|再做|再给我|补|多来|继续生成|继续出)${countToken}?张`,
|
||||||
@ -141,7 +141,7 @@ export function isExplicitImageGenerationRequest(text: string): boolean {
|
|||||||
|
|
||||||
function isLikelyImageEditRequest(text: string): boolean {
|
function isLikelyImageEditRequest(text: string): boolean {
|
||||||
const normalizedText = text.toLowerCase();
|
const normalizedText = text.toLowerCase();
|
||||||
return hasDirectAgentLocalEditIntent(text) || /抠图|去背景|透明背景|去背|扣图|移除背景|去水印|水印|去logo|去\s*logo|移除logo|去除logo|换背景|改背景|背景换成|背景改成|白底|纯色背景|替换.*背景|变清晰|高清|锐化|降噪|提升质量|修复|清晰化|扩展|拓展|延伸|扩图|outpainting|拓宽|扩大|换成.*(?:比例|竖版|横版|竖图|横图|9:16|16:9|1:1)|改成.*(?:比例|竖版|横版|竖图|横图|9:16|16:9|1:1)|比例|画幅|竖版|横版|多视角|换视角|改视角|视角变体|新角度|换角度|改角度|侧面|侧视|背面|背后|俯视|俯拍|仰视|仰拍|低机位|高机位|去除|消除|擦掉|删除|移除|p掉|重绘|风格化|卡通化|动漫风|油画风|转.*风格|变.*风格|改.*风格|调色|调整颜色|亮度|对比度|饱和度|色调|曝光|滤镜|改成.*色|换成.*色|变成.*色|颜色改成|颜色换成|颜色变成|缩放|放大|缩小|调整尺寸|裁剪|resize|crop|修图|p图|改图|调图|编辑.*图|优化.*图|重做|再调整|继续改|继续调/.test(
|
return hasDirectAgentLocalEditIntent(text) || /抠图|去背景|透明背景|去背|扣图|抠出|扣出|抠出来|扣出来|移除背景|去水印|水印|去logo|去\s*logo|移除logo|去除logo|换背景|改背景|背景换成|背景改成|白底|纯色背景|替换.*背景|变清晰|高清|锐化|降噪|提升质量|修复|清晰化|扩展|拓展|延伸|扩图|outpainting|拓宽|扩大|换成.*(?:比例|竖版|横版|竖图|横图|9:16|16:9|1:1)|改成.*(?:比例|竖版|横版|竖图|横图|9:16|16:9|1:1)|比例|画幅|竖版|横版|多视角|换视角|改视角|视角变体|新角度|换角度|改角度|侧面|侧视|背面|背后|俯视|俯拍|仰视|仰拍|低机位|高机位|去除|消除|擦掉|删除|移除|p掉|重绘|风格化|卡通化|动漫风|油画风|转.*风格|变.*风格|改.*风格|调色|调整颜色|亮度|对比度|饱和度|色调|曝光|滤镜|改成.*色|换成.*色|变成.*色|颜色改成|颜色换成|颜色变成|缩放|放大|缩小|调整尺寸|裁剪|resize|crop|修图|p图|改图|调图|编辑.*图|优化.*图|重做|再调整|继续改|继续调/.test(
|
||||||
normalizedText,
|
normalizedText,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -2331,7 +2331,7 @@ export function fallbackKeywordAnalysis(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 抠图/去背景
|
// 抠图/去背景
|
||||||
if (/抠图|去背景|透明背景|去背|扣图|移除背景/.test(lowerText)) {
|
if (/抠图|去背景|透明背景|去背|扣图|抠出|扣出|抠出来|扣出来|移除背景/.test(lowerText)) {
|
||||||
return {
|
return {
|
||||||
intent: "image_edit",
|
intent: "image_edit",
|
||||||
subIntent: "remove_background",
|
subIntent: "remove_background",
|
||||||
|
|||||||
@ -250,7 +250,7 @@ function normalizeEditCapability(
|
|||||||
subIntent: "remove_watermark",
|
subIntent: "remove_watermark",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (subIntent === "remove_background" || /抠图|去背|去背景|透明底/.test(normalizedText)) {
|
if (subIntent === "remove_background" || /抠图|去背|去背景|透明底|抠出|扣出|抠出来|扣出来/.test(normalizedText)) {
|
||||||
return {
|
return {
|
||||||
capability: "remove_background",
|
capability: "remove_background",
|
||||||
toolName: "removeBackground",
|
toolName: "removeBackground",
|
||||||
|
|||||||
@ -48,9 +48,11 @@ import {
|
|||||||
import { shouldBypassCanvasAgentMissingTargetClarification } from "@/lib/ai/runtime/canvas-agent-clarification";
|
import { shouldBypassCanvasAgentMissingTargetClarification } from "@/lib/ai/runtime/canvas-agent-clarification";
|
||||||
import { hasCanvasAgentRoutingContext } from "@/lib/ai/canvas-agent-routing";
|
import { hasCanvasAgentRoutingContext } from "@/lib/ai/canvas-agent-routing";
|
||||||
import {
|
import {
|
||||||
|
inferCanvasAgentImageEditCapability,
|
||||||
resolveCanvasAgentRequestPolicy,
|
resolveCanvasAgentRequestPolicy,
|
||||||
resolveCanvasAgentTargetPreference,
|
resolveCanvasAgentTargetPreference,
|
||||||
} from "@/lib/ai/runtime/canvas-agent-kernel";
|
} from "@/lib/ai/runtime/canvas-agent-kernel";
|
||||||
|
import { isExplicitImageGenerationRequest } from "@/lib/ai/smart-analyzer";
|
||||||
import { inferAgentImageCountFromText } from "@/lib/ai/runtime/image-count-intent";
|
import { inferAgentImageCountFromText } from "@/lib/ai/runtime/image-count-intent";
|
||||||
import {
|
import {
|
||||||
buildRecentDialogueMemory,
|
buildRecentDialogueMemory,
|
||||||
@ -1463,6 +1465,64 @@ test.describe("canvas chat pure helpers", () => {
|
|||||||
expect(directContext?.targetImageUrl).toBe(canvasImageUrl);
|
expect(directContext?.targetImageUrl).toBe(canvasImageUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("routes canvas subject cutout wording to remove background instead of image generation", () => {
|
||||||
|
const canvasImageUrl = "https://cdn.example.com/canvas-dog.png";
|
||||||
|
const userText = "帮我将画布中的狗的图扣出来";
|
||||||
|
|
||||||
|
expect(isExplicitImageGenerationRequest(userText)).toBe(false);
|
||||||
|
expect(inferCanvasAgentImageEditCapability(userText)).toBe("remove_background");
|
||||||
|
|
||||||
|
const directContext = resolveDirectAgentToolContext({
|
||||||
|
execution: {
|
||||||
|
mode: "edit_image",
|
||||||
|
routeIntent: "image_edit",
|
||||||
|
routeSubIntent: "remove_background",
|
||||||
|
targetSource: "text_reference",
|
||||||
|
preferredCapability: "remove_background",
|
||||||
|
shouldUseStructuredReply: true,
|
||||||
|
shouldOptimizePrompt: true,
|
||||||
|
shouldApplyImagePipeline: true,
|
||||||
|
shouldApplyVideoPipeline: false,
|
||||||
|
shouldStayConversational: false,
|
||||||
|
shouldPublishToCanvas: false,
|
||||||
|
toolCalls: [],
|
||||||
|
availableTools: ["resolveTargetImage", "removeBackground"],
|
||||||
|
} as any,
|
||||||
|
requestResolvedMediaInput: {
|
||||||
|
imageRefs: [],
|
||||||
|
currentMessageImageRefs: [],
|
||||||
|
selectedImageRefs: [],
|
||||||
|
canvasImageRefs: [
|
||||||
|
{
|
||||||
|
url: canvasImageUrl,
|
||||||
|
normalizedUrl: canvasImageUrl,
|
||||||
|
nodeId: "dog-node",
|
||||||
|
source: "canvas_context",
|
||||||
|
role: "reference",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
historyUserImageRefs: [],
|
||||||
|
historyAssistantImageRefs: [],
|
||||||
|
resolvedImageRefs: [],
|
||||||
|
currentMessageImageUrls: [],
|
||||||
|
selectedImageUrls: [],
|
||||||
|
canvasImageUrls: [canvasImageUrl],
|
||||||
|
historyUserImageUrls: [],
|
||||||
|
historyAssistantImageUrls: [],
|
||||||
|
resolvedImageUrls: [],
|
||||||
|
selectedAssetIds: [],
|
||||||
|
candidateAssetIds: [],
|
||||||
|
uploadedAssetIds: [],
|
||||||
|
hasImage: true,
|
||||||
|
source: "canvas_context",
|
||||||
|
fromHistory: false,
|
||||||
|
} as any,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(directContext?.execution.toolName).toBe("removeBackground");
|
||||||
|
expect(directContext?.targetImageUrl).toBe(canvasImageUrl);
|
||||||
|
});
|
||||||
|
|
||||||
test("only runs direct image edit batches for explicit safe batch requests", () => {
|
test("only runs direct image edit batches for explicit safe batch requests", () => {
|
||||||
expect(
|
expect(
|
||||||
shouldRunDirectAgentBatchImageEdit({
|
shouldRunDirectAgentBatchImageEdit({
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user