from __future__ import annotations from .shared import * class ModelCapabilityValidationMixin: def __init__(self, db: AsyncSession, ai_service: AIService): self.db = db self.ai_service = ai_service async def validate_model_requirement_match( self, truth_node_id: int, model_version_id: int, tenant_id: int, project_id: Optional[int] = None, ) -> Dict[str, Any]: """智能匹配校验:评估需求与模型的匹配度 Args: truth_node_id: 宪章(需求)ID model_version_id: 模型版本 ID tenant_id: 租户 ID Returns: 包含匹配度、成本预估、风险提示、能力差距等信息的字典 """ # 获取宪章和模型信息 truth_node = await self._get_truth_node(truth_node_id, tenant_id) model_version = await self._get_model_version(model_version_id) if not truth_node: raise ValueError(f"宪章 {truth_node_id} 不存在") if not model_version: raise ValueError(f"模型版本 {model_version_id} 不存在") if project_id is not None: await self.validate_truth_node_in_project( truth_node_id=truth_node_id, tenant_id=tenant_id, project_id=project_id, ) # 格式化模型信息用于 AI 分析 model_info = self._format_model_info(model_version) charter_content = str(truth_node.content or truth_node.title or "") # AI 评估匹配度 evaluation = await self.ai_service.evaluate_model_requirement( charter_content=charter_content, model_version_id=model_version_id, model_info=json.dumps(model_info, ensure_ascii=False), tenant_id=tenant_id, db=self.db, ) # 创建或更新绑定记录 binding = await self._upsert_binding( truth_node_id, model_version_id, evaluation, tenant_id ) # 检测能力差距 gaps = await self._detect_capability_gaps( truth_node_id, model_version_id, evaluation, tenant_id ) return { "binding_id": binding.id, "compatibility_score": evaluation.get("compatibility_score", 0), "cost_estimate_usd": evaluation.get("cost_estimate_usd", 0.0), "cost_estimate_tokens_per_day": evaluation.get("cost_estimate_tokens_per_day", 0), "risk_hints": evaluation.get("risk_hints", []), "capability_gaps": gaps, "recommendations": evaluation.get("recommendations", []), } async def validate_truth_node_in_project( self, truth_node_id: int, tenant_id: int, project_id: int ) -> TruthNode: """校验宪章是否属于指定项目。""" truth_node = await self._get_truth_node(truth_node_id, tenant_id) if not truth_node: raise ValueError(f"宪章 {truth_node_id} 不存在") if truth_node.project_id != project_id: raise ValueError( f"宪章 {truth_node_id} 不属于项目 {project_id},拒绝跨项目操作" ) return truth_node async def _get_truth_node( self, truth_node_id: int, tenant_id: int ) -> Optional[TruthNode]: """获取宪章""" result = await self.db.execute( select(TruthNode).where( and_( TruthNode.id == truth_node_id, TruthNode.tenant_id == tenant_id, ) ) ) return result.scalar_one_or_none() async def _get_model_version( self, model_version_id: int ) -> Optional[ModelVersion]: """获取模型版本""" result = await self.db.execute( select(ModelVersion).where(ModelVersion.id == model_version_id) ) return result.scalar_one_or_none() def _format_model_info(self, model: ModelVersion) -> Dict[str, Any]: """格式化模型信息用于 AI 分析""" return { "name": model.name, "provider": model.provider, "version": model.version, "capability_boundaries": model.capability_boundaries or {}, "cost_per_1k_tokens": float(model.cost_per_1k_tokens) if model.cost_per_1k_tokens else 0.0, } async def _upsert_binding( self, truth_node_id: int, model_version_id: int, evaluation: Dict[str, Any], tenant_id: int, ) -> ModelRequirementBinding: """创建或更新模型-需求绑定""" # 检查是否已存在绑定 result = await self.db.execute( select(ModelRequirementBinding).where( and_( ModelRequirementBinding.truth_node_id == truth_node_id, ModelRequirementBinding.model_version_id == model_version_id, ) ) ) binding = result.scalar_one_or_none() compatibility_score = evaluation.get("compatibility_score", 0) cost_estimate = evaluation.get("cost_estimate_usd", 0.0) risk_notes = ", ".join(evaluation.get("risk_hints", [])) if binding: # 更新现有绑定 binding.compatibility_score = int(compatibility_score) binding.estimated_cost = float(cost_estimate) binding.risk_notes = risk_notes[:1000] if risk_notes else None # 限制长度 else: # 创建新绑定 binding = ModelRequirementBinding( tenant_id=tenant_id, truth_node_id=truth_node_id, model_version_id=model_version_id, compatibility_score=int(compatibility_score), estimated_cost=float(cost_estimate), risk_notes=risk_notes[:1000] if risk_notes else None, ) self.db.add(binding) await self.db.commit() await self.db.refresh(binding) return binding async def _detect_capability_gaps( self, truth_node_id: int, model_version_id: int, evaluation: Dict[str, Any], tenant_id: int, ) -> List[Dict[str, Any]]: """检测能力差距并创建预警记录""" gaps = [] # 检查兼容性评分 compatibility = evaluation.get("compatibility_score", 100) if compatibility < 60: gap = await self._create_capability_gap( truth_node_id=truth_node_id, model_version_id=model_version_id, gap_type="capability", severity="critical" if compatibility < 40 else "high", description=f"模型兼容性评分仅 {compatibility}/100,可能无法满足需求", ai_analysis={"compatibility_score": compatibility}, tenant_id=tenant_id, ) gaps.append(gap) # 检查成本风险 cost_estimate = evaluation.get("cost_estimate_usd", 0) if cost_estimate > 100: # 日成本超过 $100 视为高风险 gap = await self._create_capability_gap( truth_node_id=truth_node_id, model_version_id=model_version_id, gap_type="cost", severity="high" if cost_estimate > 500 else "medium", description=f"预估日成本 ${cost_estimate:.2f},超出常规范围", ai_analysis={"cost_estimate_usd": cost_estimate}, tenant_id=tenant_id, ) gaps.append(gap) # 检查风险提示 risk_hints = evaluation.get("risk_hints", []) if risk_hints: gap = await self._create_capability_gap( truth_node_id=truth_node_id, model_version_id=model_version_id, gap_type="risk", severity="medium", description="检测到潜在风险", ai_analysis={"risk_hints": risk_hints}, tenant_id=tenant_id, ) gaps.append(gap) return gaps async def _create_capability_gap( self, truth_node_id: int, model_version_id: int, gap_type: str, severity: str, description: str, ai_analysis: Dict[str, Any], tenant_id: int, ) -> Dict[str, Any]: """创建能力差距记录""" # 检查是否已存在相同类型的未解决差距 existing = await self.db.execute( select(ModelCapabilityGap).where( and_( ModelCapabilityGap.truth_node_id == truth_node_id, ModelCapabilityGap.model_version_id == model_version_id, ModelCapabilityGap.gap_type == gap_type, ModelCapabilityGap.status.in_(["open", "acknowledged"]), ) ) ) existing_gap = existing.scalar_one_or_none() if existing_gap: # 更新现有差距 existing_gap.description = description existing_gap.ai_analysis = ai_analysis existing_gap.severity = severity await self.db.commit() await self.db.refresh(existing_gap) return { "id": existing_gap.id, "gap_type": existing_gap.gap_type, "severity": existing_gap.severity, "description": existing_gap.description, "status": existing_gap.status, } else: # 创建新差距 gap = ModelCapabilityGap( tenant_id=tenant_id, model_version_id=model_version_id, truth_node_id=truth_node_id, gap_type=gap_type, severity=severity, description=description, ai_analysis=ai_analysis, status="open", ) self.db.add(gap) await self.db.commit() await self.db.refresh(gap) return { "id": gap.id, "gap_type": gap.gap_type, "severity": gap.severity, "description": gap.description, "status": gap.status, }