Ver Fonte

fix: 强化各审查模块提示词职责边界,防止串台

- grammar_check: 明确限制仅审查错别字、多字少字、重复字词、标点、"的地得"混用,禁止审查逻辑/语义/事实类问题
- semantic_logic_check: 禁止审查词句语法问题和敏感词问题,限定仅审查逻辑矛盾/因果错误/条件结论不匹配/口语化
- sensitive_word_check: 禁止审查词句语法问题和语义逻辑问题,限定仅审查敏感词层面
- 重命名 sensitive_word_check.py → sensitive_check_reviewer.py,收敛命名语义

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
WangXuMing há 4 semanas atrás
pai
commit
d9249c2f05

BIN
config/.DS_Store


+ 3 - 18
core/base/task_models.py

@@ -35,20 +35,8 @@ class TaskFileInfo:
         self.file_content = file_info.get('file_content', b'')
 
         # 审查配置信息(使用 .get() 提供默认值,支持键不存在的情况)
-        review_config_raw = file_info.get('review_config')
         review_item_config_raw = file_info.get('review_item_config')
 
-        # 类型校验:确保review_config是列表
-        if isinstance(review_config_raw, list):
-            self.review_config = review_config_raw
-        else:
-            # 如果不是列表,记录警告并使用默认空列表
-            logger.warning(
-                f"review_config类型错误,期望list,实际{type(review_config_raw).__name__},"
-                f"值: {review_config_raw},将使用空列表"
-            )
-            self.review_config = []
-
         # 类型校验:确保review_item_config是列表
         if isinstance(review_item_config_raw, list):
             self.review_item_config = review_item_config_raw
@@ -76,8 +64,9 @@ class TaskFileInfo:
         return self._file_info.copy()
 
     def get_review_config_list(self) -> list:
-        """获取审查配置列表"""
-        return self.review_config.copy()
+        """[已废弃] 获取审查配置列表 — 旧 review_config 已移除,仅保留兼容性"""
+        logger.warning("get_review_config_list() 已废弃,请使用 get_review_item_config_list()")
+        return []
 
     def get_review_item_config_list(self) -> list:
         """获取审查项配置列表(章节_审查维度格式)"""
@@ -95,10 +84,6 @@ class TaskFileInfo:
         """获取测试定位标志符"""
         return self.test_designation_chunk_flag
 
-    def has_review_type(self, review_type: str) -> bool:
-        """检查是否包含指定的审查类型"""
-        return review_type in self.review_config
-
     def get_file_size(self) -> int:
         """获取文件大小(字节)"""
         return len(self.file_content) if self.file_content else 0

+ 11 - 99
core/construction_review/component/ai_review_engine.py

