Selaa lähdekoodia

v0.0.4-优化时效、规范性审查并发处理

WangXuMing 2 kuukautta sitten
vanhempi
sitoutus
9b376e80ce

+ 1 - 1
config/config.ini

@@ -1,7 +1,7 @@
 
 
 [model]
-MODEL_TYPE=gemini
+MODEL_TYPE=lq_qwen3_8b
 
 
 

+ 65 - 31
core/construction_review/component/reviewers/prep_basis_reviewer.py

@@ -54,6 +54,10 @@ class TextProcessor:
         if not text:
             return []
 
+        # 检查是否是"无明显问题"
+        if "无明显问题" in text or "无明显" in text:
+            return []
+
         try:
             obj = json.loads(text.strip())
             if isinstance(obj, list):
@@ -81,7 +85,7 @@ class ResultFormatter:
         name = obj.get("name", "")
         is_standard = obj.get("is_standard", False)
         status = obj.get("status", "")
-        meg = obj.get("meg", "")
+        meg = obj.get("meg", "")  # 注意:这里应该是meg字段
 
         if isinstance(is_standard, str):
             is_standard = is_standard.strip().lower() in ("true", "1", "yes")
@@ -121,13 +125,16 @@ class MessageBuilder:
             ])
 
         try:
+            # 强制重新加载提示词,避免缓存问题
             template = self.prompt_loader.get_prompt_template(
                 reviewer_type="prep_basis",
-                prompt_name="basis_status_check"
+                prompt_name="basis_status_check",
+                force_reload=True  # 强制重新加载
             )
 
             # 验证返回的是ChatPromptTemplate对象
             if hasattr(template, 'format_messages'):
+                logger.info(f"成功加载编制依据审查提示词")
                 return template
             else:
                 logger.warning(f" PromptLoader返回了意外类型: {type(template)}")
@@ -311,8 +318,6 @@ class LLMReviewClient:
 
         if not trace_id:
             trace_id = f"prep_basis_review_{int(time.time())}"
-
-        # ==================== 修复开始 ====================
         try:
             from langchain_core.prompts import ChatPromptTemplate
 
@@ -329,10 +334,7 @@ class LLMReviewClient:
 
             # 情况2: 如果传入的是 List (消息列表)
             elif isinstance(prompt_template, list):
-                # 将 List 包装回 ChatPromptTemplate 对象
-                # 这样底层调用 .format_messages() 时就不会报错了
                 final_prompt_obj = ChatPromptTemplate.from_messages(prompt_template)
-                # 如果列表中包含未格式化的变量(罕见情况),尝试 partial,否则忽略
                 try:
                     final_prompt_obj = final_prompt_obj.partial(review_content=user_content)
                 except Exception:
@@ -505,68 +507,100 @@ class BasisReviewService:
             batch = items[i:i + 3]
             batches.append(batch)
 
-        # 逐个批次执行,每个批次完成后立即推送SSE
-        processed_results = []
-        total_items = 0
-        standard_items = 0
-        successful_batches = 0
-
-        for i, batch in enumerate(batches):
+        # 异步并发执行所有批次,使用回调处理SSE推送
+        async def process_batch_with_callback(batch_index: int, batch: List[str]) -> List[Dict[str, Any]]:
+            """处理单个批次并执行SSE回调"""
             try:
                 # 执行单个批次审查
                 result = await self.review_batch(batch, collection_name)
-                processed_results.append(result)
-                successful_batches += 1
 
                 # 统计当前批次结果
                 batch_standard_count = 0
                 for item in result:
-                    total_items += 1
                     if isinstance(item, dict) and item.get('is_standard', False):
-                        standard_items += 1
                         batch_standard_count += 1
 
                 # 立即推送当前批次完成的SSE消息
-                logger.info("应触发SSE消息")
+                logger.info(f"批次{batch_index + 1}完成,准备推送SSE")
                 if progress_manager and callback_task_id:
-                    logger.info("已触发SSE消息")
                     try:
