Przeglądaj źródła

Merge branch 'dev' of http://192.168.0.3:3000/CRBC-MaaS-Platform-Project/LQAgentPlatform into dev

ZengChao 1 miesiąc temu
rodzic
commit
07b617226a

+ 0 - 1
.gitignore

@@ -69,7 +69,6 @@ todo.md
 .R&D
 .R&D
 .RaD
 .RaD
 temp/
 temp/
-*.json
 test_rawdata/
 test_rawdata/
 temp/AI审查结果.json
 temp/AI审查结果.json
 mineru_temp/
 mineru_temp/

+ 20 - 0
.vscode/launch.json

@@ -0,0 +1,20 @@
+{
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Python 调试程序: 当前文件(lqagentplatform)",
+            "type": "debugpy",
+            "request": "launch",
+            "program": "${file}",
+            "console": "integratedTerminal",
+            // 关键:指定 lqagentplatform 环境的 Python 解释器路径
+            "python": "D:\\Program Files\\Anaconda\\envs\\lqagentplatform\\python.exe",
+            // 可选:添加环境变量增强兼容性(避免依赖找不到)
+            "env": {
+                "PATH": "${env:PATH};D:\\Program Files\\Anaconda\\envs\\lqagentplatform\\Scripts"
+            },
+            // 自动激活 Conda 环境(无需手动在终端激活)
+            "preLaunchTask": "activate_lqagentplatform"
+        }
+    ]
+}

+ 5 - 0
.vscode/settings.json

@@ -0,0 +1,5 @@
+{
+    "liveServer.settings.port": 5501,
+    "python-envs.defaultEnvManager": "ms-python.python:conda",
+    "python-envs.defaultPackageManager": "ms-python.python:conda"
+}

+ 15 - 0
.vscode/tasks.json

@@ -0,0 +1,15 @@
+{
+    "version": "2.0.0",
+    "tasks": [
+        {
+            "label": "activate_lqagentplatform", // 必须和 launch.json 里的 preLaunchTask 名称一致
+            "type": "shell",
+            "command": "conda activate lqagentplatform", // 激活 Conda 环境的命令
+            "args": [],
+            "presentation": {
+                "reveal": "silent" // 执行时不弹窗(可选)
+            },
+            "problemMatcher": []
+        }
+    ]
+}

+ 38 - 0
core/base/workflow_manager.py

@@ -10,6 +10,7 @@
 import asyncio
 import asyncio
 import time
 import time
 import json
 import json
+import os
 from typing import Dict, Optional, Any
 from typing import Dict, Optional, Any
 from datetime import datetime
 from datetime import datetime
 from langgraph.graph import StateGraph, END
 from langgraph.graph import StateGraph, END
@@ -496,9 +497,46 @@ class WorkflowManager:
         # 编译工作流图
         # 编译工作流图
         compiled_graph = workflow.compile()
         compiled_graph = workflow.compile()
 
 
+        # 保存工作流图到 temp/construction_review 目录
+        self._save_workflow_graph(compiled_graph, "temp/construction_review/task_chain_workflow.png")
+
         logger.info("LangGraph 任务链工作流图构建完成")
         logger.info("LangGraph 任务链工作流图构建完成")
         return compiled_graph
         return compiled_graph
 
 
+    def _save_workflow_graph(self, compiled_graph: StateGraph, output_path: str):
+        """
+        保存 LangGraph 工作流图为 PNG 图片
+
+        Args:
+            compiled_graph: 编译后的 LangGraph 工作流图
+            output_path: 输出文件路径
+        """
+        try:
+            # 确保输出目录存在
+            output_dir = os.path.dirname(output_path)
+            if output_dir and not os.path.exists(output_dir):
+                os.makedirs(output_dir, exist_ok=True)
+                logger.info(f"创建输出目录:{output_dir}")
+
+            # 使用 graphviz 保存图片
+            # 需要安装 graphviz 和 graphviz Python 包
+            try:
+                graph = compiled_graph.get_graph()
+                graph_image = graph.draw_mermaid_png()
+                with open(output_path, "wb") as f:
+                    f.write(graph_image)
+                logger.info(f"工作流图已保存到:{output_path}")
+            except Exception as graphviz_error:
+                logger.warning(f"Graphviz 保存失败:{str(graphviz_error)}, 尝试使用 JSON 格式保存")
+                # 备用方案:保存为 JSON 格式
+                json_path = output_path.replace(".png", ".json")
+                with open(json_path, "w", encoding="utf-8") as f:
+                    json.dump(compiled_graph.get_graph().to_json(), f, indent=2, ensure_ascii=False)
+                logger.info(f"工作流图已保存到 (JSON 格式): {json_path}")
+
+        except Exception as e:
+            logger.warning(f"保存工作流图失败:{str(e)}")
+
     async def _start_chain_node(self, state: TaskChainState) -> TaskChainState:
     async def _start_chain_node(self, state: TaskChainState) -> TaskChainState:
         """
         """
         任务链开始节点
         任务链开始节点

+ 2 - 4
core/construction_review/component/document_processor.py

@@ -14,7 +14,7 @@ from datetime import datetime
 import asyncio
 import asyncio
 
 
 from foundation.observability.logger.loggering import review_logger as logger
 from foundation.observability.logger.loggering import review_logger as logger
-
+from foundation.observability.cachefiles import cache, CacheBaseDir
 # 引入doc_worker核心组件
 # 引入doc_worker核心组件
 try:
 try:
     from .doc_worker.interfaces import DocumentSource, TOCExtractor, FullTextExtractor, TextSplitter
     from .doc_worker.interfaces import DocumentSource, TOCExtractor, FullTextExtractor, TextSplitter
@@ -91,9 +91,7 @@ class DocumentProcessor:
 
 
             # 结构化内容
             # 结构化内容
             structured_result = self.structure_content(result)
             structured_result = self.structure_content(result)
-
-
-
+            cache.document_temp(structured_result, base_cache_dir=CacheBaseDir.CONSTRUCTION_REVIEW)
             return structured_result
             return structured_result
 
 
         except Exception as e:
         except Exception as e:

+ 82 - 63
core/construction_review/component/report_generator.py

@@ -1,6 +1,6 @@
 """
 """
 报告生成器
 报告生成器
-负责基于AI审查结果生成审查报告摘要
+负责基于 AI 审查结果生成审查报告摘要
 """
 """
 
 
 import asyncio
 import asyncio
@@ -32,7 +32,7 @@ class FinalReport:
     generated_at: datetime
     generated_at: datetime
 
 
 class ReportGenerator:
 class ReportGenerator:
-    """报告生成器 - 基于高中风险问题使用LLM生成摘要"""
+    """报告生成器 - 基于高中风险问题使用 LLM 生成摘要"""
 
 
     def __init__(self):
     def __init__(self):
         """初始化报告生成器"""
         """初始化报告生成器"""
@@ -52,10 +52,10 @@ class ReportGenerator:
 
 
             # 手动加载 report 类型的 prompt
             # 手动加载 report 类型的 prompt
             self.prompt_loader._load_prompt('report', 'report_summary')
             self.prompt_loader._load_prompt('report', 'report_summary')
-            logger.info(f"成功加载 report_summary prompt,目录: {prompt_dir}")
+            logger.info(f"成功加载 report_summary prompt,目录{prompt_dir}")
 
 
         except Exception as e:
         except Exception as e:
-            logger.error(f"加载 report_summary prompt 失败: {str(e)}", exc_info=True)
+            logger.error(f"加载 report_summary prompt 失败{str(e)}", exc_info=True)
         finally:
         finally:
             # 恢复原始目录
             # 恢复原始目录
             self.prompt_loader.prompt_config_dir = original_prompt_dir
             self.prompt_loader.prompt_config_dir = original_prompt_dir
@@ -68,9 +68,9 @@ class ReportGenerator:
         生成审查报告(基于高中风险问题)
         生成审查报告(基于高中风险问题)
 
 
         Args:
         Args:
-            file_id: 文件ID
+            file_id: 文件 ID
             file_name: 文件名称
             file_name: 文件名称
-            review_results: AI审查结果(包含issues列表)
+            review_results: AI 审查结果(包含 issues 列表)
             progress_callback: 进度回调函数
             progress_callback: 进度回调函数
 
 
         Returns:
         Returns:
@@ -79,7 +79,7 @@ class ReportGenerator:
         start_time = time.time()
         start_time = time.time()
 
 
         try:
         try:
-            logger.info(f"开始生成报告,文件ID: {file_id}, 文件名: {file_name}")
+            logger.info(f"开始生成报告,文件 ID: {file_id}, 文件名:{file_name}")
 
 
             if progress_callback:
             if progress_callback:
                 progress_callback(20, "提取高中风险问题")
                 progress_callback(20, "提取高中风险问题")
@@ -88,12 +88,12 @@ class ReportGenerator:
             high_risk_issues, medium_risk_issues, low_risk_count = await self._extract_high_medium_risk_issues(review_results)
             high_risk_issues, medium_risk_issues, low_risk_count = await self._extract_high_medium_risk_issues(review_results)
             total_issues = len(high_risk_issues) + len(medium_risk_issues) + low_risk_count
             total_issues = len(high_risk_issues) + len(medium_risk_issues) + low_risk_count
 
 
-            logger.info(f"问题统计 - 高风险: {len(high_risk_issues)}, 中风险: {len(medium_risk_issues)}, 低风险: {low_risk_count}, 总计: {total_issues}")
+            logger.info(f"问题统计 - 高风险:{len(high_risk_issues)}, 中风险:{len(medium_risk_issues)}, 低风险:{low_risk_count}, 总计:{total_issues}")
 
 
             if progress_callback:
             if progress_callback:
-                progress_callback(50, "调用LLM生成报告摘要")
+                progress_callback(50, "调用 LLM 生成报告摘要")
 
 
-            # 2. 调用LLM生成摘要
+            # 2. 调用 LLM 生成摘要
             llm_summary = await self._call_llm_for_summary(
             llm_summary = await self._call_llm_for_summary(
                 file_name=file_name,
                 file_name=file_name,
                 high_risk_issues=high_risk_issues,
                 high_risk_issues=high_risk_issues,
@@ -103,9 +103,9 @@ class ReportGenerator:
             )
             )
 
 
             if progress_callback:
             if progress_callback:
-                progress_callback(80, "解析LLM响应")
+                progress_callback(80, "解析 LLM 响应")
 
 
-            # 3. 解析LLM响应
+            # 3. 解析 LLM 响应
             parsed_summary = self._parse_llm_response(llm_summary)
             parsed_summary = self._parse_llm_response(llm_summary)
 
 
             if progress_callback:
             if progress_callback:
@@ -128,12 +128,12 @@ class ReportGenerator:
             )
             )
 
 
             execution_time = time.time() - start_time
             execution_time = time.time() - start_time
-            logger.info(f"报告生成完成,文件ID: {file_id}, 耗时: {execution_time:.2f}秒")
+            logger.info(f"报告生成完成,文件 ID: {file_id}, 耗时:{execution_time:.2f}秒")
 
 
             return final_report
             return final_report
 
 
         except Exception as e:
         except Exception as e:
-            logger.error(f"报告生成失败: {str(e)}", exc_info=True)
+            logger.error(f"报告生成失败{str(e)}", exc_info=True)
             raise
             raise
 
 
     async def _extract_high_medium_risk_issues(self, review_results: Dict[str, Any]) -> tuple:
     async def _extract_high_medium_risk_issues(self, review_results: Dict[str, Any]) -> tuple:
@@ -141,7 +141,7 @@ class ReportGenerator:
         从审查结果中提取高中风险问题
         从审查结果中提取高中风险问题
 
 
         Args:
         Args:
-            review_results: AI审查结果
+            review_results: AI 审查结果
 
 
         Returns:
         Returns:
             tuple: (high_risk_issues, medium_risk_issues, low_risk_count)
             tuple: (high_risk_issues, medium_risk_issues, low_risk_count)
@@ -150,15 +150,15 @@ class ReportGenerator:
         medium_risk_issues = []
         medium_risk_issues = []
         low_risk_count = 0
         low_risk_count = 0
 
 
-        # 获取issues列表(键名为 'review_results')
+        # 获取 issues 列表(键名为 'review_results')
         issues = review_results.get('review_results', [])
         issues = review_results.get('review_results', [])
 
 
-        logger.info(f"开始提取风险问题,issues数量: {len(issues)}")
+        logger.info(f"开始提取风险问题,issues 数量:{len(issues)}")
 
 
         for issue_wrapper in issues:
         for issue_wrapper in issues:
-            # issue_wrapper是一个字典,key是问题ID,value是问题详情
+            # issue_wrapper 是一个字典,key 是问题 ID,value 是问题详情
             for issue_id, issue_detail in issue_wrapper.items():
             for issue_id, issue_detail in issue_wrapper.items():
-                # 获取review_lists
+                # 获取 review_lists
                 review_lists = issue_detail.get('review_lists', [])
                 review_lists = issue_detail.get('review_lists', [])
 
 
                 # 获取元数据
                 # 获取元数据
@@ -166,7 +166,7 @@ class ReportGenerator:
                 location = metadata.get('review_location_label', '未知位置')
                 location = metadata.get('review_location_label', '未知位置')
 
 
                 for review_item in review_lists:
                 for review_item in review_lists:
-                    # 只处理exist_issue=True的项
+                    # 只处理 exist_issue=True 的项
                     if not review_item.get('exist_issue', False):
                     if not review_item.get('exist_issue', False):
                         continue
                         continue
 
 
@@ -190,56 +190,77 @@ class ReportGenerator:
                     elif '低' in risk_level_raw or 'low' in risk_level_raw:
                     elif '低' in risk_level_raw or 'low' in risk_level_raw:
                         risk_level = 'low'
                         risk_level = 'low'
                     else:
                     else:
-                        # 默认按risk_info判断
+                        # 默认按 risk_info 判断
                         risk_info = review_item.get('risk_info', {})
                         risk_info = review_item.get('risk_info', {})
                         if isinstance(risk_info, dict):
                         if isinstance(risk_info, dict):
                             risk_level = risk_info.get('risk_level', 'low')
                             risk_level = risk_info.get('risk_level', 'low')
                         else:
                         else:
                             risk_level = 'low'
                             risk_level = 'low'
 
 
-                    # 获取检查项类型
+                    # 获取检查项类型和章节代码
                     check_item = review_item.get('check_item', 'unknown')
                     check_item = review_item.get('check_item', 'unknown')
                     check_item_code = review_item.get('check_item_code', '')
                     check_item_code = review_item.get('check_item_code', '')
 
 
-                    # 构建问题对象(包含检查项信息)
+                    # 从 check_item_code 中提取 chapter_code (格式:chapter_code_check_item)
+                    chapter_code = check_item_code.rsplit('_', 1)[0] if '_' in check_item_code else 'unknown'
+
+                    # 构建问题对象(只包含分析结果,不包含原文材料)
+                    # 按照指定格式组织数据结构
                     issue_obj = {
                     issue_obj = {
                         'check_item': check_item,
                         'check_item': check_item,
+                        'chapter_code': chapter_code,
                         'check_item_code': check_item_code,
                         'check_item_code': check_item_code,
-                        'location': location,
-                        'issue_point': check_result.get('issue_point', '') if isinstance(check_result, dict) else str(check_result),
-                        'suggestion': check_result.get('suggestion', '') if isinstance(check_result, dict) else '',
-                        'reason': check_result.get('reason', '') if isinstance(check_result, dict) else '',
-                        'reference_source': check_result.get('reference_source', '') if isinstance(check_result, dict) else ''
+                        'check_result': {
+                            'issue_point': check_result.get('issue_point', '') if isinstance(check_result, dict) else str(check_result),
+                            'location': check_result.get('location', location),
+                            'suggestion': check_result.get('suggestion', ''),
+                            'reason': check_result.get('reason', ''),
+                            'risk_level': check_result.get('risk_level', risk_level)
+                        }
+                        # 注意:不包含 reference_source(原文材料),只传入分析结果
                     }
                     }
 
 
-                    # 按风险级别分类
+                    # 按风险级别分类(只处理高风险和中风险)
                     if risk_level == 'high':
                     if risk_level == 'high':
                         high_risk_issues.append(issue_obj)
                         high_risk_issues.append(issue_obj)
                     elif risk_level == 'medium':
                     elif risk_level == 'medium':
                         medium_risk_issues.append(issue_obj)
                         medium_risk_issues.append(issue_obj)
-                    else:
+                    elif risk_level == 'low':
                         low_risk_count += 1
                         low_risk_count += 1
 
 
-        logger.info(f"风险问题提取完成 - 高风险: {len(high_risk_issues)}, 中风险: {len(medium_risk_issues)}, 低风险: {low_risk_count}")
+        logger.info(f"风险问题提取完成 - 高风险:{len(high_risk_issues)}, 中风险:{len(medium_risk_issues)}, 低风险:{low_risk_count}")
 
 
         return high_risk_issues, medium_risk_issues, low_risk_count
         return high_risk_issues, medium_risk_issues, low_risk_count
 
 
     async def _call_llm_for_summary(self, file_name: str, high_risk_issues: List[Dict],
     async def _call_llm_for_summary(self, file_name: str, high_risk_issues: List[Dict],
                                    medium_risk_issues: List[Dict], total_issues: int, trace_id: str) -> str:
                                    medium_risk_issues: List[Dict], total_issues: int, trace_id: str) -> str:
         """
         """
-        调用LLM生成报告摘要
+        调用 LLM 生成报告摘要(只处理高中风险问题,不传入原文材料)
 
 
         Args:
         Args:
             file_name: 文件名称
             file_name: 文件名称
             high_risk_issues: 高风险问题列表
             high_risk_issues: 高风险问题列表
             medium_risk_issues: 中风险问题列表
             medium_risk_issues: 中风险问题列表
             total_issues: 总问题数
             total_issues: 总问题数
-            trace_id: 追踪ID
+            trace_id: 追踪 ID
 
 
         Returns:
         Returns:
-            str: LLM生成的摘要文本(JSON格式)
+            str: LLM 生成的摘要文本(JSON 格式)
         """
         """
         try:
         try:
+            # 限制问题数量,避免 prompt 过长导致超时
+            # 高风险问题最多取 10 个,中风险问题最多取 15 个
+            MAX_HIGH_RISK = 10
+            MAX_MEDIUM_RISK = 15
+
+            if len(high_risk_issues) > MAX_HIGH_RISK:
+                logger.warning(f"高风险问题数量过多 ({len(high_risk_issues)}),仅取前{MAX_HIGH_RISK}个")
+                high_risk_issues = high_risk_issues[:MAX_HIGH_RISK]
+
+            if len(medium_risk_issues) > MAX_MEDIUM_RISK:
+                logger.warning(f"中风险问题数量过多 ({len(medium_risk_issues)}),仅取前{MAX_MEDIUM_RISK}个")
+                medium_risk_issues = medium_risk_issues[:MAX_MEDIUM_RISK]
+
             # 构建问题清单文本
             # 构建问题清单文本
             high_risk_text = self._format_issues_text(high_risk_issues)
             high_risk_text = self._format_issues_text(high_risk_issues)
             medium_risk_text = self._format_issues_text(medium_risk_issues)
             medium_risk_text = self._format_issues_text(medium_risk_issues)
@@ -260,30 +281,30 @@ class ReportGenerator:
                 "task_name": "报告摘要生成"
                 "task_name": "报告摘要生成"
             }
             }
 
 
-            logger.debug(f"开始调用LLM生成报告摘要")
+            logger.debug(f"开始调用 LLM 生成报告摘要")
 
 
-            # 调用模型(参考base_reviewer.py的实现)
+            # 调用模型 - 增加超时阈值到 180 秒(3 分钟),因为报告生成需要更多时间
             model_response = await self.model_client.get_model_generate_invoke(
             model_response = await self.model_client.get_model_generate_invoke(
                 trace_id=trace_id,
                 trace_id=trace_id,
                 task_prompt_info=task_prompt_info,
                 task_prompt_info=task_prompt_info,
-                timeout=60,
+                timeout=180,
                 model_name="qwen3_30b"
                 model_name="qwen3_30b"
             )
             )
 
 
-            logger.info(f"LLM摘要生成成功,响应长度: {len(model_response)} 字符")
+            logger.info(f"LLM 摘要生成成功,响应长度:{len(model_response)} 字符")
 
 
             return model_response
             return model_response
 
 
         except Exception as e:
         except Exception as e:
-            logger.error(f"调用LLM生成摘要失败: {str(e)}", exc_info=True)
+            logger.error(f"调用 LLM 生成摘要失败:{str(e)}", exc_info=True)
             raise
             raise
 
 
     def _format_issues_text(self, issues: List[Dict]) -> str:
     def _format_issues_text(self, issues: List[Dict]) -> str:
         """
         """
-        格式化问题列表为文本(按 check_item 分组)
+        格式化问题列表为文本(按 check_item 分组,只包含分析结果
 
 
         Args:
         Args:
-            issues: 问题列表
+            issues: 问题列表(每个问题包含 check_item, chapter_code, check_item_code, check_result)
 
 
         Returns:
         Returns:
             str: 格式化后的问题文本
             str: 格式化后的问题文本
@@ -308,7 +329,7 @@ class ReportGenerator:
             'mandatory_standards_check': '强制性标准审查',
             'mandatory_standards_check': '强制性标准审查',
             'technical_parameters_check': '技术参数审查',
             'technical_parameters_check': '技术参数审查',
             'design_values_check': '设计值审查',
             'design_values_check': '设计值审查',
-            'rag_enhanced_review': 'RAG增强审查',
+            'rag_enhanced_review': 'RAG 增强审查',
             'professional_suggestion': '专业建议',
             'professional_suggestion': '专业建议',
             'unknown': '其他审查'
             'unknown': '其他审查'
         }
         }
@@ -320,36 +341,34 @@ class ReportGenerator:
             result.append(f"## 【{check_item_name}】({len(item_issues)}个问题)")
             result.append(f"## 【{check_item_name}】({len(item_issues)}个问题)")
             result.append("")
             result.append("")
 
 
-            # 添加该组的所有问题
+            # 添加该组的所有问题(只传入分析结果,不包含原文材料)
             for idx, issue in enumerate(item_issues, 1):
             for idx, issue in enumerate(item_issues, 1):
+                check_result = issue.get('check_result', {})
                 result.append(f"### 问题 {idx}")
                 result.append(f"### 问题 {idx}")
-                result.append(f"- 位置: {issue.get('location', '未知')}")
-                result.append(f"- 问题点: {issue.get('issue_point', '未知')}")
-                result.append(f"- 建议: {issue.get('suggestion', '无')}")
-                result.append(f"- 依据: {issue.get('reason', '无')}")
-
-                reference_source = issue.get('reference_source', '无')
-                if reference_source and reference_source != '无':
-                    result.append(f"- 参考来源: {reference_source}")
+                result.append(f"- 章节:{issue.get('chapter_code', '未知')}")
+                result.append(f"- 位置:{check_result.get('location', '未知')}")
+                result.append(f"- 问题点:{check_result.get('issue_point', '未知')}")
+                result.append(f"- 建议:{check_result.get('suggestion', '无')}")
+                result.append(f"- 依据:{check_result.get('reason', '无')}")
+                # 注意:不包含 reference_source(原文材料)
+                result.append("")
 
 
-                result.append("")  # 空行分隔
-
-            result.append("")  # 分组之间额外空行
+            result.append("")
 
 
         return "\n".join(result)
         return "\n".join(result)
 
 
     def _parse_llm_response(self, llm_response: str) -> Dict[str, Any]:
     def _parse_llm_response(self, llm_response: str) -> Dict[str, Any]:
         """
         """
-        解析LLM响应
+        解析 LLM 响应
 
 
         Args:
         Args:
-            llm_response: LLM返回的JSON字符串
+            llm_response: LLM 返回的 JSON 字符串
 
 
         Returns:
         Returns:
             Dict: 解析后的摘要字典
             Dict: 解析后的摘要字典
         """
         """
         try:
         try:
-            # 移除可能的markdown代码块标记
+            # 移除可能的 markdown 代码块标记
             cleaned_response = llm_response.strip()
             cleaned_response = llm_response.strip()
             if cleaned_response.startswith('```json'):
             if cleaned_response.startswith('```json'):
                 cleaned_response = cleaned_response[7:]
                 cleaned_response = cleaned_response[7:]
@@ -360,7 +379,7 @@ class ReportGenerator:
 
 
             cleaned_response = cleaned_response.strip()
             cleaned_response = cleaned_response.strip()
 
 
-            # 解析JSON
+            # 解析 JSON
             parsed = json.loads(cleaned_response)
             parsed = json.loads(cleaned_response)
 
 
             # 验证必需字段
             # 验证必需字段
@@ -374,18 +393,18 @@ class ReportGenerator:
 
 
             for field in required_fields:
             for field in required_fields:
                 if field not in parsed:
                 if field not in parsed:
-                    logger.warning(f"LLM响应缺少必需字段: {field},使用默认值")
+                    logger.warning(f"LLM 响应缺少必需字段:{field},使用默认值")
                     if field == "overall_assessment":
                     if field == "overall_assessment":
                         parsed[field] = "无法生成总体评价"
                         parsed[field] = "无法生成总体评价"
                     else:
                     else:
                         parsed[field] = []
                         parsed[field] = []
 
 
-            logger.info("LLM响应解析成功")
+            logger.info("LLM 响应解析成功")
             return parsed
             return parsed
 
 
         except json.JSONDecodeError as e:
         except json.JSONDecodeError as e:
-            logger.error(f"解析LLM响应失败: {str(e)}")
-            logger.error(f"原始响应: {llm_response[:500]}...")  # 打印前500字符用于调试
+            logger.error(f"解析 LLM 响应失败:{str(e)}")
+            logger.error(f"原始响应:{llm_response[:500]}...")
 
 
             # 返回默认结构
             # 返回默认结构
             return {
             return {
@@ -396,5 +415,5 @@ class ReportGenerator:
                 "risk_alerts": []
                 "risk_alerts": []
             }
             }
         except Exception as e:
         except Exception as e:
-            logger.error(f"解析LLM响应时发生未知错误: {str(e)}", exc_info=True)
+            logger.error(f"解析 LLM 响应时发生未知错误:{str(e)}", exc_info=True)
             raise
             raise

+ 244 - 242
core/construction_review/workflows/ai_review_workflow.py

@@ -137,7 +137,7 @@ class AIReviewWorkflow:
         workflow = StateGraph(AIReviewState)
         workflow = StateGraph(AIReviewState)
         workflow.add_node("start", self._start_node)
         workflow.add_node("start", self._start_node)
         workflow.add_node("initialize_progress", self._initialize_progress_node)
         workflow.add_node("initialize_progress", self._initialize_progress_node)
-        workflow.add_node("ai_review", self._ai_review_node)
+        #workflow.add_node("ai_review", self._ai_review_node)
         workflow.add_node("ai_review_check_item",self._ai_review_node_check_item)
         workflow.add_node("ai_review_check_item",self._ai_review_node_check_item)
         workflow.add_node("save_results", self._save_results_node)  # 添加保存结果节点
         workflow.add_node("save_results", self._save_results_node)  # 添加保存结果节点
         workflow.add_node("complete", self._complete_node)
         workflow.add_node("complete", self._complete_node)
@@ -152,20 +152,20 @@ class AIReviewWorkflow:
             self._should_check_item_or_dimensions,
             self._should_check_item_or_dimensions,
             {
             {
                 "activate_ai_review_check_item": "ai_review_check_item",  # 使用 review_item_config
                 "activate_ai_review_check_item": "ai_review_check_item",  # 使用 review_item_config
-                "activate_ai_review": "ai_review"                 # 使用 review_config
+                # "activate_ai_review": "ai_review"                 # 使用 review_config
             }
             }
         )
         )
 
 
-        # 添加条件边(错误处理 + 终止检查)
-        workflow.add_conditional_edges(
-            "ai_review",
-            self._should_terminate_or_error,
-            {
-                "terminate": "terminate",  # 终止路径
-                "success": "save_results",  # 成功后先保存结果
-                "error": "error_handler"  # 错误处理
-            }
-        )
+        # # 添加条件边(错误处理 + 终止检查)
+        # workflow.add_conditional_edges(
+        #     "ai_review",
+        #     self._should_terminate_or_error,
+        #     {
+        #         "terminate": "terminate",  # 终止路径
+        #         "success": "save_results",  # 成功后先保存结果
+        #         "error": "error_handler"  # 错误处理
+        #     }
+        # )
 
 
                 # 添加条件边(错误处理 + 终止检查)
                 # 添加条件边(错误处理 + 终止检查)
         workflow.add_conditional_edges(
         workflow.add_conditional_edges(
@@ -397,7 +397,8 @@ class AIReviewWorkflow:
                 all_check_items.extend(check_list)  # 把每个分类的检查项加入总列表
                 all_check_items.extend(check_list)  # 把每个分类的检查项加入总列表
 
 
             # 统计所有 filtered_chunks 作为总块数(与实际处理的块数保持一致)
             # 统计所有 filtered_chunks 作为总块数(与实际处理的块数保持一致)
-            total_chunks = len(filtered_chunks)
+            # 注意:+1 为目录审查单元(系统强制添加,不在用户配置的 review_item_config 中)
+            total_chunks = len(filtered_chunks) + 1
 
 
             # 初始化issues列表
             # 初始化issues列表
             all_issues = []
             all_issues = []
@@ -452,6 +453,7 @@ class AIReviewWorkflow:
                 logger.info(f"章节 {chapter_code} 处理完成")
                 logger.info(f"章节 {chapter_code} 处理完成")
 
 
             # 主流程完成后,串行处理 catalogue(目录审查)
             # 主流程完成后,串行处理 catalogue(目录审查)
+            # 注意:catalogue 是系统强制添加的审查单元,已计入 total_chunks
             logger.info("开始处理目录审查(catalogue)")
             logger.info("开始处理目录审查(catalogue)")
             chunks_completed, all_issues = await self.core_fun._process_chapter_item(
             chunks_completed, all_issues = await self.core_fun._process_chapter_item(
                 "catalogue",                 # chapter_code
                 "catalogue",                 # chapter_code
@@ -510,237 +512,237 @@ class AIReviewWorkflow:
                 "messages": [AIMessage(content=f"AI审查项检查失败: {str(e)}")]
                 "messages": [AIMessage(content=f"AI审查项检查失败: {str(e)}")]
             }
             }
 
 
-    async def _ai_review_node(self, state: AIReviewState) -> AIReviewState:
-        """
-        AI审查节点
-
-        Args:
-            state: AI审查状态
-
-        Returns:
-            AIReviewState: 更新后的审查状态
-        """
-        try:
-            logger.info(f"AI审查节点开始执行,任务ID: {self.task_info.callback_task_id}")
-
-            # ⚠️ 检查终止信号(执行前)
-            if await self.workflow_manager.check_terminate_signal(state["callback_task_id"]):
-                logger.warning(f"AI审查节点检测到终止信号,任务ID: {state['callback_task_id']}")
-                return {
-                    "status": "terminated",
-                    "current_stage": "ai_review",
-                    "messages": [AIMessage(content="检测到终止信号")]
-                }
-
-            test_designation_chunk_flag = self.task_info.get_test_designation_chunk_flag()
-            logger.debug(f"测试定位标志: {test_designation_chunk_flag}")
-
-            # 1. 准备审查单元数据
-            review_chunks, total_units = await self.core_fun._prepare_review_units(state, test_designation_chunk_flag)
-
-            # 检查指定测试章节是否未找到
-            if test_designation_chunk_flag is not None and not review_chunks:
-                error_msg = f"AI审查测试失败:未找到指定审查标志「{test_designation_chunk_flag}」。请修改指定审查标识字段串,建议去除前后符号等(如书名号《》、括号()等),使用更简洁的关键词重新尝试。"
-                logger.error(f"🔴 {error_msg}")
-                return {
-                    "current_stage": "ai_review",
-                    "error_message": error_msg,
-                    "status": "failed",
-                    "messages": [AIMessage(content=error_msg)]
-                }
-
-            logger.info(f"准备审查单元完成,总单元数: {total_units}, 实际审查: {len(review_chunks)}")
-
-            if not review_chunks:
-                logger.warning(f"没有可审查的单元,任务ID: {state['callback_task_id']}")
-                return {
-                    "current_stage": "ai_review",
-                    "error_message": "没有可审查的单元",
-                    "status": "failed",
-                    "messages": [AIMessage(content=f"没有可审查的单元,任务ID: {state['callback_task_id']}")]
-                }
-
-            logger.info(f"开始核心审查,任务ID: {state['callback_task_id']}")
-            await self.core_fun._send_start_review_progress(state, total_units,'core_review')
-            # 2. 执行基础并发审查(内部会检测终止信号)
-            logger.info(f"开始执行并发审查,任务ID: {state['callback_task_id']}")
-            successful_results = await self.core_fun._execute_concurrent_reviews(
-                review_chunks, total_units, state, check_terminate=True
-            )
-            logger.info(f"并发审查完成,成功结果: {len(successful_results)}, 任务ID: {state['callback_task_id']}")
-
-            # ⚠️ 再次检查终止信号(并发审查后)
-            if await self.workflow_manager.check_terminate_signal(state["callback_task_id"]):
-                logger.warning(f"AI审查节点并发审查后检测到终止信号,任务ID: {state['callback_task_id']}")
-                return {
-                    "status": "terminated",
-                    "current_stage": "ai_review",
-                    "messages": [AIMessage(content="检测到终止信号")]
-                }
-
-            # 开始条文完整性审查
+    # async def _ai_review_node(self, state: AIReviewState) -> AIReviewState:
+    #     """
+    #     AI审查节点
+
+    #     Args:
+    #         state: AI审查状态
+
+    #     Returns:
+    #         AIReviewState: 更新后的审查状态
+    #     """
+    #     try:
+    #         logger.info(f"AI审查节点开始执行,任务ID: {self.task_info.callback_task_id}")
+
+    #         # ⚠️ 检查终止信号(执行前)
+    #         if await self.workflow_manager.check_terminate_signal(state["callback_task_id"]):
+    #             logger.warning(f"AI审查节点检测到终止信号,任务ID: {state['callback_task_id']}")
+    #             return {
+    #                 "status": "terminated",
+    #                 "current_stage": "ai_review",
+    #                 "messages": [AIMessage(content="检测到终止信号")]
+    #             }
+
+    #         test_designation_chunk_flag = self.task_info.get_test_designation_chunk_flag()
+    #         logger.debug(f"测试定位标志: {test_designation_chunk_flag}")
+
+    #         # 1. 准备审查单元数据
+    #         review_chunks, total_units = await self.core_fun._prepare_review_units(state, test_designation_chunk_flag)
+
+    #         # 检查指定测试章节是否未找到
+    #         if test_designation_chunk_flag is not None and not review_chunks:
+    #             error_msg = f"AI审查测试失败:未找到指定审查标志「{test_designation_chunk_flag}」。请修改指定审查标识字段串,建议去除前后符号等(如书名号《》、括号()等),使用更简洁的关键词重新尝试。"
+    #             logger.error(f"🔴 {error_msg}")
+    #             return {
+    #                 "current_stage": "ai_review",
+    #                 "error_message": error_msg,
+    #                 "status": "failed",
+    #                 "messages": [AIMessage(content=error_msg)]
+    #             }
+
+    #         logger.info(f"准备审查单元完成,总单元数: {total_units}, 实际审查: {len(review_chunks)}")
+
+    #         if not review_chunks:
+    #             logger.warning(f"没有可审查的单元,任务ID: {state['callback_task_id']}")
+    #             return {
+    #                 "current_stage": "ai_review",
+    #                 "error_message": "没有可审查的单元",
+    #                 "status": "failed",
+    #                 "messages": [AIMessage(content=f"没有可审查的单元,任务ID: {state['callback_task_id']}")]
+    #             }
+
+    #         logger.info(f"开始核心审查,任务ID: {state['callback_task_id']}")
+    #         await self.core_fun._send_start_review_progress(state, total_units,'core_review')
+    #         # 2. 执行基础并发审查(内部会检测终止信号)
+    #         logger.info(f"开始执行并发审查,任务ID: {state['callback_task_id']}")
+    #         successful_results = await self.core_fun._execute_concurrent_reviews(
+    #             review_chunks, total_units, state, check_terminate=True
+    #         )
+    #         logger.info(f"并发审查完成,成功结果: {len(successful_results)}, 任务ID: {state['callback_task_id']}")
+
+    #         # ⚠️ 再次检查终止信号(并发审查后)
+    #         if await self.workflow_manager.check_terminate_signal(state["callback_task_id"]):
+    #             logger.warning(f"AI审查节点并发审查后检测到终止信号,任务ID: {state['callback_task_id']}")
+    #             return {
+    #                 "status": "terminated",
+    #                 "current_stage": "ai_review",
+    #                 "messages": [AIMessage(content="检测到终止信号")]
+    #             }
+
+    #         # 开始条文完整性审查
             
             
 
 
-            # 开始大纲审查
-            await self.core_fun._send_start_review_progress(state, total_units,'outline')
-            completeness_check = "completeness_check" in  self.task_info.get_review_config_list()
-            if not completeness_check:
-                logger.info(f"跳过执行大纲审查")
-            else:
-            # 3. 执行大纲审查
-                logger.info(f"开始执行大纲审查")
-
-
-
-
-                check_completeness_result = await self.ai_review_engine.check_completeness(
-                    trace_id_idx = state["callback_task_id"],
-                    review_content = state["structured_content"]["chunks"],
-                    state = state,
-                    stage_name = state.get("stage_name", "完整性审查")
-                )
-                outline_review_result = {} 
-
-
-
-            # # 4. 执行编制依据审查
-            # #await self.core_fun._send_start_review_progress(state, total_units,'prpe_basis')
-            reference_check = "reference_check" in self.task_info.get_review_config_list()
-            timeliness_check = "timeliness_check" in self.task_info.get_review_config_list()
-            reference_check_result = None
-            timeliness_check_result = None
-
-            # 统一提取一次编制依据内容(任一审查开启时才提取)
-            basis_content = ""
-            basis_items = None
-            if reference_check or timeliness_check:
-                prep_basis_content = self._extract_prep_basis_content(state["structured_content"])
-                if prep_basis_content:
-                    try:
-                        basis_items = await extract_basis_with_langchain_qwen(
-                            progress_manager=state.get("progress_manager"),
-                            callback_task_id=state.get("callback_task_id"),
-                            text=prep_basis_content,
-                        )
-                        basis_content = "\n".join(
-                            [
-                                item.raw
-                                for item in getattr(basis_items, "items", [])
-                                if getattr(item, "raw", None)
-                            ]
-                        ).strip()
-                        if not basis_content:
-                            basis_content = prep_basis_content
-                        logger.info(
-                            f"编制依据AI提取完成,条数: {len(getattr(basis_items, 'items', []))}"
-                        )
-                    except Exception as e:
-                        logger.error(f"编制依据AI提取失败,回退原文: {e}", exc_info=True)
-                        basis_content = prep_basis_content
-                else:
-                    logger.warning(f"未找到编制依据内容,跳过编制依据审查准备")
-
-            logger.info(f"执行编制依据审查")
-            if not reference_check:
-                logger.info(f"跳过执行编制依据审查")
-            else:
-                if basis_content:
-                    logger.info(f"开始执行编制依据审查")
-
-                    prep_basis_review_data = {
-                        'content': basis_content,
-                        'basis_items': basis_items,
-                        'max_concurrent': self.max_concurrent
-                    }
-
-                    reference_check_result = await self.ai_review_engine.reference_basis_reviewer(
-                        review_data=prep_basis_review_data,
-                        trace_id=state["callback_task_id"],
-                        state=state,
-                        stage_name="编制依据审查"
-                    )
-                else:
-                    logger.warning(f"未找到编制依据内容,跳过编制依据审查")
-
-            logger.info(f"执行编制依据审查(时效性)")
-            if not timeliness_check:
-                logger.info(f"跳过执行编制依据审查(时效性)")
-            else:
-                if basis_content:
-                    logger.info(f"开始执行编制依据审查(时效性)")
-
-                    timeliness_check_data = {
-                        'content': basis_content,
-                        'basis_items': basis_items,
-                        'max_concurrent': self.max_concurrent
-                    }
-
-                    timeliness_check_result = await self.ai_review_engine.timeliness_basis_reviewer(
-                        review_data=timeliness_check_data,
-                        trace_id=state["callback_task_id"],
-                        state=state,
-                        stage_name="编制依据审查"
-                    )
-                else:
-                    logger.warning(f"未找到编制依据内容,跳过编制依据审查(时效性)")
-
-
-            # 6. 汇总结果
-            summary = self.inter_tool._aggregate_results(successful_results)
-
-            # 将所有单元的issues合并成一个列表
-            all_issues = []
-            if completeness_check:
-                all_issues.append(outline_review_result)
-                all_issues.append(check_completeness_result)
-            if reference_check and reference_check_result:
-                all_issues.append(reference_check_result)
-            if timeliness_check and timeliness_check_result:
-                all_issues.append(timeliness_check_result)
-            for unit_issues in successful_results:
-                if unit_issues and isinstance(unit_issues, list):
-                    all_issues.extend(unit_issues)
-
-            # 构建符合格式的review_results(兼容 execute() 方法的期望格式)
-            review_results = {
-                # 兼容旧版格式的字段
-                "total_units": total_units,
-                "successful_units": len(successful_results),
-                "failed_units": max(0, total_units - len(successful_results)),
-                "review_results": all_issues,  # 审查结果列表
-                "summary": summary,
-
-                # 额外的元信息
-                "callback_task_id": state["callback_task_id"],
-                "file_name": state.get("file_name", ""),
-                "user_id": state["user_id"],
-                "current": 100,
-                "stage_name": "完整审查结果",
-                "status": "full_review_result",
-                "message": f"审查完成,共发现{summary.get('total_issues', 0)}个问题",
-                "updated_at": int(time.time())
-            }
-
-            logger.info(f"AI审查节点执行成功,任务ID: {state['callback_task_id']}, 总单元数: {total_units}, 成功: {len(successful_results)}")
-
-            # 返回新的状态,避免原地修改导致的LangGraph冲突
-            return {
-                "current_stage": "ai_review_completed",
-                "review_results": review_results,
-                "status": "completed",
-                "messages": [AIMessage(content="AI审查完成")]
-            }
-
-        except Exception as e:
-            logger.error(f"AI审查节点执行失败,任务ID: {state['callback_task_id']}, 错误: {str(e)}", exc_info=True)
-
-            # 返回错误状态
-            return {
-                "current_stage": "ai_review_failed",
-                "error_message": str(e),
-                "status": "failed",
-                "messages": [AIMessage(content=f"AI审查失败: {str(e)}")]
-            }
+    #         # 开始大纲审查
+    #         await self.core_fun._send_start_review_progress(state, total_units,'outline')
+    #         completeness_check = "completeness_check" in  self.task_info.get_review_config_list()
+    #         if not completeness_check:
+    #             logger.info(f"跳过执行大纲审查")
+    #         else:
+    #         # 3. 执行大纲审查
+    #             logger.info(f"开始执行大纲审查")
+
+
+
+
+    #             check_completeness_result = await self.ai_review_engine.check_completeness(
+    #                 trace_id_idx = state["callback_task_id"],
+    #                 review_content = state["structured_content"]["chunks"],
+    #                 state = state,
+    #                 stage_name = state.get("stage_name", "完整性审查")
+    #             )
+    #             outline_review_result = {} 
+
+
+
+    #         # # 4. 执行编制依据审查
+    #         # #await self.core_fun._send_start_review_progress(state, total_units,'prpe_basis')
+    #         reference_check = "reference_check" in self.task_info.get_review_config_list()
+    #         timeliness_check = "timeliness_check" in self.task_info.get_review_config_list()
+    #         reference_check_result = None
+    #         timeliness_check_result = None
+
+    #         # 统一提取一次编制依据内容(任一审查开启时才提取)
+    #         basis_content = ""
+    #         basis_items = None
+    #         if reference_check or timeliness_check:
+    #             prep_basis_content = self._extract_prep_basis_content(state["structured_content"])
+    #             if prep_basis_content:
+    #                 try:
+    #                     basis_items = await extract_basis_with_langchain_qwen(
+    #                         progress_manager=state.get("progress_manager"),
+    #                         callback_task_id=state.get("callback_task_id"),
+    #                         text=prep_basis_content,
+    #                     )
+    #                     basis_content = "\n".join(
+    #                         [
+    #                             item.raw
+    #                             for item in getattr(basis_items, "items", [])
+    #                             if getattr(item, "raw", None)
+    #                         ]
+    #                     ).strip()
+    #                     if not basis_content:
+    #                         basis_content = prep_basis_content
+    #                     logger.info(
+    #                         f"编制依据AI提取完成,条数: {len(getattr(basis_items, 'items', []))}"
+    #                     )
+    #                 except Exception as e:
+    #                     logger.error(f"编制依据AI提取失败,回退原文: {e}", exc_info=True)
+    #                     basis_content = prep_basis_content
+    #             else:
+    #                 logger.warning(f"未找到编制依据内容,跳过编制依据审查准备")
+
+    #         logger.info(f"执行编制依据审查")
+    #         if not reference_check:
+    #             logger.info(f"跳过执行编制依据审查")
+    #         else:
+    #             if basis_content:
+    #                 logger.info(f"开始执行编制依据审查")
+
+    #                 prep_basis_review_data = {
+    #                     'content': basis_content,
+    #                     'basis_items': basis_items,
+    #                     'max_concurrent': self.max_concurrent
+    #                 }
+
+    #                 reference_check_result = await self.ai_review_engine.reference_basis_reviewer(
+    #                     review_data=prep_basis_review_data,
+    #                     trace_id=state["callback_task_id"],
+    #                     state=state,
+    #                     stage_name="编制依据审查"
+    #                 )
+    #             else:
+    #                 logger.warning(f"未找到编制依据内容,跳过编制依据审查")
+
+    #         logger.info(f"执行编制依据审查(时效性)")
+    #         if not timeliness_check:
+    #             logger.info(f"跳过执行编制依据审查(时效性)")
+    #         else:
+    #             if basis_content:
+    #                 logger.info(f"开始执行编制依据审查(时效性)")
+
+    #                 timeliness_check_data = {
+    #                     'content': basis_content,
+    #                     'basis_items': basis_items,
+    #                     'max_concurrent': self.max_concurrent
+    #                 }
+
+    #                 timeliness_check_result = await self.ai_review_engine.timeliness_basis_reviewer(
+    #                     review_data=timeliness_check_data,
+    #                     trace_id=state["callback_task_id"],
+    #                     state=state,
+    #                     stage_name="编制依据审查"
+    #                 )
+    #             else:
+    #                 logger.warning(f"未找到编制依据内容,跳过编制依据审查(时效性)")
+
+
+    #         # 6. 汇总结果
+    #         summary = self.inter_tool._aggregate_results(successful_results)
+
+    #         # 将所有单元的issues合并成一个列表
+    #         all_issues = []
+    #         if completeness_check:
+    #             all_issues.append(outline_review_result)
+    #             all_issues.append(check_completeness_result)
+    #         if reference_check and reference_check_result:
+    #             all_issues.append(reference_check_result)
+    #         if timeliness_check and timeliness_check_result:
+    #             all_issues.append(timeliness_check_result)
+    #         for unit_issues in successful_results:
+    #             if unit_issues and isinstance(unit_issues, list):
+    #                 all_issues.extend(unit_issues)
+
+    #         # 构建符合格式的review_results(兼容 execute() 方法的期望格式)
+    #         review_results = {
+    #             # 兼容旧版格式的字段
+    #             "total_units": total_units,
+    #             "successful_units": len(successful_results),
+    #             "failed_units": max(0, total_units - len(successful_results)),
+    #             "review_results": all_issues,  # 审查结果列表
+    #             "summary": summary,
+
+    #             # 额外的元信息
+    #             "callback_task_id": state["callback_task_id"],
+    #             "file_name": state.get("file_name", ""),
+    #             "user_id": state["user_id"],
+    #             "current": 100,
+    #             "stage_name": "完整审查结果",
+    #             "status": "full_review_result",
+    #             "message": f"审查完成,共发现{summary.get('total_issues', 0)}个问题",
+    #             "updated_at": int(time.time())
+    #         }
+
+    #         logger.info(f"AI审查节点执行成功,任务ID: {state['callback_task_id']}, 总单元数: {total_units}, 成功: {len(successful_results)}")
+
+    #         # 返回新的状态,避免原地修改导致的LangGraph冲突
+    #         return {
+    #             "current_stage": "ai_review_completed",
+    #             "review_results": review_results,
+    #             "status": "completed",
+    #             "messages": [AIMessage(content="AI审查完成")]
+    #         }
+
+    #     except Exception as e:
+    #         logger.error(f"AI审查节点执行失败,任务ID: {state['callback_task_id']}, 错误: {str(e)}", exc_info=True)
+
+    #         # 返回错误状态
+    #         return {
+    #             "current_stage": "ai_review_failed",
+    #             "error_message": str(e),
+    #             "status": "failed",
+    #             "messages": [AIMessage(content=f"AI审查失败: {str(e)}")]
+    #         }
 
 
     async def _save_results_node(self, state: AIReviewState) -> AIReviewState:
     async def _save_results_node(self, state: AIReviewState) -> AIReviewState:
         """
         """

+ 518 - 0
core/construction_write/component/prompt/keyword_rules_3.json

@@ -0,0 +1,518 @@
+[
+  {
+    "basis_LawsAndRegulations_NationalLawsAndRegulations": "国家级、法律、法规、规章、强制力、普遍适用、基础框架、顶层设计、行业准则、合规性、统一标准、权威性、强制性条文、基本要求。"
+  },
+  {
+    "basis_LawsAndRegulations_ProvincialLawsAndRegulationsOfProjectLocation": "地方性、区域性、细化补充、因地制宜、执行细则、地方特色、适应性要求、属地管理、动态调整、配套政策、本地化实施。"
+  },
+  {
+    "basis_StandardsAndSpecifications_IndustryStandards": "需符合国家/行业强制或推荐性标准(如GB/T、JTG等),时效性强(需跟踪最新版)、覆盖全生命周期(设计→施工→运维),是定义工程项目的最低技术要求、质量验收准则、安全红线。"
+  },
+  {
+    "basis_StandardsAndSpecifications_TechnicalRegulations": "操作流程标准化,工艺参数,量化风险管控,节点设备使用,规范施工验收细则,人员资质要求,应急预案模板,数字化交付标准,BIM协同规则,绿色施工指标,强制性条款需100%执行(如安全操作规范),包含可视化元素(图表、流程图、三维模型指引),与BIM技术深度融合(4D进度模拟、5D成本控制)。"
+  },
+  {
+    "basis_DocumentSystems_SichuanRoadAndBridgeDocumentSystemsAndManagementProcedures": "需包含集团级BIM实施标准、EPC总承包管理模式细则,强制要求下属单位接入集团统一的数字化管理平台(如PM系统),设置科技创新成果转化的量化考核指标。"
+  },
+  {
+    "basis_DocumentSystems_RoadAndBridgeGroupDocumentSystemsAndManagementProcedures": "区域化管理细则,属地化政策适配,项目分级管控,应急响应机制,分包商信用评价,农民工工资支付,保障绿色施工区域,标准智慧工地建设指南,隐蔽工程验收流程,工程变更索赔指引。"
+  },
+  {
+    "basis_DocumentSystems_BridgeCompanyDocumentSystemsAndManagementProcedures": "专业技术标准,工艺工法创新,特种设备管理,试验检测规程,安全生产责任制,班前安全教育工程,质量三检制技术交底,标准化竣工资料归档,规范创优工程培育计划。"
+  },
+  {
+    "basis_DocumentSystems_ConstructionUnitDocumentSystemsAndManagementProcedures": "项目合同履约,工程款支付管理,设计变更审批,竣工验收标准,运营移交协议,保修期责任划分,参建单位考核评价,档案管理实施细则,信息沟通机制,争议解决程序。"
+  },
+  {
+    "basis_CompilationPrinciples_NationalPoliciesStandardsAndDesignDocument": "需动态更新(如新版《公路工程技术标准》实施后同步调整),涉及多部门联合审查(发改委、住建部、生态环境部)。\n**重点** focus:\n盖国家战略导向,法规强制性,设计文件权威性,技术基准统一合规性,底线政策更新,跟踪跨部门协同依据。"
+  },
+  {
+    "basis_CompilationPrinciples_BasicConstructionProcedures": "法定程序刚性,四阶段闭环管理,审批链条完整性,阶段成果验收,逆程序风险管控,数字化流程跟踪。\n**重点** focus:\n\n- 不同类型项目(房建/市政/交通)存在程序差异(如公路需行业主管部门专项审批) \n- 重大变更需重新履行审批程序(如超概算10%以上)"
+  },
+  {
+    "basis_CompilationPrinciples_ProjectFunctionImplementation": "需求匹配度,全功能交付,使用效能保障,用户需求反演,系统集成测试,缺陷责任期追溯\n**重点** focus:\n\n- 需编制《功能需求说明书》作为验收依据 \n- 涉及多方利益相关者(业主、监理、最终用户)的功能确认"
+  },
+  {
+    "basis_CompilationPrinciples_ContractPerformance": "契约精神,权利义务对等,支付节点刚性,变更索赔闭环,信用评价联动,争议解决机制\n**重点** focus:\n\n- 需同步签订《廉政协议》作为合同附件 \n- 履约评价结果直接影响后续项目投标资格"
+  },
+  {
+    "basis_CompilationPrinciples_ConstructionForceConcentration": "资源集约化,专业化,班组机械配置标准化,劳动力调度,算法工序穿插优化,进度风险预警"
+  },
+  {
+    "basis_CompilationPrinciples_ProcessControl": "工序逻辑链,工艺标准化,交接检验制度化,关键线路动态监测,平行检验机制,隐蔽工程追溯"
+  },
+  {
+    "basis_CompilationScope_ProjectCoverage": "项目范围完整性,子项划分颗粒度,工程量清单闭合性,专业界面划分,变更管理阈值,风险识别矩阵价,值工程分析,可施工性评审,全生命周期覆盖,涉密工程隔离。"
+  },
+  {
+    "basis_CompilationScope_ConstructionTechnology": "工艺标准化体系,工法创新等级,质量控制关键点,机械化作业率,绿色施工技术,BIM协同设计,装配式构件应用,智能监测覆盖率,特殊环境适应性,非遗工艺传承。"
+  },
+  {
+    "overview_DesignSummary_ProjectIntroduction": "工程名称、工程类型(如住宅、桥梁、隧道)、建设规模(如建筑面积、长度、高度)、工程地址、投资额、工程性质(新建/改建/扩建)、设计单位、设计依据(如合同编号)、工程范围(如施工边界坐标)。\n**重点** focus:\n关注名称类,具体数值类,量化单位类。"
+  },
+  {
+    "overview_DesignSummary_MainTechnicalStandards": "技术规范编号(如GB50021-2001《岩土工程勘察规范》)、设计使用年限、荷载标准(如活荷载、恒荷载)、抗震设防烈度、防火等级、环保标准(如绿色建筑认证)、材料标准(如混凝土强度等级C30)、施工工艺标准。\n**重点** focus:\n技术标准需引用国家或行业规范,关注标准号数字类,各类年限、等级数值、量化单位。"
+  },
+  {
+    "overview_GeologyWeather_HydrologicalConditions": "地下水位(历史最高水位、当前水位)、含水层类型(孔隙水、裂隙水、承压水)、隔水层厚度、渗透系数(K值)、给水度、水质腐蚀性(如pH值、氯离子含量)、补径排条件(补给源、径流方向)、水文地质参数(如越流系数)、地下水动态监测数据。\n**重点** focus:\n关注地名类,具体水位数值类,量化单位类。"
+  },
+  {
+    "overview_GeologyWeather_ClimaticConditions": "气候类型(如亚热带季风气候)、年平均气温、极端气温、年降水量、降雨强度(如小时最大降雨量)、蒸发量、湿度、主导风向、风速、冰冻期、台风频率、气象数据来源(如当地气象站)。\n**重点** focus:\n关注记录降雨量、水位、气温等数值量化单位类。"
+  },
+  {
+    "overview_Surroundings_PositionalRelationship": "相邻建(构)筑物(如住宅楼、桥梁)、距离(米)、方位角、山体坡度(°)、边坡稳定性(如安全系数)、河谷宽度、深基坑深度(米)、道路等级(如城市主干道)、高压电电压(kV)、地下管线类型(给水、燃气、电缆)、埋深(米)、保护距离。\n**重点** focus:\n周边环境需分析地形地貌名词、建筑分布名词及交通状况道路名词、相关量化单位,相关数字数值。"
+  },
+  {
+    "overview_Surroundings_StructuralDimensions": "建筑物高度/层数、山体海拔、边坡坡比(如1:1.5)、河谷断面尺寸、深基坑支护结构(如桩径、墙厚)、道路宽度(米)、高压电塔高度、地下管线段径(如DN100)。\n**重点** focus:\n各类专业性名词,建筑物名词,米或毫米等单位类。"
+  },
+  {
+    "overview_LayoutPlan_TemporaryFacilityLocation": "拌和站坐标(如X,Y)、钢筋加工场距工程距离(米)、材料堆码区域面积(㎡)、临时占地红线、与工程最近点距离、场地利用率(%)。\n**重点** focus:\n场地名词,数值类,量化单位类。"
+  },
+  {
+    "overview_LayoutPlan_ConstructionWorkPlatform": "作业平台尺寸(长×宽,米)、地面形式(混凝土硬化、砂石铺垫)、施工便道长度(米)、宽度(米)、路面形式(沥青、碎石)、最小弯曲半径(米)、坡度(%)、承载力(kPa)。\n**重点** focus:\n场地名词,数值类,量化单位类。"
+  },
+  {
+    "overview_LayoutPlan_TemporaryWaterAndElectricityArrangement": "临时用水源(市政管网、地下水井)、管径(如DN100)、管线布置图(走向、节点距离)、供水压力(MPa)、变压器容量(kVA)、配电箱位置、线路走向(架空/埋地)、敷设方式(直埋、穿管)、电缆规格(如YJV22)。\n**重点** focus:\n名称类,数值类,量化单位类。"
+  },
+  {
+    "overview_RequirementsTech_DurationTarget": "开工日期(年月日)、竣工日期、总工期(天)、关键节点工期(如基础完工期)、进度计划(甘特图编号)、工期保证措施(如资源调配)。\n**重点** focus:\n名称类,日期类。"
+  },
+  {
+    "overview_RequirementsTech_QualityTarget": "质量目标(如合格率100%、鲁班奖)、合同条款编号、业主具体要求(如绿色施工认证)。\n**重点** focus:\n名称类,量化单位类,数值类。"
+  },
+  {
+    "overview_RequirementsTech_SecurityGoals": "安全目标(如零死亡事故、隐患整改率)、合同条款编号、业主具体要求(如绿色施工认证)。\n**重点** focus:\n名称类,量化单位类,数值类。"
+  },
+  {
+    "overview_RequirementsTech_EnvironmentalGoals": "环境目标(如扬尘控制、噪声限值)、合同条款编号、业主具体要求(如绿色施工认证)。\n**重点** focus:\n名称类,量化单位类,数值类。"
+  },
+  {
+    "overview_RiskLevel_DangerSource": "地质灾害(地面沉降、滑坡)、水文风险(管涌、流砂)、施工风险(坍塌、触电)、环境风险(污染、火灾)、机械伤害(塔吊倾覆)、法律法规依据(如《安全生产法》)。\n**重点** focus:\n危害隐患性词汇类,法规名称类、标准编号类。"
+  },
+  {
+    "overview_RiskLevel_ClassificationAndResponseMeasures": "风险等级(重大、较大、一般)、分级标准(如LEC法)、应对措施(监测、支护、疏散)、应急预案编号、责任部门、监控频率。\n**重点** focus:\n风险等级相关专业性词汇、属于、标准编号或其他编号,部门名称类、数值类,量化单位类。"
+  },
+  {
+    "overview_Stakeholders_UnitType": "建设单位(业主)、设计单位、监理单位、施工单位(总包)、监控单位(监测机构)、专业分包单位(如桩基分包)、统一社会信用代码、项目负责人。\n**重点** focus:\n名称类,数值类。"
+  },
+  {
+    "plan_Schedule_ProcessOperationTimeAnalysis": "需明确各工序的持续时间、逻辑关系及资源需求,是进度计划的基础;"
+  },
+  {
+    "plan_Schedule_KeyProjectNodeArrangement": "主要工程(工序)节点的起止时间和持续时间,聚焦影响总工期的关键工序(如基础浇筑、主体封顶),是进度控制的核心;"
+  },
+  {
+    "plan_Schedule_ConstructionScheduleGanttChart": "直观展示进度安排的标准工具,需包含主要工序名称、起始时间、截止时间、持续时间、时间横道、责任人等信息;"
+  },
+  {
+    "plan_Materials_ListOfConstructionMeasuresAndMaterials": "排除主题工程材料,施工措施材料应包含如临时支撑结构材料、辅助施工材料、非主体工程的挡防措施、作业平台处理、模板配置、人员上下通道、安全防护措施和安全防护用品等,详细列出材料名称、规格、数量、重量及来源(如厂家、经销商),是材料计划的核心输出\n**重点** focus:\n名称类、规格类、数值类、数值单位类"
+  },
+  {
+    "plan_Equipment_MainConstructionMachineryAndEquipment": "列出关键设备(如起重吊装设备、混凝土浇筑设备、张拉压浆设备、人员升降设备、钻孔设备、隧道专用设备、监测\n监控设备、质量检查验收设备等),明确其设备名称、规格(如额定功率)、数量及来源(自有或租赁);\n**重点** focus:\n设备名称类、规格类、数值类、数值单位类、时间日期类"
+  },
+  {
+    "plan_Workforce_WorkforceAllocationPlan": "明确工种投入(如木工、钢筋工等)情况,按施工阶段(如基础、主体、装饰)列出各工种(如模板工、混凝土工)的投入数量,确保劳动力与进度匹配;"
+  },
+  {
+    "plan_Workforce_StageLaborDemand": "明确周/旬/月的劳动力峰值及低谷,优化人员调度;"
+  },
+  {
+    "plan_SafetyCost_CategoryOfSafetyProductionExpenses": "符合《企业安全生产费用提取和使用管理办法》(财资〔2022〕136号)及地方规定(如广东省水利厅2025年办法),如安全防护设施、应急救援等;"
+  },
+  {
+    "plan_SafetyCost_SecurityFeeName": "具体(如“施工现场临时用电系统改造”“应急救援器材采购”),避免模糊表述;"
+  },
+  {
+    "plan_SafetyCost_SingleInvestmentAmount": "明确每项费用的具体数值(如“临时防护栏杆采购:5万元”),确保费用可量化;"
+  },
+  {
+    "plan_SafetyCost_TotalSafetyProductionExpenses": "根据工程规模、风险等级计算,确保足额投入"
+  },
+  {
+    "technology_MethodsOverview_ConstructionTechnologySelection": "需明确工程采用的核心工艺(如“现浇混凝土框架工艺”“装配式构件安装工艺”),是施工方法概述的基础;"
+  },
+  {
+    "technology_MethodsOverview_MainConstructionMethods": "需概括各分部分项工程的关键做法(如“基础采用旋挖钻孔灌注桩施工”“主体采用铝模板体系施工”);"
+  },
+  {
+    "technology_MethodsOverview_TemplateConfigurationQuantity": "需根据施工进度和构件尺寸计算(如“柱模板配置20套”“梁模板配置15套”),是模板管理的关键指标;"
+  },
+  {
+    "technology_TechParams_MaterialType": "需明确主要材料的类别(如“钢筋”“混凝土”“防水卷材”),是技术参数的基础;"
+  },
+  {
+    "technology_TechParams_MaterialSpecifications": "需细化材料的尺寸、型号(如“钢筋HRB400EΦ16”“混凝土C30P6”),直接影响工程质量;"
+  },
+  {
+    "technology_TechParams_DeviceName": "需列出关键设备的全称(如“挖掘机”“塔式起重机”“混凝土泵车”),是设备管理的核心;"
+  },
+  {
+    "technology_TechParams_DeviceModel": "需明确设备的规格型号(如“徐工XE200挖掘机”“中联重科TC6013塔式起重机”),用于设备的采购和维护;"
+  },
+  {
+    "technology_TechParams_EquipmentPerformanceParameters": "需包含设备的额定功率、工作效率等(如“塔式起重机最大起重量8t”“混凝土泵车输送量60m³/h”),是设备选型的依据;"
+  },
+  {
+    "technology_TechParams_EquipmentManufacturingTime": "需包含设备的出厂时间"
+  },
+  {
+    "technology_TechParams_EquipmentWeight": "需记录设备的自身重量(如“塔式起重机自重50t”),用于基础设计和运输规划。"
+  },
+  {
+    "technology_PrepWork_MeasurementAndStakeout": "需明确测量的基准点、控制网设置(如“建立施工平面控制网”“放出建筑物轴线”),是施工定位的关键;"
+  },
+  {
+    "technology_PrepWork_TemporaryWaterAndElectricityConsumption": "需计算施工期间的用水、用电量(如“临时用水管径DN100”“临时用电容量500kW”),用于临时设施的设计;"
+  },
+  {
+    "technology_PrepWork_TheSiteIsFlat": "需明确平整的范围、标高(如“平整场地至设计标高±0.000”“压实度达到90%”),是施工场地准备的基础;"
+  },
+  {
+    "technology_PrepWork_Staffing": "需列出各岗位的人员数量(如“项目经理1名”“施工员2名”“钢筋工10名”),是劳动力管理的核心;"
+  },
+  {
+    "technology_PrepWork_EquipmentEntry": "需明确设备的进场时间、运输方式(如“塔式起重机进场时间2026年3月1日”“采用平板车运输”),是设备准备的关键;"
+  },
+  {
+    "technology_PrepWork_SafetyProtectionFacilities": "需列出现场的安全设施(如“安全网”“防护栏杆”“消防栓”),是安全保障的基础;"
+  },
+  {
+    "technology_PrepWork_PersonnelAccess": "需明确通道的形式、位置(如“楼梯间通道”“脚手架斜道”),是人员通行的安全保障。"
+  },
+  {
+    "technology_Process_ConstructionProcess": "需列出工程的主要工序(如“地基处理→基础浇筑→主体结构→装饰装修”),是工艺流程的核心;"
+  },
+  {
+    "technology_Process_ProcessSequence": "需明确工序的先后逻辑(如“先绑扎钢筋后支模板”“先浇筑混凝土后养护”),是流程执行的关键;"
+  },
+  {
+    "technology_Process_ProcessFlowDiagram": "需用图形展示工序的衔接(如“地基处理流程图”“主体结构施工流程图”),是流程可视化的工具;"
+  },
+  {
+    "technology_Operations_ConstructionProcessOperations": "需详细描述各工序的操作步骤(如“钢筋绑扎操作流程”“模板安装操作步骤”),是操作指导的核心;"
+  },
+  {
+    "technology_Operations_ConstructionPoints": "需明确工序的关键要求(如“钢筋绑扎需保证间距均匀”“模板安装需保证垂直度”),是质量控制的关键;"
+  },
+  {
+    "technology_Operations_FAQPrevention": "需列出工序的常见问题及预防措施(如“预防混凝土蜂窝麻面:控制混凝土坍落度”“预防模板漏浆:密封模板缝隙”),是风险防控的重点 focus:;"
+  },
+  {
+    "technology_Operations_ProblemSolvingMeasures": "需明确问题的解决方法(如“混凝土蜂窝处理:剔除松散部分,用高一等级混凝土填补”“模板漏浆处理:用海绵条密封缝隙”),是问题解决的指南;"
+  },
+  {
+    "technology_Inspection_MaterialInspectionUponArrival": "需明确材料的检验项目(如“钢筋的屈服强度检验”“混凝土的抗压强度检验”),是材料质量控制的基础;"
+  },
+  {
+    "technology_Inspection_RandomInspectionOfIncomingComponents": "需明确构配件的抽查比例(如“构配件抽查比例为10%”“每批抽查5件”),是构配件质量控制的手段;"
+  },
+  {
+    "technology_Inspection_ProcessInspectionContent": "需列出各工序的检查项目(如“钢筋绑扎的检查内容:间距、数量、锚固长度”“模板安装的检查内容:垂直度、平整度、支撑稳定性”),是工序检查的核心;"
+  },
+  {
+    "technology_Inspection_ProcessInspectionStandards": "需明确检查的合格标准(如“钢筋间距允许偏差±10mm”“模板垂直度允许偏差5mm”),是工序验收的依据;"
+  },
+  {
+    "safety_SafetySystem_SafetyProductionAssuranceSystemFrameworkDiagram": "安全保证体系的视觉化呈现,需明确体系的核心要素(如组织机构、制度流程、资源保障)及逻辑关系,是公司标准体系的具象化载体;"
+  },
+  {
+    "safety_SafetySystem_CompanyStandardSystemReference": "强调安全保证体系需承接公司现有标准(如《公司安全生产管理办法》《公司安全技术规程》),确保体系的一致性与延续性;"
+  },
+  {
+    "safety_Organization_SafetyManagementOrganization": "基于项目经理为组长的安全工作领导小组,关注岗位组织架构名称类,部门名称类,关系结构类名词;"
+  },
+  {
+    "safety_Organization_PersonnelSafetyResponsibilities": "关注岗位名称类,人名类,责任制度名词类,岗位职责名词类,安全制度名词类;"
+  },
+  {
+    "safety_TechMeasures_OverallSecurityMeasures": "包含保证施工过程中主要工序的人员、材料、机械设备安全所采取的技术措施,以及材料运输、吊装,施工作业区域的临边、临空、洞口安全防护设施、安全母绳布\n置,人员上下(横向)通道布置等,是针对项目整体的安全技术规划(如“施工现场临时用电总体方案”“高空作业总体防护措施”),需覆盖所有施工环节;"
+  },
+  {
+    "safety_TechMeasures_SafetyAssuranceMeasuresForKeyProcesses": "是针对关键工序的具体安全要求(如“深基坑开挖支护措施”“模板安装拆除安全规范”),需明确每一步操作的安全要点;"
+  },
+  {
+    "safety_Monitoring_MonitoringOrganization": "监测监控的责任主体,需明确监测人员的资质(如注册安全工程师、监测技术员)及职责(如数据采集、分析、报告),确保监测工作的专业性;"
+  },
+  {
+    "safety_Monitoring_MonitoringRange": "需覆盖施工区域内的所有风险点(如深基坑周边、高支模体系、临时用电线路),避免遗漏;"
+  },
+  {
+    "safety_Monitoring_MonitoringItems": "需明确监测的具体内容(如深基坑的水平位移、高支模的立杆轴力、临时用电的电压电流),是监测的核心;"
+  },
+  {
+    "safety_Monitoring_MonitoringPointSettings": "需根据风险点的分布确定(如深基坑每10米设置一个位移监测点),需符合《建筑基坑支护技术规程》(JGJ 120-2012)等行业标准;"
+  },
+  {
+    "safety_Monitoring_MonitoringInstrumentsAndEquipment": "需明确仪器的名称(如全站仪、测斜仪、应力传感器)、型号(如徕卡TS60全站仪)及精度(如0.5秒级),确保数据的准确性;"
+  },
+  {
+    "safety_Monitoring_MonitoringMethods": "需明确数据采集的方式(如人工读数、自动采集)、及数据处理方法(如统计分析、趋势预测),是监测的关键环节;"
+  },
+  {
+    "safety_Monitoring_MonitoringFrequency": "需明确监测频率,(如深基坑每天一次,高支模每周两次)。"
+  },
+  {
+    "safety_Monitoring_WarningValuesAndControlValues": "需根据设计文件及行业标准确定(如深基坑水平位移预警值为30mm,控制值为50mm),是判断风险的重要依据;"
+  },
+  {
+    "safety_Monitoring_InformationFeedbackMechanism": "监测数据的传递流程(如监测人员→项目安全部→项目经理→公司总部),需明确反馈的时间要求(如实时反馈、每日汇总),确保风险及时处理。"
+  },
+  {
+    "safety_Emergency_EmergencyProcedures": "应采用公司标准应急处理程序图(附件16),应急响应的步骤流程(如“事故报告→现场警戒→人员疏散→救援实施→善后处理”),需明确每一步的责任部门及时间要求,确保响应及时;"
+  },
+  {
+    "safety_Emergency_EmergencyMeasures": "应根据方案实施过程潜在的危险源,判断出可能造成的伤害类型,制定出有针对性的救援措施,保证在事故发生后伤者能得到有效和及时的救治,如触电、有毒有害气体中毒、高处坠落、物体打击、施工现场及驻地火灾等事故,针对不同类型事故的具体处理方法(如“火灾事故使用干粉灭火器扑救”“坍塌事故使用千斤顶支撑”),需明确操作要点(如灭火器的使用方法、千斤顶的支撑位置),确保救援有效;"
+  },
+  {
+    "safety_Emergency_EmergencySuppliesAndEquipmentSupport": "应根据事故的不同,以表格的形式说明救援的物品名称、规格型号、单位、数量、监管人、联系电话等内容,应急处置的物质基础,需明确物资的名称(如灭火器、急救箱、千斤顶)、数量(如每100平方米配备2个灭火器)、存放位置(如施工现场入口处)及维护要求(如每月检查一次灭火器压力),确保物资随时可用;"
+  },
+  {
+    "safety_Emergency_TrafficManagementAndMedicalRescue": "应以表格的形式明确施工工点附近的医疗救援机构名称、联系电话、距离等,并附应急救援线路图;"
+  },
+  {
+    "safety_Emergency_Post-disposal": "包括善后处理、调查与评估、恢复生产等三个方面,事故后的恢复工作,需明确善后处理(如伤亡人员家属安抚、财产损失统计)、事故调查(如原因分析、责任认定评估)及整改措施(如完善安全制度、加强培训),避免事故重复发生;"
+  },
+  {
+    "quality_QualitySystem_QualityAssuranceSystemFramework": "应引用公司标准体系框图,质量体系的视觉化呈现,需明确体系的核心要素(如组织机构、制度流程、资源保障)及逻辑关系,是体系落地的框架基础;"
+  },
+  {
+    "quality_QualitySystem_QualityManagementOrganization": "基于项目经理为组长的工作领导小组,小组中包括项目经理、项目总工、质量总监、工程部门、质检部门、专业分包单位(协作队伍)项目负责人和项目技术负责人等,需明确层级(如公司级、项目级、班组级)及组成部门(如质量部、工程部、技术部),形成“横向到边、纵向到底”的管理网络;"
+  },
+  {
+    "quality_QualitySystem_PersonnelResponsibilities": "需细化每个岗位的质量责任(如项目经理的“第一责任人”职责、质量员的“现场监督”职责),避免职责模糊导致的管理漏洞;"
+  },
+  {
+    "quality_QualityGoals_DecompositionOfQualityObjectives": "根据施工合同和业主要求填写,需将总目标拆解为分部(基础、主体、装饰)、分项工程的具体目标(如“主体结构混凝土强度合格率100%”),是目标落地的关键;\n**重点** focus:\n目标标准词汇类、合同条款类、具体工程名称类、量化数值类、数值单位类。"
+  },
+  {
+    "quality_Excellence_OverallPlanForEngineeringExcellence": "需明确创优的阶段目标(如“基础工程创优”“主体工程创优”)及关键节点,是创优工作的路线图;"
+  },
+  {
+    "quality_Excellence_TechnicalPreparation": "需涵盖BIM技术应用、施工方案优化等,为创优提供技术支撑;"
+  },
+  {
+    "quality_Excellence_ProcessControl": "需聚焦关键工序(如“大体积混凝土浇筑”“钢结构安装”),打造精品工序;"
+  },
+  {
+    "quality_Excellence_DetailedTreatment": "需优化节点做法(如“墙面抹灰阴阳角顺直”“防水卷材搭接严密”),提升工程观感质量;"
+  },
+  {
+    "quality_Excellence_NewTechnologyPromotion": "需应用“四新技术”(新技术、新材料、新工艺、新设备),提升创优的技术含量;"
+  },
+  {
+    "quality_Excellence_PreparationOfApplicationMaterials": "需整理创优所需的资料(如工程质量报告、技术创新成果),是创优申报的核心材料;"
+  },
+  {
+    "quality_Excellence_EngineeringDataArchiving": "需确保资料真实、完整,符合创优评审要求。"
+  },
+  {
+    "quality_QualityControl_RawMaterialInspection": "需执行“三证一检”(合格证、质检报告、生产许可证+进场复检),确保材料质量;"
+  },
+  {
+    "quality_QualityControl_PhysicalProjectQualityAcceptance": "需按分项(如“钢筋绑扎”)、分部工程(如“基础工程”)进行验收,符合规范要求;"
+  },
+  {
+    "quality_QualityControl_PreventionAndControlOfCommonQualityDefectsInProcesses": "需针对常见问题(如“墙面空鼓”“屋面渗漏”)制定专项措施(如“抹灰前基层凿毛”“防水附加层施工”),减少质量缺陷;"
+  },
+  {
+    "quality_QualityControl_SeasonalConstructionQualityAssuranceMeasures": "需针对冬期(混凝土保温)、雨期(防水加强)、高温(混凝土保湿)制定专项措施,确保施工质量;"
+  },
+  {
+    "environment_EnvSystem_EnvironmentalAssuranceSystemFramework": "环境保证体系的视觉化呈现,需明确体系的核心要素(如组织机构、制度流程、资源保障)及逻辑关系,是公司标准体系的具象化载体;"
+  },
+  {
+    "environment_EnvSystem_CompanyStandardSystemReference": "应引用公司标准体系框图,强调环境保证体系需承接公司现有标准(如《公司环境管理体系手册》《公司环境保护管理办法》),确保体系的一致性与延续性;"
+  },
+  {
+    "environment_EnvOrg_EnvironmentalAssuranceSystemFramework": "包含管理人员姓名、职务、职责,环境管理的责任主体,基于项目经理为组长的工作领导小组,小组中包括项目经理、项目副经理、项目总工、工程部门、质检部门、安全环保部门、专业分包单位(协作队伍)项目负责人和项目技术负责人等,需明确机构的层级(如公司级、项目级、班组级)及组成部门(如环境部、工程部、技术部),形成“横向到边、纵向到底”的管理网络;"
+  },
+  {
+    "environment_EnvOrg_EnvironmentalManagementJobResponsibilities": "需明确各岗位的环境责任(如项目经理的“环境第一责任人”职责、环境专员的“现场巡查”职责),是组织保证的基石;"
+  },
+  {
+    "environment_EnvOrg_ResponsibilityAssessmentMechanism": "对环境职责履行情况的评价方式(如月度考核、年度评优),需与环境绩效挂钩(如奖金发放、晋升晋级),强化责任意识。"
+  },
+  {
+    "environment_EnvProtection_EnvironmentalSanitationGuaranteeMeasuresForOfficeAndLiving_areas": "需明确责任分工(如保洁人员配置、卫生区域划分)及管理流程(如每日清扫、每周检查),确保环境整洁;"
+  },
+  {
+    "environment_EnvProtection_SoilAndWaterConservationMeasuresInTheConstructionArea": "需通过“截(截水沟)、排(排水沟)、拦(拦挡坝)、护(边坡防护)”综合措施,减少雨水对裸露土壤的冲刷;"
+  },
+  {
+    "environment_EnvProtection_NoiseEmissionMonitoring": "需按照《建筑施工场界环境噪声排放标准》(GB 12523-2011)要求,在施工现场边界设置监测点,每日监测1次,记录等效声级(Leq)和最大声级(Lmax);"
+  },
+  {
+    "environment_EnvProtection_WaterPollutionPreventionAndControlMeasures": "需在搅拌机、运输车清洗处设置沉淀池,施工废水经沉淀后回用(如洒水降尘),避免直接排入市政管网;"
+  },
+  {
+    "environment_EnvProtection_AirPollutionPreventionAndControlMeasures": "需采取“洒水降尘、裸土覆盖、车辆冲洗、道路硬化”等措施,确保施工现场目测扬尘高度小于1.5m(土方作业阶段)或0.5m(结构施工阶段)。"
+  },
+  {
+    "Management_Managers_ConstructionManagementPersonnelList": "需以表格形式明确项目管理人员(如项目经理、项目书记、项目总工、项目副经理、质量总监、安全总监、各职能部门、主管技术员、测量员、质检员,以及专业分包单位(协作队伍)项目负责人和项目技术负责人等)的姓名、岗位及联系方式,是人员管理的基础台账;"
+  },
+  {
+    "Management_Managers_JobResponsibilitiesList": "需细化每个管理岗位的职责(如项目经理的“项目全面管理”职责、技术负责人的“技术方案审核”职责),避免职责模糊导致的管理漏洞;"
+  },
+  {
+    "Management_SafetyStaff_ListOfFullTimeSafetyProductionManagementPersonnel": "需以表格形式明确专职安全员(如项目安全总监、专职安全员)的姓名、岗位及联系方式,是安全管理的核心台账;"
+  },
+  {
+    "Management_SafetyStaff_SafetyProductionQualificationCertificate": "需明确证书类型(如“建筑施工企业专职安全生产管理人员证书”)、编号及有效期,是上岗的必备资质;"
+  },
+  {
+    "Management_SafetyStaff_SafetyProductionManagementJobResponsibilities": "需细化专职安全员的职责(如“现场安全检查”“隐患整改监督”“安全培训实施”),确保安全管理工作落地;"
+  },
+  {
+    "Management_SpecialWorkers_ListOfSecialOperationsPersonnel": "需以表格形式明确特种作业人员(如建筑电工、建筑架子工、建筑起重机械司机等)的姓名、工种及联系方式,是特种作业管理的基础台账;"
+  },
+  {
+    "Management_SpecialWorkers_SpecialOperationsQualificationCertificate": "需明确证书类型(如“建筑施工特种作业操作资格证书”)、编号及有效期,是上岗的必备资质;"
+  },
+  {
+    "Management_SpecialWorkers_SpecialOperationsJobResponsibilities": "需明确作业人员从事的具体工种(如“塔式起重机司机”“高处作业吊篮安装拆卸工”),细化特种作业人员的职责是工种管理的关键;"
+  },
+  {
+    "Management_OtherWorkers_NumberOfManagementPersonnelInProfessionalSubcontractingUnits": "需明确分包单位(如劳务分包、专业分包)的管理人员(如分包项目经理、技术负责人)数量,是分包管理的基础;"
+  },
+  {
+    "Management_OtherWorkers_NumberOfWorkersInDifferentJobCategories": "需以表格形式明确各工种(如木工、钢筋工、混凝土工、砌筑工等)的作业人员数量,是劳动力调配的依据;"
+  },
+  {
+    "Management_OtherWorkers_WorkersLlog": "需记录作业人员的姓名、工种、身份证号、联系方式等信息,是人员管理的重要档案;"
+  },
+  {
+    "acceptance_Standards_NationalStandardsSpecificationsAndOperatingProcedures": "是验收的基础依据,需明确具体规范名称(如JTG F80/1-2017),避免使用“国家规范”等泛化表述;"
+  },
+  {
+    "acceptance_Standards_IndustryStandardOperatingProcedures": "需指向具体行业的内部文件(如行业标准(如《公路桥涵施工技术规范》JTG_T 3650-2020),体现行业管理要求;"
+  },
+  {
+    "acceptance_Standards_SichuanRoadAndBridgeManagementRegulations": "需关联企业管理办法(如《四川路桥施工验收管理办法》),体现企业特色和企业管理要求;"
+  },
+  {
+    "acceptance_Standards_ManagementRegulationsOfLuqiaoGroup": "需关联集团管理办法(如《路桥集团专项施工方案验收条件》),体现集团特色和集团管理要求;"
+  },
+  {
+    "acceptance_Standards_ManagementRegulationsOfBridgeCompany": "需关联桥梁施工管理办法(如《桥梁施工安全操作规程》),体现桥梁施工重点 focus:和桥梁施工标准管理要求;"
+  },
+  {
+    "acceptance_Procedure_OnsiteAcceptance": "需明确验收对象(如“钢筋进场验收”“塔式起重机进场验收”),是质量控制的第一道防线;"
+  },
+  {
+    "acceptance_Procedure_ProcessAcceptance": "需关联施工工序(如“混凝土浇筑过程验收”“钢筋绑扎过程验收”),强调动态管控;"
+  },
+  {
+    "acceptance_Procedure_StageAcceptance": "需对应工程阶段(如“基础工程阶段验收”“主体结构阶段验收”),是阶段性成果确认的关键;"
+  },
+  {
+    "acceptance_Procedure_CompletionAcceptance": "需明确验收内容(如“工程竣工预验收”“专项施工方案完工验收”),是竣工验收的前提。"
+  },
+  {
+    "acceptance_Content_SafetyProductionConditionAcceptance": "验收表格中要明确项目的具体验收标准,验收标准应尽量量化到具体参数或标准,需细化具体内容(如“安全防护设施验收”包括“安全网张挂验收”“防护栏杆安装验收”),避免“安全生产验收”等泛化表述;"
+  },
+  {
+    "acceptance_Content_ResourceAllocationAcceptance": "验收表格中要明确项目的具体验收标准,验收标准应尽量量化到具体参数或标准,需关联资源类型(如“人员配置验收”包括“特种作业人员资质验收”“管理人员到位验收”),体现资源的针对性;"
+  },
+  {
+    "acceptance_Content_ConstructionProcessAcceptance": "验收表格中要明确项目的具体验收标准,验收标准应尽量量化到具体参数或标准,需明确工艺环节(如“模板安装工艺验收”包括“模板垂直度验收”“模板拼接缝验收”),强调工艺的标准化;"
+  },
+  {
+    "acceptance_Content_AcceptanceOfMechanicalEquipment": "验收表格中要明确项目的具体验收标准,验收标准应尽量量化到具体参数或标准,需指向具体设备(如“塔式起重机验收”包括“设备型号验收”“安全装置验收”),确保设备符合施工要求。"
+  },
+  {
+    "acceptance_Content_TemporarySupportStructure": "验收表格中要明确项目的具体验收标准,验收标准应尽量量化到具体参数或标准,专项施工方案及审批记录、技术交底记录、构配件质量证明文件(合格证/检测报告)、地基承载力报告、搭设过程检查记录、荷载试验报告(高支模/大跨度)、验收记录表(含实测数据/影像资料)、整改复查记录。"
+  },
+  {
+    "acceptance_Content_PersonnelOperationPlatform": "验收表格中要明确项目的具体验收标准,验收标准应尽量量化到具体参数或标准,架体材质(如钢管无裂纹、弯曲,型钢无开焊);架体构造(立杆间距、剪刀撑设置、连墙件固定);稳定性(移动式平台刹车装置、落地式平台基础坚实度);荷载限制(平台荷载不超过设计值,悬挂限载标志);防护栏杆(高度≥1.2m,竖向栏杆间距≤1.5m,底部设挡脚板);平台铺板(满铺、固定,无空隙);登高扶梯(防滑、固定,与平台连接牢固);安全网(平台周边设置密目网或安全平网)。"
+  },
+  {
+    "acceptance_Content_SafetyProtectionFacilities": "验收表格中要明确项目的具体验收标准,验收标准应尽量量化到具体参数或标准,需结合场景需求(如建筑施工中的基坑临边防护、电梯井防护门)、功能定位(如预防事故的防护栏杆、减少事故影响的安全网)、技术要求(如材质、构造、固定方式)。其核心逻辑是“隔离危险、承接冲击、提醒注意”"
+  },
+  {
+    "acceptance_Timing_AcceptanceTimeOfSpecialConstructionPlan": "需关联具体表格(如“《专项施工方案验收条件一览表》预估时间”),体现时间的可追溯性;"
+  },
+  {
+    "acceptance_Timing_AcceptanceTimeAdjustment": "需明确调整依据(如“根据施工进度调整验收时间”),避免“时间调整”等泛化表述;"
+  },
+  {
+    "acceptance_Timing_AcceptanceConditionTriggerTime": "需明确时间节点(如“具备验收条件后15日内组织验收”),强调时效性。"
+  },
+  {
+    "acceptance_Personnel_ConstructionUnitAcceptancePersonnel": "需明确具体角色(如“建设单位项目负责人”),避免“建设单位人员”等泛化表述;由施工作业班组在施工过程中自行对照方案自检,施工完成后由方案编制负责人、项目经理、项目副经理、项目技术负责人、安全环保处、工程处、机料处、合同处、专业分包单位(协作队伍)项目负责人和项目技术负责人等部门人员参加方案验收。"
+  },
+  {
+    "acceptance_Personnel_DesignUnitAcceptancePersonnel": "需明确验收人员姓名,关联专业(如“设计单位专业工程师”),体现设计的专业性;由施工作业班组在施工过程中自行对照方案自检,施工完成后由方案编制负责人、项目经理、项目副经理、项目技术负责人、安全环保处、工程处、机料处、合同处、专业分包单位(协作队伍)项目负责人和项目技术负责人等部门人员参加方案验收。"
+  },
+  {
+    "acceptance_Personnel_ConstructionUnitAcceptancePersonnel": "需明确验收人员姓名,指向管理岗位(如“施工单位项目经理”“施工单位技术负责人”),强调施工单位的主体责任;由施工作业班组在施工过程中自行对照方案自检,施工完成后由方案编制负责人、项目经理、项目副经理、项目技术负责人、安全环保处、工程处、机料处、合同处、专业分包单位(协作队伍)项目负责人和项目技术负责人等部门人员参加方案验收。"
+  },
+  {
+    "acceptance_Personnel_InspectionPersonnelOfTheSupervisionUnit": "需明确验收人员姓名,监理角色(如“总监理工程师”“专业监理工程师”),体现监理的监督职责;由施工作业班组在施工过程中自行对照方案自检,施工完成后由方案编制负责人、项目经理、项目副经理、项目技术负责人、安全环保处、工程处、机料处、合同处、专业分包单位(协作队伍)项目负责人和项目技术负责人等部门人员参加方案验收。"
+  },
+  {
+    "acceptance_Personnel_MonitoringUnitAcceptancePersonnel": "需明确验收人员姓名,关联监测内容(如“监测项目负责人”“监测技术员”),确保监测数据的准确性;由施工作业班组在施工过程中自行对照方案自检,施工完成后由方案编制负责人、项目经理、项目副经理、项目技术负责人、安全环保处、工程处、机料处、合同处、专业分包单位(协作队伍)项目负责人和项目技术负责人等部门人员参加方案验收。"
+  },
+  {
+    "other_Calculations_ContentRequirements": "专项施工方案中包含承重结构、重要临时设施、设备选型、吊绳吊具受力计算;地基承载力等工作内容编制的专项计算书,内容应包含编制依据、工程简况、方案简述、设计参数、主要工况计算、局部计算、结论及建议。"
+  },
+  {
+    "other_Calculations_CalculationOfMainWorkingConditions": "需针对关键施工工况(如“盖梁浇筑工况”“桩基础施工工况”),包含“本工况描述”“应力/变形/反力/屈曲分析结果”,是计算书的核心内容;"
+  },
+  {
+    "other_Calculations_LocalCalculation": "需对于受力集中、结构复杂的局部重要节点进行细部分析(如“钢管桩与横梁连接节点”“模板支撑体系节点”),确保结构安全;"
+  },
+  {
+    "other_Drawings_OverallLayoutPlan": "需展示项目整体布局(如“施工便道”“材料堆放区”“临时设施”),是施工部署的可视化基础;"
+  },
+  {
+    "other_Drawings_ConstructionSiteLayoutPlan": "需围绕“空间规划”“功能实现”“安全文明”三大核心,覆盖从边界界定到具体设施的全流程要素。"
+  },
+  {
+    "other_Drawings_LongitudinalElevationLayoutOfSupportingStructure": "需明确支撑体系(如“钢管桩支架”“满堂脚手架”)的纵向布置(如“桩长”“间距”“标高”),是结构安全的关键依据;"
+  },
+  {
+    "other_Drawings_Cross-sectionalLayoutDiagram": "需围绕“断面类型”“组成要素”“尺寸参数”“坡度设置”“附属设施”五大维度,覆盖道路、桥梁等工程的通用及专业要素。"
+  },
+  {
+    "other_Drawings_FloorPlan": "需围绕“空间布局”“功能分区”“施工支持”三大核心,覆盖从区域划分到具体设施的全流程要素。"
+  },
+  {
+    "other_Drawings_DetailedStructuralDiagram": "需细化关键节点(如“模板拼接节点”“支撑体系连接节点”),标注尺寸、材料及工艺要求,指导现场施工;"
+  },
+  {
+    "other_Drawings_FormworkLayoutDrawing": "需明确模板的平面位置(如“柱模板”“梁模板”)、尺寸及支撑方式,确保模板安装符合设计要求。"
+  },
+  {
+    "other_Drawings_TemplateConstructionDiagram": "需覆盖模板体系组成、构造细节、支撑系统、节点处理及精度控制等全流程要素。"
+  },
+  {
+    "other_Tables_ConstructionScheduleNetworkDiagram": "需用节点表示工序逻辑关系(如“桩基础施工→承台施工→盖梁施工”),是进度控制的核心工具;"
+  },
+  {
+    "other_Tables_ConstructionScheduleGanttChart": "围绕“时间维度”“任务要素”“进度关系”“调整控制”四大核心,覆盖从计划编制到动态监控的全流程。"
+  },
+  {
+    "other_Tables_HazardAnalysisAndResponseMeasuresTable": "需识别施工中的危险源(如“高处坠落”“物体打击”),制定针对性应对措施(如“设置防护栏杆”“佩戴安全带”),是安全保障的关键文档;"
+  },
+  {
+    "other_Tables_ScannedCopyOfTheCertificateOfFulltimSafetyManagementPersonnel": "需包含“安全生产考核合格证书”“证书编号”“有效期”,确保管理人员资质符合要求;"
+  },
+  {
+    "other_Tables_ScannedCopyOfSpecialOperationsPersonnelsCertificate": "需围绕“信息真实性”“管理规范性”“使用便捷性”三大核心,覆盖证件内容、“证书编号”、“有效期”、法规标准四大维度"
+  },
+  {
+    "other_Tables_ScannedCopyOfProfessionalSubcontractorsQualifications": "需包含“营业执照”“资质证书”“安全生产许可证”,确保分包单位具备施工能力。"
+  },
+  {
+    "other_Team": "需明确验收的前提条件(如“计算书完成”“设计图审核通过”),是验收的流程依据;"
+  },
+  {
+    "other_Team_PreparePersonnelInformation": "需包含“姓名”“职务”“职称”(如“张三 技术员 助理工程师”),确保编制人员具备专业能力;"
+  },
+  {
+    "other_Team_ReviewerInformation": "需包含“姓名”“职务”“职称”(如“李四 项目技术负责人 工程师”),确保审核流程的严谨性;"
+  },
+  {
+    "other_Team_ApprovalPersonnelInformation": "需包含“姓名”“职务”“职称”(如“王五 项目经理 高级工程师”),确保方案符合项目整体要求"
+  }
+]

+ 39 - 0
core/construction_write/workflows/outline_workflow.py

@@ -16,6 +16,8 @@ from langgraph.checkpoint.memory import MemorySaver
 from foundation.observability.logger.loggering import write_logger as logger
 from foundation.observability.logger.loggering import write_logger as logger
 from ..component.state_models import OutlineGenerationState
 from ..component.state_models import OutlineGenerationState
 from ..component.outline_generator import OutlineGenerator
 from ..component.outline_generator import OutlineGenerator
+import os
+import json
 
 
 
 
 class OutlineWorkflow:
 class OutlineWorkflow:
@@ -198,9 +200,46 @@ class OutlineWorkflow:
         # 编译图,使用内存检查点保存器
         # 编译图,使用内存检查点保存器
         compiled_graph = workflow.compile(checkpointer=self.checkpoint_saver)
         compiled_graph = workflow.compile(checkpointer=self.checkpoint_saver)
 
 
+        # 保存工作流图到 temp/construction_write 目录
+        self._save_workflow_graph(compiled_graph, "temp/construction_write/outline_workflow.png")
+
         logger.info("大纲生成 LangGraph 工作流图构建完成")
         logger.info("大纲生成 LangGraph 工作流图构建完成")
         return compiled_graph
         return compiled_graph
 
 
+    def _save_workflow_graph(self, compiled_graph: StateGraph, output_path: str):
+        """
+        保存 LangGraph 工作流图为 PNG 图片
+
+        Args:
+            compiled_graph: 编译后的 LangGraph 工作流图
+            output_path: 输出文件路径
+        """
+        try:
+            # 确保输出目录存在
+            output_dir = os.path.dirname(output_path)
+            if output_dir and not os.path.exists(output_dir):
+                os.makedirs(output_dir, exist_ok=True)
+                logger.info(f"创建输出目录:{output_dir}")
+
+            # 使用 graphviz 保存图片
+            # 需要安装 graphviz 和 graphviz Python 包
+            try:
+                graph = compiled_graph.get_graph()
+                graph_image = graph.draw_mermaid_png()
+                with open(output_path, "wb") as f:
+                    f.write(graph_image)
+                logger.info(f"工作流图已保存到:{output_path}")
+            except Exception as graphviz_error:
+                logger.warning(f"Graphviz 保存失败:{str(graphviz_error)}, 尝试使用 JSON 格式保存")
+                # 备用方案:保存为 JSON 格式
+                json_path = output_path.replace(".png", ".json")
+                with open(json_path, "w", encoding="utf-8") as f:
+                    json.dump(compiled_graph.get_graph().to_json(), f, indent=2, ensure_ascii=False)
+                logger.info(f"工作流图已保存到 (JSON 格式): {json_path}")
+
+        except Exception as e:
+            logger.warning(f"保存工作流图失败:{str(e)}")
+
     # ==================== 条件判断函数 ====================
     # ==================== 条件判断函数 ====================
 
 
     def _check_terminate_or_error(self, state: OutlineGenerationState) -> str:
     def _check_terminate_or_error(self, state: OutlineGenerationState) -> str:

+ 27 - 0
data_pipeline/sgfa_bz_data/片段结构.json

@@ -0,0 +1,27 @@
+{
+[
+{
+  "oringin_content": "片段1",
+  "chapter_classification":"plan_type",
+  "matedata": {
+    "file_name": "G85G76 重庆(川渝界)至成都高速公路扩容工程施工总承包ZCB5-TJ2标段",
+    "plan_type": "桥梁下部结构专项施工方案",
+    "file_path": "D:/G85G76/G85G76_0.doc",
+    "file_create_time": "2021-09-01 09:09:09"
+  }
+
+},
+{
+  "oringin_content": "片段2",
+  "chapter_classification":"plan_type",
+  "matedata": {
+    "file_name": "G85G76 重庆(川渝界)至成都高速公路扩容工程施工总承包ZCB5-TJ2标段",
+    "plan_type": "桥梁下部结构专项施工方案",
+    "file_path": "D:/G85G76/G85G76_0.doc",
+    "file_create_time": "2021-09-01 09:09:09"
+  }
+
+},
+]
+
+}

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

@@ -222,7 +222,8 @@ class GenerateModelClient:
         system_prompt: Optional[str] = None,
         system_prompt: Optional[str] = None,
         user_prompt: Optional[str] = None,
         user_prompt: Optional[str] = None,
         prompt: Optional[str] = None,
         prompt: Optional[str] = None,
-        timeout: Optional[int] = None
+        timeout: Optional[int] = None,
+        model_name: Optional[str] = None
     ):
     ):
         """模型流式生成(同步生成器)
         """模型流式生成(同步生成器)
 
 
@@ -240,6 +241,7 @@ class GenerateModelClient:
             user_prompt: 用户提示词字符串
             user_prompt: 用户提示词字符串
             prompt: 单条用户提示词字符串
             prompt: 单条用户提示词字符串
             timeout: 超时时间(秒)
             timeout: 超时时间(秒)
+            model_name: 模型名称(可选),支持 doubao/qwen/deepseek/gemini 等
 
 
         Yields:
         Yields:
             str: 生成的文本块
             str: 生成的文本块
@@ -251,6 +253,10 @@ class GenerateModelClient:
         current_timeout = timeout or self.default_timeout
         current_timeout = timeout or self.default_timeout
 
 
         try:
         try:
+            # 选择模型
+            llm_to_use = self.model_handler.get_model_by_name(model_name) if model_name else self.llm
+            logger.info(f"[模型流式调用] 使用{'指定' if model_name else '默认'}模型:{model_name or 'default'}, trace_id: {trace_id}")
+
             logger.info(f"[模型流式调用] 开始处理 trace_id: {trace_id}, 超时配置: {current_timeout}s")
             logger.info(f"[模型流式调用] 开始处理 trace_id: {trace_id}, 超时配置: {current_timeout}s")
 
 
             # 构建消息列表
             # 构建消息列表