@@ -101,7 +101,6 @@ class Stage(Enum):
     """工作流状态"""
     BASIC = {
         'reviewer_type':'basic',
-        'grammar': 'sensitive_word_check',
         'sensitive':'sensitive_check',
         'semantic': 'semantic_logic_check',
         'completeness': 'completeness_check',
@@ -145,7 +144,6 @@ class AIReviewEngine(BaseReviewer):
         self.file_id = task_file_info.file_id
         self.callback_task_id = task_file_info.callback_task_id
         self.user_id = task_file_info.user_id
-        self.review_config = task_file_info.review_config
         self.project_plan_type = task_file_info.project_plan_type
         self.tendency_review_role = task_file_info.tendency_review_role
 
@@ -601,36 +599,6 @@ class AIReviewEngine(BaseReviewer):
         logger.info(f"[技术审查] 成功处理 {len(flattened_results)} 个审查结果")
         return flattened_results
 
-    async def sensitive_word_check(self, trace_id_idx: str, review_content: str,
-                          state: str, stage_name: str) -> Dict[str, Any]:
-        """
-        敏感词 LLM 审查
-
-        Args:
-            trace_id_idx: 追踪ID索引
-            review_content: 审查内容
-            state: 状态字典
-            stage_name: 阶段名称
-
-        Returns:
-            ReviewResult: 敏感词审查结果
-        """
-        from core.construction_review.component.reviewers.sensitive_word_check import sensitive_word_check_reviewer
-
-        # 构造trace_id
-        prompt_name = Stage.BASIC.value['grammar']
-        trace_id = prompt_name + trace_id_idx
-
-        # 调用敏感词 LLM 审查模块
-        result = await sensitive_word_check_reviewer.check_sensitive_word(
-            trace_id=trace_id,
-            review_content=review_content,
-            state=state,
-            stage_name=stage_name
-        )
-
-        return result
-
     async def grammar_check(self, trace_id_idx: str, review_content: str,
                           state: str, stage_name: str) -> Dict[str, Any]:
         """
@@ -1066,74 +1034,18 @@ class AIReviewEngine(BaseReviewer):
         Returns:
             Dict[str, Any]: 敏感信息检查结果
         """
-        from core.construction_review.component.reviewers.utils import (
-            check_sensitive_words_async,
-            format_check_results
-        )
-        from foundation.observability.logger.loggering import review_logger as logger
-        import time
-        
-        start_time = time.time()
+        from core.construction_review.component.reviewers.sensitive_check_reviewer import sensitive_check_reviewer
+
         trace_id = "sensitive_check" + trace_id_idx
-        first_results = await check_sensitive_words_async(review_content) # 先使用关键词匹配式审查
-        
-        # 判断是否检测到敏感词
-        if first_results:
-            logger.info(f"检测到 {len(first_results)} 个敏感词,准备送入大模型二审")
-            # 有敏感词,拼接原文与敏感词列表,进入大模型二审
-            # 格式化敏感词列表
-            sensitive_words_info = []
-            for item in first_results:
-                sensitive_words_info.append(
-                    f"敏感词: {item['word']}, 位置: {item['position']}-{item['end_position']}, 来源: {item['source']}"
-                )
-            formatted_sensitive_words = "\n".join(sensitive_words_info)
-            
-            logger.info(f"格式化后的敏感词信息:\n{formatted_sensitive_words}")
-            
-            # 调用大模型得到敏感词审查结果(通过 function_name 从 model_setting.yaml 加载模型配置)
-            return await self.review("sensitive_check", trace_id, "basic", "sensitive_word_check",
-                                   review_content, formatted_sensitive_words,
-                                   None, state, stage_name, timeout=60, function_name="sensitive_check")
-        else:
-            # 没有检测到敏感词,构造返回体
-            logger.info("没有检测到敏感词,未进入二审")
-            from core.construction_review.component.reviewers.base_reviewer import ReviewResult
-            
-            execution_time = time.time() - start_time
-            result = ReviewResult(
-                success = True,
-                details = {"name": "sensitive_check", "response": "无明显问题"},
-                error_message = None,
-                execution_time = execution_time
-            )
-            
-            # 推送审查完成信息
-            if state and state.get("progress_manager"):
-                import asyncio
-                review_result_data = {
-                    'name': 'sensitive_check',
-                    'success': result.success,
-                    'details': result.details,
-                    'error_message': result.error_message,
-                    'execution_time': result.execution_time,
-                    'timestamp': time.time()
-                }
-                asyncio.create_task(
-                    state["progress_manager"].update_stage_progress(
-                        callback_task_id=state["callback_task_id"],
-                        stage_name=stage_name,
-                        current=None,
-                        status="processing",
-                        message=f"sensitive_check 审查完成,未检测到敏感词,耗时: {result.execution_time:.2f}s",
-                        issues=[review_result_data],
-                        event_type="processing"
-                    )
-                )
-            
-            logger.info(f"sensitive_check 审查完成,未检测到敏感词,耗时: {result.execution_time:.2f}s")
-            
-            return result
+
+        result = await sensitive_check_reviewer.check_sensitive(
+            trace_id=trace_id,
+            review_content=review_content,
+            state=state,
+            stage_name=stage_name
+        )
+
+        return result
 
     async def check_non_parameter_compliance(self, trace_id_idx: str, review_content: str, review_references: str,
                                          reference_source: str, state: str, stage_name: str,

+ 82 - 26
core/construction_review/component/reviewers/prompt/basic_reviewers.yaml

@@ -5,7 +5,7 @@ grammar_check:
   system_prompt: |
     system
     # role
-    你是词句语法审查专家
+    你是词句语法审查专家,专注于文字的**书写正确性**和**语法规范性**。
 
     ## workflow
     - 负责检查文本中的错别字和重复字词等语法问题。
@@ -20,10 +20,10 @@ grammar_check:
     ## example
     1. 出现了明显错字,出现了的地得使用混乱。
     2. 如果出现了错字,大概率是因为拼音拼写错误导致的错字,请根据其相似读音推测正确的字,如果拿不准请不给出修改为xx的建议。
-    3. 如“供气”错打为了“供器”,应当结合上下文推测“供qi”应该为是什么。
+    3. 如”供气”错打为了”供器”,应当结合上下文推测”供qi”应该为是什么。
     4. 对于条款编号而言,'一)'这样的结构是正确的,符合中文规范
-    5. 所有建议都要基于审查内容给出忠实建议,禁止给出不符合逻辑的错误建议,如建议将A修改成A,例如:"将'确定的'改为'确定的'(原字为'确',应为'确')",这种建议就是错误的
-    6. 请对汉语中经典易错字如“辩”与“辨”等等的混用请多加注意。
+    5. 所有建议都要基于审查内容给出忠实建议,禁止给出不符合逻辑的错误建议,如建议将A修改成A,例如:”将'确定的'改为'确定的'(原字为'确',应为'确')”,这种建议就是错误的
+    6. 请对汉语中经典易错字如”辩”与”辨”等等的混用请多加注意。
 
     ## rule
     - 不需要强求要输出问题,除非是非常明显的错误。小问题可以忽略。
@@ -44,6 +44,22 @@ grammar_check:
         6. 统一解释:如果表格中出现了多列相同的表头标题,不是错误,而是解析时这几个是合并的表头。
         7. 所有建议都要基于审查内容给出忠实建议,禁止给出不符合逻辑的错误建议,如建议将A修改成A,例如:将'若'改为'若'(应为'若'),这种建议就是错误的
 
+    ## ⚠️ 严格禁止审查以下内容(与词句语法无关)
+    **你只负责检查词句语法层面的问题**,以下类型的问题**一律跳过,不输出任何 issue**:
+    1. **逻辑矛盾**:如前后陈述不一致、数据前后冲突等(例:前文说”采用A方法”,后文说”不采用A方法”)
+    2. **时间/日期错误**:如开工日期晚于竣工日期、时间顺序倒置等(例:”计划2026年开工,2025年竣工”——这是逻辑错误,不是词句错误)
+    3. **因果关系错误**:如原因与结果不匹配(例:”因为天气晴朗,所以混凝土强度不足”)
+    4. **条件与结论不匹配**:如条件无法推导出结论(例:”当温度低于5℃时,可正常施工”)
+    5. **常识/事实错误**:如技术参数不合理、施工方法选择错误等
+    6. **语义矛盾**:如表达意思前后冲突、语义歧义
+    7. **数值计算错误**:如加法结果不对、数据不一致
+    8. **业务逻辑问题**:如施工方案不合理、工艺流程错误
+    9. **数据单位/格式**:如单位使用不当、数字格式问题
+    10. **语义逻辑问题**:任何与语义、逻辑、事实相关的内容
+
+    **你的职责范围仅限于**:错别字(如”混泥土”→”混凝土”)、多字/少字、重复字词(如”公司公司”)、标点符号错误、”的地得”混用、明显的语法结构错误。
+    超出以上范围的所有问题,请忽略并输出”无明显问题”。
+
     ## output
     ```json
         {{
@@ -60,10 +76,18 @@ grammar_check:
     - 低风险:形式问题、不影响实质内容的词句错误。
 
   user_prompt_template: |
-    请审查以下内容的词句语法错误,重点关注错别字、重复字词和语法结构:
+    请审查以下内容的词句语法错误,**仅限**错别字、多字、少字、重复字词、标点符号错误、"的地得"混用、明显的语法结构错误。
+
     【待检查文本】
     {review_content}
 
+    ⚠️ 重要提醒:以下类型的问题**不属于本次审查范围,请跳过不输出**:
+    - 逻辑矛盾(如前后数据不一致、时间顺序倒置、因果关系错误)
+    - 事实/常识错误(如施工方案不合理、技术参数错误)
+    - 数值计算错误、单位格式问题
+    - 语义歧义、条件结论不匹配
+    - 任何与逻辑、语义、事实相关的问题
+
     输出格式:务必须严格按照以下标准json格式输出审查结果
     如果未发现明显的词句语法错误,请输出:无明显问题。
     **禁止**输出建议类似于"将'设'改为'设'(原字为'设',应为'设')",没有问题就是没有问题,不可造假,谢谢。
@@ -121,21 +145,32 @@ semantic_logic_check:
     4. 禁止对表格格式、制表符进行检查
     5. 禁止对同一问题重复输出
     6. 禁止在没有明确逻辑错误时输出问题
-    7. 禁止将"表达不够完美"当作"逻辑错误"
-    8. 禁止将"可以优化"当作"必须修改"
-    9. 禁止对所谓的"表述不恰当"、"表述过于严格"当做错误点,如你不能将“禁止夜间施工”改为“应在增加照明条件下允许夜间施工”这样的建议端上来
+    7. 禁止将”表达不够完美”当作”逻辑错误”
+    8. 禁止将”可以优化”当作”必须修改”
+    9. 禁止对所谓的”表述不恰当”、”表述过于严格”当做错误点,如你不能将”禁止夜间施工”改为”应在增加照明条件下允许夜间施工”这样的建议端上来
     10. **禁止审查过于专业的知识**,你只是审查通用的语义逻辑关系,而并非需要你根据你的知识去审查过多的部分如涉及到架桥参数、施工参数等等一些列的问题,这些问题有后续流程会处理,你暂且跳过
     11. **禁止对专业知识进行点评**,如技术参数、技术规范、技术条文,你对这方面知识还是较为落后的,你不需要对这方便进行涉猎
-    12. 所有建议都要基于审查内容给出忠实建议,禁止给出不符合逻辑的错误建议,如建议将A修改成A,例如:"将'确定的'改为'确定的'(原字为'确',应为'确')",这种建议就是错误的
+    12. 所有建议都要基于审查内容给出忠实建议,禁止给出不符合逻辑的错误建议,如建议将A修改成A,例如:”将'确定的'改为'确定的'(原字为'确',应为'确')”,这种建议就是错误的
     13. **禁止回复**如A修改为A这种假回答,文段没有问题必须直接输出:无明显问题
+    14. **禁止审查词句语法问题**:错别字、多字、少字、重复字词、标点符号错误、”的地得”混用、语法结构等,这些问题由词句语法审查模块负责,不属于你的职责范围
+    15. **禁止审查敏感词问题**:政治敏感、商业机密、表述适宜性等,这些问题由敏感词审查模块负责,不属于你的职责范围
 
     必须遵守:
     1. 必须基于客观事实和逻辑规则判断
     2. 必须确保问题的真实性和严重性
-    3. 必须区分"逻辑错误"与"表达习惯"
-    4. 必须在不确定时选择"无明显问题"
+    3. 必须区分”逻辑错误”与”表达习惯”
+    4. 必须在不确定时选择”无明显问题”
     5. 必须保持高标准:宁缺毋滥
-    6. 给出的上下文可能不完整,你需要注意不同序号下,有些地方可能来源于不同模块,如前面4条是第(2)节内容,后面有明确申明如“(3)xxx规范”这样的那么前后文就没有参考价值,属于前后文不相关
+    6. 给出的上下文可能不完整,你需要注意不同序号下,有些地方可能来源于不同模块,如前面4条是第(2)节内容,后面有明确申明如”(3)xxx规范”这样的那么前后文就没有参考价值,属于前后文不相关
+
+    ## ⚠️ 严格禁止审查以下内容(非语义逻辑范畴)
+    你只负责检查语义逻辑层面的问题,以下类型的问题**一律跳过,不输出任何 issue**:
+    1. **词句语法问题**:错别字、多字/少字、重复字词、标点符号错误、”的地得”混用、语法结构错误 —— 这些由**词句语法审查模块**负责
+    2. **敏感词问题**:政治敏感、商业机密、表述不当、工程绝对化用语 —— 这些由**敏感词审查模块**负责
+    3. **专业参数/技术问题**:架桥参数、施工参数、技术规范 —— 由专业技术审查流程处理
+    4. **格式/排版问题**:段落缩进、字体大小、制表符等
+
+    遇到以上类型的问题,请忽略,仅审查逻辑矛盾、因果关系错误、条件结论不匹配、口语化表达四类问题。
     
     # 风险等级判定标准 (Risk Level)
     
@@ -162,8 +197,17 @@ semantic_logic_check:
 
   user_prompt_template: |
     # 审查任务
-    请对以下施工方案内容进行语义逻辑审查,严格按照系统提示词中的三类问题范围进行检查。
-    
+    请对以下施工方案内容进行语义逻辑审查,**仅**检查以下四类问题:
+    1. **逻辑矛盾** - 前后相互冲突的陈述
+    2. **因果关系错误** - 原因与结果之间不存在合理的逻辑关联
+    3. **条件与结论不匹配** - 给定条件无法推导出所述结论
+    4. **口语化表达** - 不符合施工方案严肃规范的口语化用语
+
+    ⚠️ **以下问题不属于本次审查范围,请跳过不输出**:
+    - 错别字、重复字词、标点符号错误、语法结构(由词句语法审查模块负责)
+    - 政治敏感、商业机密、表述不当(由敏感词审查模块负责)
+    - 技术参数、施工规范(由专业技术审查流程处理)
+
     ## 待审查内容:
     {review_content}
 
@@ -171,12 +215,12 @@ semantic_logic_check:
     1. 仅识别明确的逻辑矛盾、因果错误、条件结论不匹配问题
     2. 必须确保问题的真实性,不得对正确内容提出修改
     3. 不确定是否为问题时,必须输出"无明显问题"
-    
+
     ## 输出格式:
-    
+
     **情况1:未发现明确的语义逻辑问题**
     直接输出:无明显问题
-    
+
     **情况2:发现明确的语义逻辑问题**
     严格按照以下JSON格式输出(仅输出确认无误的问题):
     ```json
@@ -188,10 +232,10 @@ semantic_logic_check:
       "risk_level": "高风险/中风险/低风险(严格按照系统提示词中的标准判定)"
     }}
     ```
-    
+
     ## 特别提醒:
     - 如果内容表达虽不完美但逻辑正确,输出"无明显问题"
-    - 如果是专业术语的标准表达,输出"无明显问题"  
+    - 如果是专业术语的标准表达,输出"无明显问题"
     - 如果只是表达习惯差异,输出"无明显问题"
     - 保持高标准:宁可漏报,不可误报
 
@@ -318,12 +362,20 @@ sensitive_word_check:
     - 重点关注政治敏感、商业机密、表述不当、工程绝对化用语(如绝对不会出现事故、绝对不会有污染等等)
     - 你只需要考虑初筛找到的敏感词与原文,不需要你自行去找敏感词
     - 通过给出的敏感词初筛内容,并联系上下文,确定初筛的关键词匹配的敏感词是否合理,如果合理则给出issue,如果初筛的敏感词在原文中的语义并无恶意则直接输出:无明显问题
-    - 例如:原文“应禁止工人在宿舍中赌博”,初筛内容:“赌博”;解释:原文中“赌博”二字无恶意;结论:无明显问题
+    - 例如:原文”应禁止工人在宿舍中赌博”,初筛内容:”赌博”;解释:原文中”赌博”二字无恶意;结论:无明显问题
     - 风险等级分类:
       * 高风险:影响审查结论、可能导致法律问题或严重安全隐患
       * 中风险:影响专业表达、可能导致理解偏差或一般性问题
       * 低风险:形式问题、不影响实质内容和安全
-    你需要注意:不要过于应激,不要反馈“提及xxx敏感词会导致不良反应”,不要没错找错,无中生有。请还需多多注意。
+    你需要注意:不要过于应激,不要反馈”提及xxx敏感词会导致不良反应”,不要没错找错,无中生有。请还需多多注意。
+
+    ## ⚠️ 严格禁止审查以下内容(非敏感词范畴)
+    你只负责检查敏感词层面的问题,以下类型的问题**一律跳过,不输出任何 issue**:
+    1. **词句语法问题**:错别字、多字/少字、重复字词、标点符号错误、”的地得”混用、语法结构错误 —— 这些由**词句语法审查模块**负责
+    2. **语义逻辑问题**:逻辑矛盾、因果关系错误、条件结论不匹配、事实错误 —— 这些由**语义逻辑审查模块**负责
+    3. **专业参数/技术问题**:架桥参数、施工参数、技术规范 —— 由专业技术审查流程处理
+
+    遇到以上类型的问题,请忽略,仅审查敏感词层面的问题。
 
   user_prompt_template: |
     以下我初步查找到的敏感词内容:
@@ -332,16 +384,20 @@ sensitive_word_check:
     请根据以下内容作为上下文,确定敏感词是否合理:
     {review_content}
 
+    ⚠️ **以下问题不属于本次审查范围,请跳过不输出**:
+    - 错别字、重复字词、标点符号错误、语法结构(由词句语法审查模块负责)
+    - 逻辑矛盾、因果错误、条件结论不匹配(由语义逻辑审查模块负责)
+
     输出格式:务必须严格按照以下标准JSON格式输出审查结果:
     如果初筛敏感词在原文中并无恶意,请输出:无明显问题
     如果发现初筛出的敏感词确实存在问题,请按以下格式输出:
     location字段直接输出原字段内容,不得猜测
     ```json
     {{
-      "issue_point": "问题标题描述",
-      "location": "当前问题对应的原始条款内容及位置,如六、验收标准 (页码: 85),以及其语境上下文",
-      "suggestion": "具体的修改建议内容",
-      "reason": "问题的原因分析和依据说明",
-      "risk_level": ""
+      “issue_point”: “问题标题描述”,
+      “location”: “当前问题对应的原始条款内容及位置,如六、验收标准 (页码: 85),以及其语境上下文,
+      “suggestion”: “具体的修改建议内容”,
+      “reason”: “问题的原因分析和依据说明”,
+      “risk_level”: “”
     }}
     ```

+ 39 - 58
core/construction_review/component/reviewers/sensitive_word_check.py → core/construction_review/component/reviewers/sensitive_check_reviewer.py

@@ -1,25 +1,20 @@
 """
-敏感词查模块
-使用通用模型底座进行敏感词上下文审查
+敏感词查模块
+两阶段审查:关键词初筛 + LLM 二审
 """
 
 import time
 import asyncio
 from typing import Dict, Any
-from core.construction_review.component.reviewers.base_reviewer import ReviewResult
-from core.construction_review.component.reviewers.utils.prompt_loader import prompt_loader
-from foundation.ai.agent.generate.model_generate import generate_model_client
+from core.construction_review.component.reviewers.base_reviewer import ReviewResult, BaseReviewer
+from core.construction_review.component.reviewers.utils import check_sensitive_words_async
 from foundation.observability.logger.loggering import review_logger as logger
 
 
-class SensitiveWordLLMReviewer:
-    """敏感词 LLM 审查器"""
+class SensitiveCheckReviewer(BaseReviewer):
+    """敏感词审查器 — 关键词初筛 + LLM 审"""
 
-    def __init__(self):
-        """初始化敏感词 LLM 审查器"""
-        self.model_client = generate_model_client
-
-    async def check_sensitive_word(
+    async def check_sensitive(
         self,
         trace_id: str,
         review_content: str,
@@ -27,7 +22,7 @@ class SensitiveWordLLMReviewer:
         stage_name: str = None
     ) -> ReviewResult:
         """
-        执行敏感词 LLM 审查
+        执行敏感词审查
 
         Args:
             trace_id: 追踪ID
@@ -41,106 +36,93 @@ class SensitiveWordLLMReviewer:
         start_time = time.time()
 
         try:
-            logger.info(f"开始敏感词 LLM 审查,trace_id: {trace_id}, 内容长度: {len(review_content)}")
-
-            # 构造提示词参数
-            prompt_kwargs = {}
-            prompt_kwargs["review_content"] = review_content
-            prompt_kwargs["review_references"] = ""  # 添加空字符串,满足模板要求
-
-            # 获取提示词模板
-            prompt_template = prompt_loader.get_prompt_template(
-                "basic",
-                "sensitive_word_check",
-                **prompt_kwargs
-            )
+            logger.info(f"开始敏感词审查,trace_id: {trace_id}, 内容长度: {len(review_content)}")
 
-            # 格式化提示词消息
-            messages = prompt_template.format_messages()
+            first_results = await check_sensitive_words_async(review_content)
 
-            logger.info("调用敏感词 LLM 审查模型")
+            if first_results:
+                logger.info(f"检测到 {len(first_results)} 个敏感词,送入大模型二审")
 
-            # 使用 function_name 从 model_setting.yaml 加载模型配置
-            model_response = await self.model_client.get_model_generate_invoke(
-                trace_id=trace_id,
-                messages=messages,
-                function_name="grammar_check"
-            )
+                sensitive_words_info = []
+                for item in first_results:
+                    sensitive_words_info.append(
+                        f"敏感词: {item['word']}, 位置: {item['position']}-{item['end_position']}, 来源: {item['source']}"
+                    )
+                formatted_sensitive_words = "\n".join(sensitive_words_info)
 
-            logger.info(f"敏感词 LLM 审查模型响应成功,响应长度: {len(model_response)}")
+                logger.info(f"格式化敏感词信息:\n{formatted_sensitive_words}")
 
-            # 计算执行时间
-            execution_time = time.time() - start_time
+                result = await self.review(
+                    "sensitive_check", trace_id, "basic", "sensitive_word_check",
+                    review_content, formatted_sensitive_words,
+                    None, state, stage_name, timeout=60, function_name="sensitive_check"
+                )
 
-            # 构造审查结果
+                logger.info(f"sensitive_check 审查完成(已二审),耗时: {time.time() - start_time:.2f}s")
+                return result
+
+            # 未检测到敏感词
+            logger.info("未检测到敏感词,跳过二审")
+            execution_time = time.time() - start_time
             result = ReviewResult(
                 success=True,
-                details={
-                    "name": "sensitive_word_check",
-                    "response": model_response
-                },
+                details={"name": "sensitive_check", "response": "无明显问题"},
                 error_message=None,
                 execution_time=execution_time
             )
 
-            # 推送审查完成信息
             if state and state.get("progress_manager"):
                 review_result_data = {
-                    'name': 'sensitive_word_check',
+                    'name': 'sensitive_check',
                     'success': result.success,
                     'details': result.details,
                     'error_message': result.error_message,
                     'execution_time': result.execution_time,
                     'timestamp': time.time()
                 }
-
                 asyncio.create_task(
                     state["progress_manager"].update_stage_progress(
                         callback_task_id=state["callback_task_id"],
                         stage_name=stage_name,
                         current=None,
                         status="processing",
-                        message=f"sensitive_word_check 审查完成,耗时: {result.execution_time:.2f}s",
+                        message=f"sensitive_check 审查完成,未检测到敏感词,耗时: {result.execution_time:.2f}s",
                         issues=[review_result_data],
                         event_type="processing"
                     )
                 )
 
-            logger.info(f"sensitive_word_check 审查完成,耗时: {result.execution_time:.2f}s")
-
+            logger.info(f"sensitive_check 审查完成,未检测到敏感词,耗时: {execution_time:.2f}s")
             return result
 
         except Exception as e:
             execution_time = time.time() - start_time
-            error_msg = f"敏感词 LLM 审查失败: {str(e)}"
+            error_msg = f"敏感词审查失败: {str(e)}"
             logger.error(error_msg, exc_info=True)
 
-            # 返回失败结果
             result = ReviewResult(
                 success=False,
-                details={"name": "sensitive_word_check"},
+                details={"name": "sensitive_check"},
                 error_message=error_msg,
                 execution_time=execution_time
             )
 
-            # 推送失败信息
             if state and state.get("progress_manager"):
                 review_result_data = {
-                    'name': 'sensitive_word_check',
+                    'name': 'sensitive_check',
                     'success': False,
                     'details': result.details,
                     'error_message': error_msg,
                     'execution_time': execution_time,
                     'timestamp': time.time()
                 }
-
                 asyncio.create_task(
                     state["progress_manager"].update_stage_progress(
                         callback_task_id=state["callback_task_id"],
                         stage_name=stage_name,
                         current=None,
                         status="processing",
-                        message=f"sensitive_word_check 审查失败: {error_msg}",
+                        message=f"sensitive_check 审查失败: {error_msg}",
                         issues=[review_result_data],
                         event_type="processing"
                     )
@@ -149,5 +131,4 @@ class SensitiveWordLLMReviewer:
             return result
 
 
-# 全局单例实例
-sensitive_word_check_reviewer = SensitiveWordLLMReviewer()
+sensitive_check_reviewer = SensitiveCheckReviewer()

+ 1 - 2
core/construction_review/workflows/ai_review_workflow.py

@@ -312,8 +312,7 @@ class AIReviewWorkflow:
 
             # 2. 解析审查项配置
             review_func_mapping: Dict[str, Union[str, List[str]]] = {
-                'sensitive_word_check': 'sensitive_word_check',
-                'grammar_check': 'grammar_check',
+                'sensitive_word_check': 'grammar_check',  # 前端接口:sensitive_word_check = 词句语法审查
                 'semantic_logic_check': 'check_semantic_logic',
                 'completeness_check': 'check_completeness',
                 'timeliness_check': 'timeliness_reviewer',  # 统一入口

+ 1 - 15
core/construction_review/workflows/core_functions/ai_review_core_fun.py

@@ -92,7 +92,6 @@ class AIReviewCoreFun:
         self.file_id = task_file_info.file_id
         self.callback_task_id = task_file_info.callback_task_id
         self.user_id = task_file_info.user_id
-        self.review_config = task_file_info.review_config
         self.project_plan_type = task_file_info.project_plan_type
         
         self.max_concurrent = 20
@@ -381,20 +380,7 @@ class AIReviewCoreFun:
         logger.debug(f"执行审查: {trace_id} -> {func_name}")
 
         # 根据func_name构建对应的参数并调用
-        if func_name == "sensitive_word_check" and not is_complete_field:
-            raw_result = await method(trace_id, review_content, state, stage_name)
-            # 基础审查方法,放入 basic_compliance
-            return UnitReviewResult(
-                unit_index=chunk_index,
-                unit_content=chunk,
-                basic_compliance={func_name: raw_result},
-                technical_compliance={},
-                rag_enhanced={},
-                overall_risk=self._calculate_single_result_risk(raw_result),
-                is_sse_push=True
-            )
-
-        elif func_name == "grammar_check" and not is_complete_field:
+        if func_name == "grammar_check" and not is_complete_field:
             raw_result = await method(trace_id, review_content, state, stage_name)
             # 基础审查方法,放入 basic_compliance
             return UnitReviewResult(

+ 3 - 0
foundation/ai/models/model_handler.py

@@ -933,6 +933,7 @@ class ModelHandler:
                 model=model_id,
                 api_key="dummy",  # 本地模型使用虚拟API key
                 timeout=self.REQUEST_TIMEOUT,
+                tiktoken_enabled=False,
             )
 
             logger.info(f"本地Qwen3-Embedding-8B模型初始化成功: {model_id}")
@@ -979,6 +980,7 @@ class ModelHandler:
                 model=model_id,
                 api_key=api_key,
                 timeout=self.REQUEST_TIMEOUT,
+                tiktoken_enabled=False,
             )
 
             logger.info(f"硅基流动Embedding模型初始化成功: {model_id} (dimensions: {dimensions})")
@@ -1126,6 +1128,7 @@ class ModelHandler:
                 model=model_id,
                 api_key=api_key,
                 timeout=self.REQUEST_TIMEOUT,
+                tiktoken_enabled=False,
             )
 
             logger.info(f"蜀天Qwen3-Embedding-8B模型初始化成功: {model_id}")

+ 6 - 79
views/construction_review/launch_review.py

@@ -46,13 +46,9 @@ class LaunchReviewRequest(BaseModel):
         ...,
         description="倾向性审查角色,暂定为 default_role"
     )
-    review_config: Optional[List[str]] = Field(
-        None,
-        description="审查配置列表,包含的项为启用状态(审查维度枚举值)。与review_item_config互斥"
-    )
     review_item_config: Optional[List[str]] = Field(
         None,
-        description="审查项配置列表,格式为「章节code_审查维度」(如 basis_sensitive_word_check)。与review_config互斥"
+        description="审查项配置列表,格式为「章节code_审查维度」(如 basis_sensitive_word_check)"
     )
     project_plan_type: str = Field(
         ...,
@@ -74,63 +70,6 @@ class LaunchReviewResponse(BaseModel):
     data: dict
 
 
-def validate_review_config(review_config: List[str]) -> None:
-    """验证审查配置参数"""
-    # 检查review_config是否为空
-    logger.info(f"审查配置: {review_config}")
-    if not review_config or len(review_config) == 0:
-        raise LaunchReviewErrors.enum_type_cannot_be_null()
-
-    # 支持的审查项枚举值
-    supported_review_items = {
-        'sensitive_word_check',       # 敏感词审查
-        'grammar_check',              # 词句语法检查
-        'semantic_logic_check',       # 语义逻辑审查
-        'completeness_check',         # 条文完整性审查
-        'timeliness_check',           # 时效性审查
-        'reference_check',            # 规范性审查
-        'sensitive_check',            # 敏感信息检查
-        'non_parameter_compliance_check',  # 非参数合规性检查功能
-        'parameter_compliance_check', # 参数合规性检查功能
-    }
-
-    # 检查是否包含不支持的审查项
-    unsupported_items = set(review_config) - supported_review_items
-    if unsupported_items:
-        raise LaunchReviewErrors.enum_type_invalid()
-
-def validate_review_config_mutually_exclusive(
-    review_config: Optional[List[str]],
-    review_item_config: Optional[List[str]]
-) -> None:
-    """
-    验证 review_config 与 review_item_config 互斥性
-
-    规则:
-    1. 两者不能同时提供(互斥)
-    2. 两者不能同时为空(必须提供其中一个)
-
-    Args:
-        review_config: 审查配置列表(审查维度枚举值)
-        review_item_config: 审查项配置列表(章节_审查维度格式)
-
-    Raises:
-        HTTPException: 当两者同时提供或同时为空时抛出异常
-    """
-    # 判断字段是否存在(只判断是否为 None,不判断长度)
-    has_review_config = review_config is not None
-    has_review_item_config = review_item_config is not None
-
-    # 情况1:两者都提供了 - 互斥错误(包括空列表的情况)
-    if has_review_config and has_review_item_config:
-        raise LaunchReviewErrors.mutually_exclusive_config()
-
-    # 情况2:两者都为空 - 必须提供一个
-    if not has_review_config and not has_review_item_config:
-        raise LaunchReviewErrors.review_config_required()
-
-    logger.info(f"审查配置验证通过: 使用{'review_config' if has_review_config else 'review_item_config'}")
-
 def validate_review_item_config(review_item_config: List[str]) -> None:
     """
     验证审查项配置参数合法性
@@ -161,7 +100,7 @@ def validate_review_item_config(review_item_config: List[str]) -> None:
             "acceptance", "other"
         },
         "review_dimensions": {
-            "sensitive_word_check", "grammar_check", "semantic_logic_check", "completeness_check",
+            "sensitive_word_check", "semantic_logic_check", "completeness_check",
             "timeliness_check", "reference_check", "sensitive_check",
             "non_parameter_compliance_check", "parameter_compliance_check"
         }
@@ -271,7 +210,6 @@ async def launch_review_sse(request_data: LaunchReviewRequest):
     callback_task_id = request_data.callback_task_id
     TraceContext.set_trace_id(callback_task_id)
     user_id = request_data.user_id
-    review_config = request_data.review_config
     review_item_config = request_data.review_item_config
     project_plan_type = request_data.project_plan_type
     tendency_review_role = request_data.tendency_review_role
@@ -282,20 +220,10 @@ async def launch_review_sse(request_data: LaunchReviewRequest):
     # 验证用户标识
     validate_user_id(user_id)
 
-    # 验证审查配置互斥性(优先验证互斥关系)
-    validate_review_config_mutually_exclusive(review_config, review_item_config)
-
-    # 根据提供的配置类型进行相应验证(只判断是否为 None)
-    if review_config is not None:
-        # 使用 review_config 时的验证
-        if len(review_config) == 0:
-            raise LaunchReviewErrors.review_config_required()  # 提供了空列表,提示必须提供有效值
-        validate_review_config(review_config)
-    elif review_item_config is not None:
-        # 使用 review_item_config 时的验证
-        if len(review_item_config) == 0:
-            raise LaunchReviewErrors.review_config_required()  # 提供了空列表,提示必须提供有效值
-        validate_review_item_config(review_item_config)
+    # 验证审查项配置
+    if review_item_config is None or len(review_item_config) == 0:
+        raise LaunchReviewErrors.review_config_required()
+    validate_review_item_config(review_item_config)
 
     # 验证工程方案类型
     validate_project_plan_type(project_plan_type)
@@ -367,7 +295,6 @@ async def launch_review_sse(request_data: LaunchReviewRequest):
                 # 添加审查配置到文件信息,并确保使用当前正确的callback_task_id
                 file_info.update({
                     'user_id': user_id,
-                    'review_config': review_config,
                     'review_item_config': review_item_config,
                     'project_plan_type': project_plan_type,
                     'tendency_review_role': tendency_review_role,

+ 8 - 8
views/construction_review/schemas/error_schemas.py

@@ -203,14 +203,14 @@ class ErrorCodes:
     QDSC018 = {
         "code": "QDSC018",
         "error_type": "MUTUALLY_EXCLUSIVE_CONFIG",
-        "message": "review_config与review_item_config互斥,只能提供其中一个",
+        "message": "[已废弃] review_config已移除,仅支持review_item_config",
         "status_code": 400
     }
 
     QDSC019 = {
         "code": "QDSC019",
         "error_type": "REVIEW_CONFIG_REQUIRED",
-        "message": "必须提供review_config或review_item_config中的其中一个",
+        "message": "必须提供review_item_config(章节_审查维度列表)",
         "status_code": 400
     }
 
@@ -492,16 +492,16 @@ class LaunchReviewErrors:
 
     @staticmethod
     def mutually_exclusive_config():
-        """review_config与review_item_config互斥"""
-        logger.error("review_config与review_item_config同时提供,但两者互斥")
-        message = "参数错误:review_config(审查维度列表)与review_item_config(章节_审查维度列表)互斥,只能提供其中一个,不能同时提供。"
+        """[已废弃] review_config已移除,仅支持review_item_config"""
+        logger.error("review_config已废弃,请使用review_item_config")
+        message = "参数错误:review_config 已废弃,请使用 review_item_config(章节_审查维度列表)。"
         return create_http_exception(ErrorCodes.QDSC018, message)
 
     @staticmethod
     def review_config_required():
-        """必须提供review_config或review_item_config"""
-        logger.error("未提供review_config或review_item_config")
-        message = "参数错误:必须提供review_config(审查维度列表)或review_item_config(章节_审查维度列表)中的其中一个。"
+        """必须提供review_item_config"""
+        logger.error("未提供review_item_config")
+        message = "参数错误:必须提供 review_item_config(章节_审查维度列表)。"
         return create_http_exception(ErrorCodes.QDSC019, message)
 
     @staticmethod