|
|
@@ -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,
|