Bladeren bron

fix(sgsc-时效性审查模型-xth): 完整性审查优化

- suggestion使用大模型生成,符合现实逻辑
- issue_point和reason使用数据拼接
- reason引用具体规范名称《桥梁公司危险性较大工程管理实施细则(2025版)》
- 优化MySQL连接池初始化逻辑
suhua31 1 week geleden
bovenliggende
commit
62be2b6d6f

+ 4 - 1
config/config.ini.template

@@ -132,11 +132,14 @@ LQ_QWEN3_8B_LQ_LORA_API_KEY=dummy
 MYSQL_HOST=192.168.92.61
 MYSQL_PORT=13306
 MYSQL_USER=root
-MYSQL_PASSWORD=lq@123
+MYSQL_PASSWORD=Lq123456!
 MYSQL_DB=lq_db
 MYSQL_MIN_SIZE=1
 MYSQL_MAX_SIZE=5
 MYSQL_AUTO_COMMIT=True
+MYSQL_CONNECT_TIMEOUT=30
+MYSQL_READ_TIMEOUT=60
+MYSQL_WRITE_TIMEOUT=30
 
 
 [pgvector]

+ 84 - 108
core/construction_review/component/reviewers/completeness_reviewer.py

@@ -328,21 +328,20 @@ class LightweightCompletenessChecker:
             context = "【问题类型】未知"
             reference = ""
 
-        prompt = f"""你是一位资深的工程施工方案审查专家。请根据以下问题上下文和规范参考信息,生成专业的审查建议。
+        prompt = f"""你是一位资深的工程施工方案审查专家。请根据以下问题上下文和规范参考信息,生成专业的补充建议。
 
 {context}
 
 {reference}
 
-请用JSON格式输出审查建议,包含以下字段:
-- issue_point: 问题摘要(简洁明了,50字以内)
+请用JSON格式输出,只包含以下字段:
 - suggestion: 具体补充建议(详细可行,100-200字,包含具体应该补充的内容要点)
-- reason: 规范依据说明(引用具体规范要求,说明为什么需要补充)
 
 注意:
 1. suggestion应该具体、可操作,引用规范中的具体内容要求
 2. 使用专业的工程术语
 3. 语气应该是指导性的,帮助编制人员理解需要补充什么内容
+4. **必须符合现实逻辑**:建议内容应基于实际工程施工的可行性,不能提出不切实际、无法操作或与工程常识相悖的建议
 
 JSON输出:"""
         return prompt
@@ -361,8 +360,10 @@ JSON输出:"""
         """
         使用大模型生成建议
 
+        【修改】只生成 suggestion,issue_point 和 reason 由调用方简单拼接
+
         Returns:
-            Dict[str, str]: 包含 issue_point, suggestion, reason 的字典
+            Dict[str, str]: 包含 suggestion 的字典,或None使用回退逻辑
         """
         if not self.model_client:
             return None
@@ -392,14 +393,12 @@ JSON输出:"""
                 trace_id=trace_id,
                 task_prompt_info=task_prompt_info,
                 timeout=timeout,
-                model_name="qwen"  # 使用默认模型,可根据需要调整
+                model_name="qwen"
             )
 
             # 解析模型返回的JSON
             try:
-                # 尝试从返回文本中提取JSON
                 response_text = model_response.strip()
-                # 查找JSON块
                 if "```json" in response_text:
                     json_str = response_text.split("```json")[1].split("```")[0].strip()
                 elif "```" in response_text:
@@ -408,13 +407,12 @@ JSON输出:"""
                     json_str = response_text
 
                 result = json.loads(json_str)
+                # 只返回 suggestion,issue_point 和 reason 由调用方处理
                 return {
-                    "issue_point": result.get("issue_point", ""),
-                    "suggestion": result.get("suggestion", ""),
-                    "reason": result.get("reason", "")
+                    "suggestion": result.get("suggestion", "")
                 }
             except (json.JSONDecodeError, IndexError) as e:
-                logger.warning(f"LLM建议生成结果解析失败: {e},返回: {model_response[:200]}")
+                logger.warning(f"LLM建议生成结果解析失败: {e}")
                 return None
 
         except Exception as e:
@@ -898,7 +896,11 @@ JSON输出:"""
 
             # ── 一级缺失 ──────────────────────────────────────────────
             if first_code not in actual_first:
-                # 尝试使用LLM生成建议
+                # issue_point 和 reason 使用简单拼接
+                issue_point = f"【一级章节缺失】'{first_name}'整个章节不存在"
+                reason = f"依据《桥梁公司危险性较大工程管理实施细则(2025版)》规定,文档必须包含'{first_name}'一级章节,当前正文中未发现该章节任何内容"
+
+                # 尝试使用LLM生成 suggestion
                 llm_result = await self._generate_recommendation_with_llm(
                     level="一级",
                     first_code=first_code,
@@ -906,28 +908,20 @@ JSON输出:"""
                     first_seq=first_seq
                 )
 
-                if llm_result:
-                    recommendations.append({
-                        "level": "一级",
-                        "issue_point": llm_result.get("issue_point", f"【一级章节缺失】'{first_name}'整个章节不存在"),
-                        "location": first_name,
-                        "suggestion": llm_result.get("suggestion", f"请添加'{first_name}'章节及其下全部子章节内容"),
-                        "reason": llm_result.get("reason", f"根据规范要求,文档必须包含'{first_name}'一级章节,当前正文中未发现该章节任何内容"),
-                        "first_seq": first_seq,
-                    })
+                if llm_result and llm_result.get("suggestion"):
+                    suggestion = llm_result.get("suggestion")
                 else:
                     # 回退到简单拼接
-                    recommendations.append({
-                        "level": "一级",
-                        "issue_point": f"【一级章节缺失】'{first_name}'整个章节不存在",
-                        "location": first_name,
-                        "suggestion": f"请添加'{first_name}'章节及其下全部子章节内容",
-                        "reason": (
-                            f"根据规范要求,文档必须包含'{first_name}'一级章节,"
-                            f"当前正文中未发现该章节任何内容"
-                        ),
-                        "first_seq": first_seq,
-                    })
+                    suggestion = f"请添加'{first_name}'章节及其下全部子章节内容"
+
+                recommendations.append({
+                    "level": "一级",
+                    "issue_point": issue_point,
+                    "location": first_name,
+                    "suggestion": suggestion,
+                    "reason": reason,
+                    "first_seq": first_seq,
+                })
                 continue
 
             # ── 一级存在,检查二级 ─────────────────────────────────────
@@ -941,7 +935,11 @@ JSON输出:"""
 
                 # ── 二级缺失 ──────────────────────────────────────────
                 if (cat1, cat2) not in actual_secondary:
-                    # 尝试使用LLM生成建议
+                    # issue_point 和 reason 使用简单拼接
+                    issue_point = f"【二级章节缺失】{first_name} > '{second_name}'整个章节不存在"
+                    reason = f"依据《桥梁公司危险性较大工程管理实施细则(2025版)》规定,'{first_name}'下应包含'{second_name}'二级章节,当前正文中未发现该章节内容"
+
+                    # 尝试使用LLM生成 suggestion
                     llm_result = await self._generate_recommendation_with_llm(
                         level="二级",
                         first_code=cat1,
@@ -950,32 +948,21 @@ JSON输出:"""
                         second_name=second_name
                     )
 
-                    if llm_result:
-                        recommendations.append({
-                            "level": "二级",
-                            "issue_point": llm_result.get("issue_point", f"【二级章节缺失】{first_name} > '{second_name}'整个章节不存在"),
-                            "location": f"{first_name} > {second_name}",
-                            "suggestion": llm_result.get("suggestion", f"请在'{first_name}'下添加'{second_name}'章节内容"),
-                            "reason": llm_result.get("reason", f"根据规范要求,'{first_name}'下应包含'{second_name}'二级章节,当前正文中未发现该章节内容"),
-                            "first_seq": first_seq,
-                            "second_seq": second_seq,
-                        })
+                    if llm_result and llm_result.get("suggestion"):
+                        suggestion = llm_result.get("suggestion")
                     else:
                         # 回退到简单拼接
-                        recommendations.append({
-                            "level": "二级",
-                            "issue_point": (
-                                f"【二级章节缺失】{first_name} > '{second_name}'整个章节不存在"
-                            ),
-                            "location": f"{first_name} > {second_name}",
-                            "suggestion": f"请在'{first_name}'下添加'{second_name}'章节内容",
-                            "reason": (
-                                f"根据规范要求,'{first_name}'下应包含'{second_name}'二级章节,"
-                                f"当前正文中未发现该章节内容"
-                            ),
-                            "first_seq": first_seq,
-                            "second_seq": second_seq,
-                        })
+                        suggestion = f"请在'{first_name}'下添加'{second_name}'章节内容"
+
+                    recommendations.append({
+                        "level": "二级",
+                        "issue_point": issue_point,
+                        "location": f"{first_name} > {second_name}",
+                        "suggestion": suggestion,
+                        "reason": reason,
+                        "first_seq": first_seq,
+                        "second_seq": second_seq,
+                    })
                     continue
 
                 # ── 二级存在,检查三级缺失 ────────────────────────────
@@ -993,7 +980,8 @@ JSON输出:"""
                 if not missing_t_items:
                     continue
 
-                # 尝试使用LLM批量生成三级缺失建议
+                # issue_point 和 reason 使用简单拼接(三级缺失)
+                # 尝试使用LLM批量生成 suggestion
                 llm_result = await self._generate_recommendation_with_llm(
                     level="三级",
                     first_code=cat1,
@@ -1003,34 +991,26 @@ JSON输出:"""
                     tertiary_items=missing_t_items
                 )
 
-                if llm_result:
-                    # LLM生成了整体建议,为每个缺失项添加相同建议(但位置不同)
-                    for t_item in missing_t_items:
-                        recommendations.append({
-                            "level": "三级",
-                            "issue_point": f"【三级内容缺失】{first_name} > {second_name} > '{t_item.third_cn}'",
-                            "location": f"{first_name} > {second_name}",
-                            "suggestion": llm_result.get("suggestion", f"请补充'{second_name}'下的'{t_item.third_cn}'内容"),
-                            "reason": llm_result.get("reason", f"'{second_name}'下缺失规范要求的'{t_item.third_cn}'内容要点"),
-                            "first_seq": first_seq,
-                            "second_seq": second_seq,
-                            "third_seq": t_item.third_seq,
-                        })
+                if llm_result and llm_result.get("suggestion"):
+                    suggestion = llm_result.get("suggestion")
                 else:
-                    # 回退到简单拼接
-                    for t_item in missing_t_items:
-                        recommendations.append({
-                            "level": "三级",
-                            "issue_point": (
-                                f"【三级内容缺失】{first_name} > {second_name} > '{t_item.third_cn}'"
-                            ),
-                            "location": f"{first_name} > {second_name}",
-                            "suggestion": f"请补充'{second_name}'下的'{t_item.third_cn}'内容",
-                            "reason": f"'{second_name}'下缺失规范要求的'{t_item.third_cn}'内容要点",
-                            "first_seq": first_seq,
-                            "second_seq": second_seq,
-                            "third_seq": t_item.third_seq,
-                        })
+                    # 回退到简单拼接:列出所有缺失项
+                    missing_names = "、".join([t.third_cn for t in missing_t_items[:5]])
+                    if len(missing_t_items) > 5:
+                        missing_names += f"等{len(missing_t_items)}项内容"
+                    suggestion = f"请补充'{second_name}'下的{missing_names}"
+
+                for t_item in missing_t_items:
+                    recommendations.append({
+                        "level": "三级",
+                        "issue_point": f"【三级内容缺失】{first_name} > {second_name} > '{t_item.third_cn}'",
+                        "location": f"{first_name} > {second_name}",
+                        "suggestion": suggestion,
+                        "reason": f"依据《桥梁公司危险性较大工程管理实施细则(2025版)》规定,'{second_name}'下应包含'{t_item.third_cn}'内容要点",
+                        "first_seq": first_seq,
+                        "second_seq": second_seq,
+                        "third_seq": t_item.third_seq,
+                    })
 
         # ── 一致性审查:目录有列但正文无内容 ─────────────────────────────
         if outline_result:
@@ -1039,7 +1019,11 @@ JSON输出:"""
                 sec_title = e.get("outline_title") or e.get("secondary_name", "")
                 location = f"{f_name} > {sec_title}" if f_name else sec_title
 
-                # 尝试使用LLM生成建议
+                # issue_point 和 reason 使用简单拼接(一致性审查)
+                issue_point = f"【目录正文不一致】'{location}'目录已列但正文无内容"
+                reason = f"依据《桥梁公司危险性较大工程管理实施细则(2025版)》规定,目录应与正文保持一致。目录页列有'{sec_title}'章节,但正文中未发现对应内容"
+
+                # 尝试使用LLM生成 suggestion
                 llm_result = await self._generate_recommendation_with_llm(
                     level="一致性",
                     first_code="",
@@ -1048,27 +1032,19 @@ JSON输出:"""
                     outline_title=sec_title
                 )
 
-                if llm_result:
-                    recommendations.append({
-                        "level": "一致性",
-                        "issue_point": llm_result.get("issue_point", f"【目录正文不一致】'{location}'目录已列但正文无内容"),
-                        "location": location,
-                        "suggestion": llm_result.get("suggestion", f"请补充'{sec_title}'章节的正文内容,或从目录中移除该章节"),
-                        "reason": llm_result.get("reason", f"目录页列有'{sec_title}'章节,但正文中未发现对应内容,存在目录与正文不一致的问题"),
-                    })
+                if llm_result and llm_result.get("suggestion"):
+                    suggestion = llm_result.get("suggestion")
                 else:
-                    recommendations.append({
-                        "level": "一致性",
-                        "issue_point": f"【目录正文不一致】'{location}'目录已列但正文无内容",
-                        "location": location,
-                        "suggestion": (
-                            f"请补充'{sec_title}'章节的正文内容,或从目录中移除该章节"
-                        ),
-                        "reason": (
-                            f"目录页列有'{sec_title}'章节,但正文中未发现对应内容,"
-                            f"存在目录与正文不一致的问题"
-                        ),
-                    })
+                    # 回退到简单拼接
+                    suggestion = f"请补充'{sec_title}'章节的正文内容,或从目录中移除该章节"
+
+                recommendations.append({
+                    "level": "一致性",
+                    "issue_point": issue_point,
+                    "location": location,
+                    "suggestion": suggestion,
+                    "reason": reason,
+                })
 
         if not recommendations:
             recommendations.append({
@@ -1076,7 +1052,7 @@ JSON输出:"""
                 "issue_point": "文档完整性良好",
                 "location": "",
                 "suggestion": "无需补充",
-                "reason": "文档已覆盖规范要求的所有章节与内容要点",
+                "reason": "依据《桥梁公司危险性较大工程管理实施细则(2025版)》规定,文档已覆盖所有章节与内容要点",
             })
 
         return recommendations

+ 35 - 22
foundation/infrastructure/mysql/async_mysql_conn_pool.py

@@ -1,3 +1,4 @@
+import asyncio
 import aiomysql
 from contextlib import asynccontextmanager
 from typing import  Dict,Optional, AsyncGenerator
@@ -8,36 +9,48 @@ from foundation.infrastructure.config import config_handler
 # 异步数据库连接池
 class AsyncMySQLPool:
     _instance = None
-    
+
     def __new__(cls, *args, **kwargs):
         if not cls._instance:
             cls._instance = super().__new__(cls)
         return cls._instance
-    
+
     def __init__(self):
         if not hasattr(self, '_pool'):
             self._pool = None
             self._initialized = False
-    
-    async def initialize(self):
-        """初始化连接池"""
-        try:
-            
-            self._pool = await aiomysql.create_pool(
-                host=config_handler.get("mysql", "MYSQL_HOST" , "localhost"),
-                port=int(config_handler.get("mysql", "MYSQL_PORT" , "3306")),
-                user=config_handler.get("mysql", "MYSQL_USER"),
-                password=config_handler.get("mysql", "MYSQL_PASSWORD"),
-                db=config_handler.get("mysql", "MYSQL_DB"),
-                minsize=int(config_handler.get("mysql", "MYSQL_MIN_SIZE" , "1")),
-                maxsize=int(config_handler.get("mysql", "MYSQL_MAX_SIZE" , "2")),
-                autocommit=config_handler.get("mysql", "MYSQL_AUTO_COMMIT")
-            )
-            self._initialized = True
-            server_logger.info("异步MySQL连接池初始化成功")
-        except Exception as e:
-            server_logger.error(f"连接池初始化失败: {e}")
-            raise
+
+    async def initialize(self, max_retries=3, retry_delay=2):
+        """初始化连接池,支持重试"""
+        last_error = None
+
+        for attempt in range(1, max_retries + 1):
+            try:
+                server_logger.info(f"尝试初始化MySQL连接池 (第{attempt}/{max_retries}次)...")
+
+                self._pool = await aiomysql.create_pool(
+                    host=config_handler.get("mysql", "MYSQL_HOST" , "localhost"),
+                    port=int(config_handler.get("mysql", "MYSQL_PORT" , "3306")),
+                    user=config_handler.get("mysql", "MYSQL_USER"),
+                    password=config_handler.get("mysql", "MYSQL_PASSWORD"),
+                    db=config_handler.get("mysql", "MYSQL_DB"),
+                    minsize=int(config_handler.get("mysql", "MYSQL_MIN_SIZE" , "1")),
+                    maxsize=int(config_handler.get("mysql", "MYSQL_MAX_SIZE" , "2")),
+                    autocommit=config_handler.get("mysql", "MYSQL_AUTO_COMMIT"),
+                    connect_timeout=int(config_handler.get("mysql", "MYSQL_CONNECT_TIMEOUT", "30"))
+                )
+                self._initialized = True
+                server_logger.info("异步MySQL连接池初始化成功")
+                return
+            except Exception as e:
+                last_error = e
+                server_logger.warning(f"连接池初始化失败 (第{attempt}次): {e}")
+                if attempt < max_retries:
+                    server_logger.info(f"{retry_delay}秒后重试...")
+                    await asyncio.sleep(retry_delay)
+
+        server_logger.error(f"连接池初始化失败,已重试{max_retries}次: {last_error}")
+        raise last_error
     
     async def close(self):
         """关闭连接池"""