-                        progress_percent = int((i + 1) / total_batches * 100)
+                        progress_percent = int((batch_index + 1) / total_batches * 100)
                         await progress_manager.update_stage_progress(
                             callback_task_id=callback_task_id,
-                            stage_name=f"编制依据审查-批次{i + 1}",
+                            stage_name=f"编制依据审查-批次{batch_index + 1}",
                             current=progress_percent,
                             status="processing",
-                            message=f"完成第{i + 1}/{total_batches}批次编制依据审查,{len(batch)}项,其中{batch_standard_count}项为标准",
+                            message=f"完成第{batch_index + 1}/{total_batches}批次编制依据审查,{len(batch)}项,其中{batch_standard_count}项为标准",
                             overall_task_status="processing",
                             event_type="processing",
                             issues=result  # 推送该批次的审查结果
                         )
+                        logger.info(f"批次{batch_index + 1} SSE推送成功")
                     except Exception as e:
-                        logger.error(f"SSE推送批次{i + 1}结果失败: {e}")
+                        logger.error(f"SSE推送批次{batch_index + 1}结果失败: {e}")
+
+                return result
 
             except Exception as e:
-                logger.error(f" 批次 {i} 处理失败: {e}")
+                logger.error(f" 批次 {batch_index} 处理失败: {e}")
                 error_result = [{"name": name, "is_standard": False, "status": "", "meg": f"批次处理失败: {str(e)}"}
                                 for name in batch]
-                processed_results.append(error_result)
 
                 # 即使失败也要推送结果
                 if progress_manager and callback_task_id:
                     try:
-                        progress_percent = int((i + 1) / total_batches * 100)
+                        progress_percent = int((batch_index + 1) / total_batches * 100)
                         await progress_manager.update_stage_progress(
                             callback_task_id=callback_task_id,
-                            stage_name=f"编制依据审查-批次{i + 1}",
+                            stage_name=f"编制依据审查-批次{batch_index + 1}",
                             current=progress_percent,
                             status="processing",
-                            message=f"第{i + 1}/{total_batches}批次处理失败",
+                            message=f"第{batch_index + 1}/{total_batches}批次处理失败",
                             overall_task_status="processing",
                             event_type="processing",
                             issues=error_result
                         )
                     except Exception as push_e:
-                        logger.error(f"SSE推送失败批次{i + 1}结果失败: {push_e}")
+                        logger.error(f"SSE推送失败批次{batch_index + 1}结果失败: {push_e}")
+
+                return error_result
+
+        # 创建所有批次的异步任务
+        batch_tasks = []
+        for i, batch in enumerate(batches):
+            task = process_batch_with_callback(i, batch)
+            batch_tasks.append(task)
+
+        # 并发执行所有批次
+        logger.info(f"开始并发执行{total_batches}个批次编制依据审查")
+        processed_results = await asyncio.gather(*batch_tasks, return_exceptions=True)
+
+        # 处理异常结果并统计
+        total_items = 0
+        standard_items = 0
+        successful_batches = 0
+
+        # 重新构建结果列表,过滤异常
+        final_results = []
+        for i, result in enumerate(processed_results):
+            if isinstance(result, Exception):
+                logger.error(f" 批次 {i} 返回异常: {result}")
+                error_batch = batches[i] if i < len(batches) else []
+                error_result = [{"name": name, "is_standard": False, "status": "", "meg": f"批次异常: {str(result)}"}
+                                for name in error_batch]
+                final_results.append(error_result)
+            else:
+                final_results.append(result)
+                successful_batches += 1
+
+        # 统计总结果
+        for result in final_results:
+            for item in result:
+                total_items += 1
+                if isinstance(item, dict) and item.get('is_standard', False):
+                    standard_items += 1
+
+        logger.info(f"并发执行完成,成功批次: {successful_batches}/{total_batches}")
 
 
         # 发送完成审查的SSE推送
@@ -587,7 +621,7 @@ class BasisReviewService:
 
         logger.info(f" 异步审查完成,耗时: {elapsed_time:.4f} 秒")
         logger.info(f" 总编制依据: {total_items}, 标准项: {standard_items}, 成功批次: {successful_batches}/{total_batches}")
-        return processed_results
+        return final_results
 
 
 # 便捷函数

+ 38 - 23
core/construction_review/component/reviewers/prompt/prep_basis_reviewers.yaml

@@ -3,37 +3,56 @@
 # 编制依据状态检查
 basis_status_check:
   system_prompt: |
-    /no_think
-    你是一个【规范性引用文件识别与状态判断助手】。
+    你是一个专业的编制依据审查AI助手。
 
-    你会收到一个JSON,包含 items 数组。每个 item 有:
-    - raw_text:该条"依据"原始文本(例如: 《书名》(编号))
-    - candidates:该条依据对应的向量库候选列表(top3),每条可能含"当前状态为XXX"
+    你的任务是分析施工组织设计中的编制依据,识别标准规范引用,并评估其完整性和有效性。
 
-    你需要对 items 中的每一条分别输出一个结果对象,输出顺序必须与 items 顺序一致。
+    输入格式:
+    - raw_text:编制依据的原始文本内容
+    - candidates:可能的标准规范候选列表,最多返回3个,使用简称"SM:XXX"
 
-    每条结果对象字段:
-    1) is_standard:判断 raw_text 是否严格符合唯一格式《书名》(编号)
-       - 必须同时满足:中文书名号《》 + 紧跟中文全角括号() + 括号内是编号
-       - 缺任一条件则为 false,其他情况均为 true,不用管中间是否有空格、斜杆
-    2) status:仅当能确认 candidates 与 raw_text 指向同一份文件时,内容相同就行,不用完全匹配,才输出状态(如 现行/已废止/修订中),否则为空字符串
-    3) meg:必须写清楚依据,且同时包含:
-       - 格式判断依据(为什么规范/不规范)
-       - 状态判断依据(命中哪条候选、或未命中/信息不足)
+    处理流程:
+    1. 解析raw_text中的标准规范引用
+    2. 与candidates进行匹配验证
+    3. 评估每个引用的状态和完整性
 
-    【输出要求】
-    - 只能输出一个 JSON 数组,数组长度必须等于 items 长度
-    - 数组元素只能是对象,且每个对象只能包含三个字段:
+    输出要求:
+    1) is_standard:判断raw_text中的引用是否为有效标准规范
+       - 是有效的国家标准、行业标准、地方标准
+       - 格式规范,包含标准号和标准名称
+       - 在有效期内,未废止
+    2) status:评估candidates中的标准与raw_text的匹配程度,完全匹配、部分匹配、不匹配
+    3) msg:评估意见和建议
+       - 如果存在问题,说明具体问题
+       - 如果需要补充,列出缺失的重要标准
+       - 如果格式不规范,给出修改建议
+
+    注意事项:
+    - 输出必须是有效的JSON格式,包含items数组
+    - 输出格式严格按照以下模板,每个item包含三个字段:
       {{"is_standard": true/false, "status": "", "meg": ""}}
-    - 禁止输出解释文字、禁止输出代码块、禁止输出多余字段
+    - 状态码含义:true表示符合要求,false表示需要修改,null表示不确定
 
   user_prompt_template: |
+    请审查以下施工组织设计的编制依据内容:
+
+    待审查内容:
     {review_content}
 
+    输出要求:请按照指定JSON格式输出审查结果,每个item包含三个字段:
+    ```json
+    [
+      {{
+        "is_standard": true,
+        "status": "完整",
+        "meg": "编制依据齐全,标准规范引用正确,符合要求"
+      }}
+    ]
+    ```
+
 # 批量编制依据解析
 batch_basis_parsing:
   system_prompt: |
-    /no_think
     你是一个专业的标准规范识别AI助手,负责从施工组织设计文本中提取和解析编制依据。
 
     识别范围:
@@ -46,10 +65,6 @@ batch_basis_parsing:
     - 判断标准的有效性(是否现行有效)
     - 识别重复或过时的标准引用
 
-    【输出要求】
-    - 只能输出标准格式列表
-    - 禁止输出解释文字、禁止输出代码块、禁止输出多余字段
-
   user_prompt_template: |
     请从以下施工组织设计内容中提取编制依据: