fix canvas cutout intent routing
This commit is contained in:
parent
dab44f3825
commit
16f23b3f7c
@ -20,7 +20,7 @@ const DEICTIC_TARGET_REGEX =
|
||||
/(这张图|这只|这个|这款|这套|当前这张|当前这个|当前这只|这一张|这一只|这个产品|这个物体|这只猫|这只狗)/i;
|
||||
|
||||
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) {
|
||||
return String(text || "").toLowerCase().replace(/\s+/g, "").trim();
|
||||
@ -42,7 +42,7 @@ export function inferCanvasAgentImageEditCapability(
|
||||
if (/去水印|水印|去logo|移除logo|去除logo/.test(normalizedText)) {
|
||||
return "remove_watermark";
|
||||
}
|
||||
if (/抠图|去背景|透明背景|去背|扣图|移除背景/.test(normalizedText)) {
|
||||
if (/抠图|去背景|透明背景|去背|扣图|抠出|扣出|抠出来|扣出来|移除背景/.test(normalizedText)) {
|
||||
return "remove_background";
|
||||
}
|
||||
if (/多视角|换视角|改视角|视角变体|新角度|换角度|改角度|侧面|侧视|背面|背后|俯视|俯拍|仰视|仰拍|低机位|高机位/.test(normalizedText)) {
|
||||
|
||||
@ -94,10 +94,10 @@ function isExplicitImageGenerationRequest(text: string): boolean {
|
||||
const visualTarget =
|
||||
"(?:图|图片|海报|头像|插画|场景|主图|产品图|商品图|电商图|壁纸|封面|logo|图标)";
|
||||
const createVisualTarget = new RegExp(
|
||||
`(?:生成|创作|创建|制作|设计|做|出|画|绘制)${countToken}?张?.{0,32}${visualTarget}`,
|
||||
`(?:生成|创作|创建|制作|设计|做|出|画(?!布)|绘制)${countToken}?张?.{0,32}${visualTarget}`,
|
||||
);
|
||||
const drawSubject = new RegExp(
|
||||
`(?:画|绘制|生成|创作|创建)${countToken}?(?:只|个|位|幅|张)?.{0,24}(?:小猫|猫咪|猫|狗|人物|角色|ip|头像|logo|图标|插画)`,
|
||||
`(?:画(?!布)|绘制|生成|创作|创建)${countToken}?(?:只|个|位|幅|张)?.{0,24}(?:小猫|猫咪|猫|狗|人物|角色|ip|头像|logo|图标|插画)`,
|
||||
);
|
||||
const continueVariants = new RegExp(
|
||||
`(?:再来|再出|再做|再给我|补|多来|继续生成|继续出)${countToken}?张`,
|
||||
@ -197,6 +197,9 @@ function buildExecutionFromAnalysis(params: {
|
||||
targetSource: ResolvedTargetAssets["resolutionMode"];
|
||||
}): IntentArbiterExecution {
|
||||
const mode = mapIntentToMode(params.analysis.intent, params.analysis.needsClarification);
|
||||
const shouldKeepPlannedToolCalls =
|
||||
params.execution?.mode === mode &&
|
||||
params.execution?.routeIntent === params.analysis.intent;
|
||||
|
||||
return {
|
||||
mode,
|
||||
@ -218,7 +221,7 @@ function buildExecutionFromAnalysis(params: {
|
||||
shouldStayConversational:
|
||||
mode === "respond" || mode === "analyze_image" || mode === "clarify",
|
||||
shouldPublishToCanvas: params.execution?.shouldPublishToCanvas || false,
|
||||
toolCalls: params.execution?.toolCalls || [],
|
||||
toolCalls: shouldKeepPlannedToolCalls ? params.execution?.toolCalls || [] : [],
|
||||
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) {
|
||||
case "image_edit_followup":
|
||||
return {
|
||||
@ -292,13 +303,6 @@ function resolvePolicyIntent(params: {
|
||||
reason: "task_boundary_execution_video",
|
||||
};
|
||||
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 {
|
||||
intent: "image_gen",
|
||||
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 {
|
||||
intent: params.analysis.intent,
|
||||
subIntent:
|
||||
|
||||
@ -33,7 +33,7 @@ export interface AnalysisResult {
|
||||
function inferDefaultImageEditSubIntent(text: string): AnalysisResult["subIntent"] {
|
||||
const lowerText = text.toLowerCase();
|
||||
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 "angle_variant";
|
||||
if (hasDirectAgentLocalEditIntent(text)) return "local_edit";
|
||||
@ -112,10 +112,10 @@ export function isExplicitImageGenerationRequest(text: string): boolean {
|
||||
const visualTarget =
|
||||
"(?:图|图片|海报|头像|插画|场景|主图|产品图|商品图|电商图|壁纸|封面|logo|图标)";
|
||||
const createVisualTarget = new RegExp(
|
||||
`(?:生成|创作|创建|制作|设计|做|出|画|绘制)${countToken}?张?.{0,32}${visualTarget}`,
|
||||
`(?:生成|创作|创建|制作|设计|做|出|画(?!布)|绘制)${countToken}?张?.{0,32}${visualTarget}`,
|
||||
);
|
||||
const drawSubject = new RegExp(
|
||||
`(?:画|绘制|生成|创作|创建)${countToken}?(?:只|个|位|幅|张)?.{0,24}(?:小猫|猫咪|猫|狗|人物|角色|ip|头像|logo|图标|插画)`,
|
||||
`(?:画(?!布)|绘制|生成|创作|创建)${countToken}?(?:只|个|位|幅|张)?.{0,24}(?:小猫|猫咪|猫|狗|人物|角色|ip|头像|logo|图标|插画)`,
|
||||
);
|
||||
const continueVariants = new RegExp(
|
||||
`(?:再来|再出|再做|再给我|补|多来|继续生成|继续出)${countToken}?张`,
|
||||
@ -141,7 +141,7 @@ export function isExplicitImageGenerationRequest(text: string): boolean {
|
||||
|
||||
function isLikelyImageEditRequest(text: string): boolean {
|
||||
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,
|
||||
);
|
||||
}
|
||||
@ -2331,7 +2331,7 @@ export function fallbackKeywordAnalysis(
|
||||
}
|
||||
|
||||
// 抠图/去背景
|
||||
if (/抠图|去背景|透明背景|去背|扣图|移除背景/.test(lowerText)) {
|
||||
if (/抠图|去背景|透明背景|去背|扣图|抠出|扣出|抠出来|扣出来|移除背景/.test(lowerText)) {
|
||||
return {
|
||||
intent: "image_edit",
|
||||
subIntent: "remove_background",
|
||||
|
||||
@ -250,7 +250,7 @@ function normalizeEditCapability(
|
||||
subIntent: "remove_watermark",
|
||||
};
|
||||
}
|
||||
if (subIntent === "remove_background" || /抠图|去背|去背景|透明底/.test(normalizedText)) {
|
||||
if (subIntent === "remove_background" || /抠图|去背|去背景|透明底|抠出|扣出|抠出来|扣出来/.test(normalizedText)) {
|
||||
return {
|
||||
capability: "remove_background",
|
||||
toolName: "removeBackground",
|
||||
|
||||
@ -48,9 +48,11 @@ import {
|
||||
import { shouldBypassCanvasAgentMissingTargetClarification } from "@/lib/ai/runtime/canvas-agent-clarification";
|
||||
import { hasCanvasAgentRoutingContext } from "@/lib/ai/canvas-agent-routing";
|
||||
import {
|
||||
inferCanvasAgentImageEditCapability,
|
||||
resolveCanvasAgentRequestPolicy,
|
||||
resolveCanvasAgentTargetPreference,
|
||||
} from "@/lib/ai/runtime/canvas-agent-kernel";
|
||||
import { isExplicitImageGenerationRequest } from "@/lib/ai/smart-analyzer";
|
||||
import { inferAgentImageCountFromText } from "@/lib/ai/runtime/image-count-intent";
|
||||
import {
|
||||
buildRecentDialogueMemory,
|
||||
@ -1463,6 +1465,64 @@ test.describe("canvas chat pure helpers", () => {
|
||||
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", () => {
|
||||
expect(
|
||||
shouldRunDirectAgentBatchImageEdit({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user