@@ -262,7 +268,7 @@ class GenerateModelClient:
                 task_prompt_info=task_prompt_info
                 task_prompt_info=task_prompt_info
             )
             )
 
 
-            response = self.llm.stream(final_messages)
+            response = llm_to_use.stream(final_messages)
 
 
             chunk_count = 0
             chunk_count = 0
             for chunk in response:
             for chunk in response:

+ 285 - 0
foundation/ai/agent/generate/model_generate.py.bak

@@ -0,0 +1,285 @@
+# !/usr/bin/ python
+# -*- coding: utf-8 -*-
+'''
+@Project    : lq-agent-api
+@File       :model_generate.py
+@IDE        :PyCharm
+@Author     :
+@Date       :2025/7/14 14:22
+'''
+
+from langchain_core.prompts import ChatPromptTemplate
+from langchain_core.messages import BaseMessage, SystemMessage, HumanMessage
+from foundation.ai.models.model_handler import model_handler
+from foundation.observability.logger.loggering import review_logger as logger
+import asyncio
+import time
+from typing import Optional, Callable, Any, List, Union
+
+class GenerateModelClient:
+    """
+        主要是生成式模型
+    """
+
+    def __init__(self, default_timeout: int = 60, max_retries: int = 3, backoff_factor: float = 1.0):
+        # 获取默认模型
+        self.llm = model_handler.get_models()
+        self.chat = self.llm  # 当前chat和llm使用相同模型
+
+        # 配置参数
+        self.default_timeout = default_timeout
+        self.max_retries = max_retries
+        self.backoff_factor = backoff_factor
+
+        # 保存model_handler引用,用于动态获取模型
+        self.model_handler = model_handler
+
+    async def _retry_with_backoff(self, func: Callable, *args, timeout: Optional[int] = None, **kwargs):
+        """
+        带指数退避的重试机制,每次重试都有独立的超时控制
+        """
+        current_timeout = timeout or self.default_timeout
+
+        for attempt in range(self.max_retries + 1):
+            try:
+                # 每次重试都有独立的超时时间
+                return await asyncio.wait_for(
+                    func(*args, **kwargs),
+                    timeout=current_timeout
+                )
+            except asyncio.TimeoutError:
+                if attempt == self.max_retries:
+                    logger.error(f"[模型调用] 达到最大重试次数 {self.max_retries},最终超时")
+                    raise TimeoutError(f"模型调用在 {self.max_retries} 次重试后均超时")
+
+                wait_time = self.backoff_factor * (2 ** attempt)
+                logger.warning(f"[模型调用] 第 {attempt + 1} 次超时, {wait_time}秒后重试...")
+                await asyncio.sleep(wait_time)
+            except Exception as e:
+                if attempt == self.max_retries:
+                    logger.error(f"[模型调用] 达到最大重试次数 {self.max_retries},最终失败: {str(e)}")
+                    raise
+
+                wait_time = self.backoff_factor * (2 ** attempt)
+                logger.warning(f"[模型调用] 第 {attempt + 1} 次尝试失败: {str(e)}, {wait_time}秒后重试...")
+                await asyncio.sleep(wait_time)
+
+    async def get_model_generate_invoke(
+        self,
+        trace_id: str,
+        task_prompt_info: Optional[dict] = None,
+        messages: Optional[List[BaseMessage]] = None,
+        system_prompt: Optional[str] = None,
+        user_prompt: Optional[str] = None,
+        prompt: Optional[str] = None,
+        timeout: Optional[int] = None,
+        model_name: Optional[str] = None
+    ) -> str:
+        """模型非流式生成(异步)
+
+        支持多种调用方式(优先级从高到低):
+        1. messages: 直接传入 LangChain Message 对象列表
+        2. system_prompt + user_prompt: 分别传入系统和用户提示词
+        3. prompt: 传入单条用户提示词字符串
+        4. task_prompt_info: 传入包含 ChatPromptTemplate 的字典(兼容旧接口)
+
+        Args:
+            trace_id: 追踪ID
+            task_prompt_info: 任务提示词信息(兼容旧接口),需包含 format_messages() 方法
+            messages: LangChain Message 对象列表(如 [SystemMessage, HumanMessage])
+            system_prompt: 系统提示词字符串
+            user_prompt: 用户提示词字符串
+            prompt: 单条用户提示词字符串(无系统提示时使用)
+            timeout: 超时时间(秒),默认使用构造时的 default_timeout
+            model_name: 模型名称(可选),支持 doubao/qwen/deepseek/gemini 等
+
+        Returns:
+            str: 模型生成的文本内容
+
+        Raises:
+            ValueError: 参数组合错误
+            TimeoutError: 调用超时
+            Exception: 模型调用异常
+
+        Examples:
+            # 方式1: 使用 Message 列表(推荐)
+            messages = [SystemMessage(content="你是专家"), HumanMessage(content="请分析...")]
+            result = await client.get_model_generate_invoke("trace-001", messages=messages)
+
+            # 方式2: 分别传入系统和用户提示词
+            result = await client.get_model_generate_invoke(
+                "trace-001",
+                system_prompt="你是专家",
+                user_prompt="请分析..."
+            )
+
+            # 方式3: 传入单条提示词
+            result = await client.get_model_generate_invoke("trace-001", prompt="请分析...")
+
+            # 方式4: 兼容旧接口(使用 PromptLoader)
+            task_prompt_info = {"task_prompt": chat_template}
+            result = await client.get_model_generate_invoke("trace-001", task_prompt_info=task_prompt_info)
+        """
+        start_time = time.time()
+        current_timeout = timeout or self.default_timeout
+
+        try:
+            # 选择模型
+            llm_to_use = self.model_handler.get_model_by_name(model_name) if model_name else self.llm
+            logger.info(f"[模型调用] 使用{'指定' if model_name else '默认'}模型: {model_name or 'default'}, trace_id: {trace_id}")
+
+            # 构建消息列表(按优先级)
+            final_messages = self._build_messages(
+                messages=messages,
+                system_prompt=system_prompt,
+                user_prompt=user_prompt,
+                prompt=prompt,
+                task_prompt_info=task_prompt_info
+            )
+
+            # 定义模型调用函数,使用原生 ainvoke
+            async def _invoke():
+                return await llm_to_use.ainvoke(final_messages)
+
+            # 调用带重试机制
+            response = await self._retry_with_backoff(_invoke, timeout=current_timeout)
+
+            elapsed_time = time.time() - start_time
+            logger.info(f"[模型调用] 成功 trace_id: {trace_id}, 耗时: {elapsed_time:.2f}s")
+            return response.content
+
+        except asyncio.TimeoutError:
+            elapsed_time = time.time() - start_time
+            logger.error(f"[模型调用] 超时 trace_id: {trace_id}, 耗时: {elapsed_time:.2f}s, 超时阈值: {current_timeout}s")
+            raise TimeoutError(f"模型调用超时,trace_id: {trace_id}")
+
+        except Exception as e:
+            elapsed_time = time.time() - start_time
+            logger.error(f"[模型调用] 异常 trace_id: {trace_id}, 耗时: {elapsed_time:.2f}s, 错误: {type(e).__name__}: {str(e)}")
+            raise
+
+    def _build_messages(
+        self,
+        messages: Optional[List[BaseMessage]] = None,
+        system_prompt: Optional[str] = None,
+        user_prompt: Optional[str] = None,
+        prompt: Optional[str] = None,
+        task_prompt_info: Optional[dict] = None
+    ) -> List[BaseMessage]:
+        """构建消息列表(内部方法)
+
+        优先级:messages > system_prompt+user_prompt > prompt > task_prompt_info
+        """
+        # 方式1: 直接使用传入的 Message 列表
+        if messages is not None:
+            if not isinstance(messages, list):
+                raise ValueError("messages 必须是列表")
+            if len(messages) == 0:
+                raise ValueError("messages 不能为空列表")
+            logger.debug(f"使用传入的 messages 列表,共 {len(messages)} 条消息")
+            return messages
+
+        # 方式2: system_prompt + user_prompt
+        if system_prompt is not None and user_prompt is not None:
+            logger.debug("使用 system_prompt + user_prompt 构建消息")
+            return [SystemMessage(content=system_prompt), HumanMessage(content=user_prompt)]
+
+        # 方式3: 单独 system_prompt(可能是特殊情况)
+        if system_prompt is not None:
+            logger.debug("使用单独的 system_prompt 构建消息")
+            return [SystemMessage(content=system_prompt)]
+
+        # 方式4: 单条 prompt 字符串
+        if prompt is not None:
+            logger.debug("使用单条 prompt 字符串构建消息")
+            return [HumanMessage(content=prompt)]
+
+        # 方式5: 兼容旧接口 task_prompt_info
+        if task_prompt_info is not None:
+            if "task_prompt" not in task_prompt_info:
+                raise ValueError("task_prompt_info 必须包含 'task_prompt' 键")
+            task_prompt = task_prompt_info["task_prompt"]
+            if hasattr(task_prompt, 'format_messages'):
+                logger.debug("使用 task_prompt_info 中的 ChatPromptTemplate 构建消息")
+                return task_prompt.format_messages()
+            elif isinstance(task_prompt, str):
+                logger.debug("使用 task_prompt_info 中的字符串构建消息")
+                return [HumanMessage(content=task_prompt)]
+            else:
+                raise ValueError(f"task_prompt 类型不支持: {type(task_prompt)}")
+
+        # 没有提供任何有效参数
+        raise ValueError(
+            "必须提供以下参数之一: "
+            "messages, system_prompt+user_prompt, prompt, 或 task_prompt_info"
+        )
+
+    def get_model_generate_stream(
+        self,
+        trace_id: str,
+        task_prompt_info: Optional[dict] = None,
+        messages: Optional[List[BaseMessage]] = None,
+        system_prompt: Optional[str] = None,
+        user_prompt: Optional[str] = None,
+        prompt: Optional[str] = None,
+        timeout: Optional[int] = None,
+        model_name: Optional[str] = None
+    ):
+        """模型流式生成(同步生成器)
+
+        支持多种调用方式(优先级从高到低):
+        1. messages: 直接传入 LangChain Message 对象列表
+        2. system_prompt + user_prompt: 分别传入系统和用户提示词
+        3. prompt: 传入单条用户提示词字符串
+        4. task_prompt_info: 传入包含 ChatPromptTemplate 的字典(兼容旧接口)
+
+        Args:
+            trace_id: 追踪ID
+            task_prompt_info: 任务提示词信息(兼容旧接口)
+            messages: LangChain Message 对象列表
+            system_prompt: 系统提示词字符串
+            user_prompt: 用户提示词字符串
+            prompt: 单条用户提示词字符串
+            timeout: 超时时间(秒)
+            model_name: 模型名称(可选),支持 doubao/qwen/deepseek/gemini 等
+
+        Yields:
+            str: 生成的文本块
+
+        Raises:
+            ValueError: 参数组合错误
+        """
+        start_time = time.time()
+        current_timeout = timeout or self.default_timeout
+
+        try:
+            logger.info(f"[模型流式调用] 开始处理 trace_id: {trace_id}, 超时配置: {current_timeout}s")
+
+            # 构建消息列表
+            final_messages = self._build_messages(
+                messages=messages,
+                system_prompt=system_prompt,
+                user_prompt=user_prompt,
+                prompt=prompt,
+                task_prompt_info=task_prompt_info
+            )
+
+            response = llm_to_use.stream(final_messages)
+
+            chunk_count = 0
+            for chunk in response:
+                chunk_count += 1
+                if hasattr(chunk, 'content') and chunk.content:
+                    yield chunk.content
+                elif chunk:
+                    yield chunk
+
+            elapsed_time = time.time() - start_time
+            logger.info(f"[模型流式调用] 成功 trace_id: {trace_id}, 生成块数: {chunk_count}, 耗时: {elapsed_time:.2f}s")
+
+        except Exception as e:
+            elapsed_time = time.time() - start_time
+            logger.error(f"[模型流式调用] 异常 trace_id: {trace_id}, 耗时: {elapsed_time:.2f}s, 错误: {type(e).__name__}: {str(e)}")
+            raise
+
+generate_model_client = GenerateModelClient(default_timeout=15, max_retries=2, backoff_factor=0.5)

