ai-json-stream-fix-debug.md 12 KB

AI流式传输JSON显示问题 - 实时显示方案

🔍 问题现象

对话框显示原始JSON(修复前):

{
  "spaceType": "入户玄关",
  "spacePositioning": "该空间作为住宅的入户过渡区域...",
  "layout": "空间采用单边动线布局..."
}

控制台日志

📥 AI流式响应: string {
  "spaceType": "入户玄关",
  ...
}

🎯 根本原因

1. 流式传输特性

  • completionJSON使用流式传输逐步发送内容
  • AI按JSON格式生成内容,在生成过程中发送不完整的JSON字符串
  • 流式回调收到的是string类型的JSON片段

2. JSON解析失败

  • 不完整的JSON无法被JSON.parse()解析
  • 例如:{"spaceType":"客厅","layout":"空间采用... (缺少结尾}
  • 花括号不匹配:{ > }

3. 用户需求

  • ❌ 不希望显示原始JSON
  • ❌ 不希望显示"正在生成..."等待提示
  • 希望实时显示已生成的分析文字(流式体验)

✅ 修复方案:实时提取和显示

核心策略:从不完整JSON中提取字段并实时格式化

逻辑流程

  1. 检测到JSON格式字符串(以{开头)
  2. 尝试JSON.parse()解析完整JSON
  3. 如果失败 → 调用extractPartialJSON()提取已完成的字段
  4. 格式化提取到的字段 → 实时显示中文文本
  5. 随着流式传输继续,不断更新显示

方案1:部分字段提取(核心修复)

extractPartialJSON方法(新增,第488-525行):

使用正则表达式从不完整的JSON中提取已生成的字段:

private extractPartialJSON(jsonString: string): any {
  const result: any = {};
  const fields = [
    'spaceType', 'spacePositioning', 'layout', 'hardDecoration',
    'colorAnalysis', 'materials', 'form', 'style', 'suggestions'
  ];
  
  for (const field of fields) {
    // 匹配完整的字段值: "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}`);
    } 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}`);
      }
    }
  }
  
  return Object.keys(result).length > 0 ? result : null;
}

提取示例

输入(不完整JSON):

{"spaceType":"客厅","spacePositioning":"该空间定位为高端住宅的核心社交区域,兼具日常休闲与接待功能...","layout":"空间采用围合式布局,以电视背景墙为

输出(提取结果):

{
  spaceType: "客厅",
  spacePositioning: "该空间定位为高端住宅的核心社交区域...",
  layout: "空间采用围合式布局,以电视背景墙为..."
}

方案2:实时格式化显示

代码design-analysis-ai.service.ts 第100-131行):

if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
  try {
    jsonObject = JSON.parse(trimmed);
    console.log('✅ JSON解析成功,完整对象');
  } catch (e) {
    // 🔥 关键修复:JSON不完整时,提取已完整的字段并格式化显示
    console.log('⚠️ JSON解析失败,尝试提取部分字段...');
    
    jsonObject = this.extractPartialJSON(trimmed);
    
    if (jsonObject && Object.keys(jsonObject).length > 0) {
      console.log('✅ 成功提取部分字段:', Object.keys(jsonObject).join(', '));
      // 继续格式化显示
    } else {
      displayText = '🔄 正在生成分析结果...'; // 完全无法提取时
    }
  }
}

// 格式化JSON对象(完整或部分)
if (jsonObject) {
  displayText = this.formatJSONToText(jsonObject);
  // 后备方案...
}

方案3:最后覆盖显示(已实现)

逻辑

  • AI完成后,调用parseJSONAnalysis生成formattedContent
  • 通过onContentStream发送最终格式化内容
  • 覆盖之前显示的进度提示或JSON片段

代码design-analysis-ai.service.ts 第204-208行):

// 🔥 关键:在最后发送完整的格式化内容
if (options.onContentStream && analysisData.formattedContent) {
  console.log('📤 发送最终格式化内容到UI...');
  options.onContentStream(analysisData.formattedContent);
}

🧪 调试步骤

1. 检查控制台日志

正常流程(修复后 - 实时显示):

📥 AI流式响应: string {"spaceType":"客厅"...
🔍 检测到JSON格式字符串,尝试解析...
⚠️ JSON解析失败,尝试提取部分字段...
🔧 [extractPartialJSON] 开始提取部分JSON字段...
✅ 提取字段 spaceType: 客厅
✅ 成功提取部分字段: spaceType
🎨 开始格式化JSON对象...
→ 显示: 空间类型:客厅

(流式传输继续...)

📥 AI流式响应: string {"spaceType":"客厅","spacePositioning":"该空间定位为...
⚠️ JSON解析失败,尝试提取部分字段...
✅ 提取字段 spaceType: 客厅
✅ 提取字段 spacePositioning: 该空间定位为高端住宅的核心社交区域...
✅ 成功提取部分字段: spaceType, spacePositioning
→ 显示: 空间类型:客厅
        一、空间定位与场景属性
        该空间定位为...

(继续...直到完成)

📥 AI最终返回结果: {spaceType: "客厅", spacePositioning: "...", ...}
✅ JSON解析成功,完整对象
📊 解析后的分析数据: {formattedContent: "一、空间定位..."}
📤 发送最终格式化内容到UI...
→ 显示: 完整的格式化文本(8个维度)

异常情况1:JSON解析成功但格式化失败

✅ JSON解析成功
🎨 开始格式化JSON对象...
⚠️ formatJSONToText结果过短,使用fallback...
✅ [fallbackFormatJSON] 后备格式化完成,长度: 523
→ 显示格式化后的中文文本

异常情况2:花括号匹配但JSON有语法错误

⚠️ JSON解析失败,可能是流式传输中数据不完整
📊 JSON不完整: {5 vs }5  (匹配)
❌ JSON解析失败: SyntaxError: Unexpected token
→ 显示: ⚠️ AI分析结果格式异常,正在重新生成...

2. 检查对话框显示

修复前

对话框内容:
{
  "spaceType": "入户玄关",
  "spacePositioning": "该空间作为住宅的..."
}

修复后(流式传输中)

对话框内容:
🔄 正在生成分析结果...

修复后(完成)

对话框内容:
一、空间定位与场景属性

该空间作为住宅的入户过渡区域,兼具交通枢纽与形象展示功能...

二、空间布局与动线

空间采用单边动线布局,电梯井道嵌入式设置于右侧墙体结构中...

3. 验证JSON不完整检测

手动测试

// 在浏览器控制台测试
const json1 = '{"spaceType":"客厅","layout":"空间采用';
const json2 = '{"spaceType":"客厅","layout":"空间采用"}';

const check = (str) => {
  const open = (str.match(/\{/g) || []).length;
  const close = (str.match(/\}/g) || []).length;
  console.log(`{${open} vs }${close}`, open > close ? '不完整' : '完整');
};

check(json1); // {2 vs }0 不完整 ✅
check(json2); // {2 vs }2 完整 ✅

📊 修复效果对比

流式传输过程(实时显示)

修复前

对话框显示:
{"spaceType":"客厅","spacePositioning":"该空间定位为高端住宅...","layout":"空间采用

修复后

对话框显示(实时更新):

空间类型:客厅

一、空间定位与场景属性

该空间定位为高端住宅的核心社交区域...

二、空间布局与动线

空间采用围合式布局...(继续生成中)

对比表格

场景 修复前 修复后
流式传输开始 {"spaceType":"客厅"... 空间类型:客厅
第1个字段完成 {"spaceType":"客厅","spacePositioning":"... 一、空间定位与场景属性
该空间定位为...
第2个字段完成 继续显示JSON 二、空间布局与动线
空间采用...
流式传输中 显示原始JSON片段 实时显示已生成的中文文本
完成时 可能显示JSON或格式化文本 显示完整的格式化中文文本 ✅
JSON解析失败 显示原始JSON 提取部分字段或显示提示

🔧 故障排除

问题1:仍然显示JSON

可能原因

  • 代码未重新编译部署
  • 浏览器缓存未清除

解决方案

# 1. 重新编译
npm run build:prod

# 2. 部署
.\deploy.ps1

# 3. 清除浏览器缓存
Ctrl + Shift + Delete

问题2:显示"正在生成"但一直不更新

可能原因

  • AI分析失败,没有调用最终的onContentStream
  • analysisData.formattedContent为空

排查步骤

  1. 查看控制台是否有"📤 发送最终格式化内容"日志
  2. 检查是否有错误日志
  3. 验证parseJSONAnalysis返回的内容

问题3:格式化内容为空

可能原因

  • AI返回的JSON缺少必要字段
  • formatJSONToTextfallbackFormatJSONbeautifyJSON都失败

排查步骤

  1. 查看控制台日志中的"AI返回JSON预览"
  2. 检查是否有字段缺失
  3. 验证getChineseTitleForKey映射是否正确

📝 测试清单

基本功能测试

  • 上传图片,开始AI分析
  • 检查控制台日志中有"🔍 检测到JSON格式字符串"
  • 验证对话框不显示原始JSON
  • 验证对话框实时显示中文分析文字(而不是等待提示)

实时流式显示测试

  • 观察对话框内容逐步增加
  • 第1个字段生成时,显示"空间类型:XXX"
  • 第2个字段生成时,显示"一、空间定位与场景属性\n\n..."
  • 后续字段逐步追加显示
  • 检查控制台日志中有"✅ 提取字段 spaceType"等日志
  • 检查控制台日志中有"✅ 成功提取部分字段: spaceType, spacePositioning..."

完成状态测试

  • 等待分析完成
  • 检查控制台日志中有"✅ JSON解析成功,完整对象"
  • 检查控制台日志中有"📤 发送最终格式化内容"
  • 验证对话框显示完整的格式化中文文本(8个维度)
  • 验证文本带标题:一、二、三...八

异常处理测试

  • 如果某个字段特别长,验证是否正常显示
  • 如果AI返回速度很快,验证是否正常显示
  • 验证没有显示"🔄 正在生成..."(除非完全无法提取字段)

🔗 相关文件

  1. design-analysis-ai.service.ts (第100-131行)

    • 流式回调JSON检测和处理
    • 调用extractPartialJSON提取部分字段
    • 实时格式化显示
  2. design-analysis-ai.service.ts (第488-525行) 🆕

    • extractPartialJSON - 从不完整JSON中提取字段
    • 使用正则表达式匹配已完成的字段
    • 支持流式传输时实时提取
  3. design-analysis-ai.service.ts (第430-482行)

    • formatJSONToText - 格式化为中文文本(带标题)
    • fallbackFormatJSON - 后备格式化方案
    • beautifyJSON - 美化JSON显示
  4. design-analysis-ai.service.ts (第290-330行)

    • parseJSONAnalysis - 解析JSON为结构化数据
    • 最终格式化内容发送(第204-208行)
  5. stage-requirements.component.ts (第3445-3454行)

    • onContentStream 回调处理
    • 更新对话框内容

📋 修复总结

核心改进

  1. 实时流式显示:不等待完整JSON,逐步提取并显示已生成的字段
  2. 智能字段提取:使用正则表达式从不完整JSON中提取完整字段值
  3. 用户体验优化:用户看到的是逐步生成的中文分析文字,而不是JSON或等待提示

技术亮点

  • 正则表达式精准匹配JSON字段
  • 支持提取不完整字段(当前正在生成的字段)
  • 三层格式化保障(formatJSONToText → fallbackFormatJSON → beautifyJSON)
  • 详细的控制台日志便于调试

创建时间: 2024-12-01 16:40 最后更新: 2024-12-01 16:50 修复状态: ✅ 实时流式显示已实现 测试状态: ⏳ 待验证