diff --git a/backend/app/services/report_agent.py b/backend/app/services/report_agent.py index bac88793..886c1f2f 100644 --- a/backend/app/services/report_agent.py +++ b/backend/app/services/report_agent.py @@ -1392,6 +1392,43 @@ class ReportAgent: logger.info(t('report.outlinePlanDone', count=len(sections))) return outline + except ValueError as e: + # JSON 解析失败,尝试用更宽松的方式提取 JSON + logger.warning(t('report.outlinePlanFailed', error=str(e))) + try: + # 尝试从错误消息中提取 JSON + error_msg = str(e) + json_start = error_msg.find('{') + json_end = error_msg.rfind('}') + 1 + if json_start >= 0 and json_end > json_start: + json_str = error_msg[json_start:json_end] + response = json.loads(json_str) + sections = [] + for section_data in response.get("sections", []): + sections.append(ReportSection( + title=section_data.get("title", ""), + content="" + )) + outline = ReportOutline( + title=response.get("title", "模拟分析报告"), + summary=response.get("summary", ""), + sections=sections + ) + outline = self._ensure_prediction_outline(outline) + logger.info(t('report.outlinePlanDone', count=len(sections))) + return outline + except Exception: + pass + # Fallback 默认大纲 + return self._ensure_prediction_outline(ReportOutline( + title="未来预测报告", + summary="基于模拟预测的未来趋势与风险分析", + sections=[ + ReportSection(title="预测场景与核心发现"), + ReportSection(title="人群行为预测分析"), + ReportSection(title="趋势展望与风险提示") + ] + )) except Exception as e: logger.error(t('report.outlinePlanFailed', error=str(e))) # 返回默认大纲(3个章节,作为fallback) diff --git a/backend/app/utils/llm_client.py b/backend/app/utils/llm_client.py index 4ae8166e..94b9232c 100644 --- a/backend/app/utils/llm_client.py +++ b/backend/app/utils/llm_client.py @@ -101,8 +101,51 @@ class LLMClient: cleaned_response = re.sub(r'\n?```\s*$', '', cleaned_response) cleaned_response = cleaned_response.strip() + # 尝试直接解析 try: return json.loads(cleaned_response) except json.JSONDecodeError: - raise ValueError(f"LLM返回的JSON格式无效: {cleaned_response}") + pass + + # 策略2: 尝试提取 JSON 对象(可能嵌套在其他文本中) + json_patterns = [ + r'\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}', # 简单嵌套 + r'(\{"[^"]*"\s*:\s*)', # JSON 开始位置 + ] + for pattern in json_patterns: + match = re.search(pattern, cleaned_response, re.DOTALL) + if match: + try: + # 尝试从匹配位置开始解析 + candidate = match.group(0) if match.group(0).startswith('{') else match.group(1) + # 向后扩展直到找到完整对象 + for end in range(len(candidate), len(cleaned_response) + 1): + try: + result = json.loads(cleaned_response[match.start():end]) + return result + except json.JSONDecodeError: + continue + except Exception: + pass + + # 策略3: 尝试修复常见 JSON 问题 + fixed = cleaned_response + # 移除控制字符 + fixed = re.sub(r'[\x00-\x1f\x7f-\x9f]', '', fixed) + # 修复单引号 + fixed = re.sub(r"'", '"', fixed) + try: + return json.loads(fixed) + except json.JSONDecodeError: + pass + + # 策略4: 查找 ```json ... ``` 块 + code_block_match = re.search(r'```json\s*\n?(.*?)\n?```', cleaned_response, re.DOTALL | re.IGNORECASE) + if code_block_match: + try: + return json.loads(code_block_match.group(1).strip()) + except json.JSONDecodeError: + pass + + raise ValueError(f"LLM返回的JSON格式无效: {cleaned_response[:500]}")