5 Commits 5f02321e15 ... 76f7cddeaa

Author SHA1 Message Date
  xgo 76f7cddeaa feat(sgsc-文档审查-xth): 目录缺失检查整合到完整性审查 1 week ago
  LingMin f4a9de29b3 Merge branch 'dev_sgsc_xth' of CRBC-MaaS-Platform-Project/LQAgentPlatform into dev 1 week ago
  LingMin b32a0e0679 Merge branch 'dev_sgsc_xth' of CRBC-MaaS-Platform-Project/LQAgentPlatform into dev 1 week ago
  LingMin 8e08cab149 Merge branch 'dev_lingmin' of CRBC-MaaS-Platform-Project/LQAgentPlatform into dev 1 week ago
  lingmin_package@163.com c4bac30d23 test(model):增加打印信息 1 week ago

+ 3 - 2
config/config.ini

@@ -70,6 +70,7 @@ ENGINE=glm-ocr
 # GLM-OCR 配置
 GLM_OCR_API_URL=http://183.220.37.46:25429/v1/chat/completions
 GLM_OCR_TIMEOUT=600
+GLM_OCR_API_KEY=2026_Unified_Secure_Key
 
 # MinerU 配置  
 MINERU_API_URL=http://183.220.37.46:25428/file_parse
@@ -147,8 +148,8 @@ LQ_QWEN3_8B_LQ_LORA_API_KEY=dummy
 MYSQL_HOST=192.168.92.61
 MYSQL_PORT=13306
 MYSQL_USER=root
-MYSQL_PASSWORD=lq@123
-MYSQL_DB=lq_db
+MYSQL_PASSWORD=Lq123456!
+MYSQL_DB=lq_db_dev
 MYSQL_MIN_SIZE=1
 MYSQL_MAX_SIZE=5
 MYSQL_AUTO_COMMIT=True

+ 139 - 0
core/construction_review/component/ai_review_engine.py

@@ -728,6 +728,20 @@ class AIReviewEngine(BaseReviewer):
             # 转换为字典
             result_dict = result_to_dict_lightweight(result)
 
+            # ========== 新增:目录一二级缺失检查 ==========
+            # 只在全文档检查或 catalogue 章节时执行,避免重复
+            outline_issues = []
+            if chapter_code in ["all", "catalogue"]:
+                try:
+                    outline_result = await self._check_outline_for_completeness(
+                        state=state,
+                        trace_id_idx=trace_id_idx
+                    )
+                    outline_issues = outline_result.get("issues", [])
+                except Exception as e:
+                    logger.warning(f"目录缺失检查失败: {str(e)}")
+            # =============================================
+
             # 使用 recommendations 构建 response_items
             # recommendations 已按 一级→二级→三级 层级判断,用 continue 阻断级联:
             #   一级缺失 → 只报一级,不再向下检查
@@ -768,6 +782,10 @@ class AIReviewEngine(BaseReviewer):
                     "risk_info": {"risk_level": risk_level_en}
                 })
             
+            # 合并目录缺失检查的问题(目录缺失作为完整性的一部分)
+            if outline_issues:
+                response_items.extend(outline_issues)
+            
             # 如果没有缺失项,显示完整度
             if not response_items:
                 completeness_rate = result_dict.get('tertiary_completeness', {}).get('completeness_rate', '0%')
@@ -854,6 +872,127 @@ class AIReviewEngine(BaseReviewer):
             }
             return error_result, trace_id_idx
 
+    async def _check_outline_for_completeness(
+        self,
+        state: Dict[str, Any],
+        trace_id_idx: str
+    ) -> Dict[str, Any]:
+        """
+        为完整性检查提供目录一二级缺失结果
+        复用 check_outline_catalogue 方法,返回标准 issue 格式列表
+        
+        Args:
+            state: AI审查状态,包含 structured_content
+            trace_id_idx: 追踪ID索引
+            
+        Returns:
+            Dict: {"issues": [...]} 标准格式的 issue 列表
+        """
+        try:
+            # 调用现有目录缺失检查方法
+            outline_data = state.get("structured_content", {})
+            result = await self.check_outline_catalogue(
+                trace_id_idx=trace_id_idx,
+                outline_data=outline_data,
+                state=state,
+                stage_name="completeness_check"
+            )
+            
+            if not result.get("success"):
+                return {"issues": []}
+            
+            # 转换为 completeness_check 格式的 issues
+            issues = []
+            details = result.get("details", {})
+            missing_first = details.get("missing_first", [])
+            missing_second = details.get("missing_second", [])
+            missing_first_count = len(missing_first)
+            missing_second_count = len(missing_second)
+            
+            # 如果有缺失目录,先添加汇总统计 issue(放在最前面)
+            if missing_first or missing_second:
+                total_missing = missing_first_count + missing_second_count
+                
+                # 构建缺失目录名称列表
+                missing_first_names = [item.get('first_name', '未知') for item in missing_first if isinstance(item, dict)]
+                missing_second_names = [f"{item.get('first_name', '')}.{item.get('secondary_name', '未知')}" for item in missing_second if isinstance(item, dict)]
+                
+                # 构建建议文本
+                suggestion_parts = []
+                if missing_first_names:
+                    suggestion_parts.append(f"一级目录({missing_first_count}个):{', '.join(missing_first_names)}")
+                if missing_second_names:
+                    suggestion_parts.append(f"二级目录({missing_second_count}个):{', '.join(missing_second_names[:10])}")  # 最多显示10个
+                    if len(missing_second_names) > 10:
+                        suggestion_parts.append(f"... 及其他 {len(missing_second_names) - 10} 个二级目录")
+                
+                suggestion_text = "建议补充以下缺失目录:\n" + "\n".join(suggestion_parts)
+                
+                # 添加汇总统计 issue
+                issues.append({
+                    "check_item": "completeness_check",
+                    "chapter_code": "catalogue",  # 汇总 issue 归属到 catalogue 章节
+                    "check_item_code": "catalogue_completeness_check",
+                    "check_result": {
+                        "issue_point": f"【目录缺失汇总】共缺失 {total_missing} 个目录(一级{missing_first_count}个,二级{missing_second_count}个)",
+                        "location": "整篇文档",
+                        "suggestion": suggestion_text,
+                        "reason": f"根据标准分类表对比,文档目录结构不完整,共发现 {total_missing} 个缺失目录",
+                        "risk_level": "中风险",
+                        "summary": {
+                            "missing_first_count": missing_first_count,
+                            "missing_second_count": missing_second_count,
+                            "missing_total": total_missing,
+                            "missing_first_names": missing_first_names,
+                            "missing_second_names": missing_second_names
+                        }
+                    },
+                    "exist_issue": True,
+                    "risk_info": {"risk_level": "medium"}
+                })
+            
+            # 缺失一级目录 → 每个生成 issue
+            for item in missing_first:
+                first_code = item.get("first_code", "unknown")
+                issues.append({
+                    "check_item": "completeness_check",
+                    "chapter_code": first_code,
+                    "check_item_code": f"{first_code}_completeness_check",
+                    "check_result": {
+                        "issue_point": f"【目录缺失】缺失一级目录:{item.get('first_name', '')}",
+                        "location": f"{item.get('first_seq', '')}. {item.get('first_name', '')}",
+                        "suggestion": f"补充一级目录:{item.get('first_name', '')}",
+                        "reason": "根据标准分类表,该一级目录应当出现",
+                        "risk_level": "高风险"
+                    },
+                    "exist_issue": True,
+                    "risk_info": {"risk_level": "high"}
+                })
+            
+            # 缺失二级目录 → 归属到对应一级章节
+            for item in missing_second:
+                first_code = item.get("first_code", "unknown")
+                issues.append({
+                    "check_item": "completeness_check",
+                    "chapter_code": first_code,
+                    "check_item_code": f"{first_code}_completeness_check",
+                    "check_result": {
+                        "issue_point": f"【目录缺失】缺失二级目录:{item.get('secondary_name', '')}",
+                        "location": f"{item.get('first_seq', '')}.{item.get('second_seq', '')} {item.get('first_name', '')} > {item.get('secondary_name', '')}",
+                        "suggestion": f"补充二级目录:{item.get('secondary_name', '')}",
+                        "reason": f"在'{item.get('first_name', '')}'章节下,该二级目录应当出现",
+                        "risk_level": "中风险"
+                    },
+                    "exist_issue": True,
+                    "risk_info": {"risk_level": "medium"}
+                })
+            
+            return {"issues": issues}
+            
+        except Exception as e:
+            logger.warning(f"目录缺失检查异常: {str(e)}")
+            return {"issues": []}
+
     async def check_outline_catalogue(
         self,
         trace_id_idx: str,

+ 0 - 1
core/construction_review/component/report_generator.py

@@ -375,7 +375,6 @@ class ReportGenerator:
             'design_values_check': '设计值审查',
             'rag_enhanced_review': 'RAG 增强审查',
             'professional_suggestion': '专业建议',
-            'outline_catalogue_check': '目录缺失审查',  # 目录一二级缺失检查
             'unknown': '其他审查'
         }
 

