|
|
@@ -1,20 +1,63 @@
|
|
|
-"""
|
|
|
-基于LangGraph的AI审查工作流
|
|
|
-负责AI审查的流程控制和业务编排,使用LangGraph进行状态管理
|
|
|
-"""
|
|
|
+#!/usr/bin/env python
|
|
|
+# -*- coding: utf-8 -*-
|
|
|
+
|
|
|
+'''
|
|
|
+@Project : lq-agent-api
|
|
|
+@File : ai_review_workflow.py
|
|
|
+@IDE : VsCode
|
|
|
+@Author :
|
|
|
+@Date : 2025-12-01 11:53:02
|
|
|
+
|
|
|
+=================================
|
|
|
+
|
|
|
+📋 方法总览 (Method Overview)
|
|
|
+
|
|
|
+🏗️ 工作流核心方法:
|
|
|
+├── _build_workflow() # 构建LangGraph工作流图
|
|
|
+├── execute() # 执行AI审查工作流
|
|
|
+├── _start_node() # 开始节点
|
|
|
+├── _initialize_progress_node() # 初始化进度节点
|
|
|
+├── _ai_review_node() # AI审查核心节点
|
|
|
+├── _complete_node() # 完成节点
|
|
|
+└── _error_handler_node() # 错误处理节点
|
|
|
+
|
|
|
+🔍 审查处理方法:
|
|
|
+├── _filter_review_units() # 筛选审查单元
|
|
|
+├── _calculate_overall_risk() # 计算总体风险等级
|
|
|
+├── _aggregate_results() # 汇总审查结果
|
|
|
+├── _format_review_results_to_issues() # 格式化审查结果为问题列表
|
|
|
+└── _parse_ai_review_response() # 解析AI审查响应
|
|
|
+
|
|
|
+🛠️ 工具辅助方法:
|
|
|
+├── _check_ai_review_result() # 检查AI审查结果
|
|
|
+├── _get_workflow_graph() # 获取工作流图(可视化)
|
|
|
+└── _get_status() # 获取工作流状态
|
|
|
+
|
|
|
+⚙️ 配置管理:
|
|
|
+├── __init__() # 初始化工作流(支持审查模式配置)
|
|
|
+└── set_review_location_label() # 设置审查位置标签
|
|
|
+'''
|
|
|
|
|
|
import asyncio
|
|
|
import json
|
|
|
-from dataclasses import asdict
|
|
|
+import random
|
|
|
import time
|
|
|
+from dataclasses import dataclass, asdict
|
|
|
from typing import Optional, Callable, Dict, Any, TypedDict, Annotated, List
|
|
|
-from dataclasses import dataclass
|
|
|
from langgraph.graph import StateGraph, END
|
|
|
from langgraph.graph.message import add_messages
|
|
|
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage
|
|
|
from foundation.logger.loggering import server_logger as logger
|
|
|
from ..component import AIReviewEngine
|
|
|
|
|
|
+# 常量定义
|
|
|
+DEFAULT_SLICE_START_INDEX = 30
|
|
|
+MAX_PROGRESS_PERCENTAGE = 100
|
|
|
+RISK_LEVELS = {"high": "高风险", "medium": "中风险", "low": "低风险"}
|
|
|
+DEFAULT_RISK_LEVEL = "medium"
|
|
|
+REVIEW_TIMEOUT = 300 # 单个审查单元超时时间(秒)
|
|
|
+WORKFLOW_TIMEOUT = 1800 # 整个工作流超时时间(秒,30分钟)
|
|
|
+
|
|
|
|
|
|
@dataclass
|
|
|
class ReviewResult:
|
|
|
@@ -79,6 +122,9 @@ class AIReviewWorkflow:
|
|
|
|
|
|
Returns:
|
|
|
List[Dict[str, Any]]: 筛选后的审查单元
|
|
|
+
|
|
|
+ Note:
|
|
|
+ 使用预设的起始索引进行切片,确保不会跳过前面的重要内容
|
|
|
"""
|
|
|
if self.max_review_units is None or self.review_mode == "all":
|
|
|
return chunks
|
|
|
@@ -89,7 +135,8 @@ class AIReviewWorkflow:
|
|
|
return []
|
|
|
|
|
|
# 安全的切片操作,考虑边界情况
|
|
|
- start_index = min(30, len(chunks) - 1) # 确保start_index不超过数组边界
|
|
|
+ # 使用预设的起始索引进行切片,避免跳过前面的内容
|
|
|
+ start_index = min(DEFAULT_SLICE_START_INDEX, len(chunks) - 1) # 确保start_index不超过数组边界
|
|
|
chunks = chunks[start_index:]
|
|
|
|
|
|
# 再次验证切片后的结果
|
|
|
@@ -107,7 +154,6 @@ class AIReviewWorkflow:
|
|
|
return chunks[:actual_review_count]
|
|
|
elif self.review_mode == "random":
|
|
|
# 随机取N个
|
|
|
- import random
|
|
|
return random.sample(chunks, actual_review_count)
|
|
|
else:
|
|
|
# 默认取前N个
|
|
|
@@ -121,7 +167,7 @@ class AIReviewWorkflow:
|
|
|
workflow.add_node("ai_review", self._ai_review_node)
|
|
|
workflow.add_node("complete", self._complete_node)
|
|
|
workflow.add_node("error_handler", self._error_handler_node)
|
|
|
- workflow.set_entry_point("start")# 设置入口节点
|
|
|
+ workflow.set_entry_point("start")
|
|
|
workflow.add_edge("start", "initialize_progress")
|
|
|
workflow.add_edge("initialize_progress", "ai_review")
|
|
|
workflow.add_edge("ai_review", "complete")
|
|
|
@@ -160,8 +206,11 @@ class AIReviewWorkflow:
|
|
|
messages=[HumanMessage(content=f"开始AI审查: {self.file_id}")]
|
|
|
)
|
|
|
|
|
|
- # 执行LangGraph工作流
|
|
|
- result = await self.graph.ainvoke(initial_state)
|
|
|
+ # 执行LangGraph工作流,添加超时控制
|
|
|
+ result = await asyncio.wait_for(
|
|
|
+ self.graph.ainvoke(initial_state),
|
|
|
+ timeout=WORKFLOW_TIMEOUT
|
|
|
+ )
|
|
|
|
|
|
logger.info(f"LangGraph AI审查工作流完成,文件ID: {self.file_id}")
|
|
|
review_results = {
|
|
|
@@ -180,6 +229,9 @@ class AIReviewWorkflow:
|
|
|
|
|
|
return review_results
|
|
|
|
|
|
+ except asyncio.TimeoutError:
|
|
|
+ logger.error(f"AI审查工作流超时({WORKFLOW_TIMEOUT}秒),文件ID: {self.file_id}")
|
|
|
+ raise TimeoutError(f"AI审查工作流执行超时,请检查文件大小或网络连接")
|
|
|
except Exception as e:
|
|
|
logger.error(f"LangGraph AI审查工作流执行失败: {str(e)}")
|
|
|
raise
|
|
|
@@ -219,31 +271,39 @@ class AIReviewWorkflow:
|
|
|
|
|
|
return state
|
|
|
|
|
|
- async def _ai_review_node(self, state: AIReviewState) -> AIReviewState:
|
|
|
- """AI审查节点"""
|
|
|
- try:
|
|
|
- # logger.info(f"正在执行: {state['file_id']}")
|
|
|
- # if state["progress_manager"]:
|
|
|
- # await state["progress_manager"].update_stage_progress(
|
|
|
- # callback_task_id=state["callback_task_id"],
|
|
|
- # stage_name="AI审查",
|
|
|
- # current=0,
|
|
|
- # status="processing",
|
|
|
- # message=f"正在执行: {state['file_id']}"
|
|
|
- # )
|
|
|
- state["current_stage"] = "ai_review"
|
|
|
+ async def _prepare_review_units(self, state: AIReviewState) -> tuple:
|
|
|
+ """
|
|
|
+ 准备审查单元数据
|
|
|
+
|
|
|
+ Args:
|
|
|
+ state: AI审查状态
|
|
|
|
|
|
+ Returns:
|
|
|
+ tuple: (review_chunks, total_units, total_all_units)
|
|
|
+ """
|
|
|
+ try:
|
|
|
# 筛选要审查的单元
|
|
|
all_chunks = state['structured_content']['chunks']
|
|
|
review_chunks = self._filter_review_units(all_chunks)
|
|
|
|
|
|
total_units = len(review_chunks)
|
|
|
total_all_units = len(all_chunks)
|
|
|
- completed_units = 0
|
|
|
|
|
|
logger.info(f"AI审查开始: 总单元数 {total_all_units}, 实际审查 {total_units} 个单元")
|
|
|
+ return review_chunks, total_units, total_all_units
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"准备审查单元失败: {str(e)}")
|
|
|
+ raise
|
|
|
+
|
|
|
+ async def _send_start_review_progress(self, state: AIReviewState, total_units: int) -> None:
|
|
|
+ """
|
|
|
+ 发送开始审查的进度更新
|
|
|
|
|
|
- # 开始AI审查进度
|
|
|
+ Args:
|
|
|
+ state: AI审查状态
|
|
|
+ total_units: 总审查单元数
|
|
|
+ """
|
|
|
+ try:
|
|
|
if state["progress_manager"]:
|
|
|
await state["progress_manager"].update_stage_progress(
|
|
|
callback_task_id=state["callback_task_id"],
|
|
|
@@ -253,148 +313,269 @@ class AIReviewWorkflow:
|
|
|
message=f"开始AI审查,共 {total_units} 个审查单元",
|
|
|
event_type="processing"
|
|
|
)
|
|
|
+ except Exception as e:
|
|
|
+ logger.warning(f"发送开始进度更新失败: {str(e)}")
|
|
|
|
|
|
-
|
|
|
- # 基本审查单元
|
|
|
- async def review_single_unit(unit_content: Dict[str, Any], unit_index: int,callback_task_id) -> ReviewResult:
|
|
|
- """使用LangGraph编排的原子化组件方法审查单个单元"""
|
|
|
- try:
|
|
|
- # 构建Trace ID
|
|
|
- trace_id_idx = "("+str(callback_task_id)+'-'+str(unit_index)+")"
|
|
|
-
|
|
|
- # 获取section_label用于stage_name
|
|
|
- section_label = unit_content.get('section_label', f'第{unit_index + 1}部分')
|
|
|
- page = unit_content.get('page', '')
|
|
|
- review_location_label = f"第{page}页:{section_label}"
|
|
|
-
|
|
|
- # 设置review_location_label到AIReviewEngine
|
|
|
- self.ai_review_engine.set_review_location_label(review_location_label)
|
|
|
-
|
|
|
- stage_name = f"AI审查:{section_label}"
|
|
|
-
|
|
|
- # 方法内部进度计算(基于当前处理的单元)
|
|
|
- current_progress = int((unit_index / total_units) * 100)
|
|
|
-
|
|
|
- # 构建进度消息
|
|
|
- message = f"正在处理第 {unit_index + 1}/{total_units} 个单元: {section_label}"
|
|
|
- logger.info(f"开始处理单元: {unit_index + 1}/{total_units} - {section_label}")
|
|
|
-
|
|
|
- # 并发执行各种原子化审查方法
|
|
|
- review_tasks = [
|
|
|
- self.ai_review_engine.basic_compliance_check(trace_id_idx, unit_content, stage_name, state, current_progress),
|
|
|
- self.ai_review_engine.technical_compliance_check(trace_id_idx, unit_content, stage_name, state, current_progress),
|
|
|
- # self.ai_review_engine.rag_enhanced_check(unit_content, trace_id_idx)
|
|
|
- ]
|
|
|
-
|
|
|
- # 等待所有审查完成
|
|
|
- review_results = await asyncio.gather(*review_tasks, return_exceptions=True)
|
|
|
-
|
|
|
- # 处理异常结果
|
|
|
- basic_result = review_results[0] if not isinstance(review_results[0], Exception) else {"error": str(review_results[0])}
|
|
|
- technical_result = review_results[1] if len(review_results) > 1 and not isinstance(review_results[1], Exception) else {"error": str(review_results[1]) if len(review_results) > 1 else "No result"}
|
|
|
-
|
|
|
- # RAG检查已注释,提供空结果
|
|
|
- rag_result = {"error": "RAG check disabled"}
|
|
|
-
|
|
|
- # 计算总体风险等级
|
|
|
- overall_risk = self._calculate_overall_risk(basic_result, technical_result, rag_result)
|
|
|
-
|
|
|
- issues = self._format_review_results_to_issues(
|
|
|
- state["callback_task_id"],
|
|
|
- unit_index,
|
|
|
- review_location_label,
|
|
|
- unit_content,
|
|
|
- basic_result,
|
|
|
- technical_result
|
|
|
- )
|
|
|
- #logger.info(f"issues: {issues}")
|
|
|
-
|
|
|
- # 单元审查完成,更新进度
|
|
|
- nonlocal completed_units
|
|
|
- completed_units += 1
|
|
|
- current = int((completed_units / total_units) * 100) # 修正进度计算
|
|
|
-
|
|
|
- # 统计发现问题数量
|
|
|
- issues_count = sum(len(issue.get("review_lists", [])) for issue in issues)
|
|
|
-
|
|
|
- # 构建完成消息
|
|
|
- if issues_count > 0:
|
|
|
- message = f"已完成第 {unit_index + 1}/{total_units} 个单元: {section_label}(已发现{issues_count}个问题)"
|
|
|
- else:
|
|
|
- message = f"已完成第 {unit_index + 1}/{total_units} 个单元: {section_label}"
|
|
|
-
|
|
|
- logger.info(f"单元审查完成,更新进度: {current}% {message}")
|
|
|
-
|
|
|
- # 发送processing_flag事件(在单元完成时)
|
|
|
- if state["progress_manager"]:
|
|
|
- logger.info(f"发送processing_flag事件: current={current}, message={message}")
|
|
|
- await state["progress_manager"].update_stage_progress(
|
|
|
- callback_task_id=state["callback_task_id"],
|
|
|
- stage_name="AI审查",
|
|
|
- current=current,
|
|
|
- status="processing",
|
|
|
- message=message,
|
|
|
- user_id=state.get("user_id", ""),
|
|
|
- overall_task_status="processing",
|
|
|
- event_type="processing_flag"
|
|
|
- )
|
|
|
-
|
|
|
- # 如果有issues,额外推送详细信息
|
|
|
- if issues:
|
|
|
- asyncio.create_task(
|
|
|
- state["progress_manager"].update_stage_progress(
|
|
|
- callback_task_id=state["callback_task_id"],
|
|
|
- stage_name=stage_name, # 保留详细的stage_name
|
|
|
- current=current,
|
|
|
- status="unit_review_update",
|
|
|
- message=f"发现{issues_count}个问题: {section_label}",
|
|
|
- issues=issues,
|
|
|
- user_id=state.get("user_id", ""),
|
|
|
- overall_task_status="processing",
|
|
|
- event_type="unit_review" # 明确设置事件类型
|
|
|
- )
|
|
|
- )
|
|
|
-
|
|
|
-
|
|
|
- # 清空当前issues
|
|
|
- await state["progress_manager"].update_stage_progress(
|
|
|
- callback_task_id=state["callback_task_id"],
|
|
|
- issues=['clear']
|
|
|
- )
|
|
|
-
|
|
|
- return ReviewResult(
|
|
|
- unit_index=unit_index,
|
|
|
- unit_content=unit_content,
|
|
|
- basic_compliance=basic_result,
|
|
|
- technical_compliance=technical_result,
|
|
|
- rag_enhanced=rag_result,
|
|
|
- overall_risk=overall_risk
|
|
|
- )
|
|
|
+ async def _review_single_unit(self, unit_content: Dict[str, Any], unit_index: int,
|
|
|
+ total_units: int, state: AIReviewState) -> ReviewResult:
|
|
|
+ """
|
|
|
+ 审查单个单元的核心业务逻辑
|
|
|
+
|
|
|
+ Args:
|
|
|
+ unit_content: 单元内容
|
|
|
+ unit_index: 单元索引
|
|
|
+ total_units: 总单元数
|
|
|
+ state: AI审查状态
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ ReviewResult: 单元审查结果
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ # 构建Trace ID
|
|
|
+ trace_id_idx = f"({state['callback_task_id']}-{unit_index})"
|
|
|
+
|
|
|
+ # 获取section_label用于stage_name
|
|
|
+ section_label = unit_content.get('section_label', f'第{unit_index + 1}部分')
|
|
|
+ page = unit_content.get('page', '')
|
|
|
+ review_location_label = f"第{page}页:{section_label}"
|
|
|
|
|
|
- except Exception as e:
|
|
|
- logger.error(f"审查单元 {unit_index} 失败: {str(e)}")
|
|
|
- return ReviewResult(
|
|
|
- unit_index=unit_index,
|
|
|
- unit_content=unit_content,
|
|
|
- basic_compliance={"error": str(e)},
|
|
|
- technical_compliance={"error": str(e)},
|
|
|
- rag_enhanced={"error": str(e)},
|
|
|
- overall_risk="error"
|
|
|
+ # 设置review_location_label到AIReviewEngine
|
|
|
+ self.ai_review_engine.set_review_location_label(review_location_label)
|
|
|
+
|
|
|
+ stage_name = f"AI审查:{section_label}"
|
|
|
+
|
|
|
+ # 方法内部进度计算(基于当前处理的单元)
|
|
|
+ current_progress = int((unit_index / total_units) * 100)
|
|
|
+
|
|
|
+ # 构建进度消息
|
|
|
+ message = f"正在处理第 {unit_index + 1}/{total_units} 个单元: {section_label}"
|
|
|
+ logger.info(f"开始处理单元: {unit_index + 1}/{total_units} - {section_label}")
|
|
|
+
|
|
|
+ # 并发执行各种原子化审查方法,添加超时控制
|
|
|
+ review_tasks = [
|
|
|
+ asyncio.wait_for(
|
|
|
+ self.ai_review_engine.basic_compliance_check(trace_id_idx, unit_content, stage_name, state, current_progress),
|
|
|
+ timeout=REVIEW_TIMEOUT
|
|
|
+ ),
|
|
|
+ asyncio.wait_for(
|
|
|
+ self.ai_review_engine.technical_compliance_check(trace_id_idx, unit_content, stage_name, state, current_progress),
|
|
|
+ timeout=REVIEW_TIMEOUT
|
|
|
+ ),
|
|
|
+ ]
|
|
|
+
|
|
|
+ # 等待所有审查完成
|
|
|
+ review_results = await asyncio.gather(*review_tasks, return_exceptions=True)
|
|
|
+
|
|
|
+ # 处理异常结果
|
|
|
+ basic_result = review_results[0] if not isinstance(review_results[0], Exception) else {"error": str(review_results[0])}
|
|
|
+ technical_result = review_results[1] if len(review_results) > 1 and not isinstance(review_results[1], Exception) else {"error": str(review_results[1]) if len(review_results) > 1 else "No result"}
|
|
|
+
|
|
|
+ # RAG检查已注释,提供空结果
|
|
|
+ rag_result = {"error": "RAG check disabled"}
|
|
|
+
|
|
|
+ # 计算总体风险等级
|
|
|
+ overall_risk = self._calculate_overall_risk(basic_result, technical_result, rag_result)
|
|
|
+
|
|
|
+ # 格式化审查结果
|
|
|
+ issues = self._format_review_results_to_issues(
|
|
|
+ state["callback_task_id"],
|
|
|
+ unit_index,
|
|
|
+ review_location_label,
|
|
|
+ unit_content,
|
|
|
+ basic_result,
|
|
|
+ technical_result
|
|
|
+ )
|
|
|
+
|
|
|
+ return ReviewResult(
|
|
|
+ unit_index=unit_index,
|
|
|
+ unit_content=unit_content,
|
|
|
+ basic_compliance=basic_result,
|
|
|
+ technical_compliance=technical_result,
|
|
|
+ rag_enhanced=rag_result,
|
|
|
+ overall_risk=overall_risk
|
|
|
+ )
|
|
|
+
|
|
|
+ except asyncio.TimeoutError:
|
|
|
+ logger.error(f"审查单元 {unit_index} 超时")
|
|
|
+ return ReviewResult(
|
|
|
+ unit_index=unit_index,
|
|
|
+ unit_content=unit_content,
|
|
|
+ basic_compliance={"error": f"审查超时({REVIEW_TIMEOUT}秒)"},
|
|
|
+ technical_compliance={"error": f"审查超时({REVIEW_TIMEOUT}秒)"},
|
|
|
+ rag_enhanced={"error": f"审查超时({REVIEW_TIMEOUT}秒)"},
|
|
|
+ overall_risk="error"
|
|
|
+ )
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"审查单元 {unit_index} 失败: {str(e)}")
|
|
|
+ return ReviewResult(
|
|
|
+ unit_index=unit_index,
|
|
|
+ unit_content=unit_content,
|
|
|
+ basic_compliance={"error": str(e)},
|
|
|
+ technical_compliance={"error": str(e)},
|
|
|
+ rag_enhanced={"error": str(e)},
|
|
|
+ overall_risk="error"
|
|
|
+ )
|
|
|
+
|
|
|
+ async def _send_unit_complete_progress(self, state: AIReviewState, unit_index: int,
|
|
|
+ total_units: int, section_label: str,
|
|
|
+ issues_count: int) -> None:
|
|
|
+ """
|
|
|
+ 发送单元完成进度更新
|
|
|
+
|
|
|
+ Args:
|
|
|
+ state: AI审查状态
|
|
|
+ unit_index: 单元索引
|
|
|
+ total_units: 总单元数
|
|
|
+ section_label: 章节标签
|
|
|
+ issues_count: 问题数量
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ current = int(((unit_index + 1) / total_units) * 100)
|
|
|
+
|
|
|
+ # 构建完成消息
|
|
|
+ if issues_count > 0:
|
|
|
+ message = f"已完成第 {unit_index + 1}/{total_units} 个单元: {section_label}(已发现{issues_count}个问题)"
|
|
|
+ else:
|
|
|
+ message = f"已完成第 {unit_index + 1}/{total_units} 个单元: {section_label}"
|
|
|
+
|
|
|
+ logger.info(f"单元审查完成,更新进度: {current}% {message}")
|
|
|
+
|
|
|
+ # 发送processing_flag事件
|
|
|
+ if state["progress_manager"]:
|
|
|
+ logger.info(f"发送processing_flag事件: current={current}, message={message}")
|
|
|
+ await state["progress_manager"].update_stage_progress(
|
|
|
+ callback_task_id=state["callback_task_id"],
|
|
|
+ stage_name="AI审查",
|
|
|
+ current=current,
|
|
|
+ status="processing",
|
|
|
+ message=message,
|
|
|
+ user_id=state.get("user_id", ""),
|
|
|
+ overall_task_status="processing",
|
|
|
+ event_type="processing_flag"
|
|
|
+ )
|
|
|
+ except Exception as e:
|
|
|
+ logger.warning(f"发送单元完成进度更新失败: {str(e)}")
|
|
|
+
|
|
|
+ async def _send_unit_review_details(self, state: AIReviewState, unit_index: int,
|
|
|
+ total_units: int, section_label: str,
|
|
|
+ issues: List[Dict], current: int) -> None:
|
|
|
+ """
|
|
|
+ 发送单元审查详细信息
|
|
|
+
|
|
|
+ Args:
|
|
|
+ state: AI审查状态
|
|
|
+ unit_index: 单元索引
|
|
|
+ total_units: 总单元数
|
|
|
+ section_label: 章节标签
|
|
|
+ issues: 问题列表
|
|
|
+ current: 当前进度
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ if issues and state["progress_manager"]:
|
|
|
+ stage_name = f"AI审查:{section_label}"
|
|
|
+ issues_count = sum(len(issue.get("review_lists", [])) for issue in issues)
|
|
|
+
|
|
|
+ asyncio.create_task(
|
|
|
+ state["progress_manager"].update_stage_progress(
|
|
|
+ callback_task_id=state["callback_task_id"],
|
|
|
+ stage_name=stage_name,
|
|
|
+ current=current,
|
|
|
+ status="unit_review_update",
|
|
|
+ message=f"发现{issues_count}个问题: {section_label}",
|
|
|
+ issues=issues,
|
|
|
+ user_id=state.get("user_id", ""),
|
|
|
+ overall_task_status="processing",
|
|
|
+ event_type="unit_review"
|
|
|
)
|
|
|
+ )
|
|
|
+
|
|
|
+ # 清空当前issues
|
|
|
+ await state["progress_manager"].update_stage_progress(
|
|
|
+ callback_task_id=state["callback_task_id"],
|
|
|
+ issues=['clear']
|
|
|
+ )
|
|
|
+ except Exception as e:
|
|
|
+ logger.warning(f"发送单元审查详情失败: {str(e)}")
|
|
|
+
|
|
|
+ async def _execute_concurrent_reviews(self, review_chunks: List[Dict[str, Any]],
|
|
|
+ total_units: int, state: AIReviewState) -> List[ReviewResult]:
|
|
|
+ """
|
|
|
+ 执行并发审查
|
|
|
+
|
|
|
+ Args:
|
|
|
+ review_chunks: 审查单元列表
|
|
|
+ total_units: 总单元数
|
|
|
+ state: AI审查状态
|
|
|
|
|
|
+ Returns:
|
|
|
+ List[ReviewResult]: 审查结果列表
|
|
|
+ """
|
|
|
+ try:
|
|
|
# 实现异步并发
|
|
|
review_tasks = [
|
|
|
- asyncio.create_task(review_single_unit(content, i,state["callback_task_id"]))
|
|
|
+ asyncio.create_task(self._review_single_unit(content, i, total_units, state))
|
|
|
for i, content in enumerate(review_chunks)
|
|
|
]
|
|
|
|
|
|
# 等待所有审查完成
|
|
|
all_results = await asyncio.gather(*review_tasks)
|
|
|
|
|
|
+ # 处理进度更新和问题详情推送
|
|
|
+ for i, result in enumerate(all_results):
|
|
|
+ if result.overall_risk != "error":
|
|
|
+ unit_content = review_chunks[i]
|
|
|
+ section_label = unit_content.get('section_label', f'第{i + 1}部分')
|
|
|
+
|
|
|
+ # 格式化issues以获取问题数量
|
|
|
+ issues = self._format_review_results_to_issues(
|
|
|
+ state["callback_task_id"],
|
|
|
+ i,
|
|
|
+ f"第{unit_content.get('page', '')}页:{section_label}",
|
|
|
+ unit_content,
|
|
|
+ result.basic_compliance,
|
|
|
+ result.technical_compliance
|
|
|
+ )
|
|
|
+
|
|
|
+ issues_count = sum(len(issue.get("review_lists", [])) for issue in issues)
|
|
|
+ current = int(((i + 1) / total_units) * 100)
|
|
|
+
|
|
|
+ # 发送进度更新
|
|
|
+ await self._send_unit_complete_progress(state, i, total_units, section_label, issues_count)
|
|
|
+
|
|
|
+ # 发送问题详情
|
|
|
+ await self._send_unit_review_details(state, i, total_units, section_label, issues, current)
|
|
|
+
|
|
|
# 过滤成功结果
|
|
|
successful_results = [result for result in all_results if result.overall_risk != "error"]
|
|
|
+ return successful_results
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"执行并发审查失败: {str(e)}")
|
|
|
+ return []
|
|
|
+
|
|
|
+ async def _ai_review_node(self, state: AIReviewState) -> AIReviewState:
|
|
|
+ """
|
|
|
+ AI审查节点 - 重构后的简化版本
|
|
|
|
|
|
- # 汇总结果
|
|
|
+ Args:
|
|
|
+ state: AI审查状态
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ AIReviewState: 更新后的审查状态
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ state["current_stage"] = "ai_review"
|
|
|
+
|
|
|
+ # 1. 准备审查单元数据
|
|
|
+ review_chunks, total_units, total_all_units = await self._prepare_review_units(state)
|
|
|
+
|
|
|
+ # 2. 发送开始审查进度
|
|
|
+ await self._send_start_review_progress(state, total_units)
|
|
|
+
|
|
|
+ # 3. 执行并发审查
|
|
|
+ successful_results = await self._execute_concurrent_reviews(review_chunks, total_units, state)
|
|
|
+
|
|
|
+ # 4. 汇总结果
|
|
|
summary = self._aggregate_results(successful_results)
|
|
|
|
|
|
review_results = {
|
|
|
@@ -471,9 +652,20 @@ class AIReviewWorkflow:
|
|
|
|
|
|
|
|
|
def _calculate_overall_risk(self, basic_result: Dict, technical_result: Dict, rag_result: Dict) -> str:
|
|
|
- """计算总体风险等级"""
|
|
|
+ """
|
|
|
+ 计算总体风险等级
|
|
|
+
|
|
|
+ Args:
|
|
|
+ basic_result: 基础合规性审查结果
|
|
|
+ technical_result: 技术性审查结果
|
|
|
+ rag_result: RAG增强审查结果
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ str: 风险等级 ("low", "medium", "high")
|
|
|
+ """
|
|
|
try:
|
|
|
# 基于各种审查结果计算风险等级
|
|
|
+ # 风险等级计算逻辑:基础和技术审查都达到90分以上为低风险,70分以上为中风险,否则为高风险
|
|
|
basic_score = basic_result.get('overall_score', 0)
|
|
|
technical_score = technical_result.get('overall_score', 0)
|
|
|
|
|
|
@@ -483,16 +675,25 @@ class AIReviewWorkflow:
|
|
|
return "medium"
|
|
|
else:
|
|
|
return "high"
|
|
|
- except:
|
|
|
- return "medium"
|
|
|
+ except (KeyError, TypeError, ValueError) as e:
|
|
|
+ logger.warning(f"风险等级计算异常: {str(e)},使用默认风险等级")
|
|
|
+ return DEFAULT_RISK_LEVEL
|
|
|
|
|
|
def _aggregate_results(self, successful_results: List[ReviewResult]) -> Dict[str, Any]:
|
|
|
- """汇总审查结果"""
|
|
|
+ """
|
|
|
+ 汇总审查结果
|
|
|
+
|
|
|
+ Args:
|
|
|
+ successful_results: 成功的审查结果列表
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ Dict[str, Any]: 汇总后的统计信息,包括风险统计、平均得分等
|
|
|
+ """
|
|
|
try:
|
|
|
if not successful_results:
|
|
|
return {}
|
|
|
|
|
|
- # 计算统计数据
|
|
|
+ # 计算风险等级统计
|
|
|
risk_stats = {"low": 0, "medium": 0, "high": 0, "error": 0}
|
|
|
for result in successful_results:
|
|
|
risk_stats[result.overall_risk] += 1
|
|
|
@@ -510,7 +711,7 @@ class AIReviewWorkflow:
|
|
|
'avg_technical_score': avg_technical_score,
|
|
|
'total_reviewed': len(successful_results)
|
|
|
}
|
|
|
- except Exception as e:
|
|
|
+ except (ZeroDivisionError, KeyError, TypeError) as e:
|
|
|
logger.error(f"结果汇总失败: {str(e)}")
|
|
|
return {}
|
|
|
|
|
|
@@ -549,7 +750,7 @@ class AIReviewWorkflow:
|
|
|
technical_result: 技术性审查结果
|
|
|
|
|
|
Returns:
|
|
|
- List[Dict]: 格式化后的issues列表
|
|
|
+ List[Dict]: 格式化后的issues列表,包含issue_id、metadata、risk_summary、review_lists等字段
|
|
|
"""
|
|
|
issues = []
|
|
|
review_lists = []
|
|
|
@@ -620,11 +821,14 @@ class AIReviewWorkflow:
|
|
|
解析AI审查的Markdown格式响应
|
|
|
|
|
|
Args:
|
|
|
- response: AI审查响应
|
|
|
+ response: AI审查响应内容
|
|
|
check_name: 检查项名称(如"词句语法检查")
|
|
|
|
|
|
Returns:
|
|
|
- List[Dict]: 解析后的审查结果列表
|
|
|
+ List[Dict]: 解析后的审查结果列表,包含check_item、check_result、exist_issue、risk_info等字段
|
|
|
+
|
|
|
+ Note:
|
|
|
+ 支持识别"无明显问题"等关键词,自动设置风险等级
|
|
|
"""
|
|
|
review_lists = []
|
|
|
|