157 lines
6.1 KiB
Python
157 lines
6.1 KiB
Python
"""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)
|