+ 0 - 104
core/construction_review/component/reviewers/utils/inter_tool.py

@@ -344,110 +344,6 @@ class InterTool:
                 logger.info(f"🔍 内容时效性审查结果处理完成,添加 {len(batch_results)} 个问题项")
                 continue
 
-            # 🔧 特殊处理:outline_catalogue_check 的返回格式(目录一二级缺失统计)
-            if check_key == 'outline_catalogue_check' and isinstance(check_result, dict):
-                details = check_result.get('details', {})
-                missing_first = details.get('missing_first', [])
-                missing_second = details.get('missing_second', [])
-                missing_first_count = details.get('missing_first_count', 0)
-                missing_second_count = details.get('missing_second_count', 0)
-                
-                logger.debug(f"🔍 [DEBUG] 处理目录缺失统计结果,缺失一级: {missing_first_count}, 缺失二级: {missing_second_count}")
-                
-                # 如果没有缺失目录,添加一个通过记录
-                if not missing_first and not missing_second:
-                    review_lists.append({
-                        "check_item": "outline_catalogue_check",
-                        "chapter_code": chapter_code,
-                        "check_item_code": f"{chapter_code}_outline_catalogue_check",
-                        "check_result": {
-                            "issue_point": "目录结构完整",
-                            "location": "整篇文档",
-                            "suggestion": "无",
-                            "reason": "一二级目录结构符合标准规范要求",
-                            "risk_level": "low"
-                        },
-                        "exist_issue": False,
-                        "risk_info": {"risk_level": "low"}
-                    })
-                else:
-                    # 有缺失目录时,先生成汇总统计
-                    total_missing = missing_first_count + missing_second_count
-                    
-                    # 构建缺失目录名称列表
-                    missing_first_names = [item.get('first_name', '未知') for item in missing_first if isinstance(item, dict)]
-                    missing_second_names = [f"{item.get('first_name', '')}.{item.get('secondary_name', '未知')}" for item in missing_second if isinstance(item, dict)]
-                    
-                    # 构建建议文本
-                    suggestion_parts = []
-                    if missing_first_names:
-                        suggestion_parts.append(f"一级目录({missing_first_count}个):{', '.join(missing_first_names)}")
-                    if missing_second_names:
-                        suggestion_parts.append(f"二级目录({missing_second_count}个):{', '.join(missing_second_names)}")
-                    suggestion_text = "建议补充以下缺失目录:\n" + "\n".join(suggestion_parts) if suggestion_parts else "无"
-                    
-                    # 添加汇总统计问题(放在最前面)
-                    review_lists.append({
-                        "check_item": "outline_catalogue_check",
-                        "chapter_code": chapter_code,
-                        "check_item_code": f"{chapter_code}_outline_catalogue_check",
-                        "check_result": {
-                            "issue_point": f"目录缺失汇总统计(共缺失 {total_missing} 个目录)",
-                            "location": "整篇文档",
-                            "suggestion": suggestion_text,
-                            "reason": f"根据标准分类表对比,共发现 {total_missing} 个缺失目录:缺失一级 {missing_first_count} 个,缺失二级 {missing_second_count} 个",
-                            "risk_level": "medium",
-                            "summary": {
-                                "missing_first_count": missing_first_count,
-                                "missing_second_count": missing_second_count,
-                                "missing_total": total_missing,
-                                "missing_first_names": missing_first_names,
-                                "missing_second_names": missing_second_names
-                            }
-                        },
-                        "exist_issue": True,
-                        "risk_info": {"risk_level": "medium"}
-                    })
-                    
-                    # 然后逐个添加详细的缺失目录问题
-                    for item in missing_first:
-                        if isinstance(item, dict):
-                            review_lists.append({
-                                "check_item": "outline_catalogue_check",
-                                "chapter_code": chapter_code,
-                                "check_item_code": f"{chapter_code}_outline_catalogue_check",
-                                "check_result": {
-                                    "issue_point": f"缺失一级目录:{item.get('first_name', '未知')}",
-                                    "location": f"{item.get('first_seq', '')}. {item.get('first_name', '')}",
-                                    "suggestion": f"补充一级目录:{item.get('first_name', '')}(编码:{item.get('first_code', '')})",
-                                    "reason": "根据标准分类表,该一级目录应当出现但未找到",
-                                    "risk_level": "medium"
-                                },
-                                "exist_issue": True,
-                                "risk_info": {"risk_level": "medium"}
-                            })
-                    
-                    for item in missing_second:
-                        if isinstance(item, dict):
-                            review_lists.append({
-                                "check_item": "outline_catalogue_check",
-                                "chapter_code": chapter_code,
-                                "check_item_code": f"{chapter_code}_outline_catalogue_check",
-                                "check_result": {
-                                    "issue_point": f"缺失二级目录:{item.get('secondary_name', '未知')}",
-                                    "location": f"{item.get('first_seq', '')}.{item.get('second_seq', '')} {item.get('first_name', '')} > {item.get('secondary_name', '')}",
-                                    "suggestion": f"补充二级目录:{item.get('secondary_name', '')}(编码:{item.get('secondary_code', '')})",
-                                    "reason": f"在'{item.get('first_name', '未知')}'章节下,该二级目录应当出现但未找到",
-                                    "risk_level": "medium"
-                                },
-                                "exist_issue": True,
-                                "risk_info": {"risk_level": "medium"}
-                            })
-                
-                total_missing = missing_first_count + missing_second_count
-                logger.info(f"🔍 目录缺失统计处理完成,缺失一级: {missing_first_count}, 缺失二级: {missing_second_count}, 共生成 {len(review_lists)} 个问题项(含汇总)")
-                continue
-
             # 🔧 类型安全检查:支持字典和 base_reviewer.ReviewResult 对象
             is_dict = isinstance(check_result, dict)
             is_review_result = hasattr(check_result, 'details') and hasattr(check_result, 'success')