+ 0 - 35
gunicorn_config.py

@@ -1,35 +0,0 @@
-# !/usr/bin/python
-# -*- coding: utf-8 -*-
-'''
-@Project    : lq-agent-api
-@File       :gunicorn_config.py
-@IDE        :PyCharm
-@Author     :
-@Date       :2025/7/23 09:07
-'''
-
-
-import multiprocessing
-
-# 基础配置
-bind = "0.0.0.0:8001"
-workers = multiprocessing.cpu_count() + 1  # 推荐公式
-worker_class = "uvicorn.workers.UvicornWorker"
-timeout = 120
-keepalive = 5
-
-# 日志配置
-accesslog = "./gunicorn_log/access_log.log"  # 输出到 stdout
-errorlog = "./gunicorn_log/error_log.log"   # 错误日志到 stderr
-loglevel = "info"
-
-# 性能优化
-max_requests = 1000     # 防止内存泄漏
-max_requests_jitter = 50
-graceful_timeout = 30   # 优雅停机时间
-
-# MCP 特定优化
-preload_app = True  # 减少内存占用,加速启动
-
-# 安全增强
-limit_request_line = 4094  # 防止过大请求头

+ 9 - 0
utils_test/Model_Test/test_rerank_request.json

@@ -0,0 +1,9 @@
+{
+  "model": "bge-reranker-v2-m3",
+  "query": "乔布斯是谁?",
+  "candidates": [
+    "大模型是一类具有大量参数的人工智能模型。",
+    "苹果是一家科技公司",
+    "大模型用于深度学习任务"
+  ]
+}