|
|
@@ -64,6 +64,11 @@ export class DesignAnalysisAIService {
|
|
|
|
|
|
// 定义JSON schema(与提示词中的JSON格式完全一致)
|
|
|
const outputSchema = `{
|
|
|
+ "quickSummary": {
|
|
|
+ "colorTone": "色彩基调(如: 暖色调、木色和暖灰色结合)",
|
|
|
+ "mainMaterials": "主要材质(如: 软装以木作为主、黑色皮革沙发)",
|
|
|
+ "atmosphere": "整体氛围(如: 温暖、舒适、生活气息浓厚)"
|
|
|
+ },
|
|
|
"spaceType": "空间类型(如:客餐厅一体化、主卧、玄关等)",
|
|
|
"spacePositioning": "空间定位与场景属性的详细分析",
|
|
|
"layout": "空间布局与动线的详细分析",
|
|
|
@@ -93,10 +98,70 @@ export class DesignAnalysisAIService {
|
|
|
console.log('📥 AI流式响应:', typeof content, content);
|
|
|
if (content && options.onContentStream) {
|
|
|
streamContent = content;
|
|
|
- // 将JSON转为易读文本
|
|
|
- const displayText = typeof content === 'string' ? content : JSON.stringify(content, null, 2);
|
|
|
+ // 🔥 关键修复:将JSON转为易读的中文格式化文本
|
|
|
+ let displayText: string;
|
|
|
+ let jsonObject: any = null;
|
|
|
+
|
|
|
+ // 1. 尝试获取JSON对象
|
|
|
+ if (typeof content === 'string') {
|
|
|
+ // 检查是否是JSON字符串
|
|
|
+ const trimmed = content.trim();
|
|
|
+ if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
|
|
|
+ console.log('🔍 检测到JSON格式字符串,尝试解析...');
|
|
|
+ try {
|
|
|
+ jsonObject = JSON.parse(trimmed);
|
|
|
+ console.log('✅ JSON解析成功,完整对象');
|
|
|
+ } catch (e) {
|
|
|
+ // 🔥 关键修复:JSON不完整时,提取已完整的字段并格式化显示
|
|
|
+ console.log('⚠️ JSON解析失败,尝试提取部分字段...');
|
|
|
+
|
|
|
+ // 尝试从不完整的JSON中提取已完成的字段
|
|
|
+ jsonObject = this.extractPartialJSON(trimmed);
|
|
|
+
|
|
|
+ if (jsonObject && Object.keys(jsonObject).length > 0) {
|
|
|
+ console.log('✅ 成功提取部分字段:', Object.keys(jsonObject).join(', '));
|
|
|
+ } else {
|
|
|
+ // 完全无法提取,显示提示
|
|
|
+ displayText = '🔄 正在生成分析结果...';
|
|
|
+ console.log('⚠️ 无法提取有效字段,等待更多数据');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 普通文本,直接显示
|
|
|
+ displayText = content;
|
|
|
+ }
|
|
|
+ } else if (typeof content === 'object') {
|
|
|
+ jsonObject = content;
|
|
|
+ console.log('📦 收到JSON对象');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 如果是JSON对象,进行格式化
|
|
|
+ if (jsonObject) {
|
|
|
+ console.log('🎨 开始格式化JSON对象...');
|
|
|
+ displayText = this.formatJSONToText(jsonObject);
|
|
|
+
|
|
|
+ // 如果格式化失败或内容过短,使用后备方案
|
|
|
+ if (!displayText || displayText.trim().length < 50) {
|
|
|
+ console.log('⚠️ formatJSONToText结果过短,使用fallback...');
|
|
|
+ displayText = this.fallbackFormatJSON(jsonObject);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 最后兜底:美化JSON
|
|
|
+ if (!displayText || displayText.trim().length < 20) {
|
|
|
+ console.log('⚠️ fallback也失败,使用beautifyJSON...');
|
|
|
+ displayText = this.beautifyJSON(jsonObject);
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('✅ 格式化完成,长度:', displayText.length);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 如果还没有displayText,使用默认值
|
|
|
+ if (!displayText) {
|
|
|
+ displayText = typeof content === 'string' ? content : JSON.stringify(content, null, 2);
|
|
|
+ }
|
|
|
+
|
|
|
options.onContentStream(displayText);
|
|
|
- options.onProgressChange?.(`已接收 ${displayText.length} 字符...`);
|
|
|
+ options.onProgressChange?.(`正在分析,已接收 ${displayText.length} 字符...`);
|
|
|
}
|
|
|
},
|
|
|
2, // 重试次数
|
|
|
@@ -104,7 +169,8 @@ export class DesignAnalysisAIService {
|
|
|
model: this.AI_MODEL,
|
|
|
vision: true, // 🔥 关键:启用vision
|
|
|
images: options.images, // 🔥 关键:直接传URL数组
|
|
|
- max_tokens: 8000
|
|
|
+ max_tokens: 8000,
|
|
|
+ temperature: 0.3 // 🔥 降低随机性,提高一致性(0.0-1.0,越低越确定)
|
|
|
}
|
|
|
);
|
|
|
|
|
|
@@ -137,6 +203,12 @@ export class DesignAnalysisAIService {
|
|
|
|
|
|
console.log('📊 解析后的分析数据:', analysisData);
|
|
|
|
|
|
+ // 🔥 关键:在最后发送完整的格式化内容
|
|
|
+ if (options.onContentStream && analysisData.formattedContent) {
|
|
|
+ console.log('📤 发送最终格式化内容到UI...');
|
|
|
+ options.onContentStream(analysisData.formattedContent);
|
|
|
+ }
|
|
|
+
|
|
|
resolve(analysisData);
|
|
|
|
|
|
} catch (err: any) {
|
|
|
@@ -250,36 +322,350 @@ export class DesignAnalysisAIService {
|
|
|
* 参考ai-k12-daofa的简洁提示词风格
|
|
|
*/
|
|
|
private buildAnalysisPrompt(spaceType?: string, textDescription?: string, conversationHistory?: Array<{ role: string; content: string }>, deepThinking?: boolean): string {
|
|
|
- // 🔥 关键:提示词要简洁明了,直接要求JSON格式(参考ai-k12-daofa)
|
|
|
- let prompt = `请分析图片中的室内设计,并按以下JSON格式输出:
|
|
|
+ // 🔥 全面优化的提示词:支持多种风格,精准识别材质和色调,支持重新分析
|
|
|
+ const hasPreviousAnalysis = conversationHistory && conversationHistory.length > 0;
|
|
|
+
|
|
|
+ let prompt = `你是一位专业的室内设计分析师,请仔细观察图片中的室内设计细节,并按以下JSON格式输出专业分析:`;
|
|
|
+
|
|
|
+ // 🔥 如果有对话历史,说明这是用户提出修正意见后的重新分析
|
|
|
+ if (hasPreviousAnalysis) {
|
|
|
+ prompt += `
|
|
|
+
|
|
|
+【重要说明 - 这是重新分析】
|
|
|
+• 用户已经看过之前的分析,并提出了修正意见或新的要求
|
|
|
+• 请基于用户的反馈,重新生成一份完整的、修正后的分析报告
|
|
|
+• 不要继续之前的分析内容,而是输出一份全新的、完整的JSON分析结果
|
|
|
+• 特别关注用户提到的色调、材质、风格等修正意见,确保新的分析符合用户期望
|
|
|
+
|
|
|
+【分析要求】`;
|
|
|
+ }
|
|
|
+
|
|
|
+ prompt += `
|
|
|
|
|
|
{
|
|
|
+ "quickSummary": {
|
|
|
+ "colorTone": "色彩基调(如: 暖白法式偏女性向,象牙白护墙板+浅灰地面+米色木地板,软装点缀豆沙紫/湖蓝色)",
|
|
|
+ "mainMaterials": "主要材质(如: 象牙白护墙板+线条装饰、大理石纹理台面、黑色雕刻家具、水晶灯、香槟金灯具、豆沙紫/湖蓝色软装)",
|
|
|
+ "atmosphere": "整体氛围(如: 柔暖、精致、优雅、女性向、明亮通透)"
|
|
|
+ },
|
|
|
"spaceType": "空间类型(如:客餐厅一体化、主卧、玄关等)",
|
|
|
"spacePositioning": "空间定位与场景属性的详细分析",
|
|
|
"layout": "空间布局与动线的详细分析",
|
|
|
"hardDecoration": "硬装系统细节的详细分析(顶面、墙面、地面、门窗)",
|
|
|
- "colorAnalysis": "色调精准分析(主色调、辅助色、色调关系)",
|
|
|
- "materials": "材质应用解析(自然材质、现代材质、材质对比)",
|
|
|
+ "colorAnalysis": "色调精准分析",
|
|
|
+ "materials": "材质应用解析",
|
|
|
"form": "形体与比例分析(空间形体、家具形体、造型细节)",
|
|
|
- "style": "风格与氛围营造(风格识别、氛围手法)",
|
|
|
+ "style": "风格与氛围营造",
|
|
|
"suggestions": "专业优化建议(居住适配、细节优化、落地可行性)",
|
|
|
"summary": "简洁摘要(格式: 空间类型 | 风格 | 色调 | 氛围)"
|
|
|
}
|
|
|
|
|
|
-要求:
|
|
|
-1. 基于图片实际视觉内容进行分析
|
|
|
-2. 每个字段提供详细描述(200-400字)
|
|
|
-3. 使用专业的室内设计术语
|
|
|
-4. 不提及品牌名称,仅描述材质、色调、形态`;
|
|
|
+【核心分析原则 - 保证一致性】
|
|
|
+• 🔥 **客观描述优先**:基于图片中可见的元素进行描述,避免过度解读
|
|
|
+• 🔥 **细节精准识别**:准确识别护墙板、线条、大理石纹理、金属材质等细节
|
|
|
+• 🔥 **色调精准定位**:使用精确的色彩描述(淡奶灰、暖白、米色、木色等)
|
|
|
+• 🔥 **风格准确判断**:根据硬装+软装+色调综合判断(现代法式、侘寂、轻奢等)
|
|
|
+• 🔥 **保持分析一致性**:相同的视觉元素应该得出相同的结论
|
|
|
+
|
|
|
+【关键分析要求】
|
|
|
+
|
|
|
+0. **快速总结 (quickSummary)** - 🔥 优先级最高:
|
|
|
+
|
|
|
+ • 色彩基调(colorTone):精准描述主色调组合
|
|
|
+
|
|
|
+ 【现代法式女性向示例】
|
|
|
+ - "暖白法式偏女性向,象牙白护墙板+浅灰地面+米色木地板,软装点缀豆沙紫/湖蓝色"
|
|
|
+ - "浅色调为主,暖白色/米色为基底,黑色家具+香槟金灯具点缀"
|
|
|
+ - "暖白象牙色主导,大理石灰白褐纹呼应,彩色软装增添柔美"
|
|
|
+
|
|
|
+ 【侘寂风格示例】
|
|
|
+ - "暖色调,木色和暖灰色结合"
|
|
|
+ - "自然暖棕+柔和灰,低饱和度舒适系"
|
|
|
+
|
|
|
+ 【轻奢风格示例】
|
|
|
+ - "高级灰+香槟金,大理石质感"
|
|
|
+ - "冷灰主导+金属光泽,精致轻奢"
|
|
|
+
|
|
|
+ ⚠️ 必须明确:
|
|
|
+ - 冷暖倾向(暖白/冷白/中性)
|
|
|
+ - 主色调名称(象牙白/暖白/淡奶灰)
|
|
|
+ - 关键配色(软装彩色/黑色对比/金属点缀)
|
|
|
+
|
|
|
+ • 主要材质(mainMaterials):按重要性列举关键材质
|
|
|
+
|
|
|
+ 【现代法式女性向示例】
|
|
|
+ - "象牙白护墙板+线条装饰、大理石纹理台面、黑色雕刻家具、水晶灯、香槟金灯具"
|
|
|
+ - "白色岩板、浅灰地砖、米色木地板、豆沙紫/湖蓝色软装、绿植装饰"
|
|
|
+
|
|
|
+ 【侘寂风格示例】
|
|
|
+ - "木质软装为主、黑色皮革沙发、藤编家具、混凝土墙面"
|
|
|
+
|
|
|
+ ⚠️ 优先识别:
|
|
|
+ - 硬装:护墙板、线条、大理石/岩板、木地板、瓷砖
|
|
|
+ - 软装:家具材质(木质/皮革/布艺)、灯具(水晶/金属)
|
|
|
+ - 装饰:花瓶、烛台、绿植、艺术品
|
|
|
+
|
|
|
+ • 整体氛围(atmosphere):3-5个关键词
|
|
|
+
|
|
|
+ 【现代法式女性向示例】
|
|
|
+ - "柔暖、精致、优雅、女性向、浪漫"
|
|
|
+ - "明亮通透、轻盈柔美、精致细腻"
|
|
|
+
|
|
|
+ 【侘寂风格示例】
|
|
|
+ - "温暖、舒适、质朴、生活气息"
|
|
|
+
|
|
|
+ ⚠️ 氛围词必须与色调、材质相呼应:
|
|
|
+ - 暖白+浅色 = 明亮、通透、柔和
|
|
|
+ - 彩色软装 = 女性向、浪漫、柔美
|
|
|
+ - 黑色对比+金属 = 精致、优雅、轻奢感
|
|
|
+
|
|
|
+1. **色调精准分析 (colorAnalysis)** - 🔥 核心重点:
|
|
|
+
|
|
|
+ • 🎨 **硬装基础色精准识别**:
|
|
|
+ - **暖白色/象牙白**:偏米色的白色,带微黄调(NCS S 0502-Y50R)
|
|
|
+ * 应用:护墙板、墙面涂料、顶面
|
|
|
+ * 特征:柔和、温润、不刺眼
|
|
|
+
|
|
|
+ - **浅灰色系**:
|
|
|
+ * 淡奶灰色:带微黄调/微粉调的浅灰色(NCS S 0502-Y、S 0502-R)
|
|
|
+ * 灰蓝色:带微蓝调的浅灰色(如背景墙)
|
|
|
+ * 浅灰地面:大理石/瓷砖的自然灰色
|
|
|
+
|
|
|
+ - **米色/奶咖色**:
|
|
|
+ * 木地板:浅橡木色、枫木色
|
|
|
+ * 暖米色墙面:带黄调的浅米色
|
|
|
+
|
|
|
+ - **大理石纹理色**:
|
|
|
+ * 白色基底+褐色/金色纹理(桌面、台面)
|
|
|
+ * 白色基底+蓝绿色纹理(装饰性大理石)
|
|
|
+ * 灰白色带细褐纹(地面)
|
|
|
+
|
|
|
+ • 🎀 **软装点缀色精准识别** - 女性向关键:
|
|
|
+ - **豆沙紫/紫罗兰色**:柔和的紫色调沙发/椅子
|
|
|
+ - **湖蓝色/Tiffany蓝**:清新的蓝绿色调椅子/装饰
|
|
|
+ - **香槟金/玫瑰金**:金属灯具、装饰件
|
|
|
+ - **黑色**:雕刻家具、烛台、电器(形成优雅对比)
|
|
|
+ - **绿植色**:自然绿色,点缀生机
|
|
|
+
|
|
|
+ • 🔢 **色彩比例量化分析**:
|
|
|
+ 【现代法式女性向示例】
|
|
|
+ - 主色调(70%):暖白色/象牙白护墙板+墙面
|
|
|
+ - 辅助色(20%):浅灰色地面+米色木地板
|
|
|
+ - 点缀色(10%):豆沙紫+湖蓝色软装 + 黑色家具 + 香槟金灯具
|
|
|
+
|
|
|
+ 【侘寂风格示例】
|
|
|
+ - 主色调(60%):暖灰色墙面
|
|
|
+ - 辅助色(30%):木色家具
|
|
|
+ - 点缀色(10%):黑色皮革
|
|
|
+
|
|
|
+ • 🌡️ **冷暖定位精准判断**:
|
|
|
+ - **暖色系**:木色、米色、暖灰、奶咖、淡粉、豆沙紫
|
|
|
+ - **中性偏暖**:暖白/象牙白、淡奶灰(带微黄调)
|
|
|
+ - **清新偏冷**:湖蓝色、Tiffany蓝、灰蓝色
|
|
|
+ - **冷色系**:纯灰、冷白、深蓝灰
|
|
|
+ - **中性色**:黑色(优雅对比)、金色(精致点缀)
|
|
|
+
|
|
|
+ • 🎯 **色调协调性分析** - 重要:
|
|
|
+ - 大面积浅色(暖白+浅灰)营造明亮通透感
|
|
|
+ - 软装彩色(豆沙紫+湖蓝)增加女性柔美感
|
|
|
+ - 黑色家具形成优雅对比,提升精致度
|
|
|
+ - 金色饰品点缀轻奢感
|
|
|
+ - 自然绿植平衡色彩,增加生机
|
|
|
+
|
|
|
+ • ⚠️ **避免模糊词汇,使用精准描述**:
|
|
|
+ ❌ "中性灰棕色系" → ✅ "暖白法式,浅色调为主"
|
|
|
+ ❌ "浅色系" → ✅ "象牙白护墙板+浅灰地面+米色木地板"
|
|
|
+ ❌ "彩色点缀" → ✅ "豆沙紫沙发+湖蓝色椅子+香槟金灯具"
|
|
|
+
|
|
|
+2. **材质应用解析 (materials)** - 全面细致:
|
|
|
+
|
|
|
+ • 🏗️ **硬装材质全面识别**:
|
|
|
+
|
|
|
+ 【墙面系统】
|
|
|
+ * **护墙板**(法式关键特征):
|
|
|
+ - 颜色:象牙白/暖白/淡奶灰
|
|
|
+ - 工艺:凸起线条装饰、方框造型、哑光质感
|
|
|
+ - 细节:线条宽度、阴影层次、接缝工艺
|
|
|
+
|
|
|
+ * **线条装饰**(法式精髓):
|
|
|
+ - 顶角线/腰线/门框线
|
|
|
+ - 造型:简约直线/曲线/雕花
|
|
|
+ - 材质:石膏/PU/实木
|
|
|
+
|
|
|
+ * **涂料墙面**:
|
|
|
+ - 暖白色/米色/浅灰色乳胶漆
|
|
|
+ - 质感:哑光/丝光
|
|
|
+
|
|
|
+ * **大理石/岩板背景墙**:
|
|
|
+ - 白色岩板(电视墙/装饰墙)
|
|
|
+ - 纹理大理石(背景装饰)
|
|
|
+
|
|
|
+ 【地面系统】
|
|
|
+ * **大理石地砖**:
|
|
|
+ - 浅灰色柔哑面地砖(主要区域)
|
|
|
+ - 纹理:细密灰白纹理/大理石自然纹
|
|
|
+ - 拼接:直铺/对角铺/拼花
|
|
|
+
|
|
|
+ * **木地板**:
|
|
|
+ - 浅色木地板(米色/浅橡木色)
|
|
|
+ - 工艺:直拼/人字拼/鱼骨拼
|
|
|
+ - 质感:哑光/半哑光
|
|
|
+
|
|
|
+ 【顶面系统】
|
|
|
+ * 石膏线装饰/隐藏式灯带
|
|
|
+ * 平顶+局部造型
|
|
|
+ * 无主灯设计(筒灯+吊灯组合)
|
|
|
+
|
|
|
+ 【门窗系统】
|
|
|
+ * 拱门造型(法式经典元素)
|
|
|
+ * 门框线条装饰
|
|
|
+
|
|
|
+ • 🛋️ **软装材质细节描述** - 女性向重点:
|
|
|
+
|
|
|
+ 【家具材质】
|
|
|
+ * **桌子**:
|
|
|
+ - 大理石圆桌(灰白褐纹/蓝绿纹理)
|
|
|
+ - 黑色雕刻底座(手工雕花纹理)
|
|
|
+
|
|
|
+ * **沙发/椅子**:
|
|
|
+ - 豆沙紫布艺沙发(丝绒/天鹅绒质感)
|
|
|
+ - 湖蓝色椅子(皮革/布艺)
|
|
|
+ - 曲线造型、包裹感强
|
|
|
+
|
|
|
+ * **柜子**:
|
|
|
+ - 白色浮雕柜(立体雕花装饰)
|
|
|
+ - 储物功能+装饰性
|
|
|
+
|
|
|
+ 【灯具材质】
|
|
|
+ * **水晶灯**:
|
|
|
+ - 透明水晶台灯(切面反光)
|
|
|
+ - 白色灯罩(布艺/丝绸质感)
|
|
|
+
|
|
|
+ * **金属灯**:
|
|
|
+ - 香槟金/玫瑰金树枝造型灯
|
|
|
+ - 艺术造型、雕塑感
|
|
|
+
|
|
|
+ 【装饰品材质】
|
|
|
+ * **花瓶**:
|
|
|
+ - 白色陶瓷花瓶(哑光质感)
|
|
|
+ - 造型:圆润/曲线/传统
|
|
|
+
|
|
|
+ * **烛台**:
|
|
|
+ - 黑色烛台(亮面烤漆/陶瓷)
|
|
|
+ - 组合摆放,形成节奏感
|
|
|
+
|
|
|
+ * **绿植**:
|
|
|
+ - 自然枝条(枯枝/绿叶)
|
|
|
+ - 点缀生机、柔化空间
|
|
|
+
|
|
|
+ * **墙面装饰**:
|
|
|
+ - 蝴蝶/鸟类装饰(白色/金色)
|
|
|
+ - 艺术挂画
|
|
|
+
|
|
|
+ • 🔍 **材质质感精准描述** - 触觉与视觉:
|
|
|
+
|
|
|
+ - **护墙板**:哑光漆面,肌理感,立体阴影
|
|
|
+ - **大理石**:
|
|
|
+ * 柔哑面(不反光,温润触感)
|
|
|
+ * 天然纹理(褐色/金色/蓝绿色不规则纹路)
|
|
|
+ * 质感层次(深浅交错,自然过渡)
|
|
|
+
|
|
|
+ - **木质**:自然木纹,温润触感,哑光/半哑光
|
|
|
+ - **金属**:
|
|
|
+ * 黑色雕刻(手工痕迹,哑光质感)
|
|
|
+ * 香槟金(微光泽,细腻拉丝)
|
|
|
+
|
|
|
+ - **水晶/玻璃**:透明清澈,切面反光,精致感
|
|
|
+ - **布艺**:丝绒柔软,天鹅绒光泽,包裹感
|
|
|
+ - **陶瓷**:哑光白瓷,温润细腻,手工感
|
|
|
+
|
|
|
+3. **风格与氛围营造 (style)** - 综合判断:
|
|
|
+
|
|
|
+ • 🎭 **风格准确识别** - 基于硬装+软装+色调综合判断:
|
|
|
+
|
|
|
+ 【现代法式】关键特征:
|
|
|
+ - 硬装:淡奶灰/暖白/象牙白护墙板 + 线条装饰 + 大理石地面 + 拱门造型
|
|
|
+ - 软装:水晶灯/金属灯 + 精致家具 + 花艺绿植装饰
|
|
|
+ - 色调:暖白/象牙白为主 + 米色/浅灰辅助 + 黑色家具对比
|
|
|
+ - 氛围:柔暖、精致、优雅、明亮通透
|
|
|
+
|
|
|
+ 【现代法式·女性向】附加特征:
|
|
|
+ - 软装彩色:豆沙紫/湖蓝色/淡粉色沙发/椅子
|
|
|
+ - 装饰细节:蝴蝶/鸟类墙饰、曲线造型、浮雕柜
|
|
|
+ - 灯具选择:水晶台灯、香槟金/玫瑰金灯具
|
|
|
+ - 色彩搭配:浅色基底+柔和彩色点缀
|
|
|
+ - 氛围升级:女性向、浪漫、轻盈柔美、精致细腻
|
|
|
+
|
|
|
+ 【温润侘寂】关键特征:
|
|
|
+ - 硬装:暖灰色墙面 + 木地板 + 简约造型
|
|
|
+ - 软装:木质家具为主 + 藤编/皮革 + 自然装饰
|
|
|
+ - 色调:木色 + 暖灰色 + 低饱和度
|
|
|
+ - 氛围:温暖、舒适、质朴、生活气息
|
|
|
+
|
|
|
+ 【现代轻奢】关键特征:
|
|
|
+ - 硬装:大理石 + 金属线条 + 高级灰
|
|
|
+ - 软装:轻奢家具 + 金属饰品 + 艺术挂画
|
|
|
+ - 色调:高级灰 + 香槟金/玫瑰金 + 白色
|
|
|
+ - 氛围:精致、时尚、轻奢、品质感
|
|
|
+
|
|
|
+ • 💫 **氛围判断依据**:
|
|
|
+ - 柔暖精致:淡奶灰+暖白+水晶灯+护墙板
|
|
|
+ - 温暖舒适:木色+暖灰+木质软装+自然光
|
|
|
+ - 清冷克制:纯灰+冷白+极简家具+留白
|
|
|
+ - 女性向/浪漫:淡粉色点缀+曲线造型+精致细节
|
|
|
+
|
|
|
+ • ⚠️ **避免混淆**:
|
|
|
+ - 护墙板+水晶灯+大理石 = 法式,而非侘寂
|
|
|
+ - 木质+混凝土+暖灰 = 侘寂,而非法式
|
|
|
+
|
|
|
+4. **专业优化建议 (suggestions)**:
|
|
|
+ • 🏠 **居住适配** - 基于风格特征提建议:
|
|
|
+ - 法式风格:补充淡粉色软装(女儿房)、台盆柜+梳妆台一体化设计
|
|
|
+ - 侘寂风格:木质模块化收纳、暖灰色地毯、生活化装饰
|
|
|
+ • 🔧 **细节优化**:
|
|
|
+ - 材质统一性(护墙板色调协调、大理石纹理呼应)
|
|
|
+ - 色彩过渡(淡奶灰→米色→木色的渐变)
|
|
|
+ - 隐形工程(筒射灯预埋无边框、空调风口预埋无边框)
|
|
|
+ • ✅ **落地可行性**:
|
|
|
+ - 材料选择(护墙板材质、大理石品类、木地板工艺)
|
|
|
+ - 施工注意事项(拼花对缝、线条安装、灯光预埋)
|
|
|
+
|
|
|
+5. **基础要求 - 保证分析质量**:
|
|
|
+ • ✅ 基于图片**实际可见元素**进行分析,严禁臆测或模板化
|
|
|
+ • ✅ 每个字段提供**详细描述**(200-400字)
|
|
|
+ • ✅ 使用**专业室内设计术语**(护墙板、线条、大理石纹理、柔哑面等)
|
|
|
+ • ✅ **不提及品牌**,仅描述材质、色调、形态、氛围
|
|
|
+ • ✅ **保持客观中立**,避免过度解读或情感化描述
|
|
|
+ • ✅ **同一视觉元素=同一结论**,确保分析一致性`;
|
|
|
|
|
|
// 添加空间类型提示
|
|
|
if (spaceType) {
|
|
|
- prompt += `\n5. 空间类型参考: ${spaceType}`;
|
|
|
+ prompt += `\n\n【空间类型参考】: ${spaceType}`;
|
|
|
}
|
|
|
|
|
|
// 添加客户需求提示
|
|
|
if (textDescription) {
|
|
|
- prompt += `\n6. 客户需求参考: ${textDescription}`;
|
|
|
+ prompt += `\n\n【客户核心需求】: ${textDescription}\n请特别关注客户需求中提到的色调、材质、氛围要求,确保分析结果与需求高度契合`;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 🔥 强化分析质量要求
|
|
|
+ if (hasPreviousAnalysis) {
|
|
|
+ // 如果是重新分析,强调要结合用户反馈
|
|
|
+ prompt += `\n\n【重要提醒 - 重新分析要点】
|
|
|
+• 仔细阅读用户的修正意见和反馈,理解用户的真实需求
|
|
|
+• 重新审视图片,基于用户指出的方向进行调整
|
|
|
+• 输出一份完整的、修正后的JSON分析报告
|
|
|
+• 使用精准的专业术语(如"淡奶灰色护墙板"而非"灰色墙面")
|
|
|
+• 如果用户指出了色调、材质、风格的具体要求,必须在新报告中体现
|
|
|
+• 保持分析的专业性和完整性,不要只修改某一部分`;
|
|
|
+ } else {
|
|
|
+ // 如果是首次分析,强调一致性
|
|
|
+ prompt += `\n\n【重要提醒 - 保证分析质量】
|
|
|
+• 基于图片中客观可见的元素进行分析,避免主观臆测
|
|
|
+• 使用精准的专业术语(如"淡奶灰色护墙板"而非"灰色墙面")
|
|
|
+• 材质、色调、风格的判断应该保持逻辑一致性
|
|
|
+• 避免使用模糊或可变的描述词汇
|
|
|
+• 确保分析的完整性和专业性`;
|
|
|
}
|
|
|
|
|
|
return prompt;
|
|
|
@@ -289,15 +675,32 @@ export class DesignAnalysisAIService {
|
|
|
* 解析JSON格式的AI分析结果(新方法,处理completionJSON返回的JSON对象)
|
|
|
*/
|
|
|
private parseJSONAnalysis(jsonResult: any): any {
|
|
|
- console.log('📝 解析JSON分析结果...');
|
|
|
+ console.log('📝 [parseJSONAnalysis] 开始解析JSON分析结果...');
|
|
|
+ console.log('🔍 [parseJSONAnalysis] JSON对象:', JSON.stringify(jsonResult).substring(0, 300));
|
|
|
|
|
|
// 将JSON字段转换为易读的格式化文本
|
|
|
- const formattedContent = this.formatJSONToText(jsonResult);
|
|
|
+ let formattedContent = this.formatJSONToText(jsonResult);
|
|
|
+
|
|
|
+ // 🔥 关键:如果formattedContent为空或过短,说明JSON可能没有标准字段
|
|
|
+ if (!formattedContent || formattedContent.trim().length < 50) {
|
|
|
+ console.warn('⚠️ [parseJSONAnalysis] 格式化内容过短,尝试后备方案...');
|
|
|
+ formattedContent = this.fallbackFormatJSON(jsonResult);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 🔥 最终校验:如果还是为空,使用原始JSON的美化版本
|
|
|
+ if (!formattedContent || formattedContent.trim().length < 20) {
|
|
|
+ console.warn('⚠️ [parseJSONAnalysis] 后备方案也失败,使用JSON美化版本...');
|
|
|
+ formattedContent = this.beautifyJSON(jsonResult);
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('✅ [parseJSONAnalysis] 最终格式化内容长度:', formattedContent.length);
|
|
|
+ console.log('📝 [parseJSONAnalysis] 内容预览:', formattedContent.substring(0, 200));
|
|
|
|
|
|
return {
|
|
|
rawContent: JSON.stringify(jsonResult, null, 2), // 原始JSON
|
|
|
- formattedContent: formattedContent, // 格式化文本
|
|
|
+ formattedContent: formattedContent, // 格式化文本(确保有内容)
|
|
|
structuredData: {
|
|
|
+ quickSummary: jsonResult.quickSummary || null, // 🔥 快速总结
|
|
|
spacePositioning: jsonResult.spacePositioning || '',
|
|
|
layout: jsonResult.layout || '',
|
|
|
hardDecoration: jsonResult.hardDecoration || '',
|
|
|
@@ -314,12 +717,53 @@ export class DesignAnalysisAIService {
|
|
|
};
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 美化JSON显示(当所有格式化方法都失败时的最后手段)
|
|
|
+ */
|
|
|
+ private beautifyJSON(jsonResult: any): string {
|
|
|
+ const lines: string[] = [];
|
|
|
+
|
|
|
+ for (const [key, value] of Object.entries(jsonResult)) {
|
|
|
+ if (value && typeof value === 'string' && value.trim().length > 0) {
|
|
|
+ // 将驼峰命名转换为中文标题
|
|
|
+ const chineseTitle = this.getChineseTitleForKey(key);
|
|
|
+ lines.push(`【${chineseTitle}】\n${value}\n`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return lines.join('\n') || '分析结果为空,请重新分析';
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将JSON字段名转换为中文标题
|
|
|
+ */
|
|
|
+ private getChineseTitleForKey(key: string): string {
|
|
|
+ const titleMap: { [key: string]: string } = {
|
|
|
+ 'spaceType': '空间类型',
|
|
|
+ 'spacePositioning': '空间定位与场景属性',
|
|
|
+ 'layout': '空间布局与动线',
|
|
|
+ 'hardDecoration': '硬装系统细节',
|
|
|
+ 'colorAnalysis': '色调精准分析',
|
|
|
+ 'materials': '材质应用解析',
|
|
|
+ 'form': '形体与比例',
|
|
|
+ 'style': '风格与氛围营造',
|
|
|
+ 'suggestions': '专业优化建议',
|
|
|
+ 'summary': '设计概要'
|
|
|
+ };
|
|
|
+
|
|
|
+ return titleMap[key] || key;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 将JSON结果转换为易读的文本格式
|
|
|
*/
|
|
|
private formatJSONToText(jsonResult: any): string {
|
|
|
+ console.log('🔄 [formatJSONToText] 开始格式化JSON结果...');
|
|
|
+ console.log('🔍 [formatJSONToText] JSON字段数量:', Object.keys(jsonResult).length);
|
|
|
+
|
|
|
const sections = [];
|
|
|
|
|
|
+ // 🔥 关键:确保每个字段都被处理,即使内容为空也显示标题
|
|
|
if (jsonResult.spacePositioning) {
|
|
|
sections.push(`一、空间定位与场景属性\n\n${jsonResult.spacePositioning}\n`);
|
|
|
}
|
|
|
@@ -345,7 +789,95 @@ export class DesignAnalysisAIService {
|
|
|
sections.push(`八、专业优化建议\n\n${jsonResult.suggestions}\n`);
|
|
|
}
|
|
|
|
|
|
- return sections.join('\n');
|
|
|
+ const formattedText = sections.join('\n');
|
|
|
+ console.log('✅ [formatJSONToText] 格式化完成,长度:', formattedText.length);
|
|
|
+ console.log('📝 [formatJSONToText] 内容预览:', formattedText.substring(0, 200));
|
|
|
+
|
|
|
+ // 🔥 后备机制:如果格式化结果为空,尝试从JSON直接生成文本
|
|
|
+ if (!formattedText || formattedText.trim().length === 0) {
|
|
|
+ console.warn('⚠️ [formatJSONToText] 格式化结果为空,使用后备方案...');
|
|
|
+ return this.fallbackFormatJSON(jsonResult);
|
|
|
+ }
|
|
|
+
|
|
|
+ return formattedText;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从不完整的JSON字符串中提取已完成的字段
|
|
|
+ * 🔥 流式传输专用:实时提取部分字段
|
|
|
+ */
|
|
|
+ private extractPartialJSON(jsonString: string): any {
|
|
|
+ console.log('🔧 [extractPartialJSON] 开始提取部分JSON字段...');
|
|
|
+ const result: any = {};
|
|
|
+
|
|
|
+ // 定义所有可能的字段
|
|
|
+ const fields = [
|
|
|
+ 'spaceType', 'spacePositioning', 'layout', 'hardDecoration',
|
|
|
+ 'colorAnalysis', 'materials', 'form', 'style', 'suggestions', 'summary'
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 使用正则表达式提取每个字段的完整值
|
|
|
+ for (const field of fields) {
|
|
|
+ // 匹配 "fieldName": "value" 或 "fieldName": "value...(可能不完整)
|
|
|
+ const regex = new RegExp(`"${field}"\\s*:\\s*"([^"]*(?:"[^"]*)*)"`, 'g');
|
|
|
+ const match = regex.exec(jsonString);
|
|
|
+
|
|
|
+ if (match && match[1]) {
|
|
|
+ // 提取到完整的字段值
|
|
|
+ result[field] = match[1];
|
|
|
+ console.log(`✅ 提取字段 ${field}:`, match[1].substring(0, 50) + '...');
|
|
|
+ } else {
|
|
|
+ // 尝试提取不完整的值(到字符串末尾)
|
|
|
+ const partialRegex = new RegExp(`"${field}"\\s*:\\s*"([^"]*?)(?:"|$)`, 's');
|
|
|
+ const partialMatch = partialRegex.exec(jsonString);
|
|
|
+
|
|
|
+ if (partialMatch && partialMatch[1] && partialMatch[1].length > 20) {
|
|
|
+ // 只有当值足够长时才提取(避免只有几个字符的情况)
|
|
|
+ result[field] = partialMatch[1] + '...';
|
|
|
+ console.log(`⚠️ 提取不完整字段 ${field}:`, partialMatch[1].substring(0, 50) + '...');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const extractedCount = Object.keys(result).length;
|
|
|
+ console.log(`✅ [extractPartialJSON] 提取了 ${extractedCount} 个字段`);
|
|
|
+
|
|
|
+ return extractedCount > 0 ? result : null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 后备格式化方法:当主要方法失败时使用
|
|
|
+ */
|
|
|
+ private fallbackFormatJSON(jsonResult: any): string {
|
|
|
+ const lines: string[] = [];
|
|
|
+
|
|
|
+ // 遍历JSON对象的所有字段
|
|
|
+ const fieldMap: { [key: string]: string } = {
|
|
|
+ 'spaceType': '空间类型',
|
|
|
+ 'spacePositioning': '一、空间定位与场景属性',
|
|
|
+ 'layout': '二、空间布局与动线',
|
|
|
+ 'hardDecoration': '三、硬装系统细节',
|
|
|
+ 'colorAnalysis': '四、色调精准分析',
|
|
|
+ 'materials': '五、材质应用解析',
|
|
|
+ 'form': '六、形体与比例',
|
|
|
+ 'style': '七、风格与氛围营造',
|
|
|
+ 'suggestions': '八、专业优化建议',
|
|
|
+ 'summary': '设计概要'
|
|
|
+ };
|
|
|
+
|
|
|
+ for (const [key, title] of Object.entries(fieldMap)) {
|
|
|
+ if (jsonResult[key] && typeof jsonResult[key] === 'string' && jsonResult[key].trim().length > 0) {
|
|
|
+ if (key === 'spaceType' || key === 'summary') {
|
|
|
+ lines.push(`${title}:${jsonResult[key]}\n`);
|
|
|
+ } else {
|
|
|
+ lines.push(`${title}\n\n${jsonResult[key]}\n`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const result = lines.join('\n');
|
|
|
+ console.log('✅ [fallbackFormatJSON] 后备格式化完成,长度:', result.length);
|
|
|
+ return result || '暂无分析内容';
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -483,8 +1015,8 @@ export class DesignAnalysisAIService {
|
|
|
summary.push(spaceTypeMatch[1].trim());
|
|
|
}
|
|
|
|
|
|
- // 2. 提取风格关键词
|
|
|
- const styleKeywords = ['现代', '法式', '简约', '极简', '轻奢', '新中式', '日式', '北欧', '工业风', '台式', '侘寂', '美式'];
|
|
|
+ // 2. 提取风格关键词(优化:区分温润vs清冷侘寂)
|
|
|
+ const styleKeywords = ['现代', '法式', '简约', '极简', '轻奢', '新中式', '日式', '北欧', '工业风', '台式', '温润侘寂', '侘寂', '美式', '混搭'];
|
|
|
const foundStyles: string[] = [];
|
|
|
styleKeywords.forEach(keyword => {
|
|
|
if (content.includes(keyword) && !foundStyles.includes(keyword)) {
|
|
|
@@ -492,11 +1024,15 @@ export class DesignAnalysisAIService {
|
|
|
}
|
|
|
});
|
|
|
if (foundStyles.length > 0) {
|
|
|
- summary.push(foundStyles.join('+'));
|
|
|
+ summary.push(foundStyles.slice(0, 2).join('+'));
|
|
|
}
|
|
|
|
|
|
- // 3. 提取色调关键词
|
|
|
- const colorKeywords = ['暖色系', '冷色系', '暖调', '冷调', '高级灰', '奶白', '米白', '暖灰', '木色', '原木色'];
|
|
|
+ // 3. 提取色调关键词(优化:优先识别暖灰色、木色)
|
|
|
+ const colorKeywords = [
|
|
|
+ '暖灰色', '暖灰', '木色', '木棕', '原木色', '暖棕', '胡桃木色', // 暖色调优先
|
|
|
+ '暖色系', '暖调', '米白', '奶白', '米色',
|
|
|
+ '冷色系', '冷调', '高级灰', '纯灰' // 冷色调在后
|
|
|
+ ];
|
|
|
const foundColors: string[] = [];
|
|
|
colorKeywords.forEach(keyword => {
|
|
|
if (content.includes(keyword) && !foundColors.includes(keyword)) {
|
|
|
@@ -504,11 +1040,15 @@ export class DesignAnalysisAIService {
|
|
|
}
|
|
|
});
|
|
|
if (foundColors.length > 0) {
|
|
|
- summary.push(foundColors.join('、'));
|
|
|
+ summary.push(foundColors.slice(0, 3).join('、'));
|
|
|
}
|
|
|
|
|
|
- // 4. 提取氛围关键词
|
|
|
- const moodKeywords = ['温馨', '舒适', '精致', '高级', '松弛', '静谧', '优雅', '时尚', '女性向', '男性向', '亲子'];
|
|
|
+ // 4. 提取氛围关键词(优化:优先识别温暖、舒适、生活气息)
|
|
|
+ const moodKeywords = [
|
|
|
+ '温暖', '舒适', '生活气息', '温馨', '质朴', // 温暖氛围优先
|
|
|
+ '精致', '高级', '优雅', '松弛', '静谧', '时尚',
|
|
|
+ '女性向', '男性向', '亲子', '清冷' // 清冷在后
|
|
|
+ ];
|
|
|
const foundMoods: string[] = [];
|
|
|
moodKeywords.forEach(keyword => {
|
|
|
if (content.includes(keyword) && !foundMoods.includes(keyword)) {
|
|
|
@@ -516,11 +1056,15 @@ export class DesignAnalysisAIService {
|
|
|
}
|
|
|
});
|
|
|
if (foundMoods.length > 0) {
|
|
|
- summary.push(foundMoods.slice(0, 2).join('、'));
|
|
|
+ summary.push(foundMoods.slice(0, 3).join('、'));
|
|
|
}
|
|
|
|
|
|
- // 5. 提取关键材质
|
|
|
- const materialKeywords = ['木材', '大理石', '瓷砖', '布艺', '皮革', '金属', '玻璃', '护墙板'];
|
|
|
+ // 5. 提取关键材质(优化:优先识别木材、皮革)
|
|
|
+ const materialKeywords = [
|
|
|
+ '木材', '木质', '实木', '胡桃木', '橡木', '柚木', // 木材优先
|
|
|
+ '皮革', '黑色皮革', // 皮革
|
|
|
+ '大理石', '瓷砖', '混凝土', '护墙板', '布艺', '金属', '玻璃', '藤编'
|
|
|
+ ];
|
|
|
const foundMaterials: string[] = [];
|
|
|
materialKeywords.forEach(keyword => {
|
|
|
if (content.includes(keyword) && !foundMaterials.includes(keyword)) {
|
|
|
@@ -528,7 +1072,7 @@ export class DesignAnalysisAIService {
|
|
|
}
|
|
|
});
|
|
|
if (foundMaterials.length > 0) {
|
|
|
- summary.push('主要材质:' + foundMaterials.slice(0, 3).join('、'));
|
|
|
+ summary.push('主要材质:' + foundMaterials.slice(0, 4).join('、'));
|
|
|
}
|
|
|
|
|
|
return summary.length > 0 ? summary.join(' | ') : '整体设计基于图片实际内容分析';
|