+ 3 - 11
core/construction_review/workflows/ai_review_workflow.py

@@ -298,8 +298,7 @@ class AIReviewWorkflow:
                 'reference_check': 'reference_basis_reviewer',
                 'sensitive_check': 'check_sensitive',
                 'non_parameter_compliance_check': 'check_non_parameter_compliance',
-                'parameter_compliance_check': 'check_parameter_compliance',
-                'outline_catalogue_check': 'check_outline_catalogue'  # 目录一二级缺失检查(模糊匹配)
+                'parameter_compliance_check': 'check_parameter_compliance'
             }
 
             # 获取审查项配置
@@ -458,18 +457,11 @@ class AIReviewWorkflow:
             # 主流程完成后,串行处理 catalogue(目录审查)
             # 注意:catalogue 是系统强制添加的审查单元,已计入 total_chunks
             logger.info("开始处理目录审查(catalogue)")
-            # 从配置中获取 catalogue 章节的方法列表,默认包含 check_completeness 和 outline_catalogue_check
-            catalogue_funcs = review_item_dict_sorted.get("catalogue", ["check_completeness", "outline_catalogue_check"])
-            # 确保 check_completeness 在列表中(向后兼容)
-            if "check_completeness" not in catalogue_funcs:
-                catalogue_funcs = ["check_completeness"] + catalogue_funcs
-            # 确保 outline_catalogue_check 在列表中(新增目录缺失统计)
-            if "outline_catalogue_check" not in catalogue_funcs:
-                catalogue_funcs.append("outline_catalogue_check")
+            # 目录审查只执行完整性检查(目录缺失统计已整合到 check_completeness 中)
             chunks_completed, all_issues = await self.core_fun._process_chapter_item(
                 "catalogue",                 # chapter_code
                 catalogue,                   # chapter_content
-                catalogue_funcs,             # func_names(从配置获取
+                ["check_completeness"],      # 只执行完整性检查(包含目录缺失统计)
                 state,
                 all_issues,
                 completed_chunks,

+ 17 - 88
core/construction_review/workflows/core_functions/ai_review_core_fun.py

@@ -354,18 +354,7 @@ class AIReviewCoreFun:
         """
       
         # 从ai_review_engine获取对应的方法
-        # 方法名映射:配置名 -> 实际方法名
-        # 用于处理配置标识名与AIReviewEngine方法名不一致的情况
-        method_name_mapping = {
-            'outline_catalogue_check': 'check_outline_catalogue',
-        }
-
-        # 转换方法名(如果存在映射)
-        actual_method_name = method_name_mapping.get(func_name, func_name)
-        original_func_name = func_name
-
-        # 从ai_review_engine获取对应的方法
-        if not hasattr(self.ai_review_engine, actual_method_name):
+        if not hasattr(self.ai_review_engine, func_name):
             logger.warning(f"AIReviewEngine中未找到方法: {func_name}")
             # 返回错误结果的 UnitReviewResult
             return UnitReviewResult(
@@ -378,16 +367,16 @@ class AIReviewCoreFun:
                 is_sse_push=True
             )
 
-        method = getattr(self.ai_review_engine, actual_method_name)
+        method = getattr(self.ai_review_engine, func_name)
 
         # 基础参数
         trace_id = f"{state['callback_task_id']}_{chapter_code}_chunk{chunk_index}"
-        stage_name = f"{chapter_code}_{original_func_name}"
+        stage_name = f"{chapter_code}_{func_name}"
 
         # 获取块内容
         review_content = chunk.get("content", "")
         is_complete_field = chunk.get("is_complete_field", False)
-        logger.debug(f"执行审查: {trace_id} -> {original_func_name}")
+        logger.debug(f"执行审查: {trace_id} -> {func_name}")
 
         # 根据func_name构建对应的参数并调用
         if func_name == "sensitive_word_check" and not is_complete_field:
@@ -433,48 +422,20 @@ class AIReviewCoreFun:
             # check_completeness 需要列表类型,将单个 chunk 包装成列表
             completeness_result, trace_id_idx = await method(trace_id, [chunk], state, stage_name)
 
-            # outline_check 方法已删除,跳过大纲审查
-            logger.info(f"[check_completeness完成] outline_check 已删除,跳过大纲审查 (trace_id_idx: {trace_id_idx})")
+            logger.info(f"[check_completeness完成] 包含目录缺失检查 (trace_id_idx: {trace_id_idx})")
 
-            if chapter_code == "catalogue":
-                # catalogues_check 已删除,目录检查逻辑已移除
-                logger.info("[目录检查] catalogues_check 模块已删除,跳过目录检查")
-                return UnitReviewResult(
-                    unit_index=chunk_index,
-                    unit_content=chunk,
-                    basic_compliance={
-                        "catalogue_check": {
-                            "success": True,
-                            "details": {
-                                "name": "catalogue_check",
-                                "response": [],
-                                "review_location_label": "",
-                                "chapter_code": "catalogue",
-                                "original_content": ""
-                            },
-                            "summary": {},
-                            "execution_time": 0.0
-                        }
-                    },
-                    technical_compliance={},
-                    rag_enhanced={},
-                    overall_risk="low",
-                    is_sse_push=True
-                )
-            else:
-                # 将两个结果都放入 basic_compliance
-                return UnitReviewResult(
-                    unit_index=chunk_index,
-                    unit_content=chunk,
-                    basic_compliance={
-                        "check_completeness": completeness_result,
-                        # "outline_check": outline_result
-                    },
-                    technical_compliance={},
-                    rag_enhanced={},
-                    overall_risk=self._calculate_single_result_risk(completeness_result),
-                    is_sse_push=True
-                )
+            # 返回完整性检查结果(目录缺失检查已整合到 check_completeness 中)
+            return UnitReviewResult(
+                unit_index=chunk_index,
+                unit_content=chunk,
+                basic_compliance={
+                    "check_completeness": completeness_result
+                },
+                technical_compliance={},
+                rag_enhanced={},
+                overall_risk=self._calculate_single_result_risk(completeness_result),
+                is_sse_push=True
+            )
         elif func_name == "check_non_parameter_compliance" and not is_complete_field:
             # 技术审查方法需要从 RAG 检索结果中获取 references
             raw_result = await self._execute_technical_review(
@@ -582,38 +543,6 @@ class AIReviewCoreFun:
                 is_sse_push=True
             )
 
-        elif original_func_name == "outline_catalogue_check":
-            # 目录一二级缺失检查(模糊匹配)- 针对整个文档的 outline 进行检查
-            outline_data = state.get("structured_content", {})
-            outline_result = await self.ai_review_engine.check_outline_catalogue(
-                trace_id_idx=trace_id,
-                outline_data=outline_data,
-                state=state,
-                stage_name=stage_name
-            )
-            
-            # 计算风险等级:如果有缺失目录则标记为 medium
-            overall_risk = "low"
-            if outline_result.get("success"):
-                missing_count = (
-                    outline_result.get("details", {}).get("missing_first_count", 0) +
-                    outline_result.get("details", {}).get("missing_second_count", 0)
-                )
-                if missing_count > 0:
-                    overall_risk = "medium"
-            else:
-                overall_risk = "error"
-            
-            return UnitReviewResult(
-                unit_index=chunk_index,
-                unit_content=chunk,
-                basic_compliance={"outline_catalogue_check": outline_result},
-                technical_compliance={},
-                rag_enhanced={},
-                overall_risk=overall_risk,
-                is_sse_push=True
-            )
-
         else:
             # 处理 check_completeness 但 is_complete_field=False 的情况
             if func_name == "check_completeness" and not is_complete_field:

+ 9 - 8
foundation/ai/agent/generate/model_generate.py

@@ -34,7 +34,7 @@ class GenerateModelClient:
         # 保存model_handler引用,用于动态获取模型
         self.model_handler = model_handler
 
-    async def _retry_with_backoff(self, func: Callable, *args, timeout: Optional[int] = None, **kwargs):
+    async def _retry_with_backoff(self, func: Callable, *args, timeout: Optional[int] = None, trace_id: Optional[str] = None, model_name: Optional[str] = None, **kwargs):
         """
         带指数退避的重试机制,每次重试都有独立的超时控制
 
@@ -42,6 +42,7 @@ class GenerateModelClient:
         避免在服务端过载时继续加重负载。
         """
         current_timeout = timeout or self.default_timeout
+        model_info = model_name or "default"
 
         def _is_server_unavailable_error(error: Exception) -> bool:
             """判断是否为服务端不可用错误(应立即失败)"""
@@ -57,28 +58,28 @@ class GenerateModelClient:
                     func(*args, **kwargs),
                     timeout=current_timeout
                 )
-            except asyncio.TimeoutError:
+            except asyncio.TimeoutError as e:
                 if attempt == self.max_retries:
-                    logger.error(f"[模型调用] 达到最大重试次数 {self.max_retries},最终超时")
+                    logger.error(f"[模型调用] 达到最大重试次数 {self.max_retries},最终超时 | trace_id: {trace_id}, model: {model_info}, timeout: {current_timeout}s, error_type: {type(e).__name__}, error_msg: {str(e)}")
                     raise TimeoutError(f"模型调用在 {self.max_retries} 次重试后均超时")
 
                 wait_time = self.backoff_factor * (2 ** attempt)
-                logger.warning(f"[模型调用] 第 {attempt + 1} 次超时, {wait_time}秒后重试...")
+                logger.warning(f"[模型调用] 第 {attempt + 1} 次超时, {wait_time}秒后重试... | trace_id: {trace_id}, model: {model_info}, timeout: {current_timeout}s, error_type: {type(e).__name__}, error_msg: {str(e)}")
                 await asyncio.sleep(wait_time)
             except Exception as e:
                 error_str = str(e)
 
                 # 服务端不可用错误(502/503/504)立即失败,不重试
                 if _is_server_unavailable_error(e):
-                    logger.error(f"[模型调用] 服务端不可用,立即失败: {error_str}")
+                    logger.error(f"[模型调用] 服务端不可用,立即失败: {error_str} | trace_id: {trace_id}, model: {model_info}")
                     raise
 
                 if attempt == self.max_retries:
-                    logger.error(f"[模型调用] 达到最大重试次数 {self.max_retries},最终失败: {error_str}")
+                    logger.error(f"[模型调用] 达到最大重试次数 {self.max_retries},最终失败: {error_str} | trace_id: {trace_id}, model: {model_info}")
                     raise
 
                 wait_time = self.backoff_factor * (2 ** attempt)
-                logger.warning(f"[模型调用] 第 {attempt + 1} 次尝试失败: {error_str}, {wait_time}秒后重试...")
+                logger.warning(f"[模型调用] 第 {attempt + 1} 次尝试失败: {error_str}, {wait_time}秒后重试... | trace_id: {trace_id}, model: {model_info}")
                 await asyncio.sleep(wait_time)
 
     async def get_model_generate_invoke(
@@ -159,7 +160,7 @@ class GenerateModelClient:
                 return await llm_to_use.ainvoke(final_messages)
 
             # 调用带重试机制
-            response = await self._retry_with_backoff(_invoke, timeout=current_timeout)
+            response = await self._retry_with_backoff(_invoke, timeout=current_timeout, trace_id=trace_id, model_name=model_name or "default")
 
             elapsed_time = time.time() - start_time
             logger.info(f"[模型调用] 成功 trace_id: {trace_id}, 耗时: {elapsed_time:.2f}s")

+ 3 - 4
views/construction_review/launch_review.py

@@ -167,8 +167,7 @@ def validate_review_item_config(review_item_config: List[str]) -> None:
         "review_dimensions": {
             "sensitive_word_check", "semantic_logic_check", "completeness_check",
             "timeliness_check", "reference_check", "sensitive_check",
-            "non_parameter_compliance_check", "parameter_compliance_check",
-            "outline_catalogue_check"  # 目录一二级缺失检查
+            "non_parameter_compliance_check", "parameter_compliance_check"
         }
     }
 
@@ -191,8 +190,8 @@ def validate_review_item_config(review_item_config: List[str]) -> None:
             invalid_chapter.append(chapter_code)
             continue  # 章节不支持时不继续检查审查项
 
-        # 5. 特殊规则:目录章节只能使用完整性审查或目录缺失统计
-        if chapter_code == "catalogue" and review_dim not in ["completeness_check", "outline_catalogue_check"]:
+        # 5. 特殊规则:目录章节只能使用完整性审查
+        if chapter_code == "catalogue" and review_dim != "completeness_check":
             catalogue_invalid.append(item)
             continue  # 目录章节违反规则时不继续检查