fix: 保持用户消息原样显示,AI自然对话后再执行编辑
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

- 用户发送的消息不再被修改,保持原样显示
- 优化后的提示词只用于API调用,不影响用户消息
- AI会先用自然语言回复用户,再显示编辑结果

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Your Name 2026-01-31 23:29:30 +08:00
parent 6d37542b0b
commit 760509100c
4 changed files with 189 additions and 103 deletions

View File

@ -331,6 +331,10 @@ export async function POST(request: Request) {
let finalModelId = selectedChatModel;
// 🆕 存储智能模式的相关信息
let smartAutoFriendlyMessage: string | undefined; // AI的自然对话回复
let smartAutoOptimizedPrompt: string | undefined; // 优化后的提示词(不修改用户消息,只用于API调用)
// ========== Smart Auto Mode Logic ==========
if (finalModelId === "auto") {
console.log("[Smart Auto Mode] 🚀 Starting intelligent analysis...");
@ -370,6 +374,12 @@ export async function POST(request: Request) {
needsClarification: analysisResult.needsClarification,
});
// 🆕 保存AI的自然对话回复(来自千问3 VL的理解)
if (analysisResult.conversationalReply) {
smartAutoFriendlyMessage = analysisResult.conversationalReply;
console.log("[Smart Auto Mode] 💬 Conversational reply:", smartAutoFriendlyMessage);
}
// 3. 如果是图片编辑类意图,优化提示词
if (analysisResult.intent === "image_edit" && analysisResult.subIntent) {
const { optimizePrompt } = await import("@/lib/ai/prompt-optimizer");
@ -384,16 +394,9 @@ export async function POST(request: Request) {
optimized: optimizedResult.optimizedPrompt.substring(0, 100),
});
// 替换消息中的文本为优化后的提示词
message.parts = message.parts.map((part) => {
if (part.type === "text") {
return {
...part,
text: optimizedResult.optimizedPrompt,
};
}
return part;
});
// 🆕 保存优化后的提示词,但不修改用户消息
// 用户消息保持原样显示,优化后的提示词只用于API调用
smartAutoOptimizedPrompt = optimizedResult.optimizedPrompt;
}
// 4. 路由到最佳模型
@ -923,9 +926,23 @@ export async function POST(request: Request) {
try {
// 只传递最后一条用户消息
const lastUserMessage = uiMessages.filter((m) => m.role === "user").pop();
const messagesForAlibaba = lastUserMessage ? [lastUserMessage] : uiMessages;
let messagesForAlibaba = lastUserMessage ? [lastUserMessage] : uiMessages;
console.log("[Chat API] Alibaba: Using only last user message");
// 🆕 如果有智能模式优化后的提示词,替换消息中的文本(仅用于API调用)
if (smartAutoOptimizedPrompt && messagesForAlibaba.length > 0) {
console.log("[Chat API] Using optimized prompt for API call");
messagesForAlibaba = messagesForAlibaba.map((msg) => ({
...msg,
parts: msg.parts.map((part) => {
if (part.type === "text") {
return { ...part, text: smartAutoOptimizedPrompt! };
}
return part;
}),
}));
}
// 转换消息(阿里生图支持 URL,会自动上传 base64)
const modelMessages = await convertMessagesWithImages(messagesForAlibaba, "alibaba-image");
@ -939,6 +956,13 @@ export async function POST(request: Request) {
const imageMarkdown = result.text || "图片生成失败";
console.log("[Chat API] Alibaba generated, length:", imageMarkdown.length);
// 🆕 如果是智能模式,在图片前加上友好消息
let finalMessage = imageMarkdown;
if (smartAutoFriendlyMessage) {
finalMessage = `${smartAutoFriendlyMessage}\n\n${imageMarkdown}`;
console.log("[Chat API] Added friendly message:", smartAutoFriendlyMessage);
}
// 生成消息 ID
const messageId = generateUUID();
@ -948,7 +972,7 @@ export async function POST(request: Request) {
data: JSON.stringify({
id: messageId,
role: "assistant",
parts: [{ type: "text", text: imageMarkdown }],
parts: [{ type: "text", text: finalMessage }],
}),
});
@ -960,7 +984,7 @@ export async function POST(request: Request) {
id: messageId,
chatId: id,
role: "assistant",
parts: [{ type: "text", text: imageMarkdown }],
parts: [{ type: "text", text: finalMessage }],
attachments: [],
createdAt: new Date(),
videoStorageStatus: null,

View File

@ -10,8 +10,45 @@ export interface OptimizeResult {
originalPrompt: string;
optimizedPrompt: string;
negativePrompt: string;
friendlyMessage?: string; // 友好的对话消息
}
// 子意图的友好名称和回复模板
const FRIENDLY_MESSAGES: Record<string, { name: string; template: string }> = {
remove_background: {
name: "抠图",
template: "好的,我来帮你抠图,去除背景保留主体~",
},
replace_background: {
name: "换背景",
template: "收到,我来帮你把背景换成{background}~",
},
enhance: {
name: "图片增强",
template: "明白,我来帮你提升图片清晰度~",
},
extend: {
name: "图像拓展",
template: "好的,我来帮你扩展图片边界~",
},
inpaint: {
name: "局部修改",
template: "收到,我来帮你{action}{target}~",
},
style_transfer: {
name: "风格迁移",
template: "好的,我来帮你转换成{style}风格~",
},
resize: {
name: "尺寸调整",
template: "明白,我来帮你调整图片尺寸~",
},
color_adjust: {
name: "调色",
template: "收到,我来帮你调整图片颜色~",
},
};
// 图片编辑子意图的提示词模板
const EDIT_TEMPLATES: Record<
string,
@ -188,10 +225,34 @@ export function optimizePrompt(params: {
optimizedPrompt = `${optimizedPrompt}\n\n用户补充要求:${originalPrompt}`;
}
// 生成友好的对话消息
let friendlyMessage = "";
const friendlyTemplate = FRIENDLY_MESSAGES[subIntent];
if (friendlyTemplate) {
friendlyMessage = friendlyTemplate.template;
// 填充友好消息中的变量
for (const [key, regex] of Object.entries(template.extractors)) {
const match = originalPrompt.match(regex);
let value = match ? match[1].trim() : template.defaults[key] || "";
value = value.replace(/[,。!?,.!?]/g, "").trim();
if (!value) {
value = template.defaults[key] || "";
}
friendlyMessage = friendlyMessage.replace(`{${key}}`, value);
}
// 填充剩余的默认值
for (const [key, defaultValue] of Object.entries(template.defaults)) {
friendlyMessage = friendlyMessage.replace(`{${key}}`, defaultValue);
}
}
const result: OptimizeResult = {
originalPrompt,
optimizedPrompt,
negativePrompt: template.negative,
friendlyMessage: friendlyMessage || `好的,我来帮你处理这张图片~`,
};
console.log("[Prompt Optimizer] Output:", result);

View File

@ -19,49 +19,74 @@ export interface AnalysisResult {
suggestedModel?: string;
needsClarification?: boolean;
clarifyingQuestions?: string[];
conversationalReply?: string; // 🆕 自然对话回复
}
// 系统提示词 - 针对图片编辑场景优化
const ANALYZER_SYSTEM_PROMPT = `你是一个专业的图片编辑意图分析专家。请分析用户的请求,判断其意图类型。
// 系统提示词 - 针对图片编辑场景优化,生成自然对话
const ANALYZER_SYSTEM_PROMPT = `你是一个友好的AI图片编辑助手。用户会给你发送图片和编辑请求,你需要:
1. 理解用户想要对图片做什么
2. 用自然、友好的语气回复用户,告诉他你理解了他的需求并会帮他处理
3. 同时输出结构化的意图分析结果
## 意图分类(按优先级)
## 意图分类
### 1. image_edit - 图片编辑(最高优先级,需要用户上传了图片)
### image_edit - 图片编辑(用户上传了图片并想编辑)
子意图:
- remove_background: 抠图、去背景、透明背景、去除背景、扣图
- replace_background: 换背景、改背景、白底、场景替换、背景换成、纯色背景
- enhance: 变清晰、高清、锐化、降噪、提升质量、修复、清晰化
- extend: 扩展、拓展、延伸、outpainting、扩图、拓图
- inpaint: 去除XX、消除、擦掉、修改XX部分、局部修改、P掉、删除某物
- style_transfer: 风格化、卡通化、动漫风、油画风、改风格、转风格
- resize: 缩放、放大、缩小、调整尺寸、裁剪
- color_adjust: 调色、调整颜色、亮度、对比度、饱和度、色调
- remove_background: 抠图、去背景、透明背景
- replace_background: 换背景、改背景、白底
- enhance: 变清晰、高清、锐化、降噪
- extend: 扩展、拓展、延伸画布
- inpaint: 去除某物、消除、擦掉、P掉、局部修改
- style_transfer: 风格化、卡通化、动漫风、改风格
- color_adjust: 调色、调整颜色、亮度、对比度
### 2. vision - 图片理解(需要用户上传了图片)
触发词:看、识别、分析、描述、这是什么、有什么、tell me、what is
### vision - 图片理解(用户想让你看图说话)
### image_gen - 图片生成(用户想生成新图片)
### video_gen - 视频生成
### chat - 普通对话
### 3. image_gen - 图片生成(不需要上传图片)
触发词:画、绘制、生成图片、创建、设计、画一个、生成一张
### 4. video_gen - 视频生成
触发词:视频、动画、动起来、让它动
### 5. chat - 普通对话(默认)
## 输出格式(严格JSON,不要有其他内容)
## 输出格式(严格JSON)
{
"intent": "image_edit",
"subIntent": "replace_background",
"subIntent": "replace_background",
"confidence": 0.95,
"conversationalReply": "好的,我来帮你把背景换成白色!这样产品会更突出~",
"needsClarification": false,
"clarifyingQuestions": []
}
## 特殊规则
1. 如果用户上传了图片且有编辑类关键词 → image_edit
2. 如果用户上传了图片但意图不明确 → needsClarification: true,并提供澄清问题
3. 如果用户要求编辑但没上传图片 → needsClarification: true,提示"请上传图片"
4. 只输出JSON,不要有任何解释文字`;
## 回复风格要求
- conversationalReply 要自然、友好、简洁
- 可以加一些语气词让对话更亲切,如"好的"、"没问题"、"~"等
- 说明你理解了什么需求,让用户知道你在做什么
- 如果意图不明确,礼貌地询问用户想要什么效果
## 示例
用户:"帮我把背景换成白色"
{
"intent": "image_edit",
"subIntent": "replace_background",
"confidence": 0.95,
"conversationalReply": "好的,我来帮你把背景换成纯白色,这样图片会更干净专业~",
"needsClarification": false
}
用户:"把这张图变清晰一点"
{
"intent": "image_edit",
"subIntent": "enhance",
"confidence": 0.9,
"conversationalReply": "收到!我来帮你提升图片清晰度,稍等一下~",
"needsClarification": false
}
用户发了图片但没说要做什么:
{
"intent": "image_edit",
"confidence": 0.5,
"conversationalReply": "我看到你发了一张图片,你想对它做什么处理呢?比如抠图、换背景、变清晰?",
"needsClarification": true
}`;
/**
* 分析用户意图
@ -101,57 +126,11 @@ export async function analyzeIntent(params: {
/**
* 快速关键词匹配(高置信度场景)
* 🆕 现在返回低置信度,强制走LLM分析以获取自然对话回复
*/
function quickKeywordMatch(text: string, hasImage: boolean): AnalysisResult {
const lowerText = text.toLowerCase();
if (hasImage) {
// 抠图/去背景 - 高频场景
if (/抠图|去背景|透明背景|去背|扣图|移除背景/.test(lowerText)) {
return {
intent: "image_edit",
subIntent: "remove_background",
confidence: 0.95,
};
}
// 换背景 - 高频场景
if (/换背景|白底|改背景|背景换成|纯色背景|白色背景/.test(lowerText)) {
return {
intent: "image_edit",
subIntent: "replace_background",
confidence: 0.95,
};
}
// 图片增强 - 高频场景
if (/变清晰|高清|锐化|降噪|清晰一点|更清晰|提升质量/.test(lowerText)) {
return {
intent: "image_edit",
subIntent: "enhance",
confidence: 0.95,
};
}
// P图/去除某物
if (/P掉|p掉|去除|消除|擦掉|删除.*(?:人|物|水印|logo)/.test(lowerText)) {
return {
intent: "image_edit",
subIntent: "inpaint",
confidence: 0.95,
};
}
}
// 视频生成
if (/生成视频|做视频|视频生成|动起来|让.{0,3}动/.test(lowerText)) {
return {
intent: "video_gen",
confidence: 0.95,
};
}
// 返回低置信度结果,触发LLM分析
// 返回低置信度,强制走LLM分析以获取自然对话回复
// 只有LLM不可用时才会使用fallback
return {
intent: "chat",
confidence: 0.3,
@ -210,7 +189,8 @@ async function analyzWithLLM(text: string, hasImage: boolean): Promise<AnalysisR
}
/**
* 回退方案:完整关键词匹配
* 回退方案:完整关键词匹配(当LLM不可用时使用)
* 🆕 添加了自然对话回复
*/
export function fallbackKeywordAnalysis(
text: string,
@ -226,15 +206,20 @@ export function fallbackKeywordAnalysis(
intent: "image_edit",
subIntent: "remove_background",
confidence: 0.9,
conversationalReply: "好的,我来帮你抠图,去除背景保留主体~",
};
}
// 换背景
if (/换背景|白底|改背景|背景换成|纯色背景|换成.*背景|替换.*背景/.test(lowerText)) {
// 提取背景颜色
const bgMatch = text.match(/(?:换成|改成|替换为|变成)(.+?)(?:背景|底|$)/);
const bg = bgMatch ? bgMatch[1].trim() : "白色";
return {
intent: "image_edit",
subIntent: "replace_background",
confidence: 0.9,
conversationalReply: `好的,我来帮你把背景换成${bg}~`,
};
}
@ -244,6 +229,7 @@ export function fallbackKeywordAnalysis(
intent: "image_edit",
subIntent: "enhance",
confidence: 0.9,
conversationalReply: "收到,我来帮你提升图片清晰度~",
};
}
@ -253,6 +239,7 @@ export function fallbackKeywordAnalysis(
intent: "image_edit",
subIntent: "extend",
confidence: 0.9,
conversationalReply: "好的,我来帮你扩展图片边界~",
};
}
@ -262,15 +249,19 @@ export function fallbackKeywordAnalysis(
intent: "image_edit",
subIntent: "inpaint",
confidence: 0.9,
conversationalReply: "明白,我来帮你处理这个区域~",
};
}
// 风格迁移
if (/风格|卡通化|动漫风|油画风|转.*风格|变.*风格|改.*风格/.test(lowerText)) {
const styleMatch = text.match(/(?:转换为|变成|改成|转成)(.+?)(?:风格|$)/);
const style = styleMatch ? styleMatch[1].trim() : "新";
return {
intent: "image_edit",
subIntent: "style_transfer",
confidence: 0.9,
conversationalReply: `好的,我来帮你转换成${style}风格~`,
};
}
@ -280,6 +271,7 @@ export function fallbackKeywordAnalysis(
intent: "image_edit",
subIntent: "color_adjust",
confidence: 0.9,
conversationalReply: "收到,我来帮你调整图片色彩~",
};
}
@ -289,6 +281,7 @@ export function fallbackKeywordAnalysis(
intent: "image_edit",
subIntent: "resize",
confidence: 0.9,
conversationalReply: "好的,我来帮你调整图片尺寸~",
};
}
@ -298,15 +291,17 @@ export function fallbackKeywordAnalysis(
intent: "image_edit",
confidence: 0.8,
needsClarification: true,
clarifyingQuestions: [
"您想对这张图片做什么处理?(抠图/换背景/变清晰/其他)",
],
conversationalReply: "我看到你想编辑这张图片,你具体想要做什么处理呢?比如抠图、换背景、变清晰?",
};
}
// 视觉理解
if (/看|识别|分析|描述|这是什么|有什么|tell me|what is|看看|帮我看/.test(lowerText)) {
return { intent: "vision", confidence: 0.85 };
return {
intent: "vision",
confidence: 0.85,
conversationalReply: "让我来看看这张图片~",
};
}
// 有图但意图不明确
@ -314,9 +309,7 @@ export function fallbackKeywordAnalysis(
intent: "image_edit",
confidence: 0.5,
needsClarification: true,
clarifyingQuestions: [
"您想对这张图片做什么处理?(抠图/换背景/变清晰/局部修改/其他)",
],
conversationalReply: "我看到你发了一张图片,你想对它做什么处理呢?比如抠图、换背景、变清晰?",
};
}
@ -324,12 +317,20 @@ export function fallbackKeywordAnalysis(
// 图片生成
if (/画|绘制|生成图|创建.*图|设计.*图|画一个|生成一张|做一张/.test(lowerText)) {
return { intent: "image_gen", confidence: 0.85 };
return {
intent: "image_gen",
confidence: 0.85,
conversationalReply: "好的,我来帮你生成这张图片~",
};
}
// 视频生成
if (/视频|动画|动起来|生成视频|做视频|视频生成/.test(lowerText)) {
return { intent: "video_gen", confidence: 0.85 };
return {
intent: "video_gen",
confidence: 0.85,
conversationalReply: "好的,我来帮你生成视频~",
};
}
// 需要图片但没有上传
@ -338,7 +339,7 @@ export function fallbackKeywordAnalysis(
intent: "image_edit",
confidence: 0.7,
needsClarification: true,
clarifyingQuestions: ["请先上传您要编辑的图片"],
conversationalReply: "你想编辑图片的话,需要先上传一张图片哦~",
};
}

File diff suppressed because one or more lines are too long