"""AI PMO decision brains (strategic/tactical/simulation/policy).""" from __future__ import annotations import re from typing import Any from .ai_pmo_intent_routing import ( INTENT_FOLLOWUP_CONFIRM, INTENT_FOLLOWUP_CONTINUE, INTENT_PROJECT_CREATE, INTENT_QUERY, INTENT_SIGNAL_CREATE, INTENT_SIGNAL_SOURCE_ONBOARD, ROUTE_PROJECT_SIGNAL_CANCEL, ROUTE_PROJECT_SIGNAL_COMMIT, ROUTE_PROJECT_SIGNAL_DISCOVER, ROUTE_PROJECT_SIGNAL_REFRESH, CanonicalIntent, ) def _normalize_workload_ratio(value: Any) -> float: try: ratio = float(value or 0.0) except (TypeError, ValueError): return 0.0 if ratio <= 0: return 0.0 return ratio / 100.0 if ratio > 1.0 else ratio class StrategicBrain: """组合级判断。""" @staticmethod def evaluate(*, intent: CanonicalIntent, world_model: dict[str, Any], message_text: str) -> dict[str, Any]: project = world_model.get("project") if isinstance(world_model.get("project"), dict) else None budget = world_model.get("budget") if isinstance(world_model.get("budget"), dict) else {} capacity = world_model.get("capacity") if isinstance(world_model.get("capacity"), list) else [] risk_count = int((world_model.get("risk_summary") or {}).get("open_risk_count") or 0) active_projects = int((world_model.get("portfolio") or {}).get("active_project_count") or 0) budget_ratio = float(budget.get("usage_ratio") or 0.0) overloaded_people = [row for row in capacity if _normalize_workload_ratio(row.get("workload_ratio")) >= 0.85] risk_score = min(1.0, 0.18 + risk_count * 0.08 + budget_ratio * 0.25 + len(overloaded_people) * 0.1) if intent.name == INTENT_PROJECT_CREATE: risk_score = max(risk_score, 0.62) if intent.name == INTENT_SIGNAL_SOURCE_ONBOARD: risk_score = max(risk_score, 0.58) return { "decision_level": "project" if project else "portfolio", "decision_object_type": "project" if project else "portfolio", "decision_object_id": _safe_int((project or {}).get("id")), "risk_score": round(risk_score, 3), "budget_impact": { "budget_id": budget.get("budget_id"), "usage_ratio": budget_ratio, "hard_limit_enabled": bool(budget.get("hard_limit_enabled")), }, "resource_impact": { "overloaded_user_ids": [row.get("user_id") for row in overloaded_people], "active_project_count": active_projects, }, "rationale": ( f"intent={intent.name}; " f"project_scope={'project' if project else 'portfolio'}; " f"budget_ratio={budget_ratio:.2f}; " f"open_risks={risk_count}; " f"overloaded={len(overloaded_people)}" ), "message_excerpt": message_text[:240], } class TacticalBrain: """周/日执行计划。""" @staticmethod def plan(*, intent: CanonicalIntent, world_model: dict[str, Any], decision: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]: project = world_model.get("project") if isinstance(world_model.get("project"), dict) else None project_ref = f"项目#{project['id']}" if project and project.get("id") is not None else "项目群" strategic_plan = { "priority": "high" if float(decision.get("risk_score") or 0.0) >= 0.6 else "medium", "objectives": [ f"围绕 {project_ref} 控制风险与节奏", "优先保证关键路径和资源边界不被破坏", ], "signals": [ f"budget_usage_ratio={float((decision.get('budget_impact') or {}).get('usage_ratio') or 0.0):.2f}", f"overloaded_users={len((decision.get('resource_impact') or {}).get('overloaded_user_ids') or [])}", ], } execution_actions = [] workflow_route = str((intent.metadata or {}).get("workflow_route") or "").strip().lower() if intent.name == INTENT_QUERY: execution_actions.append({"operation_code": "query.answer", "mode": "read_only"}) elif intent.name == INTENT_PROJECT_CREATE: execution_actions.append({"operation_code": "project.create", "mode": "write"}) elif intent.name == INTENT_SIGNAL_SOURCE_ONBOARD: execution_actions.append({"operation_code": "signal.enter", "mode": "write"}) execution_actions.append({"operation_code": "signal_source.onboard", "mode": "write"}) elif intent.name in {INTENT_FOLLOWUP_CONFIRM, INTENT_FOLLOWUP_CONTINUE}: execution_actions.append({"operation_code": "work_item.resume", "mode": "write"}) elif intent.name == INTENT_SIGNAL_CREATE: if workflow_route == ROUTE_PROJECT_SIGNAL_DISCOVER: execution_actions.append({"operation_code": ROUTE_PROJECT_SIGNAL_DISCOVER, "mode": "write"}) elif workflow_route == ROUTE_PROJECT_SIGNAL_COMMIT: execution_actions.append({"operation_code": ROUTE_PROJECT_SIGNAL_COMMIT, "mode": "write"}) elif workflow_route == ROUTE_PROJECT_SIGNAL_REFRESH: execution_actions.append({"operation_code": ROUTE_PROJECT_SIGNAL_REFRESH, "mode": "write"}) elif workflow_route == ROUTE_PROJECT_SIGNAL_CANCEL: execution_actions.append({"operation_code": ROUTE_PROJECT_SIGNAL_CANCEL, "mode": "write"}) else: execution_actions.append({"operation_code": "signal.enter", "mode": "write"}) execution_plan = { "intent": intent.name, "project_id": intent.project_id, "actions": execution_actions, "summary": f"执行 {intent.name},共 {len(execution_actions)} 个 typed actions", } return strategic_plan, execution_plan class SimulationEngine: """执行前仿真。""" BUDGET_COST_BY_OPERATION = { "query.answer": 1.0, "project.create": 120.0, "signal.enter": 8.0, "signal_source.onboard": 40.0, ROUTE_PROJECT_SIGNAL_DISCOVER: 3.0, ROUTE_PROJECT_SIGNAL_COMMIT: 8.0, ROUTE_PROJECT_SIGNAL_REFRESH: 3.0, ROUTE_PROJECT_SIGNAL_CANCEL: 0.2, } HOURS_BY_OPERATION = { "query.answer": 0.2, "project.create": 12.0, "signal.enter": 1.5, "signal_source.onboard": 6.0, ROUTE_PROJECT_SIGNAL_DISCOVER: 0.8, ROUTE_PROJECT_SIGNAL_COMMIT: 1.5, ROUTE_PROJECT_SIGNAL_REFRESH: 0.8, ROUTE_PROJECT_SIGNAL_CANCEL: 0.1, } @classmethod def simulate(cls, *, intent: CanonicalIntent, world_model: dict[str, Any], execution_plan: dict[str, Any]) -> dict[str, Any]: actions = execution_plan.get("actions") if isinstance(execution_plan.get("actions"), list) else [] operation_codes = [str(item.get("operation_code") or "").strip() for item in actions if isinstance(item, dict)] estimated_budget_delta = sum(cls.BUDGET_COST_BY_OPERATION.get(code, 0.0) for code in operation_codes) estimated_capacity_delta = sum(cls.HOURS_BY_OPERATION.get(code, 0.0) for code in operation_codes) budget = world_model.get("budget") if isinstance(world_model.get("budget"), dict) else {} budget_amount = float(budget.get("budget_amount_usd") or 0.0) current_ratio = float(budget.get("usage_ratio") or 0.0) projected_budget_ratio = current_ratio + (estimated_budget_delta / budget_amount if budget_amount > 0 else 0.0) capacity = world_model.get("capacity") if isinstance(world_model.get("capacity"), list) else [] current_peak_ratio = max([_normalize_workload_ratio(item.get("workload_ratio")) for item in capacity], default=0.0) project_member_count = max(1, int(world_model.get("member_count") or len(capacity) or 1)) projected_capacity_peak_ratio = current_peak_ratio + (estimated_capacity_delta / (40.0 * project_member_count)) task_summary = world_model.get("task_summary") if isinstance(world_model.get("task_summary"), dict) else {} blocked_count = int((task_summary.get("by_status") or {}).get("blocked") or 0) overdue_count = int(task_summary.get("overdue_count") or 0) missing_skill_tag_ids = task_summary.get("uncovered_skill_tag_ids") if isinstance(task_summary.get("uncovered_skill_tag_ids"), list) else [] charter = world_model.get("charter") if isinstance(world_model.get("charter"), list) else [] dependency_pressure = "high" if blocked_count > 0 or overdue_count > 0 else ("medium" if not charter else "low") projected_risk_score = min( 1.0, 0.12 + projected_budget_ratio * 0.24 + projected_capacity_peak_ratio * 0.28 + blocked_count * 0.08 + overdue_count * 0.05 + len(missing_skill_tag_ids) * 0.07, ) return { "intent": intent.name, "operation_codes": operation_codes, "estimated_budget_delta_usd": round(estimated_budget_delta, 2), "projected_budget_usage_ratio": round(projected_budget_ratio, 4), "estimated_capacity_delta_hours": round(estimated_capacity_delta, 2), "projected_capacity_peak_ratio": round(projected_capacity_peak_ratio, 4), "blocked_task_count": blocked_count, "overdue_task_count": overdue_count, "dependency_pressure": dependency_pressure, "missing_skill_tag_ids": missing_skill_tag_ids[:20], "missing_skill_count": len(missing_skill_tag_ids), "projected_risk_score": round(projected_risk_score, 4), } 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, Any], intent: CanonicalIntent, simulation: dict[str, Any], ) -> dict[str, Any]: 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_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 float(simulation.get("projected_capacity_peak_ratio") or 0.0) >= 1.0: return { "auto_execute": False, "policy_gate_hit": True, "policy_gate_code": "simulation_capacity_limit", "approval_requirement": { "required": True, "reason": "simulation_capacity_limit", "message": "执行前仿真显示产能会超载,自动执行被拦截。", }, } 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}, } class DecisionEngine: """汇总三脑输出。""" def __init__(self, *, policy_gate_rules: tuple[tuple[str, tuple[str, ...]], ...]) -> None: self.strategic_brain = StrategicBrain() self.tactical_brain = TacticalBrain() self.simulation_engine = SimulationEngine() self.execution_policy_brain = ExecutionPolicyBrain(policy_gate_rules=policy_gate_rules) def decide(self, *, intent: CanonicalIntent, world_model: dict[str, Any], message_text: str) -> dict[str, Any]: strategic = self.strategic_brain.evaluate(intent=intent, world_model=world_model, message_text=message_text) strategic_plan, execution_plan = self.tactical_brain.plan(intent=intent, world_model=world_model, decision=strategic) simulation = self.simulation_engine.simulate(intent=intent, world_model=world_model, execution_plan=execution_plan) strategic_plan["simulation"] = simulation execution_plan["simulation"] = simulation policy = self.execution_policy_brain.evaluate( message_text=message_text, world_model=world_model, intent=intent, simulation=simulation, ) strategic["risk_score"] = max(float(strategic.get("risk_score") or 0.0), float(simulation.get("projected_risk_score") or 0.0)) return { "strategic": strategic, "strategic_plan": strategic_plan, "execution_plan": execution_plan, "simulation": simulation, "policy": policy, } def _safe_int(value: Any) -> int | None: try: if value is None or value == "": return None return int(value) except (TypeError, ValueError): return None