ソースを参照

v0.0.4-增加条文完整性审查
- 大纲完整性校验

WangXuMing 2 ヶ月 前
コミット
edf577f622

+ 6 - 8
core/construction_review/component/ai_review_engine.py

@@ -145,7 +145,7 @@ class AIReviewEngine(BaseReviewer):
     
 
     async def outline_check(self, trace_id_idx: str, outline_content: Dict[str, Any],
-                                   state:str,stage_name:str) -> Dict[str, Any]:
+                                   state:dict,stage_name:str) -> Dict[str, Any]:
         """
         大纲审查
 
@@ -211,18 +211,16 @@ class AIReviewEngine(BaseReviewer):
 
         # 调用outline_reviewer进行审查
         try:
-            review_result = await self.outline_reviewer.outline_review(review_data, trace_id_idx, stage_name, state)
+            outline_review_result = await self.outline_reviewer.outline_review(review_data, trace_id_idx, state,stage_name)
         except Exception as e:
             logger.warning(f"大纲审查失败,但返回提取结果: {str(e)}")
-            review_result = None
-        logger.info(f"大纲审查结果: {review_result}")
+            outline_review_result = None
+        logger.info(f"大纲审查结果: {outline_review_result}")
         with open("temp\outline_result_temp\outline_result.json","w",encoding="utf-8") as f:
-            json.dump(review_result,f,ensure_ascii=False,indent=4)
+            json.dump(outline_review_result,f,ensure_ascii=False,indent=4)
         # 返回提取的大纲结果和审查结果
         return {
-            'overall_outline': overall_outline,
-            'detailed_outline': detailed_outline,
-            'review_result': review_result
+            'outline_review_result': outline_review_result
         }
 
     async def basic_compliance_check(self,trace_id_idx: str, unit_content: Dict[str, Any],

+ 180 - 100
core/construction_review/component/reviewers/outline_reviewer.py

@@ -42,7 +42,7 @@ class OutlineReviewer:
         # self.review_location_label = review_location_label
         self.reviewer_type = "outline"
 
-    async def outline_review(self, review_data: Dict[str, Any], trace_id: str, stage_name: str = None, state: dict = None) -> Dict[str, Any]:
+    async def outline_review(self, review_data: Dict[str, Any], trace_id: str,  state: dict = None,stage_name: str = None) -> Dict[str, Any]:
         """
         执行两阶段大纲审查:1.整体大纲完整性审查 2.详细大纲逐项审查
 
@@ -79,13 +79,13 @@ class OutlineReviewer:
             # 阶段1:整体大纲完整性审查(仅在有数据时执行)
             if overall_outline and overall_outline.strip():
                 logger.info("启动阶段1:整体大纲完整性审查...")
-                overall_task = self._overall_completeness_review(overall_outline, trace_id)
+                overall_task = self._overall_completeness_review(overall_outline, trace_id, state, stage_name)
                 tasks.append(("overall", overall_task))
 
             # 阶段2:详细大纲逐项审查
             if detailed_outline:
                 logger.info("启动阶段2:详细大纲逐项审查...")
-                detailed_task = self._detailed_item_review(detailed_outline, trace_id)
+                detailed_task = self._detailed_item_review(detailed_outline, trace_id, state, stage_name)
                 tasks.append(("detailed", detailed_task))
 
             # 处理空数据情况
@@ -138,9 +138,9 @@ class OutlineReviewer:
                     # 降级为串行处理
                     logger.warning("降级为串行执行两阶段审查")
                     if overall_outline and overall_outline.strip():
-                        overall_review_result = await self._overall_completeness_review(overall_outline, trace_id)
+                        overall_review_result = await self._overall_completeness_review(overall_outline, trace_id, state, stage_name)
                     if detailed_outline:
-                        detailed_review_results = await self._detailed_item_review(detailed_outline, trace_id)
+                        detailed_review_results = await self._detailed_item_review(detailed_outline, trace_id, state, stage_name)
 
             # 返回完整结果
             return {
@@ -164,13 +164,15 @@ class OutlineReviewer:
                 "stage2_detailed_review": []
             }
 
