from __future__ import annotations from sqlalchemy import select from app.core.agent_enums import ( AgentAssetContentType, AgentAssetDomain, AgentAssetStatus, AgentAssetType, AgentName, ) from app.models.agent_asset import AgentAsset from app.services.agent_foundation_constants import ( DIGITAL_EMPLOYEE_PROFILE_SCAN_TASK_CODE, DIGITAL_EMPLOYEE_RISK_GRAPH_SCAN_TASK_CODE, DIGITAL_EMPLOYEE_RULE_DISCOVERY_TASK_CODE, DIGITAL_EMPLOYEE_SKILL_CATEGORIES, ) class AgentFoundationDigitalEmployeeTaskMixin: def _runtime_digital_employee_task_specs(self) -> tuple[dict[str, object], ...]: return ( { "code": DIGITAL_EMPLOYEE_RISK_GRAPH_SCAN_TASK_CODE, "name": "财务风险图谱巡检", "description": "按计划扫描报销单、票据、审批链、员工画像和规则命中结果,生成风险观察与可复核证据链。", "scenario_json": ["schedule", "expense", "risk_graph", "risk_observation"], "owner": "风控与审计部", "reviewer": "顾承宇", "cron": "0 9 * * *", "skill_category": "评估", "markdown": self._financial_risk_graph_scan_skill_markdown, "change_note": "初始化财务风险图谱巡检能力。", "config": { "skill_name": "financial-risk-graph-scanner", "scan_scope": [ "expense_claims", "invoices", "approval_chain", "employee_profiles", "risk_rules", ], "output_format": "risk_observation_report", "writes_risk_observations": True, }, }, { "code": DIGITAL_EMPLOYEE_PROFILE_SCAN_TASK_CODE, "name": "员工行为画像巡检", "description": "按计划更新员工费用行为、材料完整性、审批效率和智能协作画像,为风险图谱提供画像基线。", "scenario_json": ["schedule", "employee_profile", "baseline", "risk_graph"], "owner": "风控与审计部", "reviewer": "顾承宇", "cron": "30 8 * * 1", "skill_category": "评估", "markdown": self._employee_behavior_profile_scan_skill_markdown, "change_note": "初始化员工行为画像巡检能力。", "config": { "skill_name": "employee-behavior-profile-scanner", "profile_dimensions": [ "expense_intensity", "material_completeness", "approval_efficiency", "ai_collaboration", ], "output_format": "employee_behavior_profile_snapshot", "writes_profile_snapshots": True, }, }, { "code": DIGITAL_EMPLOYEE_RULE_DISCOVERY_TASK_CODE, "name": "风险规则候选发现", "description": "按计划复盘风险观察和人工反馈,生成带证据、来源和置信度的候选规则,不直接上线。", "scenario_json": ["schedule", "risk_observation", "feedback", "rule_candidate"], "owner": "风控与审计部", "reviewer": "顾承宇", "cron": "0 10 * * 1", "skill_category": "升级", "markdown": self._risk_rule_discovery_skill_markdown, "change_note": "初始化风险规则候选发现能力。", "config": { "skill_name": "risk-rule-discovery", "input_sources": [ "risk_observations", "risk_observation_feedback", "algorithm_replay_sets", ], "output_format": "candidate_risk_rules", "auto_publish": False, }, }, ) def _upsert_runtime_digital_employee_tasks(self, existing_codes: set[str]) -> None: for spec in self._runtime_digital_employee_task_specs(): self._upsert_runtime_digital_employee_task(existing_codes, spec) def _upsert_runtime_digital_employee_task( self, existing_codes: set[str], spec: dict[str, object], ) -> None: code = str(spec["code"]) config = self._build_runtime_digital_employee_config(spec) if code not in existing_codes: asset = self._create_seed_asset( asset_type=AgentAssetType.TASK.value, code=code, name=str(spec["name"]), description=str(spec["description"]), domain=AgentAssetDomain.SYSTEM.value, scenario_json=list(spec["scenario_json"]), owner=str(spec["owner"]), reviewer=str(spec["reviewer"]), status=AgentAssetStatus.ACTIVE.value, current_version="v1.0.0", config_json=config, ) else: asset = self.db.scalar(select(AgentAsset).where(AgentAsset.code == code)) if asset is None: return self._refresh_runtime_digital_employee_asset(asset, spec) markdown_builder = spec["markdown"] if not callable(markdown_builder): return self._ensure_asset_version( asset, version="v1.0.0", content=markdown_builder(), content_type=AgentAssetContentType.MARKDOWN.value, change_note=str(spec["change_note"]), created_by="系统初始化", ) def _build_runtime_digital_employee_config( self, spec: dict[str, object], *, existing_config: dict[str, object] | None = None, ) -> dict[str, object]: code = str(spec["code"]) cron = str(spec["cron"]) base = { **self._digital_employee_task_config(code, cron), "schedule": cron, "cron_expression": cron, **dict(spec["config"]), } if not existing_config: return base existing_cron = ( existing_config.get("cron") or existing_config.get("schedule") or existing_config.get("cron_expression") ) schedule_config = ( {"cron": existing_cron, "schedule": existing_cron, "cron_expression": existing_cron} if existing_cron else {} ) return { **existing_config, "agent": AgentName.HERMES.value, "task_type": code.replace("task.hermes.", "").replace(".", "_"), "skill_category": str(spec["skill_category"]), "skill_category_options": list(DIGITAL_EMPLOYEE_SKILL_CATEGORIES), **dict(spec["config"]), **schedule_config, } def _refresh_runtime_digital_employee_asset( self, asset: AgentAsset, spec: dict[str, object], ) -> None: asset.name = str(spec["name"]) asset.description = str(spec["description"]) asset.owner = str(spec["owner"]) asset.reviewer = str(spec["reviewer"]) asset.domain = AgentAssetDomain.SYSTEM.value asset.scenario_json = list(spec["scenario_json"]) if not str(asset.status or "").strip(): asset.status = AgentAssetStatus.ACTIVE.value if not str(asset.current_version or "").strip(): asset.current_version = "v1.0.0" if not str(asset.working_version or "").strip(): asset.working_version = asset.current_version asset.config_json = self._build_runtime_digital_employee_config( spec, existing_config=dict(asset.config_json or {}), ) self.db.add(asset)