"""Execution policy brain for AI PMO decision runtime.""" from __future__ import annotations import os from .shared import ( CanonicalIntent, INTENT_QUERY, INTENT_TASK_CREATE, INTENT_SIGNAL_CREATE, INTENT_SIGNAL_SOURCE_ONBOARD, re, ) class ExecutionPolicyBrain: """自动执行边界。""" def __init__(self, *, policy_gate_rules: tuple[tuple[str, tuple[str, ...]], ...]) -> None: self.policy_gate_rules = policy_gate_rules def evaluate( self, *, message_text: str, world_model: dict[str, object], intent: CanonicalIntent, simulation: dict[str, object], ) -> dict[str, object]: warn_threshold = self._env_float("CAPACITY_WARN_THRESHOLD", 1.0) block_threshold = self._env_float("CAPACITY_BLOCK_THRESHOLD", 1.5) if block_threshold < warn_threshold: block_threshold = warn_threshold projected_capacity_ratio = float(simulation.get("projected_capacity_peak_ratio") or 0.0) capacity_warning: dict[str, object] | None = None normalized = re.sub(r"\s+", "", str(message_text or "")).lower() for gate_code, tokens in self.policy_gate_rules: if any(token.lower().replace(" ", "") in normalized for token in tokens): return { "auto_execute": False, "policy_gate_hit": True, "policy_gate_code": gate_code, "approval_requirement": { "required": True, "reason": gate_code, "message": "命中硬策略边界,必须进入显式审批/人工确认。", }, } if intent.name == INTENT_SIGNAL_SOURCE_ONBOARD and intent.project_id is None: return { "auto_execute": False, "policy_gate_hit": True, "policy_gate_code": "missing_project_scope", "approval_requirement": { "required": True, "reason": "missing_project_scope", "message": "信号源接入缺少项目归属,必须先确认项目。", }, } if intent.name in {INTENT_QUERY, INTENT_TASK_CREATE, INTENT_SIGNAL_CREATE}: return { "auto_execute": True, "policy_gate_hit": False, "policy_gate_code": None, "approval_requirement": {"required": False}, } budget = world_model.get("budget") if isinstance(world_model.get("budget"), dict) else {} if bool(budget.get("hard_limit_enabled")) and float(budget.get("usage_ratio") or 0.0) >= 1.0: return { "auto_execute": False, "policy_gate_hit": True, "policy_gate_code": "budget_hard_limit", "approval_requirement": { "required": True, "reason": "budget_hard_limit", "message": "项目预算已触达硬限制,自动执行被拦截。", }, } if float(simulation.get("projected_budget_usage_ratio") or 0.0) >= 1.0: return { "auto_execute": False, "policy_gate_hit": True, "policy_gate_code": "simulation_budget_limit", "approval_requirement": { "required": True, "reason": "simulation_budget_limit", "message": "执行前仿真显示预算将触顶,自动执行被拦截。", }, } if projected_capacity_ratio >= block_threshold: return { "auto_execute": False, "policy_gate_hit": True, "policy_gate_code": "simulation_capacity_limit", "approval_requirement": { "required": True, "reason": "simulation_capacity_limit", "message": "执行前仿真显示产能严重超载,自动执行被拦截。", }, } if projected_capacity_ratio >= warn_threshold: capacity_warning = { "enabled": True, "severity": "warning", "peak_ratio": round(projected_capacity_ratio, 4), "warn_threshold": round(warn_threshold, 4), "block_threshold": round(block_threshold, 4), } if intent.name == INTENT_SIGNAL_SOURCE_ONBOARD and int(simulation.get("missing_skill_count") or 0) > 0: return { "auto_execute": False, "policy_gate_hit": True, "policy_gate_code": "simulation_skill_gap", "approval_requirement": { "required": True, "reason": "simulation_skill_gap", "message": "执行前仿真显示当前项目存在技能缺口,信号源接入先进入待确认。", }, } if intent.name in {INTENT_SIGNAL_SOURCE_ONBOARD, INTENT_SIGNAL_CREATE} and str(simulation.get("dependency_pressure") or "") == "high": return { "auto_execute": False, "policy_gate_hit": True, "policy_gate_code": "simulation_dependency_pressure", "approval_requirement": { "required": True, "reason": "simulation_dependency_pressure", "message": "执行前仿真显示关键依赖压力过高,当前先进入待人工确认。", }, } return { "auto_execute": True, "policy_gate_hit": False, "policy_gate_code": None, "approval_requirement": {"required": False}, "capacity_warning": capacity_warning, } @staticmethod def _env_float(name: str, default: float) -> float: raw = str(os.getenv(name, "")).strip() if not raw: return float(default) try: return float(raw) except (TypeError, ValueError): return float(default)