-    async def _overall_completeness_review(self, overall_outline: str, trace_id: str) -> Dict[str, Any]:
+    async def _overall_completeness_review(self, overall_outline: str, trace_id: str, state: dict = None, stage_name: str = None) -> Dict[str, Any]:
         """
         阶段1:整体大纲完整性审查 - 检查十大类章节是否有缺失
 
         Args:
             overall_outline: 整体大纲内容
             trace_id: 追踪ID
+            state: 状态字典
+            stage_name: 阶段名称
 
         Returns:
             整体大纲审查结果
@@ -210,9 +212,27 @@ class OutlineReviewer:
 
         response_text = model_response
         overall_completeness_result = self._extract_json_from_text(response_text)
+        filtered_issues = [r for r in overall_completeness_result if self._is_non_compliant_item(r)]
+        message=f"整体大纲完整性审查完成,发现 {len(filtered_issues)} 个问题",
         if not overall_completeness_result:
-            overall_completeness_result.append("整体大纲一级目录检查已通过,未发现缺失项")
-
+            message = "整体大纲一级目录检查已通过,未发现缺失项"
+ 
+        if state and state.get("progress_manager"):
+            # 使用try-catch确保SSE推送失败不会影响主流程
+            try:
+                await state["progress_manager"].update_stage_progress(
+                    callback_task_id=state["callback_task_id"],
+                    stage_name=f"{stage_name} - 阶段1:整体大纲审查",
+                    current=None,  # 明确不更新current,保持主流程进度
+                    status="completed",
+                    message=message,
+                    issues=filtered_issues,
+                    event_type="processing"  # 使用专门的事件类型
+                )
+                logger.info("SSE推送成功: 整体大纲完整性审查完成")
+            except Exception as e:
+                logger.error(f"SSE推送失败: 整体大纲完整性审查, 错误: {str(e)}")
+                # 不抛出异常,避免影响主流程
 
         return {
             "success": True,
@@ -220,7 +240,7 @@ class OutlineReviewer:
             "parsed_result": overall_completeness_result
         }
 
-    async def _detailed_item_review(self, detailed_outline: list, trace_id: str) -> list:
+    async def _detailed_item_review(self, detailed_outline: list, trace_id: str,state, stage_name) -> list:
         """
         阶段2:详细大纲并发审查 - 对detailed_outline中的所有项目进行并发审查
 
@@ -249,7 +269,7 @@ class OutlineReviewer:
         tasks = []
 
         for i, outline_item in valid_items:
-            task = self._concurrent_single_review(i, outline_item, trace_id, semaphore)
+            task = self._concurrent_single_review(i, outline_item, trace_id, semaphore, state, stage_name)
             tasks.append(task)
 
         # 等待所有任务完成
@@ -259,7 +279,7 @@ class OutlineReviewer:
         except Exception as e:
             logger.error(f"并发审查失败: {str(e)}")
             # 如果并发失败,降级为串行处理
-            return await self._fallback_sequential_review(valid_items, trace_id)
+            return await self._fallback_sequential_review(valid_items, trace_id,state, stage_name)
 
         # 处理结果
         detailed_review_results = []
@@ -288,7 +308,7 @@ class OutlineReviewer:
 
         return detailed_review_results
 
-    async def _concurrent_single_review(self, item_index: int, outline_item: str, trace_id: str, semaphore: asyncio.Semaphore) -> Dict[str, Any]:
+    async def _concurrent_single_review(self, item_index: int, outline_item: str, trace_id: str, semaphore: asyncio.Semaphore, state, stage_name) -> Dict[str, Any]:
         """
         单个项目的并发审查
 
@@ -297,6 +317,8 @@ class OutlineReviewer:
             outline_item: 大纲项目内容
             trace_id: 追踪ID
             semaphore: 并发控制信号量
+            state: 状态字典
+            stage_name: 阶段名称
 
         Returns:
             单项审查结果
@@ -304,7 +326,7 @@ class OutlineReviewer:
         async with semaphore:
             try:
                 logger.info(f"开始审查第{item_index+1}项: {outline_item[:50]}...")
-                result = await self._single_item_review(outline_item, trace_id, item_index)
+                result = await self._single_item_review(outline_item, trace_id, item_index, state, stage_name)
                 logger.info(f"完成审查第{item_index+1}项,成功: {result.get('success', False)}")
                 return result
             except Exception as e:
@@ -316,7 +338,7 @@ class OutlineReviewer:
                     "parsed_result": None
                 }
 
-    async def _fallback_sequential_review(self, valid_items: list, trace_id: str) -> list:
+    async def _fallback_sequential_review(self, valid_items: list, trace_id: str, state, stage_name) -> list:
         """
         降级串行审查(当并发失败时使用)
 
@@ -333,7 +355,7 @@ class OutlineReviewer:
         for i, outline_item in valid_items:
             try:
                 logger.info(f"串行审查第{i+1}项: {outline_item[:50]}...")
-                item_review_result = await self._single_item_review(outline_item, trace_id, i)
+                item_review_result = await self._single_item_review(outline_item, trace_id, i, state, stage_name)
 
                 detailed_review_results.append({
                     "item_index": i,
@@ -355,7 +377,7 @@ class OutlineReviewer:
 
         return detailed_review_results
 
-    async def _single_item_review(self, outline_item: str, trace_id: str, item_index: int) -> Dict[str, Any]:
+    async def _single_item_review(self, outline_item: str, trace_id: str, item_index: int, state: dict = None, stage_name: str = None) -> Dict[str, Any]:
         """
         单项大纲审查 - 调用原有逻辑
 
@@ -363,6 +385,8 @@ class OutlineReviewer:
             outline_item: 单个大纲项目
             trace_id: 追踪ID
             item_index: 项目索引
+            state: 状态字典
+            stage_name: 阶段名称
 
         Returns:
             单项审查结果
@@ -404,6 +428,34 @@ class OutlineReviewer:
         response_text = model_response
         parsed_result = self._extract_json_from_text(response_text)
 
+        # 发送单项审查完成进度
+        logger.info(f"state参数检查: state存在={state is not None}")
+        if state:
+            logger.info(f"state keys: {list(state.keys())}")
+            logger.info(f"progress_manager存在: {'progress_manager' in state}")
+        if state and state.get("progress_manager"):
+            # 过滤并计算问题数量(与过滤逻辑保持一致)
+            filtered_result = [r for r in (parsed_result or []) if self._is_non_compliant_item(r)] if parsed_result else []
+            message = f"第{item_index+1}项{category}审查完成,发现 {len(filtered_result)} 个问题",
+            if not filtered_result:
+                    message = f"第{item_index+1}项{category}审查完成,无问题"
+            # 使用try-catch确保SSE推送失败不会影响主流程
+            try:
+                await state["progress_manager"].update_stage_progress(
+                    callback_task_id=state["callback_task_id"],
+                    stage_name=f"{stage_name} - 阶段2:详细大纲审查",
+                    current=item_index + 1,  # 显示当前审查项目索引
+                    status="processing",
+                    message=message,
+                    issues=filtered_result,
+                    event_type="processing"  # 使用专门的事件类型
+                )
+                logger.info(f"SSE推送成功: 第{item_index+1}项{category}审查完成")
+                logger.info(f"发送单项审查完成进度: 第{item_index+1}项{category}审查完成")
+            except Exception as e:
+                logger.error(f"SSE推送失败: 第{item_index+1}项{category}, 错误: {str(e)}")
+                # 不抛出异常,避免影响主流程
+
         return {
             "success": True,
             "category": category,
@@ -520,7 +572,7 @@ class OutlineReviewer:
 
     def _extract_json_from_text(self, text: str):
         """
-        从文本中提取JSON内容
+        从文本中提取JSON内容,复用inter_tool中的格式化方法
 
         Args:
             text: 包含JSON的文本
@@ -531,87 +583,70 @@ class OutlineReviewer:
         if not text:
             return None
 
-        # 方法1: 尝试找到JSON代码块
-        json_block_pattern = r'```(?:json)?\s*(\[.*?\])\s*```'
-        match = re.search(json_block_pattern, text, re.DOTALL)
-        if match:
-            json_text = match.group(1)
-            try:
-                json_data = json.loads(json_text)
+        # 复用 inter_tool.py 中的 _extract_json_data 方法
+        try:
+            from core.construction_review.component.reviewers.utils.inter_tool import InterTool
+            inter_tool = InterTool()
+
+            json_data = inter_tool._extract_json_data(text)
+            if json_data:
+                # 确保返回的是列表格式,保持与原来方法的兼容性
                 if isinstance(json_data, list):
                     # 过滤掉"✔ 符合要求"的结果
                     filtered_data = self._filter_compliant_items(json_data)
                     return filtered_data
-            except json.JSONDecodeError:
-                pass
-
-        # 方法2: 尝试找到第一个 [ 到最后一个 ] 之间的内容
-        bracket_positions = []
-        for i, char in enumerate(text):
-            if char == '[':
-                bracket_positions.append(('start', i))
-            elif char == ']':
-                bracket_positions.append(('end', i))
-
-        if bracket_positions:
-            # 找到最后一个 ] 的位置
-            last_end = None
-            for pos_type, pos in reversed(bracket_positions):
-                if pos_type == 'end':
-                    last_end = pos
-                    break
-
-            if last_end:
-                # 从最后一个 ] 往前找匹配的 [
-                depth = 0
-                start_pos = None
-                for pos_type, pos in reversed(bracket_positions):
-                    if pos > last_end:
-                        continue
-                    if pos_type == 'end':
-                        depth += 1
-                    elif pos_type == 'start':
-                        depth -= 1
-                        if depth == 0:
-                            start_pos = pos
-                            break
-
-                if start_pos is not None:
-                    json_text = text[start_pos:last_end + 1]
-                    try:
-                        json_data = json.loads(json_text)
-                        if isinstance(json_data, list):
-                            # 过滤掉"✔ 符合要求"的结果
-                            filtered_data = self._filter_compliant_items(json_data)
-                            return filtered_data
-                    except json.JSONDecodeError:
-                        pass
-
-        # 方法3: 尝试直接匹配简单的JSON数组
-        json_array_pattern = r'(\[[\s\S]*?\])'
-        matches = re.finditer(json_array_pattern, text)
-        for match in matches:
-            json_text = match.group(1)
-            try:
-                json_data = json.loads(json_text)
-                if isinstance(json_data, list):
-                    # 过滤掉"✔ 符合要求"的结果
-                    filtered_data = self._filter_compliant_items(json_data)
+                elif isinstance(json_data, dict):
+                    # 如果是字典,转换为列表
+                    filtered_data = self._filter_compliant_items([json_data])
                     return filtered_data
-            except json.JSONDecodeError:
-                continue
+
+        except Exception as e:
+            logger.warning(f"使用inter_tool提取JSON失败,回退到原有方法: {str(e)}")
+
+        # 回退到原有的简单提取方法
+        try:
+            # 方法1: 尝试找到JSON代码块
+            json_block_pattern = r'```(?:json)?\s*(\[.*?\])\s*```'
+            match = re.search(json_block_pattern, text, re.DOTALL)
+            if match:
+                json_text = match.group(1)
+                try:
+                    json_data = json.loads(json_text)
+                    if isinstance(json_data, list):
+                        # 过滤掉"✔ 符合要求"的结果
+                        filtered_data = self._filter_compliant_items(json_data)
+                        return filtered_data
+                except json.JSONDecodeError:
+                    pass
+
+            # 方法2: 尝试直接匹配简单的JSON数组
+            json_array_pattern = r'(\[[\s\S]*?\])'
+            matches = re.finditer(json_array_pattern, text)
+            for match in matches:
+                json_text = match.group(1)
+                try:
+                    json_data = json.loads(json_text)
+                    if isinstance(json_data, list):
+                        # 过滤掉"✔ 符合要求"的结果
+                        filtered_data = self._filter_compliant_items(json_data)
+                        return filtered_data
+                except json.JSONDecodeError:
+                    continue
+
+        except Exception as e:
+            logger.error(f"JSON提取完全失败: {str(e)}")
 
         return None
 
     def _filter_compliant_items(self, json_data: list) -> list:
         """
-        过滤掉"✔ 符合要求"的结果,只保留有问题的问题
+        从issue_point字段判断,过滤掉符合要求的结果,只保留有问题的项目
 
         Args:
             json_data: 原始JSON数据列表
 
         Returns:
-            过滤后的JSON数据列表
+            过滤后的JSON数据列表,只包含"✘ 不符合要求"、"⚠ 部分符合"、"⚠ 部分缺失"等有问题的项目
         """
         if not isinstance(json_data, list):
             return json_data
@@ -619,23 +654,35 @@ class OutlineReviewer:
         filtered_items = []
         for item in json_data:
             if isinstance(item, dict):
-                # 检查是否符合要求的字段
-                status_field = item.get('是否符合要求') or item.get('完整性状态') or item.get('状态')
-
-                # 如果状态字段表示完全符合要求,则过滤掉
-                if status_field:
-                    status_str = str(status_field).strip()
-                    # 精确匹配完全符合要求的情况,避免误过滤部分符合的项目
-                    if (status_str == '✔ 符合要求' or
-                        status_str == '完整包含' or
-                        status_str.startswith('[完整]') or
-                        status_str == '✔ 完整包含' or
-                        (status_str.startswith('✔') and '符合要求' in status_str and '部分' not in status_str)):
-                        logger.info(f"过滤符合要求的项目: {item.get('项目', item.get('章节类别', 'N/A'))}")
-                        continue  # 跳过符合要求的项目
-
-                # 保留有问题或不符合要求的项目
-                filtered_items.append(item)
+                # 检查issue_point字段是否包含"✘ 不符合要求"
+                issue_point = item.get('issue_point', '')
+
+                if issue_point:
+                    issue_point_str = str(issue_point).strip()
+                    # 保留包含"✘ 不符合要求"或"⚠ 部分符合"的项目
+                    if ('✘ 不符合要求' in issue_point_str or
+                       '⚠ 部分符合' in issue_point_str or
+                       '⚠ 部分缺失' in issue_point_str):
+                        filtered_items.append(item)
+                        logger.info(f"保留有问题项目: {issue_point_str}")
+                    else:
+                        logger.info(f"过滤符合要求的项目: {issue_point_str}")
+                else:
+                    # 如果没有issue_point字段,检查旧格式的状态字段
+                    status_field = item.get('是否符合要求') or item.get('完整性状态') or item.get('状态')
+                    if status_field:
+                        status_str = str(status_field).strip()
+                        # 对于旧格式,保留包含"不符合要求"或"缺失"的项目
+                        if ('不符合要求' in status_str or '缺失' in status_str or
+                            status_str.startswith('✘') or '完全缺失' in status_str):
+                            filtered_items.append(item)
+                            logger.info(f"保留旧格式不符合要求的项目: {item.get('项目', 'N/A')} - {status_str}")
+                        else:
+                            logger.info(f"过滤旧格式符合要求的项目: {item.get('项目', 'N/A')} - {status_str}")
+                    else:
+                        # 既没有issue_point也没有状态字段,直接保留
+                        filtered_items.append(item)
+                        logger.warning(f"项目缺少状态标识字段,直接保留: {item}")
             else:
                 # 非字典类型的项目直接保留
                 filtered_items.append(item)
@@ -643,4 +690,37 @@ class OutlineReviewer:
         logger.info(f"过滤结果: 原始项目数 {len(json_data)}, 过滤后项目数 {len(filtered_items)}")
         return filtered_items
 
+    def _is_non_compliant_item(self, item: dict) -> bool:
+        """
+        判断项目是否有问题(与_filter_compliant_items逻辑保持一致)
+
+        Args:
+            item: 要判断的项目字典
+
+        Returns:
+            bool: True表示有问题需要保留,False表示符合要求需要过滤
+        """
+        if not isinstance(item, dict):
+            return False
+
+        # 检查issue_point字段是否包含"✘ 不符合要求"
+        issue_point = item.get('issue_point', '')
+
+        if issue_point:
+            issue_point_str = str(issue_point).strip()
+            # 保留包含"✘ 不符合要求"或"⚠ 部分符合"的项目
+            return ('✘ 不符合要求' in issue_point_str or
+                   '⚠ 部分符合' in issue_point_str or
+                   '⚠ 部分缺失' in issue_point_str)
+        else:
+            # 如果没有issue_point字段,检查旧格式的状态字段
+            status_field = item.get('是否符合要求') or item.get('完整性状态') or item.get('状态')
+            if status_field:
+                status_str = str(status_field).strip()
+                # 对于旧格式,保留包含"不符合要求"或"缺失"的项目
+                return ('不符合要求' in status_str or '缺失' in status_str or
+                       status_str.startswith('✘') or '完全缺失' in status_str)
+            else:
+                # 既没有issue_point也没有状态字段,默认为不符合要求(保守处理)
+                return True
 

+ 33 - 26
core/construction_review/component/reviewers/prompt/outline_reviewers.yaml

@@ -82,26 +82,30 @@ outline_completeness_review:
     【输出格式规范】
 
     ## 格式要求
+    如果未发现明显的完整性问题,请输出:无明显问题
+    如果发现问题,请按以下格式输出:
     使用 JSON 数组格式输出审查结果,每个元素对应审查规范中的一个方面。
+    location字段直接输出原字段内容,不得猜测
 
     ## 字段说明
-    - **项目**:审查规范中要求的内容方面名称(必须与审查规范中的方面一一对应)
-    - **是否符合要求**:判定结果,格式为"符号 + 简要结论"
+    - **issue_point**:问题标题描述,对应审查规范中要求的内容方面名称(必须与审查规范中的方面一一对应),包含判定结果,格式为"符号 + 简要结论"
       - ✔ 表示符合要求
       - ⚠ 表示部分符合或需要注意
       - ✘ 表示不符合要求
-    - **备注**:详细说明,包括:
-      - 在待审查目录中找到的对应章节位置
-      - 存在的问题描述
-      - 整改建议(如果不符合要求
+    - **location**:当前问题对应的原始条款内容及位置,以及其语境上下文
+    - **suggestion**:具体的修改建议内容,包括在待审查目录中找到的对应章节位置、存在的问题描述、整改建议(如果不符合要求)
+    - **reason**:问题的原因分析和依据说明,对应目录的审查规范原文
+    - **risk_level**:风险等级分类(高风险[不符合要求]/中风险[部分符合]/无风险[符合要求]
 
     ## 输出示例
     ```json
     [
       {{
-        "项目": "设计概况",
-        "是否符合要求": "✔ 符合要求/⚠ 部分符合/✘ 不符合要求 + 简要结论",
-        "备注": "补充说明或整改建议"
+        "issue_point": "设计概况 ✘ 不符合要求",
+        "location": "当前问题对应的原始条款内容及位置",
+        "suggestion": "在待审查目录中找到的对应章节位置:第2章;存在问题:缺少设计概况相关内容;整改建议:建议补充项目基本情况、设计标准等信息",
+        "reason": "依据××规范要求(对应目录的审查规范原文),工程概况需包含参建各方责任主体等七项核心内容,本章未涵盖该部分信息,因此需补充完善",
+        "risk_level": ""
       }}
     ]
     ```
@@ -109,7 +113,7 @@ outline_completeness_review:
     ## 注意事项
     - JSON 格式必须严格规范,可被标准 JSON 解析器解析
     - 每个审查规范中的方面都必须有对应的判定结果
-    - 备注字段应提供足够详细的信息,便于理解和整改
+    - location字段应提供足够详细的信息,便于定位问题
 
 # 整体大纲完整性审查器
 overall_outline_completeness_review:
@@ -152,34 +156,37 @@ overall_outline_completeness_review:
     - **✘ 完全缺失**:该类别在整体大纲中完全缺失
 
     【输出格式要求】
+    如果未发现明显的完整性问题,请输出:无明显问题
+    如果发现问题,请按以下格式输出:
     使用 JSON 数组格式输出审查结果,每个元素对应十大类章节中的一个类别。
+    location字段直接输出原字段内容,不得猜测
 
     ## 字段说明
-    - **章节类别**:十大类章节的名称
-    - **完整性状态**:判定结果,格式为"符号 + 状态描述"
+    - **issue_point**:问题标题描述,包含章节类别和完整性状态,格式为"章节类别 + 符号 + 状态描述"
       - ✔ 完整包含
       - ⚠ 部分缺失
       - ✘ 完全缺失
-    - **对应位置**:在大纲中找到的具体章节位置或页码
-    - **问题描述**:存在问题的详细说明
-    - **整改建议**:针对问题的具体改进建议
+    - **location**:当前问题对应的原始条款内容及位置,以及其语境上下文
+    - **suggestion**:具体的修改建议内容,包括在大纲中找到的具体章节位置或页码、存在问题的详细说明、针对问题的具体改进建议
+    - **reason**:给出该建议的参考考依据原文,与解释的依据,对应目录的审查规范原文
+    - **risk_level**:风险等级分类(高风险[不符合要求]/中风险[部分符合]/无风险[符合要求])
 
     ## 输出示例
     ```json
     [
       {{
-        "章节类别": "一、编制依据",
-        "完整性状态": "✔ 完整包含",
-        "对应位置": "第1章",
-        "问题描述": "",
-        "整改建议": ""
+        "issue_point": "一、编制依据 ✔ 完整包含",
+        "location": "当前问题对应的原始条款内容及位置",
+        "suggestion": "对应位置:第1章;问题描述:无;整改建议:无",
+        "reason": "编制依据章节完整,符合规范要求",
+        "risk_level": "无风险"
       }},
       {{
-        "章节类别": "二、工程概况",
-        "完整性状态": "⚠ 部分缺失",
-        "对应位置": "第2章",
-        "问题描述": "缺少参建各方责任主体单位相关内容",
-        "整改建议": "建议在工程概况中补充参建各方责任主体单位的详细信息"
+        "issue_point": "二、工程概况 ⚠ 部分缺失",
+        "location": "当前问题对应的原始条款内容及位置",
+        "suggestion": "对应位置:第2章;存在问题:缺少参建各方责任主体单位相关内容;整改建议:建议在工程概况中补充参建各方责任主体单位的详细信息",
+        "reason": "依据××规范要求,(对应目录的审查规范原文)工程概况需包含参建各方责任主体等七项核心内容,本章未涵盖该部分信息,因此需补充完善",
+        "risk_level": "中风险"
       }}
     ]
     ```
@@ -187,4 +194,4 @@ overall_outline_completeness_review:
     【注意事项】
     - 必须对全部十大类章节进行审查,不能遗漏
     - JSON 格式必须严格规范
-    - 整改建议要具体可操作
+    - location字段应提供足够详细的信息,便于定位问题

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

@@ -279,9 +279,9 @@ class AIReviewWorkflow:
             await self.core_fun._send_start_review_progress(state, total_units)
 
             # 3. 执行大纲审查
-            await self.ai_review_engine.outline_check(state["file_id"], state["structured_content"],
-                                                state.get("state", {}), state.get("stage_name", "大纲审查"))
-
+            outline_review_result = await self.ai_review_engine.outline_check(state["callback_task_id"], state["structured_content"],
+                                                state, state.get("stage_name", "大纲审查"))
+            
             # 4. 执行并发审查
             logger.info(f"开始执行并发审查,任务ID: {state['callback_task_id']}")
             successful_results = await self.core_fun._execute_concurrent_reviews(review_chunks, total_units, state)
@@ -292,6 +292,7 @@ class AIReviewWorkflow:
 
             # 将所有单元的issues合并成一个列表
             all_issues = []
+            all_issues.append(outline_review_result)
             for unit_issues in successful_results:
                 if unit_issues and isinstance(unit_issues, list):
                     all_issues.extend(unit_issues)

ファイルの差分が大きいため隠しています
+ 28 - 5228
logs/agent_info.log.1


ファイルの差分が大きいため隠しています
+ 63490 - 0
logs/agent_info.log.2


この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません